Repository: Provenance-Emu/Provenance (language: cpp)

Issue:
Provenance on iOS not releasing memory after quitting a game leading to inevitable crash!
Provenance on iOS does not release memory after quitting a game. The tvOS build does release the memory after launching a game so something odd is up between the two platforms!

----- BUG REPORT: Be clear, concise and thorough, beginning with a summary. --->
On first launch, Provenance will use around 60 or so MB of memory. Subsequently launching a game will add around 120MB to that memory footprint, bringing the new memory total to around 180MB. Quitting the game causes Provenance to still retain the same 180MB memory footprint and will not release the used memory back to the OS and lowering our memory footprint back to the original 60MB size. 

For every game launched after, the memory used by Provenance on iOS will continue to grow, even when just re-launching the same game multiple times. 

Repeatedly launching games in Provenance will cause Provenance to run out of Memory on iOS sooner or later and be killed by the OS with the following message:
_Message from debugger: Terminated due to memory issue_

<!--- What did you expect to happen? --------------------------------------------> 
### Expected: the iOS build to release the used System memory when quitting a game just like the tvOS build does.

<!--- What happened instead? ---------------------------------------------------> 
### Actual: The used System memory on iOS is retained and not released after launching a game and quitting.

<!--- What are steps we can follow to reproduce this issue? ---------------------->
 ### Reproduce: Launch a game whilst observing the Memory usage graph in the Xcode Debugging Instruments. Close/Quit the game and notice that the memory was not released.

#### APP VERSION
- [x ] 1.5 *beta* - Current develop as of Jan 9th 2021
- [ ] 1.4

#### APP SOURCE
- [x ] Provenance-Emu GitHub  *&nbsp;(Official)*
- [ ] Other  *&nbsp;(Unofficial)*

#### INSTALLED BY 
- [ ] [Side-Loading](https://git.io/vpYzf)
- [x ] [Building](https://git.io/vpYzv) *from Source*

#### PLATFORM
- [x ] iPhone
- [ ] iPad
- [ ] Apple TV

#### iOS/tvOS VERSION
- [ ] 14.x
- [x ] 13.x
- [ ] 12.x
- [ ] 11.x
- [ ] 10.x

-------------------------------------------
<sup> 🚫 We **DO NOT** support unofficial builds installed from 3rd-party sites. ([Official Install](https://git.io/vxABg)) </sup><br><sup> ❓ **Need help or have a suggestion?** Join our [Official Discord](https://discord.gg/4TK7PU5) </sup><br>



Discussion / hints:

Fix patch:
diff --git a/Provenance.xcodeproj/project.pbxproj b/Provenance.xcodeproj/project.pbxproj
index 6816ffc502..105cf8d0b9 100644
--- a/Provenance.xcodeproj/project.pbxproj
+++ b/Provenance.xcodeproj/project.pbxproj
@@ -64,6 +64,8 @@
 		11A8A9E72496A3E500C1C6B4 /* PVGameLibraryUpdatesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1180B3232492AFC900636C8A /* PVGameLibraryUpdatesController.swift */; };
 		11ADB31F2471A34E001C7B27 /* PVTVTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11ADB31E2471A34E001C7B27 /* PVTVTabBarController.swift */; };
 		11AFB2B72487C120000A3922 /* UISearchController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11AFB2B62487C120000A3922 /* UISearchController+Rx.swift */; };
+		11BB0A2225AF7DFE004DEDC3 /* PVEmulatorViewController+PauseMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38A2A452102328600374CDD /* PVEmulatorViewController+PauseMenu.swift */; };
+		11BB0A2325AF7DFF004DEDC3 /* PVEmulatorViewController+PauseMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38A2A452102328600374CDD /* PVEmulatorViewController+PauseMenu.swift */; };
 		14C4C1A121EBB0040055C6DC /* PVTGBDual.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14C8EF2121D9443E001B7E37 /* PVTGBDual.framework */; };
 		14C4C1A221EBB0040055C6DC /* PVTGBDual.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 14C8EF2121D9443E001B7E37 /* PVTGBDual.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		1A19EE142080EA2000A9147B /* PVMupen64PlusVideoGlideN64.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFB603BD2038B6C0001E70F1 /* PVMupen64PlusVideoGlideN64.framework */; };
