├── BUILD.md ├── LICENSE.md ├── README.txt ├── Snk.xcodeproj └── project.pbxproj ├── Snk ├── AppDelegate.swift ├── Base.lproj │ └── MainMenu.xib ├── Credits.rtf ├── GameVC.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── AppIcon128@2x.png │ │ ├── AppIcon16@2x.png │ │ ├── AppIcon32@2x.png │ │ └── Contents.json │ └── Contents.json ├── Info.plist ├── MainVC.swift ├── MenuVC.swift ├── MoCocoa.swift ├── MoCode.swift ├── Snk.entitlements ├── Snk.swift ├── SnkAudio.swift ├── SnkHoverButton.swift ├── SnkImageView.swift ├── SnkLevelButton.swift ├── SnkScoreLabel.swift ├── SnkTheme.swift ├── SplashVC.swift ├── _images │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── digitsbg.png │ ├── digitsfg.png │ ├── frames.png │ ├── mowglii.png │ ├── ok.png │ └── snk.png ├── _psd │ └── icon512.psd ├── _sketch │ ├── AppIcon.sketch │ ├── AppIcon2.sketch │ ├── AppIcon3.sketch │ ├── AppIcon4.sketch │ └── AppIconBigSur.sketch ├── _sounds │ ├── 3d.mp3 │ ├── bibo.mp3 │ ├── boop.mp3 │ ├── explosion.mp3 │ ├── fotbb.mp3 │ ├── gameover.mp3 │ ├── lance.mp3 │ ├── loop.mp3 │ ├── spin.mp3 │ ├── splash.mp3 │ ├── tada.mp3 │ ├── tick.mp3 │ └── woosh.mp3 └── _sparkle │ └── Sparkle.framework │ ├── Headers │ ├── Modules │ ├── PrivateHeaders │ ├── Resources │ ├── Sparkle │ └── Versions │ ├── A │ ├── Headers │ │ ├── SPUDownloadData.h │ │ ├── SPUDownloader.h │ │ ├── SPUDownloaderDelegate.h │ │ ├── SPUDownloaderProtocol.h │ │ ├── SPUDownloaderSession.h │ │ ├── SPUURLRequest.h │ │ ├── SUAppcast.h │ │ ├── SUAppcastItem.h │ │ ├── SUCodeSigningVerifier.h │ │ ├── SUErrors.h │ │ ├── SUExport.h │ │ ├── SUStandardVersionComparator.h │ │ ├── SUUpdater.h │ │ ├── SUUpdaterDelegate.h │ │ ├── SUVersionComparisonProtocol.h │ │ ├── SUVersionDisplayProtocol.h │ │ └── Sparkle.h │ ├── Modules │ │ └── module.modulemap │ ├── PrivateHeaders │ │ └── SUUnarchiver.h │ ├── Resources │ │ ├── Autoupdate.app │ │ │ └── Contents │ │ │ │ ├── Info.plist │ │ │ │ ├── MacOS │ │ │ │ ├── Autoupdate │ │ │ │ └── fileop │ │ │ │ ├── PkgInfo │ │ │ │ └── Resources │ │ │ │ ├── AppIcon.icns │ │ │ │ ├── Base.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── SUStatus.nib │ │ │ │ ├── de.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── es.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── fr.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ ├── it.lproj │ │ │ │ └── Sparkle.strings │ │ │ │ └── zh_CN.lproj │ │ │ │ └── Sparkle.strings │ │ ├── Base.lproj │ │ │ ├── SUAutomaticUpdateAlert.nib │ │ │ ├── SUUpdateAlert.nib │ │ │ ├── SUUpdatePermissionPrompt.nib │ │ │ │ ├── keyedobjects-110000.nib │ │ │ │ └── keyedobjects.nib │ │ │ └── Sparkle.strings │ │ ├── Info.plist │ │ ├── ReleaseNotesColorStyle.css │ │ ├── SUModelTranslation.plist │ │ ├── SUStatus.nib │ │ ├── de.lproj │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ ├── SUUpdateAlert.strings │ │ │ ├── SUUpdatePermissionPrompt.strings │ │ │ └── Sparkle.strings │ │ ├── en.lproj │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ ├── SUUpdateAlert.strings │ │ │ └── SUUpdatePermissionPrompt.strings │ │ ├── es.lproj │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ ├── SUUpdateAlert.strings │ │ │ ├── SUUpdatePermissionPrompt.strings │ │ │ └── Sparkle.strings │ │ ├── fr.lproj │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ ├── SUUpdateAlert.strings │ │ │ ├── SUUpdatePermissionPrompt.strings │ │ │ └── Sparkle.strings │ │ ├── it.lproj │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ ├── SUUpdateAlert.strings │ │ │ ├── SUUpdatePermissionPrompt.strings │ │ │ └── Sparkle.strings │ │ └── zh_CN.lproj │ │ │ ├── SUAutomaticUpdateAlert.strings │ │ │ ├── SUUpdateAlert.strings │ │ │ ├── SUUpdatePermissionPrompt.strings │ │ │ └── Sparkle.strings │ └── Sparkle │ └── Current └── make_zips_and_appcast.sh /BUILD.md: -------------------------------------------------------------------------------- 1 | ### Build and Notarize 2 | 3 | 1. In Xcode, choose Product > Archive 4 | 5 | 2. In Xcode Organizer, choose Distribute App and follow flow 6 | for uploading Developer ID app for notarization. The default 7 | selections should be ok. 8 | 9 | 3. Wait for notification that app is successfully notarized. 10 | 11 | 4. Export notarized app to the Desktop. 12 | 13 | 5. Run ./make_zips_and_appcast.sh 14 | 15 | ### Resources 16 | 17 | [Notarizing Your App Before Distribution](https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution?language=objc) 18 | 19 | [Customizing the Notarization Workflow](https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow?language=objc) 20 | 21 | [Resolving Common Notarization Issues](https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/resolving_common_notarization_issues?language=objc) 22 | 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 mowglii.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | 2 | @@@@@@@@@@ @@@@ 3 | @@@@@@@@@@@@ @@@@ @@@@ @@@@ @@@@ 4 | @@@@ @@@@ @@@@@@ @@@@ @@@@ @@@@ 5 | @@@@ @@@@@@@@@@@@ @@@@@@@@@@@@ 6 | @@@@@@@@@@@@ @@@@ @@@@@@ @@@@@@@@ 7 | @@@@ @@@@ @@@@ @@@@@@@@@@@@ 8 | @@@@@@@@@@@@ @@@@ @@@@ @@@@ @@@@ 9 | @@@@ @@@@@@@@@@ 10 | 11 | 12 | Snk is a Swift implementation of the classic game Snake for macOS 10.10+. 13 | 14 | Snk is written in Swift 5 and requires Xcode 10.2+. 15 | 16 | This app uses the Sparkle framework which can be found here: 17 | http://sparkle-project.org/ 18 | 19 | Keyboard Controls 20 | ================= 21 | 22 | Choose level 1, 2 or 3 23 | Move snake Arrows or WASD 24 | Game Over 25 | OK Space 26 | Play again 1, 2 or 3 27 | 28 | Music Credits 29 | ============= 30 | 31 | - 8-Bit Melody 32 | http://www.looperman.com/loops/detail/68575/8-bit-melody-by-kinggjmaytryx-free-156bpm-electro-sh-loop 33 | by KingGJMaytryx 34 | 35 | - Bibo 36 | https://soundcloud.com/lv-7 37 | by Yo, L 38 | 39 | - Flight of the Bumblebee 40 | https://www.youtube.com/watch?v=wOFgh2IdnZI 41 | by 8BitUzz 42 | -------------------------------------------------------------------------------- /Snk.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9202554F1B14FD3B007E21E3 /* MoCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9202554E1B14FD3B007E21E3 /* MoCode.swift */; }; 11 | 92175E8F1B14F3BD007151EA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92175E8E1B14F3BD007151EA /* AppDelegate.swift */; }; 12 | 92175E911B14F3BD007151EA /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92175E901B14F3BD007151EA /* Images.xcassets */; }; 13 | 92175E941B14F3BD007151EA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 92175E921B14F3BD007151EA /* MainMenu.xib */; }; 14 | 92175EAA1B14F598007151EA /* Snk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92175EA91B14F598007151EA /* Snk.swift */; }; 15 | 92175EAC1B14F7C0007151EA /* MainVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92175EAB1B14F7C0007151EA /* MainVC.swift */; }; 16 | 922FB6F626882ECF0070C1F3 /* loop.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 922FB6F526882ECE0070C1F3 /* loop.mp3 */; }; 17 | 92664BE91B2CE93900784BC4 /* mowglii.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664BE71B2CE93800784BC4 /* mowglii.png */; }; 18 | 92664BEA1B2CE93900784BC4 /* snk.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664BE81B2CE93900784BC4 /* snk.png */; }; 19 | 92664BED1B2D279000784BC4 /* MenuVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92664BEC1B2D279000784BC4 /* MenuVC.swift */; }; 20 | 92664BEF1B2E9C2F00784BC4 /* SnkHoverButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92664BEE1B2E9C2F00784BC4 /* SnkHoverButton.swift */; }; 21 | 92664BF11B2F48B300784BC4 /* SnkLevelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92664BF01B2F48B300784BC4 /* SnkLevelButton.swift */; }; 22 | 92664C071B2FE2B800784BC4 /* 1.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C001B2FE2B800784BC4 /* 1.png */; }; 23 | 92664C081B2FE2B800784BC4 /* 2.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C011B2FE2B800784BC4 /* 2.png */; }; 24 | 92664C091B2FE2B800784BC4 /* 3.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C021B2FE2B800784BC4 /* 3.png */; }; 25 | 92664C0A1B2FE2B800784BC4 /* digitsfg.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C031B2FE2B800784BC4 /* digitsfg.png */; }; 26 | 92664C0B1B2FE2B800784BC4 /* digitsbg.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C041B2FE2B800784BC4 /* digitsbg.png */; }; 27 | 92664C0C1B2FE2B800784BC4 /* frames.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C051B2FE2B800784BC4 /* frames.png */; }; 28 | 92664C0D1B2FE2B800784BC4 /* ok.png in Resources */ = {isa = PBXBuildFile; fileRef = 92664C061B2FE2B800784BC4 /* ok.png */; }; 29 | 92664C0F1B30996900784BC4 /* SnkScoreLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92664C0E1B30996800784BC4 /* SnkScoreLabel.swift */; }; 30 | 92664C111B32292C00784BC4 /* GameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92664C101B32292C00784BC4 /* GameVC.swift */; }; 31 | 92726CE61B33DB4A00B944FD /* SnkAudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92726CE51B33DB4A00B944FD /* SnkAudio.swift */; }; 32 | 92726CF31B33DDB600B944FD /* 3d.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CE71B33DDB600B944FD /* 3d.mp3 */; }; 33 | 92726CF41B33DDB600B944FD /* bibo.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CE81B33DDB600B944FD /* bibo.mp3 */; }; 34 | 92726CF51B33DDB600B944FD /* boop.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CE91B33DDB600B944FD /* boop.mp3 */; }; 35 | 92726CF61B33DDB600B944FD /* explosion.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CEA1B33DDB600B944FD /* explosion.mp3 */; }; 36 | 92726CF71B33DDB600B944FD /* fotbb.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CEB1B33DDB600B944FD /* fotbb.mp3 */; }; 37 | 92726CF81B33DDB600B944FD /* gameover.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CEC1B33DDB600B944FD /* gameover.mp3 */; }; 38 | 92726CF91B33DDB600B944FD /* lance.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CED1B33DDB600B944FD /* lance.mp3 */; }; 39 | 92726CFB1B33DDB600B944FD /* spin.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CEF1B33DDB600B944FD /* spin.mp3 */; }; 40 | 92726CFC1B33DDB600B944FD /* splash.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CF01B33DDB600B944FD /* splash.mp3 */; }; 41 | 92726CFD1B33DDB600B944FD /* tick.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CF11B33DDB600B944FD /* tick.mp3 */; }; 42 | 92726CFE1B33DDB600B944FD /* woosh.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 92726CF21B33DDB600B944FD /* woosh.mp3 */; }; 43 | 928993031BDEE9D4008C0DEA /* tada.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 928993021BDEE9D4008C0DEA /* tada.mp3 */; }; 44 | 9294D1611B38CAC80030C8D9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9294D1601B38CAC80030C8D9 /* Sparkle.framework */; }; 45 | 9294D1621B38CAD10030C8D9 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9294D1601B38CAC80030C8D9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 46 | 92ACF31A1B2CB7AB007494A6 /* SnkImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ACF3191B2CB7AB007494A6 /* SnkImageView.swift */; }; 47 | 92C9D5821D83CEE5002B966F /* SnkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92C9D5811D83CEE5002B966F /* SnkTheme.swift */; }; 48 | 92EF54D11B242A3800A32629 /* SplashVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92EF54D01B242A3800A32629 /* SplashVC.swift */; }; 49 | 92FB2D431B3A309900D7774A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 92FB2D421B3A309900D7774A /* Credits.rtf */; }; 50 | 92FCC0161B2B4B6A00F4BF7E /* MoCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCC0151B2B4B6A00F4BF7E /* MoCocoa.swift */; }; 51 | /* End PBXBuildFile section */ 52 | 53 | /* Begin PBXCopyFilesBuildPhase section */ 54 | 9294D15E1B38C74E0030C8D9 /* CopyFiles */ = { 55 | isa = PBXCopyFilesBuildPhase; 56 | buildActionMask = 2147483647; 57 | dstPath = ""; 58 | dstSubfolderSpec = 10; 59 | files = ( 60 | 9294D1621B38CAD10030C8D9 /* Sparkle.framework in CopyFiles */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXCopyFilesBuildPhase section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 9202554E1B14FD3B007E21E3 /* MoCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoCode.swift; sourceTree = ""; }; 68 | 92175E891B14F3BD007151EA /* Snk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Snk.app; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 92175E8D1B14F3BD007151EA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 92175E8E1B14F3BD007151EA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 71 | 92175E901B14F3BD007151EA /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 72 | 92175E931B14F3BD007151EA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 73 | 92175EA91B14F598007151EA /* Snk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Snk.swift; sourceTree = ""; }; 74 | 92175EAB1B14F7C0007151EA /* MainVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainVC.swift; sourceTree = ""; }; 75 | 922FB6F526882ECE0070C1F3 /* loop.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = loop.mp3; path = _sounds/loop.mp3; sourceTree = ""; }; 76 | 92664BE71B2CE93800784BC4 /* mowglii.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = mowglii.png; path = _images/mowglii.png; sourceTree = ""; }; 77 | 92664BE81B2CE93900784BC4 /* snk.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = snk.png; path = _images/snk.png; sourceTree = ""; }; 78 | 92664BEC1B2D279000784BC4 /* MenuVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuVC.swift; sourceTree = ""; }; 79 | 92664BEE1B2E9C2F00784BC4 /* SnkHoverButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnkHoverButton.swift; sourceTree = ""; }; 80 | 92664BF01B2F48B300784BC4 /* SnkLevelButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnkLevelButton.swift; sourceTree = ""; }; 81 | 92664C001B2FE2B800784BC4 /* 1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = 1.png; path = _images/1.png; sourceTree = ""; }; 82 | 92664C011B2FE2B800784BC4 /* 2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = 2.png; path = _images/2.png; sourceTree = ""; }; 83 | 92664C021B2FE2B800784BC4 /* 3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = 3.png; path = _images/3.png; sourceTree = ""; }; 84 | 92664C031B2FE2B800784BC4 /* digitsfg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = digitsfg.png; path = _images/digitsfg.png; sourceTree = ""; }; 85 | 92664C041B2FE2B800784BC4 /* digitsbg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = digitsbg.png; path = _images/digitsbg.png; sourceTree = ""; }; 86 | 92664C051B2FE2B800784BC4 /* frames.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = frames.png; path = _images/frames.png; sourceTree = ""; }; 87 | 92664C061B2FE2B800784BC4 /* ok.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ok.png; path = _images/ok.png; sourceTree = ""; }; 88 | 92664C0E1B30996800784BC4 /* SnkScoreLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnkScoreLabel.swift; sourceTree = ""; }; 89 | 92664C101B32292C00784BC4 /* GameVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameVC.swift; sourceTree = ""; }; 90 | 92726CE51B33DB4A00B944FD /* SnkAudio.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnkAudio.swift; sourceTree = ""; }; 91 | 92726CE71B33DDB600B944FD /* 3d.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = 3d.mp3; path = _sounds/3d.mp3; sourceTree = ""; }; 92 | 92726CE81B33DDB600B944FD /* bibo.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = bibo.mp3; path = _sounds/bibo.mp3; sourceTree = ""; }; 93 | 92726CE91B33DDB600B944FD /* boop.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = boop.mp3; path = _sounds/boop.mp3; sourceTree = ""; }; 94 | 92726CEA1B33DDB600B944FD /* explosion.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = explosion.mp3; path = _sounds/explosion.mp3; sourceTree = ""; }; 95 | 92726CEB1B33DDB600B944FD /* fotbb.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = fotbb.mp3; path = _sounds/fotbb.mp3; sourceTree = ""; }; 96 | 92726CEC1B33DDB600B944FD /* gameover.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = gameover.mp3; path = _sounds/gameover.mp3; sourceTree = ""; }; 97 | 92726CED1B33DDB600B944FD /* lance.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = lance.mp3; path = _sounds/lance.mp3; sourceTree = ""; }; 98 | 92726CEF1B33DDB600B944FD /* spin.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = spin.mp3; path = _sounds/spin.mp3; sourceTree = ""; }; 99 | 92726CF01B33DDB600B944FD /* splash.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = splash.mp3; path = _sounds/splash.mp3; sourceTree = ""; }; 100 | 92726CF11B33DDB600B944FD /* tick.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = tick.mp3; path = _sounds/tick.mp3; sourceTree = ""; }; 101 | 92726CF21B33DDB600B944FD /* woosh.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = woosh.mp3; path = _sounds/woosh.mp3; sourceTree = ""; }; 102 | 928993021BDEE9D4008C0DEA /* tada.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = tada.mp3; path = _sounds/tada.mp3; sourceTree = ""; }; 103 | 9294D1601B38CAC80030C8D9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = _sparkle/Sparkle.framework; sourceTree = ""; }; 104 | 92ACF3191B2CB7AB007494A6 /* SnkImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnkImageView.swift; sourceTree = ""; }; 105 | 92C9D5811D83CEE5002B966F /* SnkTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnkTheme.swift; sourceTree = ""; }; 106 | 92EF54D01B242A3800A32629 /* SplashVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashVC.swift; sourceTree = ""; }; 107 | 92FACE12225AFD7C00B9EB06 /* Snk.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Snk.entitlements; sourceTree = ""; }; 108 | 92FB2D421B3A309900D7774A /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 109 | 92FCC0151B2B4B6A00F4BF7E /* MoCocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoCocoa.swift; sourceTree = ""; }; 110 | /* End PBXFileReference section */ 111 | 112 | /* Begin PBXFrameworksBuildPhase section */ 113 | 92175E861B14F3BD007151EA /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 9294D1611B38CAC80030C8D9 /* Sparkle.framework in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | 92175E801B14F3BD007151EA = { 125 | isa = PBXGroup; 126 | children = ( 127 | 92175E8B1B14F3BD007151EA /* Snk */, 128 | 92175E8A1B14F3BD007151EA /* Products */, 129 | ); 130 | sourceTree = ""; 131 | }; 132 | 92175E8A1B14F3BD007151EA /* Products */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 92175E891B14F3BD007151EA /* Snk.app */, 136 | ); 137 | name = Products; 138 | sourceTree = ""; 139 | }; 140 | 92175E8B1B14F3BD007151EA /* Snk */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 92FACE12225AFD7C00B9EB06 /* Snk.entitlements */, 144 | 9294D15A1B38C6BE0030C8D9 /* Sparkle */, 145 | 92726CFF1B33DDBD00B944FD /* Sounds */, 146 | 92664BEB1B2CE94100784BC4 /* Images */, 147 | 92175E8C1B14F3BD007151EA /* Supporting Files */, 148 | 92175E901B14F3BD007151EA /* Images.xcassets */, 149 | 92175E921B14F3BD007151EA /* MainMenu.xib */, 150 | 92FCC0151B2B4B6A00F4BF7E /* MoCocoa.swift */, 151 | 9202554E1B14FD3B007E21E3 /* MoCode.swift */, 152 | 92175EA91B14F598007151EA /* Snk.swift */, 153 | 92726CE51B33DB4A00B944FD /* SnkAudio.swift */, 154 | 92ACF3191B2CB7AB007494A6 /* SnkImageView.swift */, 155 | 92664C0E1B30996800784BC4 /* SnkScoreLabel.swift */, 156 | 92664BEE1B2E9C2F00784BC4 /* SnkHoverButton.swift */, 157 | 92664BF01B2F48B300784BC4 /* SnkLevelButton.swift */, 158 | 92C9D5811D83CEE5002B966F /* SnkTheme.swift */, 159 | 92175E8E1B14F3BD007151EA /* AppDelegate.swift */, 160 | 92175EAB1B14F7C0007151EA /* MainVC.swift */, 161 | 92EF54D01B242A3800A32629 /* SplashVC.swift */, 162 | 92664BEC1B2D279000784BC4 /* MenuVC.swift */, 163 | 92664C101B32292C00784BC4 /* GameVC.swift */, 164 | ); 165 | path = Snk; 166 | sourceTree = ""; 167 | }; 168 | 92175E8C1B14F3BD007151EA /* Supporting Files */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 92175E8D1B14F3BD007151EA /* Info.plist */, 172 | 92FB2D421B3A309900D7774A /* Credits.rtf */, 173 | ); 174 | name = "Supporting Files"; 175 | sourceTree = ""; 176 | }; 177 | 92664BEB1B2CE94100784BC4 /* Images */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 92664C001B2FE2B800784BC4 /* 1.png */, 181 | 92664C011B2FE2B800784BC4 /* 2.png */, 182 | 92664C021B2FE2B800784BC4 /* 3.png */, 183 | 92664C031B2FE2B800784BC4 /* digitsfg.png */, 184 | 92664C041B2FE2B800784BC4 /* digitsbg.png */, 185 | 92664C051B2FE2B800784BC4 /* frames.png */, 186 | 92664C061B2FE2B800784BC4 /* ok.png */, 187 | 92664BE71B2CE93800784BC4 /* mowglii.png */, 188 | 92664BE81B2CE93900784BC4 /* snk.png */, 189 | ); 190 | name = Images; 191 | sourceTree = ""; 192 | }; 193 | 92726CFF1B33DDBD00B944FD /* Sounds */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 922FB6F526882ECE0070C1F3 /* loop.mp3 */, 197 | 928993021BDEE9D4008C0DEA /* tada.mp3 */, 198 | 92726CE71B33DDB600B944FD /* 3d.mp3 */, 199 | 92726CE81B33DDB600B944FD /* bibo.mp3 */, 200 | 92726CE91B33DDB600B944FD /* boop.mp3 */, 201 | 92726CEA1B33DDB600B944FD /* explosion.mp3 */, 202 | 92726CEB1B33DDB600B944FD /* fotbb.mp3 */, 203 | 92726CEC1B33DDB600B944FD /* gameover.mp3 */, 204 | 92726CED1B33DDB600B944FD /* lance.mp3 */, 205 | 92726CEF1B33DDB600B944FD /* spin.mp3 */, 206 | 92726CF01B33DDB600B944FD /* splash.mp3 */, 207 | 92726CF11B33DDB600B944FD /* tick.mp3 */, 208 | 92726CF21B33DDB600B944FD /* woosh.mp3 */, 209 | ); 210 | name = Sounds; 211 | sourceTree = ""; 212 | }; 213 | 9294D15A1B38C6BE0030C8D9 /* Sparkle */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | 9294D1601B38CAC80030C8D9 /* Sparkle.framework */, 217 | ); 218 | name = Sparkle; 219 | sourceTree = ""; 220 | }; 221 | /* End PBXGroup section */ 222 | 223 | /* Begin PBXNativeTarget section */ 224 | 92175E881B14F3BD007151EA /* Snk */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = 92175EA31B14F3BD007151EA /* Build configuration list for PBXNativeTarget "Snk" */; 227 | buildPhases = ( 228 | 92175E851B14F3BD007151EA /* Sources */, 229 | 92175E861B14F3BD007151EA /* Frameworks */, 230 | 92175E871B14F3BD007151EA /* Resources */, 231 | 9294D15E1B38C74E0030C8D9 /* CopyFiles */, 232 | 92FACE14225B09A200B9EB06 /* ShellScript */, 233 | ); 234 | buildRules = ( 235 | ); 236 | dependencies = ( 237 | ); 238 | name = Snk; 239 | productName = Squirm; 240 | productReference = 92175E891B14F3BD007151EA /* Snk.app */; 241 | productType = "com.apple.product-type.application"; 242 | }; 243 | /* End PBXNativeTarget section */ 244 | 245 | /* Begin PBXProject section */ 246 | 92175E811B14F3BD007151EA /* Project object */ = { 247 | isa = PBXProject; 248 | attributes = { 249 | LastSwiftUpdateCheck = 0700; 250 | LastUpgradeCheck = 1250; 251 | ORGANIZATIONNAME = mowglii.com; 252 | TargetAttributes = { 253 | 92175E881B14F3BD007151EA = { 254 | CreatedOnToolsVersion = 6.3.2; 255 | DevelopmentTeam = HFT3T55WND; 256 | LastSwiftMigration = 1020; 257 | ProvisioningStyle = Manual; 258 | SystemCapabilities = { 259 | com.apple.HardenedRuntime = { 260 | enabled = 1; 261 | }; 262 | }; 263 | }; 264 | }; 265 | }; 266 | buildConfigurationList = 92175E841B14F3BD007151EA /* Build configuration list for PBXProject "Snk" */; 267 | compatibilityVersion = "Xcode 3.2"; 268 | developmentRegion = en; 269 | hasScannedForEncodings = 0; 270 | knownRegions = ( 271 | en, 272 | Base, 273 | ); 274 | mainGroup = 92175E801B14F3BD007151EA; 275 | productRefGroup = 92175E8A1B14F3BD007151EA /* Products */; 276 | projectDirPath = ""; 277 | projectRoot = ""; 278 | targets = ( 279 | 92175E881B14F3BD007151EA /* Snk */, 280 | ); 281 | }; 282 | /* End PBXProject section */ 283 | 284 | /* Begin PBXResourcesBuildPhase section */ 285 | 92175E871B14F3BD007151EA /* Resources */ = { 286 | isa = PBXResourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | 92664C081B2FE2B800784BC4 /* 2.png in Resources */, 290 | 928993031BDEE9D4008C0DEA /* tada.mp3 in Resources */, 291 | 92664C0A1B2FE2B800784BC4 /* digitsfg.png in Resources */, 292 | 92726CF91B33DDB600B944FD /* lance.mp3 in Resources */, 293 | 92664BE91B2CE93900784BC4 /* mowglii.png in Resources */, 294 | 92726CFE1B33DDB600B944FD /* woosh.mp3 in Resources */, 295 | 92726CF31B33DDB600B944FD /* 3d.mp3 in Resources */, 296 | 92726CF71B33DDB600B944FD /* fotbb.mp3 in Resources */, 297 | 92726CFC1B33DDB600B944FD /* splash.mp3 in Resources */, 298 | 922FB6F626882ECF0070C1F3 /* loop.mp3 in Resources */, 299 | 92175E911B14F3BD007151EA /* Images.xcassets in Resources */, 300 | 92664C0B1B2FE2B800784BC4 /* digitsbg.png in Resources */, 301 | 92726CF41B33DDB600B944FD /* bibo.mp3 in Resources */, 302 | 92664C0C1B2FE2B800784BC4 /* frames.png in Resources */, 303 | 92726CF51B33DDB600B944FD /* boop.mp3 in Resources */, 304 | 92726CF61B33DDB600B944FD /* explosion.mp3 in Resources */, 305 | 92664C0D1B2FE2B800784BC4 /* ok.png in Resources */, 306 | 92664BEA1B2CE93900784BC4 /* snk.png in Resources */, 307 | 92726CF81B33DDB600B944FD /* gameover.mp3 in Resources */, 308 | 92726CFB1B33DDB600B944FD /* spin.mp3 in Resources */, 309 | 92FB2D431B3A309900D7774A /* Credits.rtf in Resources */, 310 | 92175E941B14F3BD007151EA /* MainMenu.xib in Resources */, 311 | 92726CFD1B33DDB600B944FD /* tick.mp3 in Resources */, 312 | 92664C091B2FE2B800784BC4 /* 3.png in Resources */, 313 | 92664C071B2FE2B800784BC4 /* 1.png in Resources */, 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | }; 317 | /* End PBXResourcesBuildPhase section */ 318 | 319 | /* Begin PBXShellScriptBuildPhase section */ 320 | 92FACE14225B09A200B9EB06 /* ShellScript */ = { 321 | isa = PBXShellScriptBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | ); 325 | inputFileListPaths = ( 326 | ); 327 | inputPaths = ( 328 | ); 329 | outputFileListPaths = ( 330 | ); 331 | outputPaths = ( 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | shellPath = /bin/sh; 335 | shellScript = "# For notarization we need to deep codesign Sparkle.\n# github.com/sparkle-project/Sparkle/issues/1266#issuecomment-421182578\nLOCATION=\"${BUILT_PRODUCTS_DIR}\"/\"${FRAMEWORKS_FOLDER_PATH}\"\nIDENTITY=\"${CODE_SIGN_IDENTITY}\"\ncodesign --verbose --force --deep -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"\ncodesign --verbose --force -o runtime --sign \"$IDENTITY\" \"$LOCATION/Sparkle.framework/Versions/A\"\n"; 336 | }; 337 | /* End PBXShellScriptBuildPhase section */ 338 | 339 | /* Begin PBXSourcesBuildPhase section */ 340 | 92175E851B14F3BD007151EA /* Sources */ = { 341 | isa = PBXSourcesBuildPhase; 342 | buildActionMask = 2147483647; 343 | files = ( 344 | 92ACF31A1B2CB7AB007494A6 /* SnkImageView.swift in Sources */, 345 | 92175EAA1B14F598007151EA /* Snk.swift in Sources */, 346 | 92726CE61B33DB4A00B944FD /* SnkAudio.swift in Sources */, 347 | 92664C0F1B30996900784BC4 /* SnkScoreLabel.swift in Sources */, 348 | 92C9D5821D83CEE5002B966F /* SnkTheme.swift in Sources */, 349 | 92EF54D11B242A3800A32629 /* SplashVC.swift in Sources */, 350 | 92664BEF1B2E9C2F00784BC4 /* SnkHoverButton.swift in Sources */, 351 | 92175EAC1B14F7C0007151EA /* MainVC.swift in Sources */, 352 | 92664BF11B2F48B300784BC4 /* SnkLevelButton.swift in Sources */, 353 | 92FCC0161B2B4B6A00F4BF7E /* MoCocoa.swift in Sources */, 354 | 92664BED1B2D279000784BC4 /* MenuVC.swift in Sources */, 355 | 9202554F1B14FD3B007E21E3 /* MoCode.swift in Sources */, 356 | 92664C111B32292C00784BC4 /* GameVC.swift in Sources */, 357 | 92175E8F1B14F3BD007151EA /* AppDelegate.swift in Sources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | /* End PBXSourcesBuildPhase section */ 362 | 363 | /* Begin PBXVariantGroup section */ 364 | 92175E921B14F3BD007151EA /* MainMenu.xib */ = { 365 | isa = PBXVariantGroup; 366 | children = ( 367 | 92175E931B14F3BD007151EA /* Base */, 368 | ); 369 | name = MainMenu.xib; 370 | sourceTree = ""; 371 | }; 372 | /* End PBXVariantGroup section */ 373 | 374 | /* Begin XCBuildConfiguration section */ 375 | 92175EA11B14F3BD007151EA /* Debug */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | ALWAYS_SEARCH_USER_PATHS = NO; 379 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 381 | CLANG_CXX_LIBRARY = "libc++"; 382 | CLANG_ENABLE_MODULES = YES; 383 | CLANG_ENABLE_OBJC_ARC = YES; 384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_COMMA = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_EMPTY_BODY = YES; 391 | CLANG_WARN_ENUM_CONVERSION = YES; 392 | CLANG_WARN_INFINITE_RECURSION = YES; 393 | CLANG_WARN_INT_CONVERSION = YES; 394 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 396 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 397 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 398 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 399 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 400 | CLANG_WARN_STRICT_PROTOTYPES = YES; 401 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 402 | CLANG_WARN_UNREACHABLE_CODE = YES; 403 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 404 | CODE_SIGN_IDENTITY = "-"; 405 | COPY_PHASE_STRIP = NO; 406 | DEBUG_INFORMATION_FORMAT = dwarf; 407 | ENABLE_STRICT_OBJC_MSGSEND = YES; 408 | ENABLE_TESTABILITY = YES; 409 | GCC_C_LANGUAGE_STANDARD = gnu99; 410 | GCC_DYNAMIC_NO_PIC = NO; 411 | GCC_NO_COMMON_BLOCKS = YES; 412 | GCC_OPTIMIZATION_LEVEL = 0; 413 | GCC_PREPROCESSOR_DEFINITIONS = ( 414 | "DEBUG=1", 415 | "$(inherited)", 416 | ); 417 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 422 | GCC_WARN_UNUSED_FUNCTION = YES; 423 | GCC_WARN_UNUSED_VARIABLE = YES; 424 | MACOSX_DEPLOYMENT_TARGET = 11.0; 425 | MTL_ENABLE_DEBUG_INFO = YES; 426 | ONLY_ACTIVE_ARCH = YES; 427 | SDKROOT = macosx; 428 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 429 | }; 430 | name = Debug; 431 | }; 432 | 92175EA21B14F3BD007151EA /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ALWAYS_SEARCH_USER_PATHS = NO; 436 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 437 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 438 | CLANG_CXX_LIBRARY = "libc++"; 439 | CLANG_ENABLE_MODULES = YES; 440 | CLANG_ENABLE_OBJC_ARC = YES; 441 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 442 | CLANG_WARN_BOOL_CONVERSION = YES; 443 | CLANG_WARN_COMMA = YES; 444 | CLANG_WARN_CONSTANT_CONVERSION = YES; 445 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 446 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 447 | CLANG_WARN_EMPTY_BODY = YES; 448 | CLANG_WARN_ENUM_CONVERSION = YES; 449 | CLANG_WARN_INFINITE_RECURSION = YES; 450 | CLANG_WARN_INT_CONVERSION = YES; 451 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 452 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 453 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 454 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 455 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 456 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 457 | CLANG_WARN_STRICT_PROTOTYPES = YES; 458 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 459 | CLANG_WARN_UNREACHABLE_CODE = YES; 460 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 461 | CODE_SIGN_IDENTITY = "-"; 462 | COPY_PHASE_STRIP = NO; 463 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 464 | ENABLE_NS_ASSERTIONS = NO; 465 | ENABLE_STRICT_OBJC_MSGSEND = YES; 466 | GCC_C_LANGUAGE_STANDARD = gnu99; 467 | GCC_NO_COMMON_BLOCKS = YES; 468 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 469 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 470 | GCC_WARN_UNDECLARED_SELECTOR = YES; 471 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 472 | GCC_WARN_UNUSED_FUNCTION = YES; 473 | GCC_WARN_UNUSED_VARIABLE = YES; 474 | MACOSX_DEPLOYMENT_TARGET = 11.0; 475 | MTL_ENABLE_DEBUG_INFO = NO; 476 | SDKROOT = macosx; 477 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 478 | }; 479 | name = Release; 480 | }; 481 | 92175EA41B14F3BD007151EA /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 485 | CODE_SIGN_IDENTITY = "Apple Development"; 486 | COMBINE_HIDPI_IMAGES = YES; 487 | CURRENT_PROJECT_VERSION = 1021; 488 | DEVELOPMENT_TEAM = HFT3T55WND; 489 | ENABLE_HARDENED_RUNTIME = YES; 490 | FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Snk/_sparkle"; 491 | INFOPLIST_FILE = Snk/Info.plist; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 493 | MARKETING_VERSION = 1.4.0; 494 | OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../Frameworks"; 495 | PRODUCT_BUNDLE_IDENTIFIER = "com.mowglii.$(PRODUCT_NAME:rfc1034identifier)"; 496 | PRODUCT_NAME = Snk; 497 | SWIFT_VERSION = 5.0; 498 | }; 499 | name = Debug; 500 | }; 501 | 92175EA51B14F3BD007151EA /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 505 | CODE_SIGN_IDENTITY = "Apple Development"; 506 | COMBINE_HIDPI_IMAGES = YES; 507 | CURRENT_PROJECT_VERSION = 1021; 508 | DEVELOPMENT_TEAM = HFT3T55WND; 509 | ENABLE_HARDENED_RUNTIME = YES; 510 | FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Snk/_sparkle"; 511 | INFOPLIST_FILE = Snk/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 513 | MARKETING_VERSION = 1.4.0; 514 | OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../Frameworks"; 515 | PRODUCT_BUNDLE_IDENTIFIER = "com.mowglii.$(PRODUCT_NAME:rfc1034identifier)"; 516 | PRODUCT_NAME = Snk; 517 | SWIFT_VERSION = 5.0; 518 | }; 519 | name = Release; 520 | }; 521 | /* End XCBuildConfiguration section */ 522 | 523 | /* Begin XCConfigurationList section */ 524 | 92175E841B14F3BD007151EA /* Build configuration list for PBXProject "Snk" */ = { 525 | isa = XCConfigurationList; 526 | buildConfigurations = ( 527 | 92175EA11B14F3BD007151EA /* Debug */, 528 | 92175EA21B14F3BD007151EA /* Release */, 529 | ); 530 | defaultConfigurationIsVisible = 0; 531 | defaultConfigurationName = Release; 532 | }; 533 | 92175EA31B14F3BD007151EA /* Build configuration list for PBXNativeTarget "Snk" */ = { 534 | isa = XCConfigurationList; 535 | buildConfigurations = ( 536 | 92175EA41B14F3BD007151EA /* Debug */, 537 | 92175EA51B14F3BD007151EA /* Release */, 538 | ); 539 | defaultConfigurationIsVisible = 0; 540 | defaultConfigurationName = Release; 541 | }; 542 | /* End XCConfigurationList section */ 543 | }; 544 | rootObject = 92175E811B14F3BD007151EA /* Project object */; 545 | } 546 | -------------------------------------------------------------------------------- /Snk/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created Sanjay Madan on May 26, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // MARK: Main window 8 | 9 | final class MainWindow: NSWindow { 10 | 11 | // The main window is non-resizable and non-zoomable. 12 | // Its fixed size is determined by the MainVC's view. 13 | // The window uses the NSFullSizeContentViewWindowMask 14 | // so that we can draw our own custom title bar. 15 | 16 | convenience init() { 17 | self.init(contentRect: NSZeroRect, styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], backing: .buffered, defer: false) 18 | self.titlebarAppearsTransparent = true 19 | self.standardWindowButton(.zoomButton)?.alphaValue = 0 20 | } 21 | 22 | // The app terminates when the main window is closed. 23 | 24 | override func close() { 25 | NSApplication.shared.terminate(nil) 26 | } 27 | 28 | // Fade the contentView when the window resigns Main. 29 | 30 | override func becomeMain() { 31 | super.becomeMain() 32 | contentView?.alphaValue = 1 33 | } 34 | override func resignMain() { 35 | super.resignMain() 36 | contentView?.alphaValue = 0.6 37 | } 38 | } 39 | 40 | // MARK: - App delegate 41 | // 42 | // AppDelegate handles the main window (it owns the main 43 | // window controller), clears the high scores, and toggles 44 | // the board size. 45 | 46 | @NSApplicationMain 47 | final class AppDelegate: NSObject, NSApplicationDelegate { 48 | 49 | @IBOutlet var themesMenu: NSMenu? 50 | 51 | let mainWC = NSWindowController(window: MainWindow()) 52 | 53 | func applicationWillFinishLaunching(_ notification: Notification) { 54 | // Prevent "Enter Full Screen" from appearing in menu. 55 | // stackoverflow.com/a/52158264/111418 56 | UserDefaults.standard.set(false, forKey: "NSFullScreenMenuItemEverywhere") 57 | } 58 | 59 | func applicationDidFinishLaunching(_ aNotification: Notification) { 60 | UserDefaults.standard.register(defaults: [ 61 | kHiScoreSlowKey: 0, 62 | kHiScoreMediumKey: 0, 63 | kHiScoreFastKey: 0, 64 | kEnableSoundsKey: true, 65 | kEnableMusicKey: true, 66 | kBigBoardKey: false, 67 | ]) 68 | setupThemesMenu() 69 | showMainWindow() 70 | } 71 | 72 | func showMainWindow() { 73 | // If we are re-showing the window (because the 74 | // user toggled its size or changed its theme), 75 | // first make sure it is not miniturized and 76 | // not showing. 77 | if mainWC.window?.isMiniaturized == true { 78 | mainWC.window?.deminiaturize(nil) 79 | } 80 | mainWC.window?.orderOut(nil) 81 | 82 | mainWC.contentViewController = MainVC() 83 | 84 | // Fade the window in. 85 | mainWC.window?.alphaValue = 0 86 | mainWC.showWindow(self) 87 | mainWC.window?.center() 88 | moDispatch(after: 0.1) { 89 | self.mainWC.window?.animator().alphaValue = 1 90 | } 91 | } 92 | 93 | @IBAction func clearScores(_ sender: AnyObject) { 94 | // Called from the Clear Scores... menu item. 95 | // Show an alert to confirm the user really 96 | // wants to erase their saved high scores. 97 | 98 | let alert = NSAlert() 99 | alert.messageText = NSLocalizedString("Clear scores?", comment: "") 100 | alert.informativeText = NSLocalizedString("Do you really want to clear your best scores?", comment: "") 101 | alert.addButton(withTitle: NSLocalizedString("No", comment: "")) 102 | alert.addButton(withTitle: NSLocalizedString("Yes", comment: "")) 103 | if alert.runModal() == NSApplication.ModalResponse.alertSecondButtonReturn { 104 | UserDefaults.standard.set(0, forKey: kHiScoreSlowKey) 105 | UserDefaults.standard.set(0, forKey: kHiScoreMediumKey) 106 | UserDefaults.standard.set(0, forKey: kHiScoreFastKey) 107 | } 108 | } 109 | 110 | @IBAction func toggleSize(_ sender: AnyObject) { 111 | // Called from Toggle Size menu item. 112 | // Toggle between standard and big board size. 113 | // Set the user default to the new value, set 114 | // kScale and kStep to their new values, hide 115 | // the main window and then re-show it. 116 | // Re-showing the window will re-instantiate 117 | // MainVC with the new sizes. 118 | 119 | SharedAudio.stopEverything() 120 | 121 | let bigBoard = kScale == 1 ? true : false 122 | UserDefaults.standard.set(bigBoard, forKey: kBigBoardKey) 123 | 124 | kScale = bigBoard ? 2 : 1 125 | kStep = kBaseStep * Int(kScale) 126 | 127 | showMainWindow() 128 | } 129 | 130 | @objc func selectTheme(_ sender: NSMenuItem) { 131 | let newIndex = sender.tag 132 | let oldIndex = SharedTheme.themeIndex 133 | guard newIndex != oldIndex else { 134 | return 135 | } 136 | 137 | // Uncheck old theme menu item, check new one. 138 | themesMenu?.item(at: oldIndex)?.state = NSControl.StateValue(rawValue: 0) 139 | themesMenu?.item(at: newIndex)?.state = NSControl.StateValue(rawValue: 1) 140 | 141 | // Save the theme name and set the theme manager 142 | // to use the new one. 143 | let savedName = SharedTheme.themes[newIndex].name.rawValue 144 | UserDefaults.standard.set(savedName, forKey: kThemeNameKey) 145 | SharedTheme.setTheme(savedName: savedName) 146 | 147 | showMainWindow() 148 | } 149 | 150 | func setupThemesMenu() { 151 | // Set the theme manager to use the saved theme. 152 | SharedTheme.setTheme(savedName: UserDefaults.standard.string(forKey: kThemeNameKey)) 153 | 154 | // Create menu items for the themes in the theme 155 | // manager's 'themes' array. 156 | for (index, theme) in SharedTheme.themes.enumerated() { 157 | let item = NSMenuItem() 158 | item.title = theme.name.rawValue 159 | item.state = NSControl.StateValue(rawValue: index == SharedTheme.themeIndex ? 1 : 0) 160 | item.tag = index 161 | item.action = #selector(selectTheme(_:)) 162 | themesMenu?.addItem(item) 163 | } 164 | } 165 | } 166 | 167 | -------------------------------------------------------------------------------- /Snk/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /Snk/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600 2 | {\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Bold;\f1\fnil\fcharset0 AppleColorEmoji;\f2\fnil\fcharset0 HelveticaNeue; 3 | } 4 | {\colortbl;\red255\green255\blue255;\red0\green0\blue0;} 5 | {\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;} 6 | {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}} 7 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} 8 | \margl1440\margr1440\vieww9000\viewh8400\viewkind0 9 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li110\fi6\slleading60\pardirnatural\partightenfactor0 10 | 11 | \f0\b\fs24 \cf2 Donate with {\field{\*\fldinst{HYPERLINK "https://www.paypal.me/mowgliiapps"}}{\fldrslt PayPal}} or {\field{\*\fldinst{HYPERLINK "https://cash.me/$Mowglii"}}{\fldrslt Square}} 12 | \f1\b0 \uc0\u55357 \u56842 13 | \f0\b \ 14 | 15 | \f2\b0\fs8 \ 16 | 17 | \f0\b\fs24 Keyboard Controls\ 18 | 19 | \f2\b0\fs8 \ 20 | \pard\tx720\tx1804\tx2406\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li110\fi6\slleading60\pardirnatural\partightenfactor0 21 | 22 | \fs24 \cf2 Choose level 1, 2 or 3\ 23 | Move snake Arrows or WASD\ 24 | Game Over \ 25 | OK Space\ 26 | Play again 1, 2 or 3\ 27 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li110\fi6\slleading60\pardirnatural\partightenfactor0 28 | 29 | \fs8 \cf2 \ 30 | 31 | \f0\b\fs24 Music Credits\ 32 | 33 | \f2\b0\fs8 \ 34 | \pard\tx143\tx352\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li1435\fi-1338\slleading60\pardirnatural\partightenfactor0 35 | \ls1\ilvl0 36 | \fs24 \cf2 {\listtext \uc0\u8226 }{\field{\*\fldinst{HYPERLINK "http://www.looperman.com/loops/detail/68575/8-bit-melody-by-kinggjmaytryx-free-156bpm-electro-sh-loop"}}{\fldrslt 8-Bit Melody}} by KingGJMaytryx\ 37 | {\listtext \uc0\u8226 }{\field{\*\fldinst{HYPERLINK "https://soundcloud.com/lv-7/bibo"}}{\fldrslt Bibo}} by Z23\ 38 | {\listtext \uc0\u8226 }{\field{\*\fldinst{HYPERLINK "https://www.youtube.com/watch?v=wOFgh2IdnZI"}}{\fldrslt Flight of the Bumblebee}} by 8BitUzz 39 | \fs8 \ 40 | } -------------------------------------------------------------------------------- /Snk/GameVC.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 17, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // Snake segments and food position are points 8 | // on a kCols x kRows grid. SnkPoint conforms 9 | // to Equatable so we can do collision testing. 10 | 11 | struct SnkPoint { 12 | var x = 0, y = 0 13 | } 14 | extension SnkPoint: Equatable {} 15 | func ==(lhs: SnkPoint, rhs: SnkPoint) -> Bool { 16 | return lhs.x == rhs.x && lhs.y == rhs.y 17 | } 18 | 19 | final class GameVC: NSViewController, CALayerDelegate, CAAnimationDelegate { 20 | 21 | enum SnkState { 22 | case initializing, playing, crashed, victorious, gameOver, paused 23 | } 24 | 25 | enum SnkDirection { 26 | case up, down, left, right 27 | } 28 | 29 | let level: SnkLevel 30 | 31 | // Layers draw and animate the various game objects. 32 | // The replLayer replicates the other layers for the 33 | // shadow when the game goes 3D. 34 | 35 | let wallLayer = CALayer() 36 | let foodLayer = CALayer() 37 | let snakeLayer = CALayer() 38 | let replLayer = CAReplicatorLayer() 39 | 40 | let scoreLabel = SnkScoreLabel(fgColor: SharedTheme.color(.background), bgColor: SharedTheme.color(.wall)) 41 | var scoreIncrement = kMaxScoreIncrement 42 | 43 | let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global()) 44 | 45 | // The snake is represented by a list of points with 46 | // the head at the end of the list (snakePoints.last). 47 | 48 | var snakePoints = [SnkPoint]() 49 | var foodPoint = SnkPoint() 50 | 51 | var direction: SnkDirection = .right 52 | var directionBuffer = [SnkDirection]() 53 | 54 | // When the score is over kScoreSpin, the board 55 | // will start to spin and the walls will disable. 56 | 57 | var wallsEnabled = true 58 | 59 | var state: SnkState = .initializing { 60 | didSet { 61 | // Hide the cursor when state == .Playing 62 | NSCursor.setHiddenUntilMouseMoves(state == .playing) 63 | } 64 | } 65 | 66 | init!(level: SnkLevel) { 67 | self.level = level 68 | super.init(nibName: nil, bundle: nil) 69 | } 70 | 71 | // MARK: - View lifecycle 72 | 73 | override func loadView() { 74 | let v = MoView() 75 | 76 | // Set up the layers that make up the game objects. 77 | 78 | var frame = CGRect(x: 0, y: 0, width: kStep * kCols, height: kStep * kRows) 79 | 80 | wallLayer.frame = frame 81 | wallLayer.borderWidth = CGFloat(kStep) 82 | wallLayer.borderColor = SharedTheme.color(.wall).cgColor 83 | 84 | snakeLayer.frame = frame 85 | snakeLayer.delegate = self 86 | // Disable implicit animations between drawing frames. 87 | snakeLayer.actions = ["contents": NSNull()] 88 | 89 | foodLayer.frame = CGRect(x: 0, y: 0, width: kStep, height: kStep) 90 | foodLayer.backgroundColor = SharedTheme.color(.food).cgColor 91 | // Disable implicit animations for changes in position. 92 | foodLayer.actions = ["position": NSNull()] 93 | 94 | // The wall, food and snake layers are sublayers of 95 | // the replicator layer so it can replicate them to 96 | // draw the 3D shadow. 97 | 98 | frame.origin = CGPoint(x: kStep, y: kStep) 99 | replLayer.frame = frame 100 | replLayer.instanceCount = 2 101 | replLayer.instanceTransform = CATransform3DMakeTranslation(0, 0, CGFloat(-4 * kStep)) 102 | replLayer.instanceAlphaOffset = -0.8 103 | replLayer.instanceRedOffset = -1 104 | replLayer.instanceGreenOffset = -1 105 | replLayer.instanceBlueOffset = -1 106 | replLayer.preservesDepth = true 107 | 108 | replLayer.addSublayer(wallLayer) 109 | replLayer.addSublayer(foodLayer) 110 | replLayer.addSublayer(snakeLayer) 111 | 112 | // Make our main view layer-hosting and add the 113 | // replicator layer. 114 | 115 | v.layer = CALayer() 116 | v.wantsLayer = true 117 | v.layer?.addSublayer(replLayer) 118 | 119 | // The score label is located in the top right corner 120 | // inset from the top and trailing egdes of the view 121 | // so that it appears inside the wall. 122 | 123 | v.addSubview(scoreLabel) 124 | scoreLabel.alignTrailing(with: v, constant: CGFloat(kStep) + 10 * kScale) 125 | scoreLabel.alignBottomToTop(of: v, constant: CGFloat(-kStep) - 12 * kScale) 126 | 127 | view = v 128 | } 129 | 130 | override func viewDidLoad() { 131 | super.viewDidLoad() 132 | 133 | // Initialize the snake halfway down the board and 134 | // 2 points to the left. 135 | 136 | snakePoints = [SnkPoint( x: 2, y: Int(kRows/2) )] 137 | snakeLayer.setNeedsDisplay() 138 | 139 | setupFood() 140 | startTimer() 141 | } 142 | 143 | override func viewDidAppear() { 144 | super.viewDidAppear() 145 | 146 | // Since we own the snakeLayer, we have responsibility 147 | // for setting its contentScale. 148 | snakeLayer.contentsScale = self.view.window!.backingScaleFactor 149 | 150 | view.window!.makeFirstResponder(self) 151 | 152 | switch level { 153 | case .slow: SharedAudio.play(music: kSong1, loop: true) 154 | case .medium: SharedAudio.play(music: kSong2, loop: true) 155 | case .fast: SharedAudio.play(music: kSong3, loop: true) 156 | } 157 | 158 | state = .playing 159 | } 160 | 161 | // MARK: - Input 162 | 163 | override func keyDown(with theEvent: NSEvent) { 164 | func key(_ x: Int) -> String { 165 | return UnicodeScalar(x)!.escaped(asASCII: false) 166 | } 167 | if let chars = theEvent.charactersIgnoringModifiers { 168 | switch chars { 169 | case "w", "W", key(NSEvent.SpecialKey.upArrow.rawValue): addDirection(.up) 170 | case "s", "S", key(NSEvent.SpecialKey.downArrow.rawValue): addDirection(.down) 171 | case "a", "A", key(NSEvent.SpecialKey.leftArrow.rawValue): addDirection(.left) 172 | case "d", "D", key(NSEvent.SpecialKey.rightArrow.rawValue): addDirection(.right) 173 | case "P": 174 | if state == .playing || state == .paused { 175 | state = state == .playing ? .paused : .playing 176 | } 177 | case "1": play(level: .slow) 178 | case "2": play(level: .medium) 179 | case "3": play(level: .fast) 180 | default: super.keyDown(with: theEvent) 181 | } 182 | } 183 | else { 184 | super.keyDown(with: theEvent) 185 | } 186 | } 187 | 188 | func addDirection(_ newDir: SnkDirection) { 189 | // Buffer directional keypresses in directionBuffer. 190 | // Only do so when state == .Playing. 191 | // Consecutive directions can't be the same or 192 | // opposite (Snake going .Up can only go .Left 193 | // or .Right next, not .Down). 194 | 195 | let oldDir = directionBuffer.count > 0 ? directionBuffer.last : direction 196 | if state != .playing || 197 | newDir == oldDir || 198 | newDir == .up && oldDir == .down || 199 | newDir == .down && oldDir == .up || 200 | newDir == .right && oldDir == .left || 201 | newDir == .left && oldDir == .right { 202 | return 203 | } 204 | directionBuffer.append(newDir) 205 | } 206 | 207 | // MARK: - Update 208 | 209 | func updateGame() { 210 | // This method is called by the timer. 211 | 212 | if state == .crashed || state == .victorious { 213 | gameOver() 214 | return 215 | } 216 | if state != .playing { 217 | return 218 | } 219 | 220 | if directionBuffer.count > 0 { 221 | direction = directionBuffer.remove(at: 0) 222 | } 223 | 224 | // Calculate where the snake's head will 225 | // be next. Wrap around if necessary if 226 | // walls are not enabled. 227 | 228 | var newHeadPoint = snakePoints.last! 229 | switch direction { 230 | case .up: newHeadPoint.y += 1 231 | case .down: newHeadPoint.y -= 1 232 | case .left: newHeadPoint.x -= 1 233 | case .right: newHeadPoint.x += 1 234 | } 235 | if wallsEnabled == false { 236 | newHeadPoint.y = newHeadPoint.y > kRows-1 ? 0 : newHeadPoint.y 237 | newHeadPoint.y = newHeadPoint.y < 0 ? kRows-1 : newHeadPoint.y 238 | newHeadPoint.x = newHeadPoint.x > kCols-1 ? 0 : newHeadPoint.x 239 | newHeadPoint.x = newHeadPoint.x < 0 ? kCols-1 : newHeadPoint.x 240 | } 241 | 242 | // The snake got the food. 243 | 244 | if newHeadPoint == foodPoint { 245 | // We must add newHeadPoint to the snake before 246 | // placing food because food placement needs to 247 | // know where all the snake points are so it can 248 | // choose an empty cell. 249 | advanceScore() 250 | snakePoints.append(newHeadPoint) 251 | explodeFood() 252 | // Did we win? Check if the snake covers the board. 253 | if snakePoints.count >= kCols * kRows { 254 | state = .victorious 255 | } 256 | else { 257 | placeFood() 258 | animateBoard() 259 | } 260 | } 261 | 262 | // The snake didn't get the food. 263 | 264 | else { 265 | // We must remove the old tail but NOT add the 266 | // new head before checking for collisions. 267 | // Otherwise we'd wrongly collide with a 268 | // phantom tail or the new head. 269 | tickScore() 270 | snakePoints.remove(at: 0) 271 | if (wallsEnabled && 272 | (newHeadPoint.x <= 0 || newHeadPoint.x >= kCols-1 || 273 | newHeadPoint.y <= 0 || newHeadPoint.y >= kRows-1)) || 274 | snakePoints.contains(newHeadPoint) { 275 | state = .crashed 276 | } 277 | snakePoints.append(newHeadPoint) 278 | } 279 | 280 | snakeLayer.setNeedsDisplay() 281 | } 282 | 283 | // MARK: - Game over 284 | 285 | func gameOver() { 286 | let reasonForGameOver = state 287 | state = .gameOver 288 | SharedAudio.stopMusic() 289 | timer.cancel() 290 | 291 | if reasonForGameOver == .crashed { 292 | // We crashed! Rattle the board. The animation is 20 random 293 | // translations about the view's center. 294 | 295 | SharedAudio.play(sound: kSoundCrash) 296 | 297 | let anim = CAKeyframeAnimation(keyPath: "position") 298 | let delta = UInt32(12 * kScale) 299 | anim.duration = 0.3 300 | anim.calculationMode = .discrete 301 | anim.values = (1...20).map { _ in 302 | let x = self.view.layer!.position.x + CGFloat(arc4random_uniform(delta)) - CGFloat(delta/2) 303 | let y = self.view.layer!.position.y + CGFloat(arc4random_uniform(delta)) - CGFloat(delta/2) 304 | return NSValue(point: CGPoint(x: x, y: y)) 305 | } 306 | view.layer!.add(anim, forKey: "rattle") 307 | } 308 | else { // reasonForGameOver == .victorious 309 | // We won! Remove the food layer, revert the board to the inital 310 | // 2D orientation, and blow up the snake. 311 | 312 | SharedAudio.play(sound: kSoundVictory) 313 | 314 | // Remove food and stop board from spinning. 315 | foodLayer.removeFromSuperlayer() 316 | replLayer.removeAllAnimations() 317 | 318 | // Revert to 2D. 319 | CATransaction.begin() 320 | CATransaction.setAnimationDuration(0.5) 321 | CATransaction.setCompletionBlock { 322 | 323 | // Cover the snake with segments which we will individually 324 | // animate in a celebratory explosion. 325 | var segments = [CALayer]() 326 | for pt in self.snakePoints.reversed() { 327 | let segment = CALayer() 328 | segment.frame = CGRect(x: pt.x * kStep, y: pt.y * kStep, width: kStep, height: kStep) 329 | segment.backgroundColor = SharedTheme.color(.snake).cgColor 330 | self.replLayer.addSublayer(segment) 331 | segments.append(segment) 332 | } 333 | 334 | // We've covered the board with segment layers which we 335 | // can animate, so now we can remove the snake layer. 336 | self.snakeLayer.removeFromSuperlayer() 337 | 338 | // After the board is back to 2D, wait 0.5 seconds and then 339 | // "explode" the snake one segment at a time from head to 340 | // tail. The animation just moves each segment along a curve. 341 | moDispatch(after: 0.5) { 342 | let boardHeight = CGFloat(kRows * kStep) 343 | for (index, segment) in segments.enumerated() { 344 | moDispatch(after: TimeInterval(index) * 0.1) { 345 | let animPath = CGMutablePath() 346 | animPath.move(to: segment.position) 347 | let midX = NSMidX(self.snakeLayer.frame) 348 | let deltaX = 2 * (segment.position.x - midX) 349 | let deltaY = CGFloat(arc4random_uniform(UInt32(15 * kStep))) 350 | let endPt = CGPoint(x: segment.position.x + deltaX, y: segment.position.y - boardHeight) 351 | let ctrlPt = CGPoint(x: segment.position.x + deltaX/2, y: segment.position.y + deltaY) 352 | animPath.addCurve(to: endPt, control1: ctrlPt, control2: ctrlPt) 353 | 354 | let moveAnim = CAKeyframeAnimation(keyPath: "position") 355 | moveAnim.path = animPath 356 | moveAnim.duration = 0.5 357 | moveAnim.timingFunction = CAMediaTimingFunction(name: .easeOut) 358 | moveAnim.delegate = self 359 | moveAnim.setValue(segment, forKey: "segmentLayer") 360 | segment.add(moveAnim, forKey: nil) 361 | } 362 | } 363 | } 364 | } 365 | replLayer.transform = CATransform3DIdentity 366 | CATransaction.commit() 367 | } 368 | 369 | // After a brief delay, dim the board and show an 370 | // ok button centered in the view. 371 | 372 | let okButtonAppearanceDelay = (reasonForGameOver == .crashed) ? 0.5 : 4 373 | 374 | moDispatch(after: okButtonAppearanceDelay) { 375 | if reasonForGameOver == .crashed { 376 | SharedAudio.play(sound: kSoundGameOver) 377 | } 378 | self.replLayer.opacity = 0.5 379 | let ok = SnkHoverButton(imageName: "ok", tint: SharedTheme.color(.logo), scale: 4) 380 | ok.dimmedAlpha = 1 381 | ok.borderWidth = 4 382 | ok.borderHighlightColor = SharedTheme.color(.wall) 383 | ok.keyEquivalent = " " 384 | ok.target = self 385 | ok.action = #selector(GameVC.goToMenu) 386 | self.view.addSubview(ok) 387 | ok.centerX(with: self.view) 388 | ok.centerY(with: self.view) 389 | } 390 | } 391 | 392 | @objc func goToMenu() { 393 | // User pressed the OK button after a game. 394 | // Go back to the main menu. 395 | 396 | SharedAudio.stopEverything() 397 | SharedAudio.play(sound: kSoundOk) 398 | let mainVC = self.parent as! MainVC 399 | mainVC.transition(to: MenuVC(), options: NSViewController.TransitionOptions.slideRight) 400 | } 401 | 402 | private func play(level: SnkLevel) { 403 | guard state == .gameOver else { return } 404 | SharedAudio.stopEverything() 405 | let mainVC = self.parent as! MainVC 406 | mainVC.transition(to: GameVC(level: level), options: NSViewController.TransitionOptions.slideLeft) 407 | SharedAudio.play(sound: kSoundStartGame) 408 | } 409 | 410 | // MARK: - Snake drawing 411 | 412 | func draw(_ layer: CALayer, in context: CGContext) { 413 | context.setFillColor(SharedTheme.color(.snake).cgColor) 414 | for p in snakePoints { 415 | context.fill(CGRect(x: p.x * kStep, y: p.y * kStep, width: kStep, height: kStep)) 416 | } 417 | } 418 | 419 | // MARK: - Score 420 | 421 | func advanceScore() { 422 | // Advance score by scoreIncrement and reset 423 | // scoreIncrement to its max value. Save the 424 | // score if it's a high score for this level. 425 | 426 | scoreLabel.score += scoreIncrement 427 | scoreIncrement = kMaxScoreIncrement 428 | 429 | let defaults = UserDefaults.standard 430 | let key: String 431 | switch level { 432 | case .slow: key = kHiScoreSlowKey 433 | case .medium: key = kHiScoreMediumKey 434 | case .fast: key = kHiScoreFastKey 435 | } 436 | if scoreLabel.score > defaults.integer(forKey: key) { 437 | defaults.set(scoreLabel.score, forKey: key) 438 | } 439 | } 440 | 441 | func tickScore() { 442 | // On each timer tick, we decrement the amount by which 443 | // we'll increment the score when the snake gets the food. 444 | // The smallest possible scoreIncrement is 1. 445 | 446 | scoreIncrement = max(1, scoreIncrement - 1) 447 | } 448 | 449 | // MARK: - Food 450 | 451 | func setupFood() { 452 | // Set up a perpetual animation on the food 453 | // which pulses its bounds and then place 454 | // the food randomly on the board. 455 | 456 | let anim = CAKeyframeAnimation(keyPath: "bounds.size") 457 | anim.duration = 0.25 458 | anim.repeatCount = Float.infinity 459 | anim.autoreverses = true 460 | anim.calculationMode = .discrete 461 | // stride by 2 ensures sides are even and fall on pixel boundaries (not blurry) 462 | anim.values = stride(from: 4, through: kStep, by: 2).map { 463 | NSValue(size: NSSize(width: $0, height: $0)) 464 | } 465 | foodLayer.add(anim, forKey: "pulseFood") 466 | 467 | placeFood() 468 | } 469 | 470 | func explodeFood() { 471 | SharedAudio.play(sound: kSoundFoodExplosion) 472 | 473 | // Create an explosion animation when the snake 474 | // gets the food. The explosion is a layer that 475 | // is placed at the food location, animates, and 476 | // is then removed. The animation is actually 2 477 | // animations: an expansion and a fade out. 478 | 479 | // Set up the explosion layer at the food location. 480 | 481 | let explosion = CALayer() 482 | explosion.frame = foodLayer.frame 483 | explosion.borderColor = SharedTheme.color(.explosion).cgColor 484 | explosion.borderWidth = CGFloat(kStep) 485 | explosion.opacity = 0 486 | 487 | // Set up the expansion animation. 488 | 489 | let animExpand = CAKeyframeAnimation(keyPath: "bounds.size") 490 | animExpand.timingFunction = CAMediaTimingFunction(name: .easeOut) 491 | animExpand.calculationMode = .discrete 492 | // stride by 2 ensures sides are even and fall on pixel boundaries (not blurry) 493 | animExpand.values = stride(from: (3 * kStep), through: 6 * kStep, by: 2).map { 494 | NSValue(size: NSSize(width: $0, height: $0)) 495 | } 496 | 497 | // Set up the fade out animtaion. 498 | 499 | let animFadeOut = CABasicAnimation(keyPath: "opacity") 500 | animFadeOut.timingFunction = CAMediaTimingFunction(name: .easeIn) 501 | animFadeOut.fromValue = 1 502 | animFadeOut.toValue = 0 503 | 504 | // Add the explosion layer, animate, and then 505 | // remove the explosion layer. 506 | 507 | replLayer.addSublayer(explosion) 508 | CATransaction.begin() 509 | CATransaction.setAnimationDuration(0.2) 510 | CATransaction.setCompletionBlock({ 511 | explosion.removeFromSuperlayer() 512 | }) 513 | explosion.add(animExpand, forKey: "expand") 514 | explosion.add(animFadeOut, forKey: "fadeout") 515 | CATransaction.commit() 516 | } 517 | 518 | func placeFood() { 519 | var frame = foodLayer.frame 520 | 521 | // Place the food randomly on the board. 522 | // First, choose a random location that isn't 523 | // on the walls if they are enabled. 524 | 525 | let xMin = wallsEnabled ? 1 : 0 526 | let yMin = wallsEnabled ? 1 : 0 527 | let xMax = wallsEnabled ? kCols-2 : kCols-1 528 | let yMax = wallsEnabled ? kRows-2 : kRows-1 529 | 530 | var x = Int(arc4random_uniform( UInt32(xMax) )) + xMin 531 | var y = Int(arc4random_uniform( UInt32(yMax) )) + yMin 532 | 533 | // Next, make sure the random location isn't on 534 | // on the snake. If it is, choose the next 535 | // available cell. 536 | 537 | while snakePoints.contains(SnkPoint(x: x, y: y)) { 538 | x = x + 1 539 | if x > xMax { 540 | x = xMin 541 | y = y + 1 542 | if y > yMax { 543 | y = yMin 544 | } 545 | } 546 | } 547 | 548 | // Update the food with the new location. 549 | 550 | frame.origin = CGPoint(x: x * kStep, y: y * kStep) 551 | foodPoint = SnkPoint(x: x, y: y) 552 | foodLayer.frame = frame 553 | } 554 | 555 | // MARK: - Animate board 556 | 557 | func animateBoard() { 558 | // The board animates when certain score 559 | // thresholds are exceeded. 560 | 561 | if scoreLabel.score > kScoreSpin && wallsEnabled { 562 | 563 | // Spin around the z-axis perpetually. This is 564 | // done with a series of key frame rotations 565 | // because a single 360 rotation is the same as 0. 566 | // Also reduce the wall border width and color in 567 | // the floor (wallLayer.backgroundColor). 568 | 569 | wallLayer.borderWidth = 2 570 | wallLayer.backgroundColor = NSColor(white: 1, alpha: 0.1).cgColor 571 | wallsEnabled = false 572 | let spinAnim = CAKeyframeAnimation(keyPath: "transform") 573 | spinAnim.values = (0...4).map { 574 | let angle = CGFloat($0) * CGFloat(-Double.pi/2) 575 | return NSValue(caTransform3D: CATransform3DRotate(self.replLayer.transform, angle, 0, 0, 1)) 576 | } 577 | spinAnim.duration = 25 578 | spinAnim.repeatCount = Float.infinity 579 | replLayer.add(spinAnim, forKey: "spin") 580 | SharedAudio.play(sound: kSoundSpinBoard) 581 | } 582 | 583 | else if scoreLabel.score > kScoreRotate && wallsEnabled { 584 | 585 | // Rotate 15 degrees. 586 | 587 | CATransaction.begin() 588 | CATransaction.setAnimationDuration(0.3) 589 | replLayer.transform = CATransform3DRotate(replLayer.transform, -15 * CGFloat(Double.pi/180), 0, 0, 1) 590 | CATransaction.commit() 591 | SharedAudio.play(sound: kSoundRotateBoard) 592 | } 593 | 594 | else if scoreLabel.score > kScore3D && CATransform3DEqualToTransform(replLayer.transform, CATransform3DIdentity) { 595 | 596 | // Transform to 3D. 597 | 598 | var t = CATransform3DIdentity 599 | t.m34 = -1/400 600 | t = CATransform3DTranslate(t, 0, 10, -75); 601 | t = CATransform3DRotate(t, -30 * CGFloat(Double.pi)/180, 1, 0, 0.2) 602 | CATransaction.begin() 603 | CATransaction.setAnimationDuration(1) 604 | replLayer.transform = t 605 | CATransaction.commit() 606 | SharedAudio.play(sound: kSoundAnimateTo3D) 607 | } 608 | } 609 | 610 | // MARK: - Timer 611 | 612 | func startTimer() { 613 | let delta: Int // nanoseconds between timer ticks 614 | switch level { 615 | case .slow: delta = Int( kLevel1SecPerFrame * Double(NSEC_PER_SEC) ) 616 | case .medium: delta = Int( kLevel2SecPerFrame * Double(NSEC_PER_SEC) ) 617 | case .fast: delta = Int( kLevel3SecPerFrame * Double(NSEC_PER_SEC) ) 618 | } 619 | timer.schedule(deadline: .now() + .nanoseconds(delta), repeating: .nanoseconds(delta)) 620 | timer.setEventHandler { [unowned self] in 621 | DispatchQueue.main.async { 622 | self.updateGame() 623 | } 624 | } 625 | timer.resume() 626 | } 627 | 628 | // MARK: - Snake-segment explosion animation did stop 629 | 630 | func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 631 | // Remove the segment layer when its animation is done. See 632 | // the .victorious section of gameOver() for animation code. 633 | if let layer = anim.value(forKey: "segmentLayer") as? CALayer { 634 | layer.removeFromSuperlayer() 635 | } 636 | } 637 | 638 | // MARK: - Unused 639 | 640 | required init?(coder: NSCoder) { 641 | fatalError("init(coder:) has not been implemented") 642 | } 643 | } 644 | -------------------------------------------------------------------------------- /Snk/Images.xcassets/AppIcon.appiconset/AppIcon128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/Images.xcassets/AppIcon.appiconset/AppIcon128@2x.png -------------------------------------------------------------------------------- /Snk/Images.xcassets/AppIcon.appiconset/AppIcon16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/Images.xcassets/AppIcon.appiconset/AppIcon16@2x.png -------------------------------------------------------------------------------- /Snk/Images.xcassets/AppIcon.appiconset/AppIcon32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/Images.xcassets/AppIcon.appiconset/AppIcon32@2x.png -------------------------------------------------------------------------------- /Snk/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "filename" : "AppIcon16@2x.png", 10 | "idiom" : "mac", 11 | "scale" : "2x", 12 | "size" : "16x16" 13 | }, 14 | { 15 | "idiom" : "mac", 16 | "scale" : "1x", 17 | "size" : "32x32" 18 | }, 19 | { 20 | "filename" : "AppIcon32@2x.png", 21 | "idiom" : "mac", 22 | "scale" : "2x", 23 | "size" : "32x32" 24 | }, 25 | { 26 | "idiom" : "mac", 27 | "scale" : "1x", 28 | "size" : "128x128" 29 | }, 30 | { 31 | "filename" : "AppIcon128@2x.png", 32 | "idiom" : "mac", 33 | "scale" : "2x", 34 | "size" : "128x128" 35 | }, 36 | { 37 | "idiom" : "mac", 38 | "scale" : "1x", 39 | "size" : "256x256" 40 | }, 41 | { 42 | "idiom" : "mac", 43 | "scale" : "2x", 44 | "size" : "256x256" 45 | }, 46 | { 47 | "idiom" : "mac", 48 | "scale" : "1x", 49 | "size" : "512x512" 50 | }, 51 | { 52 | "idiom" : "mac", 53 | "scale" : "2x", 54 | "size" : "512x512" 55 | } 56 | ], 57 | "info" : { 58 | "author" : "xcode", 59 | "version" : 1 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Snk/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Snk/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSApplicationCategoryType 26 | public.app-category.arcade-games 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | NSHumanReadableCopyright 30 | Copyright © 2015-2021 mowglii.com 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | SUEnableAutomaticChecks 36 | 37 | SUFeedURL 38 | https://s3.amazonaws.com/mowglii/snk.xml 39 | 40 | 41 | -------------------------------------------------------------------------------- /Snk/MainVC.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on May 26, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | final class MainVC: NSViewController { 8 | 9 | // Child view controller views are subviews of contentView. 10 | // It sits just below the toolbar drawn by our main view. 11 | 12 | let contentView = MoView() 13 | 14 | // The main view draws the titlebar and hosts the views of 15 | // child view controllers. The titlebar is drawn as a semi- 16 | // transparent gradient that allows the window's background 17 | // color to show through. 18 | // 19 | // This view's dimensions determine the size of the window. 20 | 21 | override func loadView() { 22 | let v = MoView() 23 | 24 | // Calculate the titlebar height of a normal window so we 25 | // have this dimension for our custom drawing and layout. 26 | 27 | let contentRect = NSRect(x: 0, y: 0, width: 200, height: 200) 28 | let frameRect = NSWindow.frameRect(forContentRect: contentRect, styleMask: [.titled]) 29 | let titlebarHeight = frameRect.height - contentRect.height 30 | 31 | // Fill background and draw title bar. 32 | 33 | v.bgColor = SharedTheme.color(.background) 34 | v.drawBlock = { (context, bounds) in 35 | // Title bar gradient. 36 | var rect = bounds 37 | rect.origin.y = bounds.height - titlebarHeight 38 | rect.size.height = titlebarHeight 39 | let c1 = NSColor(white: 1, alpha: 0.8) 40 | let c2 = NSColor(white: 1, alpha: 0.4) 41 | NSGradient(starting: c1, ending: c2)?.draw(in: rect, angle: -90) 42 | // Title bar top highlight. 43 | rect.origin.y = bounds.height - 1 44 | rect.size.height = 1 45 | NSColor(white: 1, alpha: 0.5).set() 46 | rect.fill(using: .sourceOver) 47 | // Shadow under title bar. 48 | rect.origin.y = bounds.height - titlebarHeight - 2 49 | rect.size.height = 2; 50 | NSColor(white: 0, alpha: 0.1).set() 51 | rect.fill(using: .sourceOver) 52 | } 53 | 54 | v.addSubview(contentView) 55 | 56 | // The view dimensions are fixed and determine the size of 57 | // the main window. They must accomodate the board as well 58 | // as the title bar. The board is kCols x kRows and has a 59 | // kStep wide margin on all sides. 60 | // 61 | // The contentView sits just below our drawn titlebar and 62 | // hugs the sides and bottom of our view. 63 | // 64 | // +--self.view--+ 65 | // | |tb | 66 | // |[contentView]| 67 | // +-------------+ 68 | 69 | let contentViewWidth = CGFloat( (1 + kCols + 1) * kStep ), 70 | contentViewHeight = CGFloat( (1 + kRows + 1) * kStep ) 71 | let metrics = ["tb": titlebarHeight, "w": contentViewWidth, "h": contentViewHeight] 72 | let views = ["contentView": contentView] 73 | 74 | v.makeConstraints(metrics: metrics as [String : NSNumber]?, views: views, formatsAndOptions: [ 75 | ("V:|-tb-[contentView(h)]|", []), 76 | ("H:|[contentView(w)]|", []) 77 | ]) 78 | 79 | view = v 80 | } 81 | 82 | // Add SplashVC as initial child. 83 | 84 | override func viewDidLoad() { 85 | super.viewDidLoad() 86 | 87 | let splashVC = SplashVC() 88 | addChild(splashVC) 89 | contentView.addSubview(splashVC.view) 90 | splashVC.view.alignFrame(with: contentView) 91 | } 92 | 93 | // Transition to a new childVC. This method is called 94 | // by childVCs to transition to a new child. 95 | 96 | func transition(to newVC: NSViewController, options: NSViewController.TransitionOptions) { 97 | // oldVC: Deactivate the constraints that bind oldVC's 98 | // view to self.contentView so that oldVC's view can 99 | // animate out. 100 | 101 | let oldVC = children[0] // There is only ever 1 child. 102 | oldVC.view.translatesAutoresizingMaskIntoConstraints = true 103 | NSLayoutConstraint.deactivate(contentView.constraints) 104 | 105 | // newVC: Add newVC as a child and set up its view. Don't 106 | // set up the newVC's view's constraints yet so it can 107 | // animate in. We'll set constraints after animating. 108 | 109 | addChild(newVC) 110 | newVC.view.translatesAutoresizingMaskIntoConstraints = true 111 | 112 | // Animate. 113 | 114 | transition(from: oldVC, to: newVC, options: options, duration: 0.25) { 115 | // Remove oldVC as a child and set up newVC's view's constraints. 116 | oldVC.removeFromParent() 117 | newVC.view.translatesAutoresizingMaskIntoConstraints = false 118 | newVC.view.alignFrame(with: self.contentView) 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Snk/MenuVC.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 13, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | final class MenuVC: NSViewController { 8 | 9 | override func loadView() { 10 | view = MoView() 11 | } 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // SNK logo. Draw so each pixel in the source image 17 | // is 7x7 points (scale = 7) on screen. 18 | 19 | let logo = SnkImageView(named: "snk", tint: SharedTheme.color(.logo), scale: 7) 20 | 21 | let button1 = SnkLevelButton(level: .slow, target: self, action: #selector(MenuVC.activate(button:))) 22 | let button2 = SnkLevelButton(level: .medium, target: self, action: #selector(MenuVC.activate(button:))) 23 | let button3 = SnkLevelButton(level: .fast, target: self, action: #selector(MenuVC.activate(button:))) 24 | 25 | // Layout logo and level buttons. 26 | // 27 | // +-view---+--------+ 28 | // | |a | 29 | // | [SNK] | 30 | // | |b | 31 | // | [1]-b-[2]-b-[3] | 32 | // | | 33 | // +-----------------+ 34 | 35 | view.addSubview(logo) 36 | view.addSubview(button1) 37 | view.addSubview(button2) 38 | view.addSubview(button3) 39 | 40 | logo.centerX(with: view) 41 | 42 | let metrics = ["a": Int(24 * kScale), "b": kStep] 43 | let views = ["logo": logo, "button1": button1, "button2": button2, "button3": button3] 44 | 45 | view.makeConstraints(metrics: metrics as [String : NSNumber]?, views: views, formatsAndOptions: [ 46 | ("V:|-a-[logo]-b-[button2]", NSLayoutConstraint.FormatOptions.alignAllCenterX), 47 | ("H:[button1]-b-[button2]-b-[button3]", NSLayoutConstraint.FormatOptions.alignAllCenterY) 48 | ]) 49 | } 50 | 51 | override func viewDidAppear() { 52 | super.viewDidAppear() 53 | view.window?.makeFirstResponder(self) 54 | } 55 | 56 | @objc func activate(button: SnkHoverButton) { 57 | // The min/max dance guarantees rawValue = 1 or 2 or 3 only 58 | let level = SnkLevel(rawValue: min(max(button.tag, 1), 3))! 59 | let mainVC = self.parent as! MainVC 60 | mainVC.transition(to: GameVC(level: level), options: NSViewController.TransitionOptions.slideLeft) 61 | SharedAudio.play(sound: kSoundStartGame) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Snk/MoCocoa.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 12, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | // Mowglii's extensions to Cocoa to make life a little easier. 6 | 7 | import Cocoa 8 | 9 | // MARK: GCD 10 | 11 | // Dispatch `work` on the main queue after `delay` seconds. 12 | 13 | func moDispatch(after delay: TimeInterval, execute work: @escaping () -> Void) { 14 | let milliseconds = Int(delay * 1000) 15 | DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(milliseconds), execute: work) 16 | } 17 | 18 | // MARK: - NSView Auto Layout 19 | 20 | // These Auto Layout convenience functions are marked with 21 | // @discardableResult because the most common case is 22 | // to use them to set fixed constraints. However, they do 23 | // return the constraints they create in case you need to 24 | // update them later. 25 | 26 | extension NSView { 27 | 28 | // Set the receiver's width or height. 29 | 30 | @discardableResult 31 | func makeConstraint(width: CGFloat) -> NSLayoutConstraint { 32 | let constraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: width) 33 | constraint.isActive = true 34 | return constraint 35 | } 36 | 37 | @discardableResult 38 | func makeConstraint(height: CGFloat) -> NSLayoutConstraint { 39 | let constraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: height) 40 | constraint.isActive = true 41 | return constraint 42 | } 43 | 44 | @discardableResult 45 | func makeConstraints(width: CGFloat, height: CGFloat) -> [NSLayoutConstraint] { 46 | var constraints = [NSLayoutConstraint]() 47 | constraints.append( makeConstraint(width: width) ) 48 | constraints.append( makeConstraint(height: height) ) 49 | return constraints 50 | } 51 | 52 | // Make an arbitrary fixed (.equal) constraint between the 53 | // receiver and `view`. Specify `attribute` and `constant`. 54 | 55 | @discardableResult 56 | func makeConstraint(with view: NSView, attribute: NSLayoutConstraint.Attribute, constant: CGFloat) -> NSLayoutConstraint { 57 | let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: .equal, toItem: view, attribute: attribute, multiplier: 1, constant: constant) 58 | constraint.isActive = true 59 | return constraint 60 | } 61 | 62 | // Convenience methods to make particular kinds of fixed 63 | // (.equal) constraints between receiver and `view`. The 64 | // constant defaults to 0 so common calls are succinct. 65 | 66 | @discardableResult 67 | func centerX(with view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 68 | return makeConstraint(with: view, attribute: .centerX, constant: constant) 69 | } 70 | 71 | @discardableResult 72 | func centerY(with view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 73 | return makeConstraint(with: view, attribute: .centerY, constant: constant) 74 | } 75 | 76 | @discardableResult 77 | func alignTop(with view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 78 | return makeConstraint(with: view, attribute: .top, constant: constant) 79 | } 80 | 81 | @discardableResult 82 | func alignBottom(with view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 83 | return makeConstraint(with: view, attribute: .bottom, constant: -constant) 84 | } 85 | 86 | @discardableResult 87 | func alignLeading(with view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 88 | return makeConstraint(with: view, attribute: .leading, constant: constant) 89 | } 90 | 91 | @discardableResult 92 | func alignTrailing(with view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 93 | return makeConstraint(with: view, attribute: .trailing, constant: -constant) 94 | } 95 | 96 | @discardableResult 97 | func alignTopToBottom(of view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 98 | let constraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: constant) 99 | constraint.isActive = true 100 | return constraint 101 | } 102 | 103 | @discardableResult 104 | func alignBottomToTop(of view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 105 | let constraint = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: -constant) 106 | constraint.isActive = true 107 | return constraint 108 | } 109 | 110 | @discardableResult 111 | func alignLeadingToTrailing(of view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 112 | let constraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: constant) 113 | constraint.isActive = true 114 | return constraint 115 | } 116 | 117 | @discardableResult 118 | func alignTrailingToLeading(of view: NSView, constant: CGFloat = 0) -> NSLayoutConstraint { 119 | let constraint = NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: -constant) 120 | constraint.isActive = true 121 | return constraint 122 | } 123 | 124 | // Make constraints for the common case where you want 125 | // receiver's frame to match that of `view`. 126 | 127 | @discardableResult 128 | func alignFrame(with view: NSView) -> [NSLayoutConstraint] { 129 | var constraints = [NSLayoutConstraint]() 130 | constraints.append( alignTop(with: view) ) 131 | constraints.append( alignBottom(with: view) ) 132 | constraints.append( alignLeading(with: view) ) 133 | constraints.append( alignTrailing(with: view) ) 134 | return constraints 135 | } 136 | 137 | // Auto Layout's visual format language (VFL) is great, but using it 138 | // can lead to verbose and unwieldy code. This method helps to make 139 | // using VFL more concise and readable by avoiding the repetition of 140 | // metrics and views while consolidating visual format strings. 141 | // 142 | // This method takes a dictionary of metrics, a dictionary of views, 143 | // and an array of (formatString, NSLayoutFormatOptions) tuples and 144 | // creates (and optionally returns) the necessary constraints. 145 | // 146 | // Example: 147 | // 148 | // let metrics = ["m1": metric1, "m2", metric2] 149 | // let views = ["v1": view1, "v2": view2] 150 | // 151 | // view.makeConstraints(metrics: metrics, views: views, formatsAndOptions: [ 152 | // ("H:|-m1-[v1]-[v2]-m2-|", .AlignAllCenterY), 153 | // ("V:|-m1-[v1]", []) 154 | // ]) 155 | 156 | @discardableResult 157 | func makeConstraints(metrics: [String: NSNumber]?, views: [String: Any], formatsAndOptions: [(format: String, options: NSLayoutConstraint.FormatOptions)]) -> [NSLayoutConstraint] { 158 | var constraints = [NSLayoutConstraint]() 159 | for (format, options) in formatsAndOptions { 160 | constraints += NSLayoutConstraint.constraints(withVisualFormat: format, options: options, metrics: metrics, views: views) 161 | } 162 | NSLayoutConstraint.activate(constraints) 163 | return constraints 164 | } 165 | } 166 | 167 | // MARK: - NSView Animation 168 | 169 | extension NSView { 170 | 171 | // Fade receiver in or out. Timing is in seconds. 172 | 173 | func fadeIn(after delay: TimeInterval, duration: TimeInterval, completionHandler: (() -> Void)?) { 174 | moDispatch(after: delay) { 175 | NSAnimationContext.runAnimationGroup({ context in 176 | context.duration = duration 177 | context.timingFunction = CAMediaTimingFunction(name: .easeOut) 178 | self.animator().alphaValue = 1 179 | }, completionHandler: completionHandler) 180 | } 181 | } 182 | 183 | func fadeOut(after delay: TimeInterval, duration: TimeInterval, completionHandler: (() -> Void)?) { 184 | moDispatch(after: delay) { 185 | NSAnimationContext.runAnimationGroup({ context in 186 | context.duration = duration 187 | context.timingFunction = CAMediaTimingFunction(name: .easeIn) 188 | self.animator().alphaValue = 0 189 | }, completionHandler: completionHandler) 190 | } 191 | } 192 | } 193 | 194 | // MARK: - NSViewController 195 | 196 | extension NSViewController { 197 | 198 | // Add a `duration` parameter to transitionFromViewController(...). 199 | 200 | func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions, duration: TimeInterval, completionHandler completion: (() -> Void)?) { 201 | NSAnimationContext.runAnimationGroup({ context in 202 | context.duration = duration 203 | context.timingFunction = CAMediaTimingFunction(name: .easeOut) 204 | self.transition(from: fromViewController, to: toViewController, options: options, completionHandler: completion) 205 | }, completionHandler: nil) 206 | } 207 | } 208 | 209 | // MARK: - NSImage 210 | 211 | extension NSImage { 212 | 213 | // Tint receiver with `color` and return as new image. 214 | 215 | func tint(color: NSColor) -> NSImage? { 216 | return NSImage(size: self.size, flipped: false, drawingHandler: { rect -> Bool in 217 | let context = NSGraphicsContext.current?.cgContext 218 | context?.interpolationQuality = .none 219 | self.draw(in: NSRect(origin: CGPoint.zero, size: self.size)) 220 | context?.setFillColor(color.cgColor) 221 | context?.setBlendMode(.sourceAtop) 222 | context?.fill(rect) 223 | return true 224 | }) 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /Snk/MoCode.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on May 26, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // MARK: - MoView 8 | 9 | // MoView is an Auto Layout-ready view that makes custom 10 | // drawing possible without the need to subclass. 11 | 12 | class MoView: NSView { 13 | 14 | // Optional background color. 15 | 16 | var bgColor: NSColor? { 17 | didSet { 18 | needsDisplay = true 19 | } 20 | } 21 | 22 | // Optional draw block allows custom drawing without subclassing. 23 | // The block is passed the current context and the view bounds. 24 | 25 | var drawBlock: ((_ context: CGContext, _ bounds: NSRect) -> Void)? { 26 | didSet { 27 | needsDisplay = true 28 | } 29 | } 30 | 31 | // By default, do not translate autoresizing mask into constraints 32 | // so doing Auto Layout in code is a little more efficient. 33 | 34 | override init(frame frameRect: NSRect) { 35 | super.init(frame: frameRect) 36 | self.translatesAutoresizingMaskIntoConstraints = false 37 | } 38 | 39 | override func draw(_ dirtyRect: NSRect) { 40 | // Fill with optional background color. 41 | 42 | if let color = bgColor { 43 | color.set() 44 | bounds.fill(using: .sourceOver) 45 | } 46 | 47 | // Draw with optional draw block. 48 | 49 | if let block = drawBlock { 50 | let context = NSGraphicsContext.current!.cgContext 51 | block(context, bounds) 52 | } 53 | 54 | super.draw(dirtyRect) 55 | } 56 | 57 | required init?(coder: NSCoder) { 58 | fatalError("init(coder:) has not been implemented") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Snk/Snk.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Snk/Snk.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on May 26, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // MARK: NSUserDefaults keys 8 | 9 | let kHiScoreSlowKey = "HiScoreSlow" 10 | let kHiScoreMediumKey = "HiScoreMedium" 11 | let kHiScoreFastKey = "HiScoreFast" 12 | let kEnableSoundsKey = "EnableSounds" 13 | let kEnableMusicKey = "EnableMusic" 14 | let kBigBoardKey = "BigBoard" 15 | let kThemeNameKey = "ThemeName" 16 | 17 | // MARK: - Sounds 18 | 19 | let kSoundStartup = "splash.mp3" 20 | let kSoundHover = "tick.mp3" 21 | let kSoundStartGame = "lance.mp3" 22 | let kSoundFoodExplosion = "boop.mp3" 23 | let kSoundAnimateTo3D = "3d.mp3" 24 | let kSoundRotateBoard = "tick.mp3" 25 | let kSoundSpinBoard = "spin.mp3" 26 | let kSoundCrash = "explosion.mp3" 27 | let kSoundGameOver = "gameover.mp3" 28 | let kSoundOk = "woosh.mp3" 29 | let kSoundVictory = "tada.mp3" 30 | 31 | // MARK: - Music 32 | 33 | // 1: 8-bit Melody http://www.looperman.com/loops/detail/68575/8-bit-melody-by-kinggjmaytryx-free-156bpm-electro-sh-loop 34 | // 2: Bibo by https://soundcloud.com/lv-7 35 | // 3: Flight of the Bumblebee https://www.youtube.com/watch?v=wOFgh2IdnZI 36 | let kSong1 = "loop.mp3" 37 | let kSong2 = "bibo.mp3" 38 | let kSong3 = "fotbb.mp3" 39 | 40 | // MARK: - Dimensions 41 | 42 | // Board dimensions. Includes the wall. 43 | let kCols = 15, 44 | kRows = 14 45 | 46 | let kBaseStep = 12 47 | 48 | // If kScale is 2, the game is scaled up by 2, 49 | // otherwise kScale is 1. 50 | // kStep is the step size in points. 51 | // Both kScale and kStep are var instead of let 52 | // because they can be reset if the user decides to 53 | // toggle the size. See AppDelegate toggleBoardSize(). 54 | var kScale: CGFloat = UserDefaults.standard.bool(forKey: kBigBoardKey) ? 2 : 1 55 | var kStep = kBaseStep * Int(kScale) 56 | 57 | // MARK: - Game settings 58 | 59 | enum SnkLevel: Int { 60 | case slow = 1, medium, fast 61 | } 62 | 63 | let kMaxScoreIncrement = 55 64 | 65 | // Seconds per frame 66 | let kLevel1SecPerFrame = 0.140 67 | let kLevel2SecPerFrame = 0.085 68 | let kLevel3SecPerFrame = 0.045 69 | 70 | // Scores which trigger the board to animate 71 | let kScoreSpin = 800 72 | let kScoreRotate = 720 73 | let kScore3D = 450 74 | -------------------------------------------------------------------------------- /Snk/SnkAudio.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 19, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // SnkAudio manages sound effects and music. 8 | // SharedAudio is a global instance of SnkAudio 9 | // to be used throughout the app. 10 | 11 | let SharedAudio = SnkAudio() 12 | 13 | final class SnkAudio: NSObject { 14 | 15 | // Map sound filenames to sounds. 16 | var sounds = [String: NSSound]() 17 | 18 | // User preferences to enable sounds and music. What we 19 | // will really do is mute or unmute the sound so that 20 | // it can be resumed if the value is changed midstream. 21 | @objc var soundsEnabled = true 22 | @objc var musicEnabled: Bool = true { 23 | didSet { 24 | guard let music = music else { return } 25 | music.volume = musicEnabled ? musicVolume : 0 26 | } 27 | } 28 | 29 | // Currently playing sound or music. 30 | var sound: NSSound? = nil 31 | var music: NSSound? = nil 32 | 33 | // Need to remember music volume in case we 34 | // disable and then re-enable music. 35 | var musicVolume: Float = 1 36 | 37 | override init() { 38 | super.init() 39 | 40 | // Bind our soundsEnabled and musicEnabled 41 | // properties to the corresponding defaults 42 | // which the user can change at any time. 43 | 44 | self.bind(NSBindingName(rawValue: "soundsEnabled"), to: NSUserDefaultsController.shared, withKeyPath: "values." + kEnableSoundsKey, options: nil) 45 | self.bind(NSBindingName(rawValue: "musicEnabled"), to: NSUserDefaultsController.shared, withKeyPath: "values." + kEnableMusicKey, options: nil) 46 | } 47 | 48 | func loadSounds() { 49 | // Populate the sounds dictionary with loaded 50 | // sounds so we can play them instantly later. 51 | 52 | for filePath in [kSoundStartup, kSoundHover, kSoundStartGame, kSoundFoodExplosion, kSoundAnimateTo3D, kSoundRotateBoard, kSoundSpinBoard, kSoundCrash, kSoundGameOver, kSoundOk, kSoundVictory] { 53 | sounds[filePath] = NSSound(named: filePath)! 54 | } 55 | } 56 | 57 | // Play or stop playing sounds and music... 58 | 59 | func play(sound filePath: String, volume: Float = 1) { 60 | sound = sounds[filePath] 61 | guard let sound = sound else { return } 62 | sound.volume = soundsEnabled ? volume : 0 63 | if sound.isPlaying { 64 | sound.stop() 65 | } 66 | sound.play() 67 | } 68 | 69 | func play(music filePath: String, volume: Float = 1, loop: Bool = false) { 70 | stopMusic() 71 | music = NSSound(named: filePath) 72 | guard let music = music else { return } 73 | music.volume = musicEnabled ? volume : 0 74 | music.loops = loop 75 | music.play() 76 | musicVolume = volume 77 | } 78 | 79 | func stopMusic() { 80 | guard let music = music else { return } 81 | music.stop() 82 | } 83 | 84 | func stopEverything() { 85 | guard let sound = sound else { return } 86 | sound.stop() 87 | stopMusic() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Snk/SnkHoverButton.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 14, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // SnkHoverButton can notify a delegate that 8 | // its hover state has changed. 9 | 10 | protocol SnkHoverButtonDelegate: AnyObject { 11 | func hoverChanged(for button: SnkHoverButton) 12 | } 13 | 14 | // SnkHoverButton shows its content in a dimmed state 15 | // by default. On hover, content is shown with 16 | // alphaValue = 1. Optionally, the button will also 17 | // draw a border on hover. You can add subviews or 18 | // provide an image for the button's content. 19 | 20 | class SnkHoverButton: NSButton { 21 | 22 | weak var delegate: SnkHoverButtonDelegate? 23 | 24 | // Appearance customization. 25 | 26 | var bgHighlightColor = NSColor(white: 1, alpha: 0.4) 27 | var borderHighlightColor = NSColor.clear 28 | var borderWidth: CGFloat = 4 29 | var dimmedAlpha: CGFloat = 0.4 30 | 31 | var hovering = false { 32 | didSet { 33 | delegate?.hoverChanged(for: self) 34 | } 35 | } 36 | 37 | override init(frame frameRect: NSRect) { 38 | super.init(frame: frameRect) 39 | self.wantsLayer = true 40 | self.title = "" 41 | self.isBordered = false 42 | self.bezelStyle = .regularSquare 43 | self.translatesAutoresizingMaskIntoConstraints = false 44 | } 45 | 46 | convenience init(imageName: String, tint: NSColor?, scale: CGFloat) { 47 | self.init() 48 | let imageView = SnkImageView(named: imageName, tint: tint, scale: scale) 49 | self.addSubview(imageView) 50 | self.alignFrame(with: imageView) 51 | } 52 | 53 | override var wantsUpdateLayer: Bool { 54 | return true 55 | } 56 | 57 | override func updateTrackingAreas() { 58 | for trackingArea in self.trackingAreas { 59 | removeTrackingArea(trackingArea as NSTrackingArea) 60 | } 61 | let ta = NSTrackingArea(rect: NSZeroRect, options: ([NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.activeAlways, NSTrackingArea.Options.inVisibleRect, NSTrackingArea.Options.enabledDuringMouseDrag]), owner: self, userInfo: nil) 62 | addTrackingArea(ta) 63 | } 64 | 65 | override func mouseEntered(with theEvent: NSEvent) { 66 | hovering = true 67 | needsDisplay = true 68 | } 69 | 70 | override func mouseExited(with theEvent: NSEvent) { 71 | hovering = false 72 | needsDisplay = true 73 | } 74 | 75 | override func updateLayer() { 76 | // Set the button's alphaValue, border color, and 77 | // background color depending on its highlight and 78 | // hovering state. 79 | 80 | let alpha = isHighlighted || hovering ? 1 : dimmedAlpha 81 | let border = isHighlighted || hovering ? borderHighlightColor : NSColor.clear 82 | let bg = isHighlighted ? bgHighlightColor : NSColor.clear 83 | 84 | self.alphaValue = alpha 85 | self.layer?.borderWidth = borderWidth * kScale 86 | self.layer?.borderColor = border.cgColor 87 | self.layer?.backgroundColor = bg.cgColor 88 | } 89 | 90 | required init?(coder: NSCoder) { 91 | fatalError("init(coder:) has not been implemented") 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Snk/SnkImageView.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 13, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // SnkImageView is a view with an image drawn into it. 8 | // It is meant to be used with pixel art where the 9 | // source image is very small and is to be scaled up, 10 | // typically by an integral amount, to a viewable size. 11 | 12 | class SnkImageView: MoView { 13 | 14 | // Create a view with `image` drawn into it. 15 | // The size (in points, not pixels) of the view is the 16 | // original image size scaled by `scale`. For example, 17 | // if `image` has size (100, 22) and `scale` is 4, the 18 | // resuling SnkImageView will have size (400, 88) and 19 | // the image will be drawn scaled to fill that size. 20 | 21 | init(image: NSImage?, scale: CGFloat = 1) { 22 | // If `image` is nil, return an empty view 23 | guard let image = image else { 24 | super.init(frame: NSZeroRect) 25 | return 26 | } 27 | let width = scale * image.size.width * kScale 28 | let height = scale * image.size.height * kScale 29 | super.init(frame: NSRect(x: 0, y: 0, width: width, height: height)) 30 | self.makeConstraints(width: width, height: height) 31 | self.drawBlock = { (context, bounds) in 32 | image.draw(in: bounds) 33 | } 34 | } 35 | 36 | // Create a view with image named `named` drawn into it. 37 | // Tint it with `tint` and scale it by `scale`. If `tint` 38 | // is nil, don't tint the image. 39 | 40 | convenience init(named name: String, tint: NSColor?, scale: CGFloat = 1) { 41 | let image = (tint == nil) ? NSImage(named: name) 42 | : NSImage(named: name)?.tint(color: tint!) 43 | self.init(image: image, scale: scale) 44 | } 45 | 46 | required init?(coder: NSCoder) { 47 | fatalError("init(coder:) has not been implemented") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Snk/SnkLevelButton.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 14, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // Dimmed high score label alphaValue 8 | let kScoreDimmedAlpha: CGFloat = 0.2 9 | 10 | // SnkLevelButton is a bit of a misnomer. It is actually a 11 | // container for a level button and a high score label. 12 | // Both the button and the label highlight when the user 13 | // mouses over the button. 14 | 15 | final class SnkLevelButton: MoView, SnkHoverButtonDelegate { 16 | 17 | let scoreLabel = SnkScoreLabel(fgColor: SharedTheme.color(.logo)) 18 | 19 | init(level: SnkLevel, target: AnyObject?, action: Selector) { 20 | super.init(frame: NSZeroRect) 21 | 22 | // There are 2 parts: the level button and the high 23 | // score label. 24 | 25 | // Let's build a level button. It's an SnkHoverButton 26 | // with 2 additional subviews: the level number and 27 | // an animated snake. The level number is just an 28 | // SnkImageView centered in the button. The animated 29 | // snake is a layer-hosting view which hosts a layer 30 | // that runs a key-frame animation. 31 | 32 | let button = SnkHoverButton() 33 | 34 | // First, do basic button setup. 35 | 36 | button.target = target 37 | button.action = action 38 | button.tag = level.rawValue 39 | button.keyEquivalent = String(level.rawValue) 40 | button.keyEquivalentModifierMask = NSEvent.ModifierFlags(rawValue: 0) // No modifier 41 | button.delegate = self 42 | 43 | // Button dimensions. Button shows a border. 44 | 45 | button.makeConstraints(width: 48 * kScale, height: 48 * kScale) 46 | button.borderHighlightColor = SharedTheme.color(.buttonBorder) 47 | button.dimmedAlpha = 1 48 | 49 | // Set up the view that shows the level number. 50 | 51 | let numberView = SnkImageView(named: String(level.rawValue), tint: SharedTheme.color(.buttonNumber), scale: 5) 52 | button.addSubview(numberView) 53 | numberView.alphaValue = 0.3 54 | numberView.centerX(with: button) 55 | numberView.centerY(with: button) 56 | 57 | // Set up a key-frame animation for the snake. 58 | // There are 24 frames in the animation and the 59 | // animation duration matches the level speed. 60 | 61 | let snakeAnim = CAKeyframeAnimation(keyPath: "contentsRect") 62 | snakeAnim.calculationMode = .discrete 63 | snakeAnim.repeatCount = Float.infinity 64 | snakeAnim.values = (0...23).map { 65 | NSValue(rect: NSRect(x: CGFloat($0)/24, y: 0, width: 1/24, height: 1)) 66 | } 67 | switch level { // anim duration matches level speed 68 | case .slow: snakeAnim.duration = 24 * kLevel1SecPerFrame 69 | case .medium: snakeAnim.duration = 24 * kLevel2SecPerFrame 70 | default: snakeAnim.duration = 24 * kLevel3SecPerFrame 71 | } 72 | 73 | // Set up the layer that shows the animated snake. 74 | 75 | let snakeLayer = CALayer() 76 | 77 | // The animated snake for level 2 is rotated 90 degrees 78 | // because the alternating orientation looks better. 79 | 80 | if level == .medium { 81 | snakeLayer.transform = CATransform3DMakeRotation(CGFloat(Double.pi/2), 0, 0, 1) 82 | } 83 | 84 | snakeLayer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable] 85 | snakeLayer.magnificationFilter = .nearest 86 | snakeLayer.contents = NSImage(named: "frames")?.tint(color: SharedTheme.color(.snake)) 87 | snakeLayer.add(snakeAnim, forKey: "snakeAnim") 88 | 89 | // Create a view to host the snake layer and center it 90 | // within the button. 91 | 92 | let snakeLayerHostingView = MoView() 93 | snakeLayerHostingView.wantsLayer = true 94 | snakeLayerHostingView.layer = CALayer() 95 | snakeLayerHostingView.layer?.addSublayer(snakeLayer) 96 | snakeLayerHostingView.makeConstraints(width: 32 * kScale, height: 32 * kScale) 97 | 98 | button.addSubview(snakeLayerHostingView) 99 | snakeLayerHostingView.centerX(with: button) 100 | snakeLayerHostingView.centerY(with: button) 101 | 102 | // Ok, the level button is done! 103 | 104 | // Set up the score label. Bind its score property to 105 | // the hi score stored in NSUserDefaults for `level`. 106 | 107 | var keyPath = "values." 108 | switch level { 109 | case .slow: keyPath += kHiScoreSlowKey 110 | case .medium: keyPath += kHiScoreMediumKey 111 | default: keyPath += kHiScoreFastKey 112 | } 113 | scoreLabel.bind(NSBindingName(rawValue: "score"), to: NSUserDefaultsController.shared, withKeyPath: keyPath, options: nil) 114 | scoreLabel.alphaValue = kScoreDimmedAlpha 115 | 116 | // Ok, the score label is done! 117 | 118 | // Finally, layout the button and score. 119 | // 120 | // +-self---+ 121 | // |[button]| 122 | // | |a | 123 | // |[scoreL]| 124 | // +--------+ 125 | 126 | self.addSubview(button) 127 | self.addSubview(scoreLabel) 128 | 129 | let metrics = ["a": 4 * kScale] 130 | let views = ["button": button, "score": scoreLabel] as [String : Any] 131 | 132 | self.makeConstraints(metrics: metrics as [String : NSNumber], views: views, formatsAndOptions: [ 133 | ("V:|[button]-a-[score]|", NSLayoutConstraint.FormatOptions.alignAllCenterX), 134 | ("H:|[button]|", []) 135 | ]) 136 | } 137 | 138 | // When the hover state of the button changes, change 139 | // the alphaValue of the score label. 140 | 141 | func hoverChanged(for button: SnkHoverButton) { 142 | scoreLabel.alphaValue = button.hovering ? 1 : kScoreDimmedAlpha 143 | if button.hovering { 144 | SharedAudio.play(sound: kSoundHover, volume: 0.3) 145 | } 146 | } 147 | 148 | required init?(coder: NSCoder) { 149 | fatalError("init(coder:) has not been implemented") 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Snk/SnkScoreLabel.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on June 16, 2015 3 | // Copyright (c) 2015 mowglii.com 4 | 5 | import Cocoa 6 | 7 | // SnkScoreLabel draws a score with foreground and 8 | // background colors. The background forms a border 9 | // (stroke) around the digits. See digitsfg.png and 10 | // digitsbg.png to see what the digits look like. 11 | // 12 | // Example: 21 13 | // X: digitFg, -: digitBg, .: viewBg 14 | // 15 | // ------------. 16 | // -XXXXX--XXX-. 17 | // -----X---XX-. 18 | // -XXXXX-.-XX-. 19 | // -X-----.-XX-. 20 | // -XXXXX-.-XX-. 21 | // -------.----. 22 | // 23 | // When drawn on screen, digits are scaled up 2x. 24 | 25 | final class SnkScoreLabel: MoView { 26 | 27 | // Only draw foreground digits when false. 28 | 29 | var drawsDigitsBackground = true 30 | 31 | // Validate score, set view size, and draw it. 32 | 33 | @objc var score: Int = 0 { 34 | didSet { 35 | self.score = max(0, self.score) // enforce score >= 0 36 | self.invalidateIntrinsicContentSize() 37 | self.needsDisplay = true 38 | } 39 | } 40 | 41 | // View size changes to fit digits in score. 42 | 43 | override var intrinsicContentSize: CGSize { 44 | let numberOfDigits = CGFloat(String(score).count) 45 | // Each digit is 12x14 points + 1 on each side for margin 46 | let width = numberOfDigits * (12 * kScale) + (2 * kScale) 47 | return CGSize(width: width, height: 14 * kScale) 48 | } 49 | 50 | let digitsFg: NSImage! 51 | let digitsBg: NSImage! 52 | 53 | // Create a score label with a digit background. 54 | 55 | init(fgColor: NSColor, bgColor: NSColor) { 56 | self.digitsFg = NSImage(named: "digitsfg")?.tint(color: fgColor) 57 | self.digitsBg = NSImage(named: "digitsbg")?.tint(color: bgColor) 58 | 59 | super.init(frame: NSZeroRect) 60 | 61 | self.drawBlock = { (context, bounds) in 62 | 63 | if self.score == 0 { return } 64 | 65 | // No anti-aliasing so pixels are sharp when scaled. 66 | context.interpolationQuality = .none 67 | 68 | // Convert the score to a string so we can enumerate its 69 | // digits. First draw the digit background and then draw 70 | // the digit foreground on top of it. 71 | 72 | let scoreString = String(self.score) 73 | for (index, digitCharacter) in scoreString.enumerated() { 74 | let digit = Int( String(digitCharacter) )! 75 | let toOffset = 12 * kScale * CGFloat(index) 76 | let toWidth = 14 * kScale 77 | let fromOffset = 7 * digit 78 | let toRect = NSRect(x: toOffset, y: 0, width: toWidth, height: toWidth) 79 | let fromRect = NSRect(x: fromOffset, y: 0, width: 7, height: 7) 80 | if self.drawsDigitsBackground { 81 | self.digitsBg.draw(in: toRect, from: fromRect, operation: .sourceOver, fraction: 1) 82 | } 83 | self.digitsFg.draw(in: toRect, from: fromRect, operation: .sourceOver, fraction: 1) 84 | } 85 | } 86 | } 87 | 88 | // Create a score label without a digit background. 89 | 90 | convenience init(fgColor: NSColor) { 91 | self.init(fgColor: fgColor, bgColor: fgColor) 92 | self.drawsDigitsBackground = false 93 | } 94 | 95 | required init?(coder: NSCoder) { 96 | fatalError("init(coder:) has not been implemented") 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Snk/SnkTheme.swift: -------------------------------------------------------------------------------- 1 | 2 | // Created by Sanjay Madan on 6/27/16. 3 | // Copyright © 2016 mowglii.com. All rights reserved. 4 | 5 | import Cocoa 6 | 7 | enum SnkThemeName: String { 8 | case standard = "Standard" 9 | case grayscale = "Grayscale" 10 | case spacecadet = "Space Cadet" 11 | case oldphone = "Old Phone" 12 | } 13 | 14 | enum SnkColorName { 15 | case background, logo, snake, wall, food, explosion, buttonBorder, buttonNumber 16 | } 17 | 18 | typealias SnkTheme = (name: SnkThemeName, colors: [SnkColorName: NSColor]) 19 | 20 | let SharedTheme = SnkThemeManager() 21 | 22 | final class SnkThemeManager { 23 | 24 | let themes: [SnkTheme] = [ 25 | (.standard, [ 26 | .background: NSColor(red: 0.45, green: 0.73, blue: 1, alpha: 1), 27 | .logo: NSColor.white, 28 | .snake: NSColor.white, 29 | .wall: NSColor(red: 1, green: 0.95, blue: 0.4, alpha:1), 30 | .food: NSColor.white, 31 | .explosion: NSColor.white, 32 | .buttonBorder: NSColor(red: 1, green: 0.95, blue: 0.4, alpha:1), 33 | .buttonNumber: NSColor.white 34 | ]), 35 | (.grayscale, [ 36 | .background: NSColor(white: 0.4, alpha: 1), 37 | .logo: NSColor(white: 0.9, alpha: 1), 38 | .snake: NSColor(white: 0.9, alpha: 1), 39 | .wall: NSColor(white: 0.7, alpha: 1), 40 | .food: NSColor(white: 0.9, alpha: 1), 41 | .explosion: NSColor(white: 1.0, alpha: 1), 42 | .buttonBorder: NSColor(white: 0.8, alpha: 1), 43 | .buttonNumber: NSColor(white: 0.7, alpha: 1), 44 | ]), 45 | (.spacecadet, [ 46 | .background: NSColor(red: 0.09, green: 0.13, blue: 0.20, alpha: 1), 47 | .logo: NSColor(red: 0.13, green: 0.36, blue: 0.52, alpha: 1), 48 | .snake: NSColor(red: 0.96, green: 0.70, blue: 0.44, alpha: 1), 49 | .wall: NSColor(red: 0.13, green: 0.36, blue: 0.52, alpha: 1), 50 | .food: NSColor(red: 0.95, green: 0.38, blue: 0.40, alpha: 1), 51 | .explosion: NSColor(red: 0.80, green: 0.80, blue: 0.75, alpha: 1), 52 | .buttonBorder: NSColor(red: 0.95, green: 0.38, blue: 0.40, alpha: 1), 53 | .buttonNumber: NSColor(red: 0.60, green: 0.60, blue: 0.50, alpha: 1), 54 | ]), 55 | (.oldphone, [ 56 | .background: NSColor(red: 0.73, green: 0.86, blue: 0.60, alpha: 1), 57 | .logo: NSColor(red: 0.21, green: 0.25, blue: 0.20, alpha: 1), 58 | .snake: NSColor(red: 0.21, green: 0.25, blue: 0.20, alpha: 1), 59 | .wall: NSColor(red: 0.39, green: 0.46, blue: 0.37, alpha: 1), 60 | .food: NSColor(red: 0.21, green: 0.25, blue: 0.20, alpha: 1), 61 | .explosion: NSColor(red: 0.21, green: 0.25, blue: 0.20, alpha: 1), 62 | .buttonBorder: NSColor(red: 0.21, green: 0.25, blue: 0.20, alpha: 1), 63 | .buttonNumber: NSColor(red: 0.36, green: 0.48, blue: 0.35, alpha: 1), 64 | ]), 65 | ] 66 | 67 | private(set) var themeIndex = 0 // Standard theme 68 | 69 | func color(_ colorName: SnkColorName) -> NSColor { 70 | // Return the .standard color (themeIndex 0) if themeIndex 71 | // is out of bounds or colorName isn't defined on the 72 | // selected theme. 73 | let index = 0.. 13 | #endif 14 | 15 | #import "SUExport.h" 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | /*! 20 | * A class for containing downloaded data along with some information about it. 21 | */ 22 | SU_EXPORT @interface SPUDownloadData : NSObject 23 | 24 | - (instancetype)initWithData:(NSData *)data textEncodingName:(NSString * _Nullable)textEncodingName MIMEType:(NSString * _Nullable)MIMEType; 25 | 26 | /*! 27 | * The raw data that was downloaded. 28 | */ 29 | @property (nonatomic, readonly) NSData *data; 30 | 31 | /*! 32 | * The IANA charset encoding name if available. Eg: "utf-8" 33 | */ 34 | @property (nonatomic, readonly, nullable, copy) NSString *textEncodingName; 35 | 36 | /*! 37 | * The MIME type if available. Eg: "text/plain" 38 | */ 39 | @property (nonatomic, readonly, nullable, copy) NSString *MIMEType; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloader.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPUDownloader.h 3 | // Downloader 4 | // 5 | // Created by Mayur Pawashe on 4/1/16. 6 | // Copyright © 2016 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | #import "SPUDownloaderProtocol.h" 15 | 16 | @protocol SPUDownloaderDelegate; 17 | 18 | // This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection. 19 | @interface SPUDownloader : NSObject 20 | 21 | // Due to XPC remote object reasons, this delegate is strongly referenced 22 | // Invoke cleanup when done with this instance 23 | - (instancetype)initWithDelegate:(id )delegate; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPUDownloaderDelegate.h 3 | // Sparkle 4 | // 5 | // Created by Mayur Pawashe on 4/1/16. 6 | // Copyright © 2016 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @class SPUDownloadData; 18 | 19 | @protocol SPUDownloaderDelegate 20 | 21 | // This is only invoked for persistent downloads 22 | - (void)downloaderDidSetDestinationName:(NSString *)destinationName temporaryDirectory:(NSString *)temporaryDirectory; 23 | 24 | // Under rare cases, this may be called more than once, in which case the current progress should be reset back to 0 25 | // This is only invoked for persistent downloads 26 | - (void)downloaderDidReceiveExpectedContentLength:(int64_t)expectedContentLength; 27 | 28 | // This is only invoked for persistent downloads 29 | - (void)downloaderDidReceiveDataOfLength:(uint64_t)length; 30 | 31 | // downloadData is nil if this is a persisent download, otherwise it's non-nil if it's a temporary download 32 | - (void)downloaderDidFinishWithTemporaryDownloadData:(SPUDownloadData * _Nullable)downloadData; 33 | 34 | - (void)downloaderDidFailWithError:(NSError *)error; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPUDownloaderProtocol.h 3 | // PersistentDownloader 4 | // 5 | // Created by Mayur Pawashe on 4/1/16. 6 | // Copyright © 2016 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @class SPUURLRequest; 18 | 19 | // The protocol that this service will vend as its API. This header file will also need to be visible to the process hosting the service. 20 | @protocol SPUDownloaderProtocol 21 | 22 | - (void)startPersistentDownloadWithRequest:(SPUURLRequest *)request bundleIdentifier:(NSString *)bundleIdentifier desiredFilename:(NSString *)desiredFilename; 23 | 24 | - (void)startTemporaryDownloadWithRequest:(SPUURLRequest *)request; 25 | 26 | - (void)downloadDidFinish; 27 | 28 | - (void)cleanup; 29 | 30 | - (void)cancel; 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPUDownloaderSession.h 3 | // Sparkle 4 | // 5 | // Created by Deadpikle on 12/20/17. 6 | // Copyright © 2017 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | #import "SPUDownloader.h" 15 | #import "SPUDownloaderProtocol.h" 16 | 17 | NS_CLASS_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0) 18 | @interface SPUDownloaderSession : SPUDownloader 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPUURLRequest.h 3 | // Sparkle 4 | // 5 | // Created by Mayur Pawashe on 5/19/16. 6 | // Copyright © 2016 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | // A class that wraps NSURLRequest and implements NSSecureCoding 18 | // This class exists because NSURLRequest did not support NSSecureCoding in macOS 10.8 19 | // I have not verified if NSURLRequest in 10.9 implements NSSecureCoding or not 20 | @interface SPUURLRequest : NSObject 21 | 22 | // Creates a new URL request 23 | // Only these properties are currently tracked: 24 | // * URL 25 | // * Cache policy 26 | // * Timeout interval 27 | // * HTTP header fields 28 | // * networkServiceType 29 | + (instancetype)URLRequestWithRequest:(NSURLRequest *)request; 30 | 31 | @property (nonatomic, readonly) NSURLRequest *request; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUAppcast.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUAppcast.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/12/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUAPPCAST_H 10 | #define SUAPPCAST_H 11 | 12 | #import 13 | #import "SUExport.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @class SUAppcastItem; 18 | SU_EXPORT @interface SUAppcast : NSObject 19 | 20 | @property (copy, nullable) NSString *userAgentString; 21 | @property (copy, nullable) NSDictionary *httpHeaders; 22 | 23 | - (void)fetchAppcastFromURL:(NSURL *)url inBackground:(BOOL)bg completionBlock:(void (^)(NSError *_Nullable))err; 24 | - (SUAppcast *)copyWithoutDeltaUpdates; 25 | 26 | @property (readonly, copy, nullable) NSArray *items; 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUAppcastItem.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/12/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUAPPCASTITEM_H 10 | #define SUAPPCASTITEM_H 11 | 12 | #if __has_feature(modules) 13 | @import Foundation; 14 | #else 15 | #import 16 | #endif 17 | #import "SUExport.h" 18 | @class SUSignatures; 19 | 20 | SU_EXPORT @interface SUAppcastItem : NSObject 21 | @property (copy, readonly) NSString *title; 22 | @property (copy, readonly) NSString *dateString; 23 | @property (copy, readonly) NSDate *date; 24 | @property (copy, readonly) NSString *itemDescription; 25 | @property (strong, readonly) NSURL *releaseNotesURL; 26 | @property (strong, readonly) SUSignatures *signatures; 27 | @property (copy, readonly) NSString *minimumSystemVersion; 28 | @property (copy, readonly) NSString *maximumSystemVersion; 29 | @property (strong, readonly) NSURL *fileURL; 30 | @property (nonatomic, readonly) uint64_t contentLength; 31 | @property (copy, readonly) NSString *versionString; 32 | @property (copy, readonly) NSString *osString; 33 | @property (copy, readonly) NSString *displayVersionString; 34 | @property (copy, readonly) NSDictionary *deltaUpdates; 35 | @property (strong, readonly) NSURL *infoURL; 36 | @property (copy, readonly) NSNumber* phasedRolloutInterval; 37 | @property (copy, readonly) NSString *minimumAutoupdateVersion; 38 | 39 | // Initializes with data from a dictionary provided by the RSS class. 40 | - (instancetype)initWithDictionary:(NSDictionary *)dict; 41 | - (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error; 42 | 43 | @property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate; 44 | @property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate; 45 | @property (getter=isMacOsUpdate, readonly) BOOL macOsUpdate; 46 | @property (getter=isInformationOnlyUpdate, readonly) BOOL informationOnlyUpdate; 47 | 48 | // Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. 49 | @property (readonly, copy) NSDictionary *propertiesDictionary; 50 | 51 | - (NSURL *)infoURL; 52 | 53 | @end 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUCodeSigningVerifier.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 7/5/12. 6 | // 7 | // 8 | 9 | #ifndef SUCODESIGNINGVERIFIER_H 10 | #define SUCODESIGNINGVERIFIER_H 11 | 12 | #if __has_feature(modules) 13 | @import Foundation; 14 | #else 15 | #import 16 | #endif 17 | #import "SUExport.h" 18 | 19 | SU_EXPORT @interface SUCodeSigningVerifier : NSObject 20 | + (BOOL)codeSignatureAtBundleURL:(NSURL *)oldBundlePath matchesSignatureAtBundleURL:(NSURL *)newBundlePath error:(NSError **)error; 21 | + (BOOL)codeSignatureIsValidAtBundleURL:(NSURL *)bundlePath error:(NSError **)error; 22 | + (BOOL)bundleAtURLIsCodeSigned:(NSURL *)bundlePath; 23 | + (NSDictionary *)codeSignatureInfoAtBundleURL:(NSURL *)bundlePath; 24 | @end 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUErrors.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUErrors.h 3 | // Sparkle 4 | // 5 | // Created by C.W. Betts on 10/13/14. 6 | // Copyright (c) 2014 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #ifndef SUERRORS_H 10 | #define SUERRORS_H 11 | 12 | #if __has_feature(modules) 13 | @import Foundation; 14 | #else 15 | #import 16 | #endif 17 | #import "SUExport.h" 18 | 19 | /** 20 | * Error domain used by Sparkle 21 | */ 22 | SU_EXPORT extern NSString *const SUSparkleErrorDomain; 23 | 24 | #pragma clang diagnostic push 25 | #pragma clang diagnostic ignored "-Wc++98-compat" 26 | typedef NS_ENUM(OSStatus, SUError) { 27 | // Appcast phase errors. 28 | SUAppcastParseError = 1000, 29 | SUNoUpdateError = 1001, 30 | SUAppcastError = 1002, 31 | SURunningFromDiskImageError = 1003, 32 | SURunningTranslocated = 1004, 33 | SUWebKitTerminationError = 1005, 34 | 35 | // Download phase errors. 36 | SUTemporaryDirectoryError = 2000, 37 | SUDownloadError = 2001, 38 | 39 | // Extraction phase errors. 40 | SUUnarchivingError = 3000, 41 | SUSignatureError = 3001, 42 | 43 | // Installation phase errors. 44 | SUFileCopyFailure = 4000, 45 | SUAuthenticationFailure = 4001, 46 | SUMissingUpdateError = 4002, 47 | SUMissingInstallerToolError = 4003, 48 | SURelaunchError = 4004, 49 | SUInstallationError = 4005, 50 | SUDowngradeError = 4006, 51 | SUInstallationCancelledError = 4007, 52 | 53 | // System phase errors 54 | SUSystemPowerOffError = 5000 55 | }; 56 | #pragma clang diagnostic pop 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUExport.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUExport.h 3 | // Sparkle 4 | // 5 | // Created by Jake Petroules on 2014-08-23. 6 | // Copyright (c) 2014 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #ifndef SUEXPORT_H 10 | #define SUEXPORT_H 11 | 12 | #ifdef BUILDING_SPARKLE 13 | #define SU_EXPORT __attribute__((visibility("default"))) 14 | #else 15 | #define SU_EXPORT 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUStandardVersionComparator.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 12/21/07. 6 | // Copyright 2007 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUSTANDARDVERSIONCOMPARATOR_H 10 | #define SUSTANDARDVERSIONCOMPARATOR_H 11 | 12 | #if __has_feature(modules) 13 | @import Foundation; 14 | #else 15 | #import 16 | #endif 17 | #import "SUExport.h" 18 | #import "SUVersionComparisonProtocol.h" 19 | 20 | NS_ASSUME_NONNULL_BEGIN 21 | 22 | /*! 23 | Sparkle's default version comparator. 24 | 25 | This comparator is adapted from MacPAD, by Kevin Ballard. 26 | It's "dumb" in that it does essentially string comparison, 27 | in components split by character type. 28 | */ 29 | SU_EXPORT @interface SUStandardVersionComparator : NSObject 30 | 31 | /*! 32 | Initializes a new instance of the standard version comparator. 33 | */ 34 | - (instancetype)init; 35 | 36 | /*! 37 | Returns a singleton instance of the comparator. 38 | 39 | It is usually preferred to alloc/init new a comparator instead. 40 | */ 41 | + (SUStandardVersionComparator *)defaultComparator; 42 | 43 | /*! 44 | Compares version strings through textual analysis. 45 | 46 | See the implementation for more details. 47 | */ 48 | - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; 49 | @end 50 | 51 | NS_ASSUME_NONNULL_END 52 | #endif 53 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUUpdater.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUUpdater.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 1/4/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUUPDATER_H 10 | #define SUUPDATER_H 11 | 12 | #if __has_feature(modules) 13 | @import Cocoa; 14 | #else 15 | #import 16 | #endif 17 | #import "SUExport.h" 18 | #import "SUVersionComparisonProtocol.h" 19 | #import "SUVersionDisplayProtocol.h" 20 | 21 | @class SUAppcastItem, SUAppcast; 22 | 23 | @protocol SUUpdaterDelegate; 24 | 25 | /*! 26 | The main API in Sparkle for controlling the update mechanism. 27 | 28 | This class is used to configure the update paramters as well as manually 29 | and automatically schedule and control checks for updates. 30 | */ 31 | SU_EXPORT @interface SUUpdater : NSObject 32 | 33 | @property (unsafe_unretained) IBOutlet id delegate; 34 | 35 | /*! 36 | The shared updater for the main bundle. 37 | 38 | This is equivalent to passing [NSBundle mainBundle] to SUUpdater::updaterForBundle: 39 | */ 40 | + (SUUpdater *)sharedUpdater; 41 | 42 | /*! 43 | The shared updater for a specified bundle. 44 | 45 | If an updater has already been initialized for the provided bundle, that shared instance will be returned. 46 | */ 47 | + (SUUpdater *)updaterForBundle:(NSBundle *)bundle; 48 | 49 | /*! 50 | Designated initializer for SUUpdater. 51 | 52 | If an updater has already been initialized for the provided bundle, that shared instance will be returned. 53 | */ 54 | - (instancetype)initForBundle:(NSBundle *)bundle; 55 | 56 | /*! 57 | Explicitly checks for updates and displays a progress dialog while doing so. 58 | 59 | This method is meant for a main menu item. 60 | Connect any menu item to this action in Interface Builder, 61 | and Sparkle will check for updates and report back its findings verbosely 62 | when it is invoked. 63 | 64 | This will find updates that the user has opted into skipping. 65 | */ 66 | - (IBAction)checkForUpdates:(id)sender; 67 | 68 | /*! 69 | The menu item validation used for the -checkForUpdates: action 70 | */ 71 | - (BOOL)validateMenuItem:(NSMenuItem *)menuItem; 72 | 73 | /*! 74 | Checks for updates, but does not display any UI unless an update is found. 75 | 76 | This is meant for programmatically initating a check for updates. That is, 77 | it will display no UI unless it actually finds an update, in which case it 78 | proceeds as usual. 79 | 80 | If automatic downloading of updates it turned on and allowed, however, 81 | this will invoke that behavior, and if an update is found, it will be downloaded 82 | in the background silently and will be prepped for installation. 83 | 84 | This will not find updates that the user has opted into skipping. 85 | */ 86 | - (void)checkForUpdatesInBackground; 87 | 88 | /*! 89 | A property indicating whether or not to check for updates automatically. 90 | 91 | Setting this property will persist in the host bundle's user defaults. 92 | The update schedule cycle will be reset in a short delay after the property's new value is set. 93 | This is to allow reverting this property without kicking off a schedule change immediately 94 | */ 95 | @property BOOL automaticallyChecksForUpdates; 96 | 97 | /*! 98 | A property indicating whether or not updates can be automatically downloaded in the background. 99 | 100 | Note that automatic downloading of updates can be disallowed by the developer 101 | or by the user's system if silent updates cannot be done (eg: if they require authentication). 102 | In this case, -automaticallyDownloadsUpdates will return NO regardless of how this property is set. 103 | 104 | Setting this property will persist in the host bundle's user defaults. 105 | */ 106 | @property BOOL automaticallyDownloadsUpdates; 107 | 108 | /*! 109 | A property indicating the current automatic update check interval. 110 | 111 | Setting this property will persist in the host bundle's user defaults. 112 | The update schedule cycle will be reset in a short delay after the property's new value is set. 113 | This is to allow reverting this property without kicking off a schedule change immediately 114 | */ 115 | @property NSTimeInterval updateCheckInterval; 116 | 117 | /*! 118 | Begins a "probing" check for updates which will not actually offer to 119 | update to that version. 120 | 121 | However, the delegate methods 122 | SUUpdaterDelegate::updater:didFindValidUpdate: and 123 | SUUpdaterDelegate::updaterDidNotFindUpdate: will be called, 124 | so you can use that information in your UI. 125 | 126 | Updates that have been skipped by the user will not be found. 127 | */ 128 | - (void)checkForUpdateInformation; 129 | 130 | /*! 131 | The URL of the appcast used to download update information. 132 | 133 | Setting this property will persist in the host bundle's user defaults. 134 | If you don't want persistence, you may want to consider instead implementing 135 | SUUpdaterDelegate::feedURLStringForUpdater: or SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: 136 | 137 | This property must be called on the main thread. 138 | */ 139 | @property (copy) NSURL *feedURL; 140 | 141 | /*! 142 | The host bundle that is being updated. 143 | */ 144 | @property (readonly, strong) NSBundle *hostBundle; 145 | 146 | /*! 147 | The bundle this class (SUUpdater) is loaded into. 148 | */ 149 | @property (strong, readonly) NSBundle *sparkleBundle; 150 | 151 | /*! 152 | The user agent used when checking for and downloading updates. 153 | 154 | The default implementation can be overrided. 155 | */ 156 | @property (nonatomic, copy) NSString *userAgentString; 157 | 158 | /*! 159 | The HTTP headers used when checking for and downloading updates. 160 | 161 | The keys of this dictionary are HTTP header fields (NSString) and values are corresponding values (NSString) 162 | */ 163 | @property (copy) NSDictionary *httpHeaders; 164 | 165 | /*! 166 | A property indicating whether or not the user's system profile information is sent when checking for updates. 167 | 168 | Setting this property will persist in the host bundle's user defaults. 169 | */ 170 | @property BOOL sendsSystemProfile; 171 | 172 | /*! 173 | A property indicating the decryption password used for extracting updates shipped as Apple Disk Images (dmg) 174 | */ 175 | @property (nonatomic, copy) NSString *decryptionPassword; 176 | 177 | /*! 178 | This function ignores normal update schedule, ignores user preferences, 179 | and interrupts users with an unwanted immediate app update. 180 | 181 | WARNING: this function should not be used in regular apps. This function 182 | is a user-unfriendly hack only for very special cases, like unstable 183 | rapidly-changing beta builds that would not run correctly if they were 184 | even one day out of date. 185 | 186 | Instead of this function you should set `SUAutomaticallyUpdate` to `YES`, 187 | which will gracefully install updates when the app quits. 188 | 189 | For UI-less/daemon apps that aren't usually quit, instead of this function, 190 | you can use the delegate method 191 | SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: 192 | or 193 | SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationBlock: 194 | to immediately start installation when an update was found. 195 | 196 | A progress dialog is shown but the user will never be prompted to read the 197 | release notes. 198 | 199 | This function will cause update to be downloaded twice if automatic updates are 200 | enabled. 201 | 202 | You may want to respond to the userDidCancelDownload delegate method in case 203 | the user clicks the "Cancel" button while the update is downloading. 204 | */ 205 | - (void)installUpdatesIfAvailable __attribute__((deprecated("This method is deprecated. Please set SUAutomaticallyUpdate in your Info.plist or use automaticallyDownloadsUpdates property instead."))); 206 | 207 | /*! 208 | Returns the date of last update check. 209 | 210 | \returns \c nil if no check has been performed. 211 | */ 212 | @property (readonly, copy) NSDate *lastUpdateCheckDate; 213 | 214 | /*! 215 | Appropriately schedules or cancels the update checking timer according to 216 | the preferences for time interval and automatic checks. 217 | 218 | This call does not change the date of the next check, 219 | but only the internal NSTimer. 220 | */ 221 | - (void)resetUpdateCycle; 222 | 223 | /*! 224 | A property indicating whether or not an update is in progress. 225 | 226 | Note this property is not indicative of whether or not user initiated updates can be performed. 227 | Use SUUpdater::validateMenuItem: for that instead. 228 | */ 229 | @property (readonly) BOOL updateInProgress; 230 | 231 | @end 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUUpdaterDelegate.h 3 | // Sparkle 4 | // 5 | // Created by Mayur Pawashe on 12/25/16. 6 | // Copyright © 2016 Sparkle Project. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SUExport.h" 16 | 17 | @protocol SUVersionComparison, SUVersionDisplay; 18 | @class SUUpdater, SUAppcast, SUAppcastItem; 19 | 20 | NS_ASSUME_NONNULL_BEGIN 21 | 22 | // ----------------------------------------------------------------------------- 23 | // SUUpdater Notifications for events that might be interesting to more than just the delegate 24 | // The updater will be the notification object 25 | // ----------------------------------------------------------------------------- 26 | SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; 27 | SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; 28 | SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; 29 | SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; 30 | #define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; 31 | #define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; 32 | 33 | // Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo 34 | SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; 35 | // Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo 36 | SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; 37 | 38 | // ----------------------------------------------------------------------------- 39 | // SUUpdater Delegate: 40 | // ----------------------------------------------------------------------------- 41 | 42 | /*! 43 | Provides methods to control the behavior of an SUUpdater object. 44 | */ 45 | @protocol SUUpdaterDelegate 46 | @optional 47 | 48 | /*! 49 | Returns whether to allow Sparkle to pop up. 50 | 51 | For example, this may be used to prevent Sparkle from interrupting a setup assistant. 52 | 53 | \param updater The SUUpdater instance. 54 | */ 55 | - (BOOL)updaterMayCheckForUpdates:(SUUpdater *)updater; 56 | 57 | /*! 58 | Returns additional parameters to append to the appcast URL's query string. 59 | 60 | This is potentially based on whether or not Sparkle will also be sending along the system profile. 61 | 62 | \param updater The SUUpdater instance. 63 | \param sendingProfile Whether the system profile will also be sent. 64 | 65 | \return An array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. 66 | */ 67 | - (NSArray *> *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; 68 | 69 | /*! 70 | Returns a custom appcast URL. 71 | 72 | Override this to dynamically specify the entire URL. 73 | 74 | An alternative may be to use SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: 75 | and let the server handle what kind of feed to provide. 76 | 77 | \param updater The SUUpdater instance. 78 | */ 79 | - (nullable NSString *)feedURLStringForUpdater:(SUUpdater *)updater; 80 | 81 | /*! 82 | Returns whether Sparkle should prompt the user about automatic update checks. 83 | 84 | Use this to override the default behavior. 85 | 86 | \param updater The SUUpdater instance. 87 | */ 88 | - (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)updater; 89 | 90 | /*! 91 | Called after Sparkle has downloaded the appcast from the remote server. 92 | 93 | Implement this if you want to do some special handling with the appcast once it finishes loading. 94 | 95 | \param updater The SUUpdater instance. 96 | \param appcast The appcast that was downloaded from the remote server. 97 | */ 98 | - (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; 99 | 100 | /*! 101 | Returns the item in the appcast corresponding to the update that should be installed. 102 | 103 | If you're using special logic or extensions in your appcast, 104 | implement this to use your own logic for finding a valid update, if any, 105 | in the given appcast. 106 | 107 | \param appcast The appcast that was downloaded from the remote server. 108 | \param updater The SUUpdater instance. 109 | */ 110 | - (nullable SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater; 111 | 112 | /*! 113 | Called when a valid update is found by the update driver. 114 | 115 | \param updater The SUUpdater instance. 116 | \param item The appcast item corresponding to the update that is proposed to be installed. 117 | */ 118 | - (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; 119 | 120 | /*! 121 | Called just before the scheduled update driver prompts the user to install an update. 122 | 123 | \param updater The SUUpdater instance. 124 | 125 | \return YES to allow the update prompt to be shown (the default behavior), or NO to suppress it. 126 | */ 127 | - (BOOL)updaterShouldShowUpdateAlertForScheduledUpdate:(SUUpdater *)updater forItem:(SUAppcastItem *)item; 128 | 129 | /*! 130 | Called after the user dismisses the update alert. 131 | 132 | \param updater The SUUpdater instance. 133 | \param permanently YES if the alert will not appear again for this update; NO if it may reappear. 134 | */ 135 | - (void)updater:(SUUpdater *)updater didDismissUpdateAlertPermanently:(BOOL)permanently forItem:(SUAppcastItem *)item; 136 | 137 | /*! 138 | Called when a valid update is not found. 139 | 140 | \param updater The SUUpdater instance. 141 | */ 142 | - (void)updaterDidNotFindUpdate:(SUUpdater *)updater; 143 | 144 | /*! 145 | Called when the user clicks the Skip This Version button. 146 | 147 | \param updater The SUUpdater instance. 148 | */ 149 | - (void)updater:(SUUpdater *)updater userDidSkipThisVersion:(SUAppcastItem *)item; 150 | 151 | /*! 152 | Called immediately before downloading the specified update. 153 | 154 | \param updater The SUUpdater instance. 155 | \param item The appcast item corresponding to the update that is proposed to be downloaded. 156 | \param request The mutable URL request that will be used to download the update. 157 | */ 158 | - (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; 159 | 160 | /*! 161 | Called immediately after succesfull download of the specified update. 162 | 163 | \param updater The SUUpdater instance. 164 | \param item The appcast item corresponding to the update that has been downloaded. 165 | */ 166 | - (void)updater:(SUUpdater *)updater didDownloadUpdate:(SUAppcastItem *)item; 167 | 168 | /*! 169 | Called after the specified update failed to download. 170 | 171 | \param updater The SUUpdater instance. 172 | \param item The appcast item corresponding to the update that failed to download. 173 | \param error The error generated by the failed download. 174 | */ 175 | - (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; 176 | 177 | /*! 178 | Called when the user clicks the cancel button while and update is being downloaded. 179 | 180 | \param updater The SUUpdater instance. 181 | */ 182 | - (void)userDidCancelDownload:(SUUpdater *)updater; 183 | 184 | /*! 185 | Called immediately before extracting the specified downloaded update. 186 | 187 | \param updater The SUUpdater instance. 188 | \param item The appcast item corresponding to the update that is proposed to be extracted. 189 | */ 190 | - (void)updater:(SUUpdater *)updater willExtractUpdate:(SUAppcastItem *)item; 191 | 192 | /*! 193 | Called immediately after extracting the specified downloaded update. 194 | 195 | \param updater The SUUpdater instance. 196 | \param item The appcast item corresponding to the update that has been extracted. 197 | */ 198 | - (void)updater:(SUUpdater *)updater didExtractUpdate:(SUAppcastItem *)item; 199 | 200 | /*! 201 | Called immediately before installing the specified update. 202 | 203 | \param updater The SUUpdater instance. 204 | \param item The appcast item corresponding to the update that is proposed to be installed. 205 | */ 206 | - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; 207 | 208 | /*! 209 | Returns whether the relaunch should be delayed in order to perform other tasks. 210 | 211 | This is not called if the user didn't relaunch on the previous update, 212 | in that case it will immediately restart. 213 | 214 | \param updater The SUUpdater instance. 215 | \param item The appcast item corresponding to the update that is proposed to be installed. 216 | \param invocation The invocation that must be completed with `[invocation invoke]` before continuing with the relaunch. 217 | 218 | \return \c YES to delay the relaunch until \p invocation is invoked. 219 | */ 220 | - (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation; 221 | 222 | /*! 223 | Returns whether the relaunch should be delayed in order to perform other tasks. 224 | 225 | This is not called if the user didn't relaunch on the previous update, 226 | in that case it will immediately restart. 227 | 228 | This method acts as a simpler alternative to SUUpdaterDelegate::updater:shouldPostponeRelaunchForUpdate:untilInvoking: avoiding usage of NSInvocation, which is not available in Swift environments. 229 | 230 | \param updater The SUUpdater instance. 231 | \param item The appcast item corresponding to the update that is proposed to be installed. 232 | 233 | \return \c YES to delay the relaunch. 234 | */ 235 | - (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item; 236 | 237 | /*! 238 | Returns whether the application should be relaunched at all. 239 | 240 | Some apps \b cannot be relaunched under certain circumstances. 241 | This method can be used to explicitly prevent a relaunch. 242 | 243 | \param updater The SUUpdater instance. 244 | */ 245 | - (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; 246 | 247 | /*! 248 | Called immediately before relaunching. 249 | 250 | \param updater The SUUpdater instance. 251 | */ 252 | - (void)updaterWillRelaunchApplication:(SUUpdater *)updater; 253 | 254 | /*! 255 | Called immediately after relaunching. SUUpdater delegate must be set before applicationDidFinishLaunching: to catch this event. 256 | 257 | \param updater The SUUpdater instance. 258 | */ 259 | - (void)updaterDidRelaunchApplication:(SUUpdater *)updater; 260 | 261 | /*! 262 | Returns an object that compares version numbers to determine their arithmetic relation to each other. 263 | 264 | This method allows you to provide a custom version comparator. 265 | If you don't implement this method or return \c nil, 266 | the standard version comparator will be used. 267 | 268 | \sa SUStandardVersionComparator 269 | 270 | \param updater The SUUpdater instance. 271 | */ 272 | - (nullable id)versionComparatorForUpdater:(SUUpdater *)updater; 273 | 274 | /*! 275 | Returns an object that formats version numbers for display to the user. 276 | 277 | If you don't implement this method or return \c nil, 278 | the standard version formatter will be used. 279 | 280 | \sa SUUpdateAlert 281 | 282 | \param updater The SUUpdater instance. 283 | */ 284 | - (nullable id)versionDisplayerForUpdater:(SUUpdater *)updater; 285 | 286 | /*! 287 | Returns the path which is used to relaunch the client after the update is installed. 288 | 289 | The default is the path of the host bundle. 290 | 291 | \param updater The SUUpdater instance. 292 | */ 293 | - (nullable NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; 294 | 295 | /*! 296 | Called before an updater shows a modal alert window, 297 | to give the host the opportunity to hide attached windows that may get in the way. 298 | 299 | \param updater The SUUpdater instance. 300 | */ 301 | - (void)updaterWillShowModalAlert:(SUUpdater *)updater; 302 | 303 | /*! 304 | Called after an updater shows a modal alert window, 305 | to give the host the opportunity to hide attached windows that may get in the way. 306 | 307 | \param updater The SUUpdater instance. 308 | */ 309 | - (void)updaterDidShowModalAlert:(SUUpdater *)updater; 310 | 311 | /*! 312 | Called when an update is scheduled to be silently installed on quit. 313 | This is after an update has been automatically downloaded in the background. 314 | (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) 315 | 316 | \param updater The SUUpdater instance. 317 | \param item The appcast item corresponding to the update that is proposed to be installed. 318 | \param invocation Can be used to trigger an immediate silent install and relaunch. 319 | */ 320 | - (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; 321 | 322 | /*! 323 | Called when an update is scheduled to be silently installed on quit. 324 | This is after an update has been automatically downloaded in the background. 325 | (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) 326 | This method acts as a more modern alternative to SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: using a block instead of NSInvocation, which is not available in Swift environments. 327 | 328 | \param updater The SUUpdater instance. 329 | \param item The appcast item corresponding to the update that is proposed to be installed. 330 | \param installationBlock Can be used to trigger an immediate silent install and relaunch. 331 | */ 332 | - (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationBlock:(void (^)(void))installationBlock; 333 | 334 | /*! 335 | Calls after an update that was scheduled to be silently installed on quit has been canceled. 336 | 337 | \param updater The SUUpdater instance. 338 | \param item The appcast item corresponding to the update that was proposed to be installed. 339 | */ 340 | - (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item; 341 | 342 | /*! 343 | Called after an update is aborted due to an error. 344 | 345 | \param updater The SUUpdater instance. 346 | \param error The error that caused the abort 347 | */ 348 | - (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; 349 | 350 | @end 351 | 352 | NS_ASSUME_NONNULL_END 353 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUVersionComparisonProtocol.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 12/21/07. 6 | // Copyright 2007 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SUVERSIONCOMPARISONPROTOCOL_H 10 | #define SUVERSIONCOMPARISONPROTOCOL_H 11 | 12 | #if __has_feature(modules) 13 | @import Foundation; 14 | #else 15 | #import 16 | #endif 17 | #import "SUExport.h" 18 | 19 | NS_ASSUME_NONNULL_BEGIN 20 | 21 | /*! 22 | Provides version comparison facilities for Sparkle. 23 | */ 24 | @protocol SUVersionComparison 25 | 26 | /*! 27 | An abstract method to compare two version strings. 28 | 29 | Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, 30 | and NSOrderedSame if they are equivalent. 31 | */ 32 | - (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | #endif 38 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUVersionDisplayProtocol.h 3 | // EyeTV 4 | // 5 | // Created by Uli Kusterer on 08.12.09. 6 | // Copyright 2009 Elgato Systems GmbH. All rights reserved. 7 | // 8 | 9 | #if __has_feature(modules) 10 | @import Foundation; 11 | #else 12 | #import 13 | #endif 14 | #import "SUExport.h" 15 | 16 | /*! 17 | Applies special display formatting to version numbers. 18 | */ 19 | @protocol SUVersionDisplay 20 | 21 | /*! 22 | Formats two version strings. 23 | 24 | Both versions are provided so that important distinguishing information 25 | can be displayed while also leaving out unnecessary/confusing parts. 26 | */ 27 | - (void)formatVersion:(NSString *_Nonnull*_Nonnull)inOutVersionA andVersion:(NSString *_Nonnull*_Nonnull)inOutVersionB; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Sparkle.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #ifndef SPARKLE_H 10 | #define SPARKLE_H 11 | 12 | // This list should include the shared headers. It doesn't matter if some of them aren't shared (unless 13 | // there are name-space collisions) so we can list all of them to start with: 14 | 15 | #pragma clang diagnostic push 16 | // Do not use <> style includes since 2.x has two frameworks that need to work: Sparkle and SparkleCore 17 | #pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" 18 | 19 | #import "SUAppcast.h" 20 | #import "SUAppcastItem.h" 21 | #import "SUStandardVersionComparator.h" 22 | #import "SUUpdater.h" 23 | #import "SUUpdaterDelegate.h" 24 | #import "SUVersionComparisonProtocol.h" 25 | #import "SUVersionDisplayProtocol.h" 26 | #import "SUErrors.h" 27 | 28 | #import "SPUDownloader.h" 29 | #import "SPUDownloaderDelegate.h" 30 | #import "SPUDownloadData.h" 31 | #import "SPUDownloaderProtocol.h" 32 | #import "SPUDownloaderSession.h" 33 | #import "SPUURLRequest.h" 34 | #import "SUCodeSigningVerifier.h" 35 | 36 | #pragma clang diagnostic pop 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Sparkle { 2 | umbrella header "Sparkle.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUUnarchiver.h 3 | // Sparkle 4 | // 5 | // Created by Andy Matuschak on 3/16/06. 6 | // Copyright 2006 Andy Matuschak. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol SUUnarchiverProtocol; 14 | 15 | @interface SUUnarchiver : NSObject 16 | 17 | + (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 20E241 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | Autoupdate 11 | CFBundleIconFile 12 | AppIcon.icns 13 | CFBundleIdentifier 14 | org.sparkle-project.Sparkle.Autoupdate 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.26.0 2-g2e93b6e4 21 | CFBundleSignature 22 | ???? 23 | CFBundleSupportedPlatforms 24 | 25 | MacOSX 26 | 27 | CFBundleVersion 28 | 1.26.0 29 | DTCompiler 30 | com.apple.compilers.llvm.clang.1_0 31 | DTPlatformBuild 32 | 12E262 33 | DTPlatformName 34 | macosx 35 | DTPlatformVersion 36 | 11.3 37 | DTSDKBuild 38 | 20E214 39 | DTSDKName 40 | macosx11.3 41 | DTXcode 42 | 1250 43 | DTXcodeBuild 44 | 12E262 45 | LSBackgroundOnly 46 | 1 47 | LSMinimumSystemVersion 48 | 10.9 49 | LSUIElement 50 | 1 51 | NSMainNibFile 52 | MainMenu 53 | NSPrincipalClass 54 | NSApplication 55 | 56 | 57 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/Base.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/Base.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUAutomaticUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUAutomaticUpdateAlert.nib -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUUpdateAlert.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUUpdateAlert.nib -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Base.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 20E241 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | Sparkle 11 | CFBundleIdentifier 12 | org.sparkle-project.Sparkle 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Sparkle 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.26.0 2-g2e93b6e4 21 | CFBundleSignature 22 | ???? 23 | CFBundleSupportedPlatforms 24 | 25 | MacOSX 26 | 27 | CFBundleVersion 28 | 1.26.0 29 | DTCompiler 30 | com.apple.compilers.llvm.clang.1_0 31 | DTPlatformBuild 32 | 12E262 33 | DTPlatformName 34 | macosx 35 | DTPlatformVersion 36 | 11.3 37 | DTSDKBuild 38 | 20E214 39 | DTSDKName 40 | macosx11.3 41 | DTXcode 42 | 1250 43 | DTXcodeBuild 44 | 12E262 45 | LSMinimumSystemVersion 46 | 10.9 47 | 48 | 49 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/ReleaseNotesColorStyle.css: -------------------------------------------------------------------------------- 1 | @media (prefers-color-scheme: dark) { 2 | html { 3 | color: white; 4 | background: transparent; 5 | } 6 | :link { 7 | color: #419CFF; 8 | } 9 | :link:active { 10 | color: #FF1919; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ADP2,1 6 | Developer Transition Kit 7 | iMac1,1 8 | iMac G3 (Rev A-D) 9 | iMac4,1 10 | iMac (Core Duo) 11 | iMac4,2 12 | iMac for Education (17 inch, Core Duo) 13 | iMac5,1 14 | iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) 15 | iMac5,2 16 | iMac (Core 2 Duo, 17 inch, Combo Drive) 17 | iMac6,1 18 | iMac (Core 2 Duo, 24 inch, SuperDrive) 19 | iMac7,1 20 | iMac Intel Core 2 Duo (aluminum enclosure) 21 | iMac8,1 22 | iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) 23 | iMac9,1 24 | iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 ) 25 | iMac10,1 26 | iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 ) 27 | iMac11,1 28 | iMac (Core i5 or i7, 27 inch Late 2009) 29 | iMac11,2 30 | 21.5" iMac (mid 2010) 31 | iMac11,3 32 | iMac (Core i5 or i7, 27 inch Mid 2010) 33 | iMac12,1 34 | iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011) 35 | iMac12,2 36 | iMac (Core i5 or i7, 27 inch Mid 2011) 37 | iMac13,1 38 | iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013) 39 | iMac13,2 40 | iMac (Core i5 or i7, 27 inch Late 2012) 41 | iMac14,1 42 | iMac (Core i5, 21.5 inch Late 2013) 43 | iMac14,2 44 | iMac (Core i5 or i7, 27 inch Late 2013) 45 | iMac14,3 46 | iMac (Core i5 or i7, 21.5 inch Late 2013) 47 | iMac14,4 48 | iMac (Core i5, 21.5 inch Mid 2014) 49 | iMac15,1 50 | iMac (Retina 5K Core i5 or i7, 27 inch Late 2014 or Mid 2015) 51 | iMac16,1 52 | iMac (Core i5, 21,5 inch Late 2015) 53 | iMac16,2 54 | iMac (Retina 4K Core i5 or i7, 21.5 inch Late 2015) 55 | iMac17,1 56 | iMac (Retina 5K Core i5 or i7, 27 inch Late 2015) 57 | MacBook1,1 58 | MacBook (Core Duo) 59 | MacBook2,1 60 | MacBook (Core 2 Duo) 61 | MacBook4,1 62 | MacBook (Core 2 Duo Feb 2008) 63 | MacBook5,1 64 | MacBook (Core 2 Duo, Late 2008, Unibody) 65 | MacBook5,2 66 | MacBook (Core 2 Duo, Early 2009, White) 67 | MacBook6,1 68 | MacBook (Core 2 Duo, Late 2009, Unibody) 69 | MacBook7,1 70 | MacBook (Core 2 Duo, Mid 2010, White) 71 | MacBook8,1 72 | MacBook (Core M, 12 inch, Early 2015) 73 | MacBookAir1,1 74 | MacBook Air (Core 2 Duo, 13 inch, Early 2008) 75 | MacBookAir2,1 76 | MacBook Air (Core 2 Duo, 13 inch, Mid 2009) 77 | MacBookAir3,1 78 | MacBook Air (Core 2 Duo, 11 inch, Late 2010) 79 | MacBookAir3,2 80 | MacBook Air (Core 2 Duo, 13 inch, Late 2010) 81 | MacBookAir4,1 82 | MacBook Air (Core i5 or i7, 11 inch, Mid 2011) 83 | MacBookAir4,2 84 | MacBook Air (Core i5 or i7, 13 inch, Mid 2011) 85 | MacBookAir5,1 86 | MacBook Air (Core i5 or i7, 11 inch, Mid 2012) 87 | MacBookAir5,2 88 | MacBook Air (Core i5 or i7, 13 inch, Mid 2012) 89 | MacBookAir6,1 90 | MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) 91 | MacBookAir6,2 92 | MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) 93 | MacBookAir7,1 94 | MacBook Air (Core i5 or i7, 11 inch, Early 2015) 95 | MacBookAir7,2 96 | MacBook Air (Core i5 or i7, 13 inch, Early 2015) 97 | MacBookPro1,1 98 | MacBook Pro Core Duo (15-inch) 99 | MacBookPro1,2 100 | MacBook Pro Core Duo (17-inch) 101 | MacBookPro2,1 102 | MacBook Pro Core 2 Duo (17-inch) 103 | MacBookPro2,2 104 | MacBook Pro Core 2 Duo (15-inch) 105 | MacBookPro3,1 106 | MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) 107 | MacBookPro3,2 108 | MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) 109 | MacBookPro4,1 110 | MacBook Pro (Core 2 Duo Feb 2008) 111 | MacBookPro5,1 112 | MacBook Pro Intel Core 2 Duo (aluminum unibody) 113 | MacBookPro5,2 114 | MacBook Pro Intel Core 2 Duo (aluminum unibody) 115 | MacBookPro5,3 116 | MacBook Pro Intel Core 2 Duo (aluminum unibody) 117 | MacBookPro5,4 118 | MacBook Pro Intel Core 2 Duo (aluminum unibody) 119 | MacBookPro5,5 120 | MacBook Pro Intel Core 2 Duo (aluminum unibody) 121 | MacBookPro6,1 122 | MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) 123 | MacBookPro6,2 124 | MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) 125 | MacBookPro7,1 126 | MacBook Pro Intel Core 2 Duo (mid 2010) 127 | MacBookPro8,1 128 | MacBook Pro Intel Core i5, Intel Core i7, 13" (early 2011) 129 | MacBookPro8,2 130 | MacBook Pro Intel Core i7, 15" (early 2011) 131 | MacBookPro8,3 132 | MacBook Pro Intel Core i7, 17" (early 2011) 133 | MacBookPro9,1 134 | MacBook Pro (15-inch, Mid 2012) 135 | MacBookPro9,2 136 | MacBook Pro (13-inch, Mid 2012) 137 | MacBookPro10,1 138 | MacBook Pro (Retina, Mid 2012) 139 | MacBookPro10,2 140 | MacBook Pro (Retina, 13-inch, Late 2012) 141 | MacBookPro11,1 142 | MacBook Pro (Retina, 13-inch, Late 2013) 143 | MacBookPro11,2 144 | MacBook Pro (Retina, 15-inch, Late 2013) 145 | MacBookPro11,3 146 | MacBook Pro (Retina, 15-inch, Late 2013) 147 | MacbookPro11,4 148 | MacBook Pro (Retina, 15-inch, Mid 2015) 149 | MacbookPro11,5 150 | MacBook Pro (Retina, 15-inch, Mid 2015) 151 | MacbookPro12,1  152 | MacBook Pro (Retina, 13-inch, Early 2015) 153 | Macmini1,1 154 | Mac Mini (Core Solo/Duo) 155 | Macmini2,1 156 | Mac mini Intel Core 157 | Macmini3,1 158 | Mac mini Intel Core 159 | Macmini4,1 160 | Mac mini Intel Core (Mid 2010) 161 | Macmini5,1 162 | Mac mini (Core i5, Mid 2011) 163 | Macmini5,2 164 | Mac mini (Core i5 or Core i7, Mid 2011) 165 | Macmini5,3 166 | Mac mini (Core i7, Server, Mid 2011) 167 | Macmini6,1 168 | Mac mini (Core i5, Late 2012) 169 | Macmini6,2 170 | Mac mini (Core i7, Normal or Server, Late 2012) 171 | Macmini7,1 172 | Mac mini (Core i5 or Core i7, Late 2014) 173 | MacPro1,1,Quad 174 | Mac Pro 175 | MacPro1,1 176 | Mac Pro (four-core) 177 | MacPro2,1 178 | Mac Pro (eight-core) 179 | MacPro3,1 180 | Mac Pro (January 2008 4- or 8- core "Harpertown") 181 | MacPro4,1 182 | Mac Pro (March 2009) 183 | MacPro5,1 184 | Mac Pro (2010 or 2012) 185 | MacPro6,1 186 | Mac Pro (Late 2013) 187 | PowerBook1,1 188 | PowerBook G3 189 | PowerBook2,1 190 | iBook G3 191 | PowerBook2,2 192 | iBook G3 (FireWire) 193 | PowerBook2,3 194 | iBook G3 195 | PowerBook2,4 196 | iBook G3 197 | PowerBook3,1 198 | PowerBook G3 (FireWire) 199 | PowerBook3,2 200 | PowerBook G4 201 | PowerBook3,3 202 | PowerBook G4 (Gigabit Ethernet) 203 | PowerBook3,4 204 | PowerBook G4 (DVI) 205 | PowerBook3,5 206 | PowerBook G4 (1GHz / 867MHz) 207 | PowerBook4,1 208 | iBook G3 (Dual USB, Late 2001) 209 | PowerBook4,2 210 | iBook G3 (16MB VRAM) 211 | PowerBook4,3 212 | iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) 213 | PowerBook5,1 214 | PowerBook G4 (17 inch) 215 | PowerBook5,2 216 | PowerBook G4 (15 inch FW 800) 217 | PowerBook5,3 218 | PowerBook G4 (17-inch 1.33GHz) 219 | PowerBook5,4 220 | PowerBook G4 (15 inch 1.5/1.33GHz) 221 | PowerBook5,5 222 | PowerBook G4 (17-inch 1.5GHz) 223 | PowerBook5,6 224 | PowerBook G4 (15 inch 1.67GHz/1.5GHz) 225 | PowerBook5,7 226 | PowerBook G4 (17-inch 1.67GHz) 227 | PowerBook5,8 228 | PowerBook G4 (Double layer SD, 15 inch) 229 | PowerBook5,9 230 | PowerBook G4 (Double layer SD, 17 inch) 231 | PowerBook6,1 232 | PowerBook G4 (12 inch) 233 | PowerBook6,2 234 | PowerBook G4 (12 inch, DVI) 235 | PowerBook6,3 236 | iBook G4 237 | PowerBook6,4 238 | PowerBook G4 (12 inch 1.33GHz) 239 | PowerBook6,5 240 | iBook G4 (Early-Late 2004) 241 | PowerBook6,7 242 | iBook G4 (Mid 2005) 243 | PowerBook6,8 244 | PowerBook G4 (12 inch 1.5GHz) 245 | PowerMac1,1 246 | Power Macintosh G3 (Blue & White) 247 | PowerMac1,2 248 | Power Macintosh G4 (PCI Graphics) 249 | PowerMac2,1 250 | iMac G3 (Slot-loading CD-ROM) 251 | PowerMac2,2 252 | iMac G3 (Summer 2000) 253 | PowerMac3,1 254 | Power Macintosh G4 (AGP Graphics) 255 | PowerMac3,2 256 | Power Macintosh G4 (AGP Graphics) 257 | PowerMac3,3 258 | Power Macintosh G4 (Gigabit Ethernet) 259 | PowerMac3,4 260 | Power Macintosh G4 (Digital Audio) 261 | PowerMac3,5 262 | Power Macintosh G4 (Quick Silver) 263 | PowerMac3,6 264 | Power Macintosh G4 (Mirrored Drive Door) 265 | PowerMac4,1 266 | iMac G3 (Early/Summer 2001) 267 | PowerMac4,2 268 | iMac G4 (Flat Panel) 269 | PowerMac4,4 270 | eMac 271 | PowerMac4,5 272 | iMac G4 (17-inch Flat Panel) 273 | PowerMac5,1 274 | Power Macintosh G4 Cube 275 | PowerMac5,2 276 | Power Mac G4 Cube 277 | PowerMac6,1 278 | iMac G4 (USB 2.0) 279 | PowerMac6,3 280 | iMac G4 (20-inch Flat Panel) 281 | PowerMac6,4 282 | eMac (USB 2.0, 2005) 283 | PowerMac7,2 284 | Power Macintosh G5 285 | PowerMac7,3 286 | Power Macintosh G5 287 | PowerMac8,1 288 | iMac G5 289 | PowerMac8,2 290 | iMac G5 (Ambient Light Sensor) 291 | PowerMac9,1 292 | Power Macintosh G5 (Late 2005) 293 | PowerMac10,1 294 | Mac Mini G4 295 | PowerMac10,2 296 | Mac Mini (Late 2005) 297 | PowerMac11,2 298 | Power Macintosh G5 (Late 2005) 299 | PowerMac12,1 300 | iMac G5 (iSight) 301 | RackMac1,1 302 | Xserve G4 303 | RackMac1,2 304 | Xserve G4 (slot-loading, cluster node) 305 | RackMac3,1 306 | Xserve G5 307 | Xserve1,1 308 | Xserve (Intel Xeon) 309 | Xserve2,1 310 | Xserve (January 2008 quad-core) 311 | Xserve3,1 312 | Xserve (early 2009) 313 | 314 | 315 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSButtonCell"; title = "Install and Relaunch"; ObjectID = "41"; */ 3 | "41.title" = "Install and Relaunch"; 4 | 5 | /* Class = "NSButtonCell"; title = "Install on Quit"; ObjectID = "42"; */ 6 | "42.title" = "Install on Quit"; 7 | 8 | /* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "43"; */ 9 | "43.title" = "Automatically download and install updates in the future"; 10 | 11 | /* Class = "NSButtonCell"; title = "Don't Install"; ObjectID = "44"; */ 12 | "44.title" = "Don't Install"; 13 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ 3 | "5.title" = "Software Update"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ 6 | "170.title" = "Release Notes:"; 7 | 8 | /* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ 9 | "171.title" = "Remind Me Later"; 10 | 11 | /* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ 12 | "172.title" = "Skip This Version"; 13 | 14 | /* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ 15 | "173.title" = "Install Update"; 16 | 17 | /* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ 18 | "175.title" = "Automatically download and install updates in the future"; 19 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ 3 | "43.title" = "Text Cell"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ 6 | "45.title" = "Text Cell"; 7 | 8 | /* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "176"; */ 9 | "176.title" = "Check Automatically"; 10 | 11 | /* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "177"; */ 12 | "177.title" = "Don’t Check"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "178"; */ 15 | "178.title" = "Check for updates automatically?"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "DO NOT LOCALIZE"; ObjectID = "179"; */ 18 | "179.title" = "DO NOT LOCALIZE"; 19 | 20 | /* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "180"; */ 21 | "180.title" = "Include anonymous system profile"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ 24 | "183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; 25 | -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/A/Sparkle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfsam/Snk/868e6f8db722d4768ee9510d72c461b25e45a5d9/Snk/_sparkle/Sparkle.framework/Versions/A/Sparkle -------------------------------------------------------------------------------- /Snk/_sparkle/Sparkle.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /make_zips_and_appcast.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # If Snk.app is not found on the Desktop, quit. 4 | APP_PATH="$HOME/Desktop/Snk.app" 5 | if [ ! -d "$APP_PATH" ] 6 | then 7 | echo "\n" 8 | echo " + \033[0;31mNOT FOUND:\033[0m $APP_PATH" 9 | echo " + Export notarized Snk.app to Desktop." 10 | echo "\n" 11 | exit 1 12 | fi 13 | 14 | # Get the bundle version from the plist. 15 | PLIST_FILE="$APP_PATH/Contents/Info.plist" 16 | VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $PLIST_FILE) 17 | SHORT_VERSION_STRING=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $PLIST_FILE) 18 | 19 | # Set up file names and paths. 20 | ZIP_NAME="Snk-$SHORT_VERSION_STRING.zip" 21 | ZIP_NAME=${ZIP_NAME// /-} 22 | DEST_DIR="$HOME/Desktop/Snk-$SHORT_VERSION_STRING" 23 | XML_PATH="$DEST_DIR/snk.xml" 24 | ZIP_PATH1="$DEST_DIR/$ZIP_NAME" 25 | ZIP_PATH2="$DEST_DIR/Snk.zip" 26 | 27 | # Run some diagnostics so we can see all is ok." 28 | echo "" 29 | ( set -x; spctl -vvv --assess --type exec $APP_PATH ) 30 | echo "" 31 | ( set -x; codesign -vvv --deep --strict $APP_PATH ) 32 | echo "" 33 | ( set -x; codesign -dvv $APP_PATH ) 34 | 35 | echo "" 36 | echo "Making zips and appcast for \033[0;32m$SHORT_VERSION_STRING ($VERSION)\033[0m..." 37 | 38 | # Make output dir (if necessary) and clear its contents. 39 | rm -frd "$DEST_DIR" 40 | mkdir -p "$DEST_DIR" 41 | 42 | # Compress Snk.app and make a copy without version suffix. 43 | ditto -c -k --rsrc --keepParent "$APP_PATH" "$ZIP_PATH1" 44 | cp "$ZIP_PATH1" "$ZIP_PATH2" 45 | 46 | # Get the date and zip file size for the Sparkle XML. 47 | DATE=$(TZ=GMT date) 48 | FILESIZE=$(stat -f "%z" "$ZIP_PATH1") 49 | 50 | # Make the Sparkle appcast XML file. 51 | cat > "$XML_PATH" < 53 | 57 | 58 | Snk Changelog 59 | https://s3.amazonaws.com/mowglii/snk.xml 60 | Most recent changes 61 | en 62 | 63 | Version $SHORT_VERSION_STRING 64 | 10.16 65 | https://mowglii.com/snk/changelog.html 66 | $DATE +0000 67 | 73 | 74 | 75 | 76 | EOF 77 | 78 | echo "Done!" 79 | echo "" 80 | 81 | open -R "$XML_PATH" 82 | 83 | --------------------------------------------------------------------------------