@@ -289,8 +291,6 @@
 		B3890BF920B9E135005BB001 /* PVGameLibraryCollectionViewCell~tvOS.xib in Resources */ = {isa = PBXBuildFile; fileRef = B3890BF820B9E135005BB001 /* PVGameLibraryCollectionViewCell~tvOS.xib */; };
 		B3890BFA20B9E513005BB001 /* UILabel+Theming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C125BF20B0826900DC045B /* UILabel+Theming.swift */; };
 		B38973B420AEC94800E36522 /* PVGameLibraryCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B38973B320AEC94800E36522 /* PVGameLibraryCollectionViewCell.xib */; };
-		B38A2A442102326800374CDD /* PVEmulatorViewController~tvOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38A2A432102326800374CDD /* PVEmulatorViewController~tvOS.swift */; };
-		B38A2A462102328600374CDD /* PVEmulatorViewController~iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38A2A452102328600374CDD /* PVEmulatorViewController~iOS.swift */; };
 		B38A2A48210233FE00374CDD /* EmulatorActionCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B38A2A47210233FE00374CDD /* EmulatorActionCell.xib */; };
 		B38D30FD202AC26500E9A068 /* GCDWebDAVServer.m in Sources */ = {isa = PBXBuildFile; fileRef = B38D30FC202AC26500E9A068 /* GCDWebDAVServer.m */; };
 		B38D30FE202AC26500E9A068 /* GCDWebDAVServer.m in Sources */ = {isa = PBXBuildFile; fileRef = B38D30FC202AC26500E9A068 /* GCDWebDAVServer.m */; };
@@ -817,8 +817,7 @@
 		B3890BF320B995A9005BB001 /* PVGameLibraryViewController+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PVGameLibraryViewController+CollectionView.swift"; sourceTree = "<group>"; };
 		B3890BF820B9E135005BB001 /* PVGameLibraryCollectionViewCell~tvOS.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = "PVGameLibraryCollectionViewCell~tvOS.xib"; sourceTree = "<group>"; };
 		B38973B320AEC94800E36522 /* PVGameLibraryCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PVGameLibraryCollectionViewCell.xib; sourceTree = "<group>"; };
-		B38A2A432102326800374CDD /* PVEmulatorViewController~tvOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PVEmulatorViewController~tvOS.swift"; sourceTree = "<group>"; };
-		B38A2A452102328600374CDD /* PVEmulatorViewController~iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PVEmulatorViewController~iOS.swift"; sourceTree = "<group>"; };
+		B38A2A452102328600374CDD /* PVEmulatorViewController+PauseMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PVEmulatorViewController+PauseMenu.swift"; sourceTree = "<group>"; };
 		B38A2A47210233FE00374CDD /* EmulatorActionCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EmulatorActionCell.xib; sourceTree = "<group>"; };
 		B38D30FB202AC26500E9A068 /* GCDWebDAVServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebDAVServer.h; sourceTree = "<group>"; };
 		B38D30FC202AC26500E9A068 /* GCDWebDAVServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebDAVServer.m; sourceTree = "<group>"; };
@@ -1168,8 +1167,7 @@
 				1A164EB217BC03E800FAC391 /* PVEmulatorViewController.swift */,
 				B3CCD2B921DAA8D30031C42C /* PVEmulatorViewController+Saves.swift */,
 				B354E7D1219A9C230041F971 /* CoreOptionsViewController.swift */,
-				B38A2A432102326800374CDD /* PVEmulatorViewController~tvOS.swift */,
-				B38A2A452102328600374CDD /* PVEmulatorViewController~iOS.swift */,
+				B38A2A452102328600374CDD /* PVEmulatorViewController+PauseMenu.swift */,
 				B38A2A47210233FE00374CDD /* EmulatorActionCell.xib */,
 				B36D9DFC203605BB00D583C4 /* PVCoreFactory.swift */,
 				CF17107025858B5400258602 /* PVEmulatorViewController+Cheats.swift */,
@@ -2520,11 +2518,11 @@
 				1A65D1C619917D55004E1777 /* UIImage+ImageEffects.m in Sources */,
 				B3532B2B21A78FB2006CDA0F /* PVSwitchCell.swift in Sources */,
 				1AB95FFD17C563C200D3E392 /* PVSettingsViewController.swift in Sources */,
-				B38A2A462102328600374CDD /* PVEmulatorViewController~iOS.swift in Sources */,
 				B3F64ECF205CD1F100C273C7 /* JSButton.swift in Sources */,
 				B3A32150218EB4D700BD7DA2 /* UIView+FrameAdditions.swift in Sources */,
 				B383F3BF219BBD8700DB6926 /* LogViewable.swift in Sources */,
 				378F4A041B63D7CD0065FA39 /* GCDWebServerResponse.m in Sources */,
+				11BB0A2225AF7DFE004DEDC3 /* PVEmulatorViewController+PauseMenu.swift in Sources */,
 				B35E6C21207ED7050040709A /* AppearanceScope.swift in Sources */,
 				B35E6C2B207ED7050040709A /* UITraitCollectionExtensions.swift in Sources */,
 				B3AB37432187F569009D9244 /* PVN64ControllerViewController.swift in Sources */,
@@ -2598,7 +2596,6 @@
 				1AD481DC1BA3544500FDA50A /* GCDWebServerDataRequest.m in Sources */,
 				B32746BE219BD65F00B78124 /* PVGameLibraryCollectionFlowLayout.swift in Sources */,
 				B3B104C3218F2EF400210C39 /* PVAtari7800ControllerViewController.swift in Sources */,
-				B38A2A442102326800374CDD /* PVEmulatorViewController~tvOS.swift in Sources */,
 				B3E3B5DA202ED84100A11871 /* PVGameLibraryViewController.swift in Sources */,
 				B3D534E320AFF21900A83D4E /* Version.swift in Sources */,
 				B3CCD2BB21DAA8D30031C42C /* PVEmulatorViewController+Saves.swift in Sources */,
@@ -2660,6 +2657,7 @@
 				B3B104D6218F2F2800210C39 /* PVButtonGroupOverlayView.swift in Sources */,
 				1180B30824924AEA00636C8A /* UICollectionView+Rx.swift in Sources */,
 				B3B104D3218F2EF400210C39 /* PVVBControllerViewController.swift in Sources */,
+				11BB0A2325AF7DFF004DEDC3 /* PVEmulatorViewController+PauseMenu.swift in Sources */,
 				1AD481D51BA3542A00FDA50A /* GCDWebUploader.m in Sources */,
 				1AD481F61BA3549F00FDA50A /* UIImage+ImageEffects.m in Sources */,
 				B33468D720AB392C00092CA6 /* CollectionViewInCollectionViewCell.swift in Sources */,
diff --git a/Provenance/Emulator/PVEmulatorViewController~iOS.swift b/Provenance/Emulator/PVEmulatorViewController+PauseMenu.swift
similarity index 87%
rename from Provenance/Emulator/PVEmulatorViewController~iOS.swift
rename to Provenance/Emulator/PVEmulatorViewController+PauseMenu.swift
index 447c5ff170..c55a1b2aef 100644
--- a/Provenance/Emulator/PVEmulatorViewController~iOS.swift
+++ b/Provenance/Emulator/PVEmulatorViewController+PauseMenu.swift
@@ -142,15 +142,7 @@ extension PVEmulatorViewController {
             }))
         #endif
         actionSheet.addAction(UIAlertAction(title: "Game Info", style: .default, handler: { action in
-            let sb = UIStoryboard(name: "Provenance", bundle: nil)
-            let moreInfoViewContrller = sb.instantiateViewController(withIdentifier: "gameMoreInfoVC") as? PVGameMoreInfoViewController
-            moreInfoViewContrller?.game = self.game
-            moreInfoViewContrller?.showsPlayButton = false
-            moreInfoViewContrller?.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.hideModeInfo))
-            let newNav = UINavigationController(rootViewController: moreInfoViewContrller ?? UIViewController())
-            self.present(newNav, animated: true) { () -> Void in }
-            self.isShowingMenu = false
-            self.enableControllerInput(false)
+            self.showMoreInfo()
         }))
         actionSheet.addAction(UIAlertAction(title: "Game Speed", style: .default, handler: { action in
             self.perform(#selector(self.showSpeedMenu), with: nil, afterDelay: 0.1)
@@ -167,6 +159,13 @@ extension PVEmulatorViewController {
                 }))
         }
         actionSheet.addAction(UIAlertAction(title: "Reset", style: .default, handler: { action in
+            let completion = {
+                self.core.setPauseEmulation(false)
+                self.core.resetEmulation()
+                self.isShowingMenu = false
+                self.enableControllerInput(false)
+            }
+
             if PVSettingsModel.shared.autoSave, self.core.supportsSaveStates {
                 self.autoSaveState { result in
                     switch result {
@@ -175,12 +174,11 @@ extension PVEmulatorViewController {
                     case let .error(error):
                         ELOG("Auto-save failed \(error.localizedDescription)")
                     }
+                    completion()
                 }
+            } else {
+                completion()
             }
-            self.core.setPauseEmulation(false)
-            self.core.resetEmulation()
-            self.isShowingMenu = false
-            self.enableControllerInput(false)
         }))
 
         let lastPlayed = game.lastPlayed ?? Date()
@@ -201,31 +199,17 @@ extension PVEmulatorViewController {
                 self.quit(optionallySave: true)
             }))
         }
-        
-        actionSheet.addAction(UIAlertAction(title: "Resume", style: .default, handler: { action in
-            actionSheet.dismiss(animated: true) {
-                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.enableControllerInput(false)
-                }
-            }
-        }))
 
-        if actionSheet.isBeingDismissed {
-            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                self.core.setPauseEmulation(false)
-                self.isShowingMenu = false
-                self.enableControllerInput(false)
-            }
-        }
+        let resumeAction = UIAlertAction(title: "Resume", style: .default, handler: { action in
+            self.core.setPauseEmulation(false)
+            self.isShowingMenu = false
+            self.enableControllerInput(false)
+        })
+        actionSheet.addAction(resumeAction)
+        actionSheet.preferredAction = resumeAction
 
         present(actionSheet, animated: true, completion: { () -> Void in
             PVControllerManager.shared.iCadeController?.refreshListener()
         })
     }
-
-    //	override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
-    //		super.dismiss(animated: flag, completion: completion)
-    //	}
 }
diff --git a/Provenance/Emulator/PVEmulatorViewController.swift b/Provenance/Emulator/PVEmulatorViewController.swift
index 4cb57415d2..08820ae813 100644
--- a/Provenance/Emulator/PVEmulatorViewController.swift
+++ b/Provenance/Emulator/PVEmulatorViewController.swift
@@ -155,9 +155,6 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
         }
         NSSetUncaughtExceptionHandler(nil)
         staticSelf = nil
-        controllerViewController?.willMove(toParent: nil)
-        controllerViewController?.view?.removeFromSuperview()
-        controllerViewController?.removeFromParent()
         glViewController.willMove(toParent: nil)
         glViewController.view?.removeFromSuperview()
         glViewController.removeFromParent()
@@ -199,8 +196,7 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
         core.romSerial = game.romSerial
     }
 
-    private func initMenuButton() {
-        //        controllerViewController = PVCoreFactory.controllerViewController(forSystem: game.system, core: core)
+    private func addControllerOverlay() {
         if let aController = controllerViewController {
             addChild(aController)
         }
@@ -208,7 +204,9 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
             view.addSubview(aView)
         }
         controllerViewController?.didMove(toParent: self)
+    }
 
+    private func initMenuButton() {
         let alpha: CGFloat = CGFloat(PVSettingsModel.shared.controllerOpacity)
         menuButton = MenuButton(type: .custom)
         menuButton?.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin]
@@ -321,6 +319,7 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
             glViewController.didMove(toParent: self)
         }
         #if os(iOS)
+            addControllerOverlay()
             initMenuButton()
         #endif
 
@@ -385,10 +384,6 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
         destroyAutosaveTimer()
     }
 
-    public override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
-    }
-
     var autosaveTimer: Timer?
 
     var gameStartTime: Date?
@@ -528,7 +523,7 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
         #endif
     }
 
-    @objc func hideModeInfo() {
+    @objc func hideMoreInfo() {
         dismiss(animated: true, completion: { () -> Void in
             #if os(tvOS)
                 self.showMenu(nil)
@@ -621,6 +616,21 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio
         })
     }
 
+    func showMoreInfo() {
+        guard let moreInfoViewController = UIStoryboard(name: "Provenance", bundle: nil).instantiateViewController(withIdentifier: "gameMoreInfoVC") as? PVGameMoreInfoViewController else { return }
+        moreInfoViewController.game = self.game
+        moreInfoViewController.showsPlayButton = false
+
+        #if os(iOS)
+        moreInfoViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.hideMoreInfo))
+        #endif
+
+        let newNav = UINavigationController(rootViewController: moreInfoViewController)
+        self.present(newNav, animated: true) { () -> Void in }
+        self.isShowingMenu = false
+        self.enableControllerInput(false)
+    }
+
     typealias QuitCompletion = () -> Void
 
     func quit(optionallySave canSave: Bool = true, completion: QuitCompletion? = nil) {
diff --git a/Provenance/Emulator/PVEmulatorViewController~tvOS.swift b/Provenance/Emulator/PVEmulatorViewController~tvOS.swift
deleted file mode 100644
index cebffac887..0000000000
--- a/Provenance/Emulator/PVEmulatorViewController~tvOS.swift
+++ /dev/null
@@ -1,220 +0,0 @@
-//
-//  PVEmulatorViewController~tvOS.swift
-//  ProvenanceTV
-//
-//  Created by Joseph Mattiello on 7/20/18.
-//  Copyright © 2018 Provenance. All rights reserved.
-//
-
-import Foundation
-import PVLibrary
-import PVSupport
-import UIKit
-
-extension PVEmulatorViewController {
-    @objc func showMenu(_: Any?) {
-        enableControllerInput(true)
-        core.setPauseEmulation(true)
-        isShowingMenu = true
-        let actionsheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
-        if traitCollection.userInterfaceIdiom == .pad {
-            actionsheet.popoverPresentationController?.sourceView = menuButton
-            actionsheet.popoverPresentationController?.sourceRect = menuButton!.bounds
-        }
-
-        if PVControllerManager.shared.iCadeController != nil {
-            actionsheet.addAction(UIAlertAction(title: "Disconnect iCade", style: .default, handler: { (_: UIAlertAction) -> Void in
-                NotificationCenter.default.post(name: .GCControllerDidDisconnect, object: PVControllerManager.shared.iCadeController)
-                self.core.setPauseEmulation(false)
-                self.isShowingMenu = false
-                self.enableControllerInput(false)
-            }))
-        }
-
-        //		if let optionCore = core as? CoreOptional {
-        if core is CoreOptional {
-            actionsheet.addAction(UIAlertAction(title: "Core Options", style: .default, handler: { _ in
-                self.showCoreOptions()
-            }))
-        }
-
-        let controllerManager = PVControllerManager.shared
-        let wantsStartSelectInMenu: Bool = PVEmulatorConfiguration.systemIDWantsStartAndSelectInMenu(game.system.identifier)
-        var hideP1MenuActions = false
-        if let player1 = controllerManager.player1 {
-            #if os(iOS)
-                if PVSettingsModel.shared.missingButtonsAlwaysOn {
-                    hideP1MenuActions = true
-                }
-            #endif
-            if player1.extendedGamepad != nil || wantsStartSelectInMenu, !hideP1MenuActions {
-                // left trigger bound to Start
-                // right trigger bound to Select
-                actionsheet.addAction(UIAlertAction(title: "P1 Start", style: .default, handler: { (_: UIAlertAction) -> Void in
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.controllerViewController?.pressStart(forPlayer: 0)
-                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { () -> Void in
-                        self.controllerViewController?.releaseStart(forPlayer: 0)
-                    })
-                    self.enableControllerInput(false)
-                }))
-                actionsheet.addAction(UIAlertAction(title: "P1 Select", style: .default, handler: { (_: UIAlertAction) -> Void in
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.controllerViewController?.pressSelect(forPlayer: 0)
-                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { () -> Void in
-                        self.controllerViewController?.releaseSelect(forPlayer: 0)
-                    })
-                    self.enableControllerInput(false)
-                }))
-            }
-            if player1.extendedGamepad != nil || wantsStartSelectInMenu {
-                actionsheet.addAction(UIAlertAction(title: "P1 Analog Mode", style: .default, handler: { (_: UIAlertAction) -> Void in
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.controllerViewController?.pressAnalogMode(forPlayer: 0)
-                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: { () -> Void in
-                        self.controllerViewController?.releaseAnalogMode(forPlayer: 0)
-                    })
-                    self.enableControllerInput(false)
-                }))
-            }
-        }
-        if let player2 = controllerManager.player2 {
-            if player2.extendedGamepad != nil || wantsStartSelectInMenu {
-                actionsheet.addAction(UIAlertAction(title: "P2 Start", style: .default, handler: { (_: UIAlertAction) -> Void in
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.controllerViewController?.pressStart(forPlayer: 1)
-                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { () -> Void in
-                        self.controllerViewController?.releaseStart(forPlayer: 1)
-                    })
-                    self.enableControllerInput(false)
-                }))
-                actionsheet.addAction(UIAlertAction(title: "P2 Select", style: .default, handler: { (_: UIAlertAction) -> Void in
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.controllerViewController?.pressSelect(forPlayer: 1)
-                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { () -> Void in
-                        self.controllerViewController?.releaseSelect(forPlayer: 1)
-                    })
-                    self.enableControllerInput(false)
-                }))
-                actionsheet.addAction(UIAlertAction(title: "P2 Analog Mode", style: .default, handler: { (_: UIAlertAction) -> Void in
-                    self.core.setPauseEmulation(false)
-                    self.isShowingMenu = false
-                    self.controllerViewController?.pressAnalogMode(forPlayer: 1)
-                    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { () -> Void in
-                        self.controllerViewController?.releaseAnalogMode(forPlayer: 1)
-                    })
-                    self.enableControllerInput(false)
-                }))
-            }
-        }
-        if let swappableCore = core as? DiscSwappable, swappableCore.currentGameSupportsMultipleDiscs {
-            actionsheet.addAction(UIAlertAction(title: "Swap Disc", style: .default, handler: { (_: UIAlertAction) -> Void in
-                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
-                    self.showSwapDiscsMenu()
-                })
-            }))
-        }
-
-        if let actionableCore = core as? CoreActions, let actions = actionableCore.coreActions {
-            actions.forEach { coreAction in
-                actionsheet.addAction(UIAlertAction(title: coreAction.title, style: .default, handler: { (_: UIAlertAction) -> Void in
-                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
-                        actionableCore.selected(action: coreAction)
-                        self.core.setPauseEmulation(false)
-                        if coreAction.requiresReset {
-                            self.core.resetEmulation()
-                        }
-                        self.isShowingMenu = false
-                        self.enableControllerInput(false)
-                    })
-                }))
-            }
-        }
-        #if os(iOS)
-            actionsheet.addAction(UIAlertAction(title: "Save Screenshot", style: .default, handler: { (_: UIAlertAction) -> Void in
-                self.perform(#selector(self.takeScreenshot), with: nil, afterDelay: 0.1)
-            }))
-        #endif
-        actionsheet.addAction(UIAlertAction(title: "Game Info", style: .default, handler: { (_: UIAlertAction) -> Void in
-            let sb = UIStoryboard(name: "Provenance", bundle: nil)
-            let moreInfoViewContrller = sb.instantiateViewController(withIdentifier: "gameMoreInfoVC") as? PVGameMoreInfoViewController
-            moreInfoViewContrller?.game = self.game
-            moreInfoViewContrller?.showsPlayButton = false
-            let newNav = UINavigationController(rootViewController: moreInfoViewContrller ?? UIViewController())
-            self.present(newNav, animated: true) { () -> Void in }
-            self.isShowingMenu = false
-            self.enableControllerInput(false)
-        }))
-        actionsheet.addAction(UIAlertAction(title: "Game Speed", style: .default, handler: { (_: UIAlertAction) -> Void in
-            self.perform(#selector(self.showSpeedMenu), with: nil, afterDelay: 0.1)
-        }))
-        if core.supportsSaveStates {
-            actionsheet.addAction(UIAlertAction(title: "Save States", style: .default, handler: { (_: UIAlertAction) -> Void in
-                self.perform(#selector(self.showSaveStateMenu), with: nil, afterDelay: 0.1)
-            }))
-        }
-        if let gameWithCheat = core as? GameWithCheat,
-           gameWithCheat.supportsCheatCode() {
-            actionsheet.addAction(UIAlertAction(title: "Cheat Codes", style: .default, handler: { action in
-                self.perform(#selector(self.showCheatsMenu), with: nil, afterDelay: 0.1)
-            }))
-        }
-        actionsheet.addAction(UIAlertAction(title: "Reset", style: .default, handler: { (_: UIAlertAction) -> Void in
-            let completion = {
-                self.core.setPauseEmulation(false)
-                self.core.resetEmulation()
-                self.isShowingMenu = false
-                self.enableControllerInput(false)
-            }
-
-            if PVSettingsModel.shared.autoSave, self.core.supportsSaveStates {
-                self.autoSaveState { result in
-                    switch result {
-                    case .success:
-                        break
-                    case let .error(error):
-                        ELOG("Auto-save failed \(error.localizedDescription)")
-                    }
-                    completion()
-                }
-            } else {
-                completion()
-            }
-        }))
-
-        let lastPlayed = game.lastPlayed ?? Date()
-        var shouldSave = PVSettingsModel.shared.autoSave
-        shouldSave = shouldSave && abs(lastPlayed.timeIntervalSinceNow) > minimumPlayTimeToMakeAutosave
-        shouldSave = shouldSave && (game.lastAutosaveAge ?? minutes(2)) > minutes(1)
-        shouldSave = shouldSave && abs(game.saveStates.sorted(byKeyPath: "date", ascending: true).last?.date.timeIntervalSinceNow ?? minutes(2)) > minutes(1)
-
-        // Add Non-Saving quit first
-        let quitTitle = shouldSave ? "Quit (without save)" : "Quit"
-        actionsheet.addAction(UIAlertAction(title: quitTitle, style: shouldSave ? .default : .destructive, handler: { (_: UIAlertAction) -> Void in
-            self.quit(optionallySave: false)
-        }))
-
-        // If save and quit is an option, add it last with different style
-        if shouldSave {
-            actionsheet.addAction(UIAlertAction(title: "Save & Quit", style: .destructive, handler: { (_: UIAlertAction) -> Void in
-                self.quit(optionallySave: true)
-            }))
-        }
-
-        let resumeAction = UIAlertAction(title: "Resume", style: .cancel, handler: { (_: UIAlertAction) -> Void in
-            self.core.setPauseEmulation(false)
-            self.isShowingMenu = false
-            self.enableControllerInput(false)
-        })
-        actionsheet.addAction(resumeAction)
-        actionsheet.preferredAction = resumeAction
-        present(actionsheet, animated: true, completion: { () -> Void in
-            PVControllerManager.shared.iCadeController?.refreshListener()
-        })
-    }
-}


Test patch: