Merge 10e2b560b9
into ec9b5a0b8f
This commit is contained in:
commit
d3d52ad8f3
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.user-fonts</key>
|
||||
<array>
|
||||
<string>app-usage</string>
|
||||
<string>system-installation</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -131,6 +131,11 @@
|
|||
"idiom" : "ipad",
|
||||
"filename" : "icon@167.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
|
@ -1,77 +1,58 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
"subtype" : "2688h",
|
||||
"filename" : "activegs-launch-iphonexsmax.png",
|
||||
"minimum-system-version" : "12.0",
|
||||
"orientation" : "portrait",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
"subtype" : "2688h",
|
||||
"filename" : "activegs-launch-iphonexsmaxland.png",
|
||||
"minimum-system-version" : "12.0",
|
||||
"orientation" : "landscape",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"subtype" : "retina4",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"subtype" : "1792h",
|
||||
"filename" : "activegs-launch-iphonexr.png",
|
||||
"minimum-system-version" : "12.0",
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadios5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "1x"
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "1792h",
|
||||
"filename" : "activegs-launch-iphonexrland.png",
|
||||
"minimum-system-version" : "12.0",
|
||||
"orientation" : "landscape",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "2436h",
|
||||
"filename" : "activegs-launch-iphonex.png",
|
||||
"minimum-system-version" : "11.0",
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPad-ios5.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "2436h",
|
||||
"filename" : "activegs-launch-iphonexland.png",
|
||||
"minimum-system-version" : "11.0",
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadland-ios5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadretina-ios5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadretina-ios5-1.png",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadretinaland-iOS5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
|
@ -148,6 +129,79 @@
|
|||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"subtype" : "retina4",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadios5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPad-ios5.png",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadland-ios5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadretina-ios5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadretina-ios5-1.png",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "activegs-launch-iPadretinaland-iOS5.png",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 732 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
After Width: | Height: | Size: 453 KiB |
Binary file not shown.
After Width: | Height: | Size: 643 KiB |
Binary file not shown.
After Width: | Height: | Size: 817 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10116" systemVersion="15A284" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KeyCapView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="183" height="156"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WuS-sc-nnF" userLabel="Key">
|
||||
<rect key="frame" x="4" y="4" width="18" height="12"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="10"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Mapped Button" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CsD-e8-zpW">
|
||||
<rect key="frame" x="100" y="140" width="79" height="12"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="10"/>
|
||||
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="S-Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DoM-mH-zdu" userLabel="Key Alt Label">
|
||||
<rect key="frame" x="4" y="140" width="29" height="12"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="10"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="CsD-e8-zpW" secondAttribute="trailing" constant="4" id="0QF-nq-bp4"/>
|
||||
<constraint firstItem="WuS-sc-nnF" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="4" id="EG2-YO-isI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="DoM-mH-zdu" secondAttribute="bottom" constant="4" id="XtK-Xm-l8n"/>
|
||||
<constraint firstAttribute="bottom" secondItem="CsD-e8-zpW" secondAttribute="bottom" constant="4" id="bLA-GT-HXu"/>
|
||||
<constraint firstItem="DoM-mH-zdu" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="4" id="iGg-gd-FfI"/>
|
||||
<constraint firstItem="WuS-sc-nnF" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="4" id="oKU-fu-MTZ"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="keyLabel" destination="WuS-sc-nnF" id="Fge-Us-qip"/>
|
||||
<outlet property="keyLabelAlt" destination="DoM-mH-zdu" id="AOv-CK-LvA"/>
|
||||
<outlet property="mappedButtonLabel" destination="CsD-e8-zpW" id="p4m-si-1HQ"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="190.5" y="137"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -6,6 +6,63 @@
|
|||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>ActiveGS</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Apple // 2MG Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.apple.disk-image-2mg</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Apple // DSK Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.apple.disk-image-dsk</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Apple // ZIP Disk Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.apple.disk-image-zip</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Unknown File</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.item</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
@ -17,13 +74,15 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.42</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
@ -32,6 +91,7 @@
|
|||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>ShastonHi640.ttf</string>
|
||||
<string>PrintChar21.ttf</string>
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
|
@ -50,5 +110,71 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.archive</string>
|
||||
<string>public-data</string>
|
||||
<string>public.disk-image</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Apple // Disk Image - 2MG</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.apple.disk-image-2mg</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>Item 0</key>
|
||||
<string>2MG</string>
|
||||
<key>public.filename-extension</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.archive</string>
|
||||
<string>public-data</string>
|
||||
<string>public.disk-image</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Apple // Disk Image - DSK</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.apple.disk-image-dsk</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>DSK</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.archive</string>
|
||||
<string>public-data</string>
|
||||
<string>public.disk-image</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Apple // Disk Image -ZIP</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.apple.disk-image-zip</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>ZIP</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -214,7 +214,6 @@
|
|||
09FD36F31278CCEB009C31AB /* BLUE_HELMET.FTA in Resources */ = {isa = PBXBuildFile; fileRef = 09FD36F01278CCEB009C31AB /* BLUE_HELMET.FTA */; };
|
||||
09FD36F41278CCEB009C31AB /* bluehelmet_1.png in Resources */ = {isa = PBXBuildFile; fileRef = 09FD36F11278CCEB009C31AB /* bluehelmet_1.png */; };
|
||||
09FD36F51278CCEB009C31AB /* bluehelmet_2.png in Resources */ = {isa = PBXBuildFile; fileRef = 09FD36F21278CCEB009C31AB /* bluehelmet_2.png */; };
|
||||
1D60589B0D05DD56006BFB54 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.mm */; };
|
||||
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
|
||||
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
|
||||
288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; };
|
||||
|
@ -237,9 +236,36 @@
|
|||
7E51482F1CA6B5CE005DA0A6 /* ShastonHi640.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7E51481C1CA6B5CE005DA0A6 /* ShastonHi640.ttf */; };
|
||||
7E5148301CA6B5CE005DA0A6 /* Spin Up Search 1.wav in Resources */ = {isa = PBXBuildFile; fileRef = 7E51481D1CA6B5CE005DA0A6 /* Spin Up Search 1.wav */; };
|
||||
7E5148311CA6B5CE005DA0A6 /* Spin Up Search 2.wav in Resources */ = {isa = PBXBuildFile; fileRef = 7E51481E1CA6B5CE005DA0A6 /* Spin Up Search 2.wav */; };
|
||||
9222DD461CBECF2300B321B9 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.mm */; };
|
||||
924A1BAC1CB81B5800D69162 /* GameControllerKeyRemapController.m in Sources */ = {isa = PBXBuildFile; fileRef = 924A1BAA1CB81B5800D69162 /* GameControllerKeyRemapController.m */; };
|
||||
924A1BAD1CB81B5800D69162 /* GameControllerKeyRemapController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 924A1BAB1CB81B5800D69162 /* GameControllerKeyRemapController.xib */; };
|
||||
924A1BAF1CB9671400D69162 /* KeyCapView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 924A1BAE1CB9671400D69162 /* KeyCapView.xib */; };
|
||||
924A1BB21CB9685700D69162 /* KeyCapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 924A1BB11CB9685700D69162 /* KeyCapView.m */; };
|
||||
924A1BB51CBA049D00D69162 /* KeyMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 924A1BB41CBA049D00D69162 /* KeyMapper.m */; };
|
||||
9250DCB31CAEEF990093CE9A /* MfiGameControllerHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 9250DCB21CAEEF990093CE9A /* MfiGameControllerHandler.m */; };
|
||||
9250DCB51CAEFD3B0093CE9A /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9250DCB41CAEFD3B0093CE9A /* GameController.framework */; };
|
||||
927E431A25C48592008E5517 /* CheatFinderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E431925C48592008E5517 /* CheatFinderManager.swift */; };
|
||||
927E436425C94C12008E5517 /* Stream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435925C94C12008E5517 /* Stream.swift */; };
|
||||
927E436525C94C12008E5517 /* Bus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435A25C94C12008E5517 /* Bus.swift */; };
|
||||
927E436625C94C12008E5517 /* CPU+Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435C25C94C12008E5517 /* CPU+Instructions.swift */; };
|
||||
927E436725C94C12008E5517 /* CPU+Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435D25C94C12008E5517 /* CPU+Flags.swift */; };
|
||||
927E436825C94C12008E5517 /* CPU+Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435E25C94C12008E5517 /* CPU+Stack.swift */; };
|
||||
927E436925C94C12008E5517 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435F25C94C12008E5517 /* CPU.swift */; };
|
||||
927E436A25C94C12008E5517 /* Instruction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436125C94C12008E5517 /* Instruction.swift */; };
|
||||
927E436B25C94C12008E5517 /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436225C94C12008E5517 /* Mnemonic.swift */; };
|
||||
927E436C25C94C12008E5517 /* AddressingMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436325C94C12008E5517 /* AddressingMode.swift */; };
|
||||
927E437025C99A11008E5517 /* Debug6502Interpreter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436F25C99A11008E5517 /* Debug6502Interpreter.swift */; };
|
||||
928410581CA8443A00DC5D93 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 928410571CA8443A00DC5D93 /* Images.xcassets */; };
|
||||
92A9D66E25CC5A96008F5031 /* DebuggerUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92A9D66D25CC5A96008F5031 /* DebuggerUtils.swift */; };
|
||||
92A9D67125CC5AF4008F5031 /* EmuMemoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92A9D67025CC5AF4008F5031 /* EmuMemoryModel.swift */; };
|
||||
92A9D67525CC5B7D008F5031 /* DebugMemoryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92A9D67425CC5B7D008F5031 /* DebugMemoryButton.swift */; };
|
||||
92A9D67925CC5BB6008F5031 /* DebugMemoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92A9D67825CC5BB6008F5031 /* DebugMemoryCell.swift */; };
|
||||
92A9D67F25CC5C66008F5031 /* DebugMemoryActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92A9D67E25CC5C66008F5031 /* DebugMemoryActionViewController.swift */; };
|
||||
92B9EADF24D3369700E6CFB2 /* EmulatorKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B9EADE24D3369700E6CFB2 /* EmulatorKeyboard.swift */; };
|
||||
92E2063225AADFB000AE3F28 /* PreviewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92E2063125AADF6E00AE3F28 /* PreviewUI.swift */; };
|
||||
92FA0F2425B52EA200663577 /* EmuWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92FA0F2325B52EA200663577 /* EmuWrapper.mm */; };
|
||||
92FA0F2A25B5353D00663577 /* DebugMemoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FA0F2925B5353D00663577 /* DebugMemoryViewController.swift */; };
|
||||
92FA0F2D25B59EF100663577 /* PrintChar21.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 92FA0F2C25B59EF100663577 /* PrintChar21.ttf */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXBuildRule section */
|
||||
|
@ -247,6 +273,8 @@
|
|||
isa = PBXBuildRule;
|
||||
compilerSpec = com.apple.compilers.proxy.script;
|
||||
fileType = pattern.proxy;
|
||||
inputFiles = (
|
||||
);
|
||||
isEditable = 1;
|
||||
outputFiles = (
|
||||
);
|
||||
|
@ -576,10 +604,42 @@
|
|||
7E51481C1CA6B5CE005DA0A6 /* ShastonHi640.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = ShastonHi640.ttf; sourceTree = "<group>"; };
|
||||
7E51481D1CA6B5CE005DA0A6 /* Spin Up Search 1.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "Spin Up Search 1.wav"; sourceTree = "<group>"; };
|
||||
7E51481E1CA6B5CE005DA0A6 /* Spin Up Search 2.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "Spin Up Search 2.wav"; sourceTree = "<group>"; };
|
||||
924A1BA91CB81B5800D69162 /* GameControllerKeyRemapController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameControllerKeyRemapController.h; sourceTree = "<group>"; };
|
||||
924A1BAA1CB81B5800D69162 /* GameControllerKeyRemapController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameControllerKeyRemapController.m; sourceTree = "<group>"; };
|
||||
924A1BAB1CB81B5800D69162 /* GameControllerKeyRemapController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = GameControllerKeyRemapController.xib; path = ../Common.iphone/GameControllerKeyRemapController.xib; sourceTree = "<group>"; };
|
||||
924A1BAE1CB9671400D69162 /* KeyCapView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KeyCapView.xib; sourceTree = "<group>"; };
|
||||
924A1BB01CB9685700D69162 /* KeyCapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyCapView.h; sourceTree = "<group>"; };
|
||||
924A1BB11CB9685700D69162 /* KeyCapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyCapView.m; sourceTree = "<group>"; };
|
||||
924A1BB31CBA049D00D69162 /* KeyMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyMapper.h; sourceTree = "<group>"; };
|
||||
924A1BB41CBA049D00D69162 /* KeyMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyMapper.m; sourceTree = "<group>"; };
|
||||
9250DCB11CAEEF990093CE9A /* MfiGameControllerHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MfiGameControllerHandler.h; sourceTree = "<group>"; };
|
||||
9250DCB21CAEEF990093CE9A /* MfiGameControllerHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MfiGameControllerHandler.m; sourceTree = "<group>"; };
|
||||
9250DCB41CAEFD3B0093CE9A /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
|
||||
927E431925C48592008E5517 /* CheatFinderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatFinderManager.swift; sourceTree = "<group>"; };
|
||||
927E435925C94C12008E5517 /* Stream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stream.swift; sourceTree = "<group>"; };
|
||||
927E435A25C94C12008E5517 /* Bus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bus.swift; sourceTree = "<group>"; };
|
||||
927E435C25C94C12008E5517 /* CPU+Instructions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CPU+Instructions.swift"; sourceTree = "<group>"; };
|
||||
927E435D25C94C12008E5517 /* CPU+Flags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CPU+Flags.swift"; sourceTree = "<group>"; };
|
||||
927E435E25C94C12008E5517 /* CPU+Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CPU+Stack.swift"; sourceTree = "<group>"; };
|
||||
927E435F25C94C12008E5517 /* CPU.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = "<group>"; };
|
||||
927E436125C94C12008E5517 /* Instruction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Instruction.swift; sourceTree = "<group>"; };
|
||||
927E436225C94C12008E5517 /* Mnemonic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = "<group>"; };
|
||||
927E436325C94C12008E5517 /* AddressingMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressingMode.swift; sourceTree = "<group>"; };
|
||||
927E436F25C99A11008E5517 /* Debug6502Interpreter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debug6502Interpreter.swift; sourceTree = "<group>"; };
|
||||
928410571CA8443A00DC5D93 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ActiveGS/Images.xcassets; sourceTree = "<group>"; };
|
||||
92A9D66D25CC5A96008F5031 /* DebuggerUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggerUtils.swift; sourceTree = "<group>"; };
|
||||
92A9D67025CC5AF4008F5031 /* EmuMemoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmuMemoryModel.swift; sourceTree = "<group>"; };
|
||||
92A9D67425CC5B7D008F5031 /* DebugMemoryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMemoryButton.swift; sourceTree = "<group>"; };
|
||||
92A9D67825CC5BB6008F5031 /* DebugMemoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMemoryCell.swift; sourceTree = "<group>"; };
|
||||
92A9D67E25CC5C66008F5031 /* DebugMemoryActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMemoryActionViewController.swift; sourceTree = "<group>"; };
|
||||
92AF5B1325D1B7A00025578D /* ActiveGS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = ActiveGS.entitlements; path = ActiveGS/ActiveGS.entitlements; sourceTree = "<group>"; };
|
||||
92B9EADD24D3369600E6CFB2 /* ActiveGS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ActiveGS-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
92B9EADE24D3369700E6CFB2 /* EmulatorKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmulatorKeyboard.swift; sourceTree = "<group>"; };
|
||||
92E2063125AADF6E00AE3F28 /* PreviewUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUI.swift; sourceTree = "<group>"; };
|
||||
92FA0F2325B52EA200663577 /* EmuWrapper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = EmuWrapper.mm; sourceTree = "<group>"; };
|
||||
92FA0F2625B52EC000663577 /* EmuWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EmuWrapper.h; sourceTree = "<group>"; };
|
||||
92FA0F2925B5353D00663577 /* DebugMemoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMemoryViewController.swift; sourceTree = "<group>"; };
|
||||
92FA0F2C25B59EF100663577 /* PrintChar21.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = PrintChar21.ttf; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -712,6 +772,8 @@
|
|||
0916BB03129473CE001727AF /* infoViewController-ipad.xib */,
|
||||
09A5CE75125D422D0018DC22 /* infoViewController.xib */,
|
||||
099CD904125E6F6E008EFD6C /* detailViewController.xib */,
|
||||
924A1BAE1CB9671400D69162 /* KeyCapView.xib */,
|
||||
924A1BAB1CB81B5800D69162 /* GameControllerKeyRemapController.xib */,
|
||||
);
|
||||
name = xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -985,6 +1047,16 @@
|
|||
09FA608B125A7B3E00B07F77 /* KBDController.mm */,
|
||||
9250DCB11CAEEF990093CE9A /* MfiGameControllerHandler.h */,
|
||||
9250DCB21CAEEF990093CE9A /* MfiGameControllerHandler.m */,
|
||||
924A1BA91CB81B5800D69162 /* GameControllerKeyRemapController.h */,
|
||||
924A1BAA1CB81B5800D69162 /* GameControllerKeyRemapController.m */,
|
||||
924A1BB01CB9685700D69162 /* KeyCapView.h */,
|
||||
924A1BB11CB9685700D69162 /* KeyCapView.m */,
|
||||
924A1BB31CBA049D00D69162 /* KeyMapper.h */,
|
||||
924A1BB41CBA049D00D69162 /* KeyMapper.m */,
|
||||
92B9EADE24D3369700E6CFB2 /* EmulatorKeyboard.swift */,
|
||||
92E2063125AADF6E00AE3F28 /* PreviewUI.swift */,
|
||||
92B9EADD24D3369600E6CFB2 /* ActiveGS-Bridging-Header.h */,
|
||||
927E435725C94BE7008E5517 /* Debugger */,
|
||||
);
|
||||
name = Common.iphone;
|
||||
path = ../Common.iphone;
|
||||
|
@ -1001,6 +1073,7 @@
|
|||
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92AF5B1325D1B7A00025578D /* ActiveGS.entitlements */,
|
||||
096604C819127DB700A22C51 /* activegs.plist */,
|
||||
09052B4219053C9F00853FAE /* Libraries */,
|
||||
09520D8516AEF8250065E84A /* Drivers */,
|
||||
|
@ -1084,6 +1157,7 @@
|
|||
7E5148141CA6B5CE005DA0A6 /* floppy_eject.wav */,
|
||||
7E5148151CA6B5CE005DA0A6 /* logo_apple2.png */,
|
||||
7E5148161CA6B5CE005DA0A6 /* logo_apple2gs.png */,
|
||||
92FA0F2C25B59EF100663577 /* PrintChar21.ttf */,
|
||||
7E5148171CA6B5CE005DA0A6 /* Search Skip Search 1.wav */,
|
||||
7E5148181CA6B5CE005DA0A6 /* Search Skip Search 2.wav */,
|
||||
7E5148191CA6B5CE005DA0A6 /* Search Skip Search 3.wav */,
|
||||
|
@ -1097,6 +1171,72 @@
|
|||
path = ../Common.res;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
927E435725C94BE7008E5517 /* Debugger */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E435825C94C12008E5517 /* 65C02Interpreter */,
|
||||
92A9D67325CC5B63008F5031 /* Views */,
|
||||
92A9D67B25CC5C24008F5031 /* ViewControllers */,
|
||||
92FA0F2325B52EA200663577 /* EmuWrapper.mm */,
|
||||
92FA0F2625B52EC000663577 /* EmuWrapper.h */,
|
||||
92A9D67025CC5AF4008F5031 /* EmuMemoryModel.swift */,
|
||||
927E431925C48592008E5517 /* CheatFinderManager.swift */,
|
||||
92A9D66D25CC5A96008F5031 /* DebuggerUtils.swift */,
|
||||
);
|
||||
path = Debugger;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
927E435825C94C12008E5517 /* 65C02Interpreter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E436F25C99A11008E5517 /* Debug6502Interpreter.swift */,
|
||||
927E435925C94C12008E5517 /* Stream.swift */,
|
||||
927E435A25C94C12008E5517 /* Bus.swift */,
|
||||
927E435B25C94C12008E5517 /* CPU */,
|
||||
927E436025C94C12008E5517 /* Instruction */,
|
||||
);
|
||||
path = 65C02Interpreter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
927E435B25C94C12008E5517 /* CPU */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E435C25C94C12008E5517 /* CPU+Instructions.swift */,
|
||||
927E435D25C94C12008E5517 /* CPU+Flags.swift */,
|
||||
927E435E25C94C12008E5517 /* CPU+Stack.swift */,
|
||||
927E435F25C94C12008E5517 /* CPU.swift */,
|
||||
);
|
||||
path = CPU;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
927E436025C94C12008E5517 /* Instruction */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E436125C94C12008E5517 /* Instruction.swift */,
|
||||
927E436225C94C12008E5517 /* Mnemonic.swift */,
|
||||
927E436325C94C12008E5517 /* AddressingMode.swift */,
|
||||
);
|
||||
path = Instruction;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92A9D67325CC5B63008F5031 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92A9D67425CC5B7D008F5031 /* DebugMemoryButton.swift */,
|
||||
92A9D67825CC5BB6008F5031 /* DebugMemoryCell.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92A9D67B25CC5C24008F5031 /* ViewControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92FA0F2925B5353D00663577 /* DebugMemoryViewController.swift */,
|
||||
92A9D67E25CC5C66008F5031 /* DebugMemoryActionViewController.swift */,
|
||||
);
|
||||
path = ViewControllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -1128,6 +1268,7 @@
|
|||
LastUpgradeCheck = 0820;
|
||||
TargetAttributes = {
|
||||
1D6058900D05DD3D006BFB54 = {
|
||||
LastSwiftMigration = 1160;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.DataProtection = {
|
||||
|
@ -1212,6 +1353,7 @@
|
|||
09087F3B12244C0500C52D88 /* delta_2.png in Resources */,
|
||||
09087F3C12244C0500C52D88 /* delta_3.png in Resources */,
|
||||
09087F3D12244C0500C52D88 /* delta_4.png in Resources */,
|
||||
924A1BAD1CB81B5800D69162 /* GameControllerKeyRemapController.xib in Resources */,
|
||||
09087F3E12244C0500C52D88 /* delta_5.png in Resources */,
|
||||
09087F3F12244C0500C52D88 /* delta_6.png in Resources */,
|
||||
7E5148311CA6B5CE005DA0A6 /* Spin Up Search 2.wav in Resources */,
|
||||
|
@ -1267,11 +1409,13 @@
|
|||
09AF98051283F0DF00083D60 /* Oil_Landers.fta in Resources */,
|
||||
09AF980C1283F12200083D60 /* StarWizard (2002).fta in Resources */,
|
||||
09AF980D1283F12200083D60 /* starwizard_2.png in Resources */,
|
||||
924A1BAF1CB9671400D69162 /* KeyCapView.xib in Resources */,
|
||||
09AF980E1283F12200083D60 /* starwizard_3.png in Resources */,
|
||||
7E5148271CA6B5CE005DA0A6 /* floppy_eject.wav in Resources */,
|
||||
09AF980F1283F12200083D60 /* starwizard_4.png in Resources */,
|
||||
09AF98101283F12200083D60 /* starwizard_5.png in Resources */,
|
||||
09AF98111283F12200083D60 /* starwizard.png in Resources */,
|
||||
92FA0F2D25B59EF100663577 /* PrintChar21.ttf in Resources */,
|
||||
7E51482C1CA6B5CE005DA0A6 /* Search Skip Search 3.wav in Resources */,
|
||||
09D8BCF91285EFE900B6D785 /* miniprix_1.png in Resources */,
|
||||
09D8BCFC1285EFFA00B6D785 /* miniprix_2.png in Resources */,
|
||||
|
@ -1304,15 +1448,21 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1D60589B0D05DD56006BFB54 /* main.mm in Sources */,
|
||||
9222DD461CBECF2300B321B9 /* main.mm in Sources */,
|
||||
09BB434511D92F65005ADA46 /* ActiveDownloadMac.cpp in Sources */,
|
||||
09BB434711D92F65005ADA46 /* CEmulatorCtrlMac.cpp in Sources */,
|
||||
92FA0F2A25B5353D00663577 /* DebugMemoryViewController.swift in Sources */,
|
||||
09BB43B011D92F70005ADA46 /* activeconfig.cpp in Sources */,
|
||||
09BB43B111D92F70005ADA46 /* ActiveDownload.cpp in Sources */,
|
||||
92B9EADF24D3369700E6CFB2 /* EmulatorKeyboard.swift in Sources */,
|
||||
927E436B25C94C12008E5517 /* Mnemonic.swift in Sources */,
|
||||
09052B9419053C9F00853FAE /* uncompr.cpp in Sources */,
|
||||
09BB43B211D92F70005ADA46 /* ActiveZip.cpp in Sources */,
|
||||
09052B8619053C9F00853FAE /* pngwtran.cpp in Sources */,
|
||||
09052B8B19053C9F00853FAE /* zip.cpp in Sources */,
|
||||
927E436825C94C12008E5517 /* CPU+Stack.swift in Sources */,
|
||||
927E436725C94C12008E5517 /* CPU+Flags.swift in Sources */,
|
||||
927E436925C94C12008E5517 /* CPU.swift in Sources */,
|
||||
09BB43B311D92F70005ADA46 /* CA2Text.cpp in Sources */,
|
||||
09BB43B411D92F70005ADA46 /* CEmulatorCtrl.cpp in Sources */,
|
||||
09BB43B511D92F70005ADA46 /* interface.cpp in Sources */,
|
||||
|
@ -1321,6 +1471,7 @@
|
|||
09BB43B711D92F70005ADA46 /* rom.cpp in Sources */,
|
||||
09FA6095125A7B3E00B07F77 /* activegsAppDelegate.mm in Sources */,
|
||||
09052B8119053C9F00853FAE /* pngset.cpp in Sources */,
|
||||
927E436425C94C12008E5517 /* Stream.swift in Sources */,
|
||||
09FA6096125A7B3E00B07F77 /* activegsList.mm in Sources */,
|
||||
09FA6097125A7B3E00B07F77 /* activegsViewController.mm in Sources */,
|
||||
09052B8D19053C9F00853FAE /* compress.cpp in Sources */,
|
||||
|
@ -1328,14 +1479,19 @@
|
|||
09FA609A125A7B3E00B07F77 /* asyncimageview.mm in Sources */,
|
||||
09FA609C125A7B3E00B07F77 /* GTMUIView+SubtreeDescription.m in Sources */,
|
||||
09052B8F19053C9F00853FAE /* deflate.cpp in Sources */,
|
||||
92A9D66E25CC5A96008F5031 /* DebuggerUtils.swift in Sources */,
|
||||
09FA609F125A7B3E00B07F77 /* KBDController.mm in Sources */,
|
||||
09AADC78125C560A00654DF1 /* detailViewController.mm in Sources */,
|
||||
09A5CE60125D41860018DC22 /* infoViewController.mm in Sources */,
|
||||
92A9D67525CC5B7D008F5031 /* DebugMemoryButton.swift in Sources */,
|
||||
0972554713CF2232006194F9 /* activegsEmulatorController.mm in Sources */,
|
||||
924A1BB51CBA049D00D69162 /* KeyMapper.m in Sources */,
|
||||
92E2063225AADFB000AE3F28 /* PreviewUI.swift in Sources */,
|
||||
0907BCC9142F567A0051CA0A /* asynccommand.mm in Sources */,
|
||||
09052B7D19053C9F00853FAE /* pngread.cpp in Sources */,
|
||||
09C81A781657ACAE008539D5 /* adb.cpp in Sources */,
|
||||
09C81A791657ACAE008539D5 /* async_event.cpp in Sources */,
|
||||
927E437025C99A11008E5517 /* Debug6502Interpreter.swift in Sources */,
|
||||
09C81A7A1657ACAE008539D5 /* clock.cpp in Sources */,
|
||||
09052B9119053C9F00853FAE /* inflate.cpp in Sources */,
|
||||
09C81A7B1657ACAE008539D5 /* compile_time.cpp in Sources */,
|
||||
|
@ -1343,6 +1499,7 @@
|
|||
09C81A7D1657ACAE008539D5 /* config_generic.cpp in Sources */,
|
||||
09052B8C19053C9F00853FAE /* adler32.cpp in Sources */,
|
||||
09C81A7F1657ACAE008539D5 /* dis.cpp in Sources */,
|
||||
92A9D67125CC5AF4008F5031 /* EmuMemoryModel.swift in Sources */,
|
||||
09C81A801657ACAE008539D5 /* engine_c.cpp in Sources */,
|
||||
09C81A811657ACAE008539D5 /* GraphCounter.cpp in Sources */,
|
||||
09C81A821657ACAE008539D5 /* iwm.cpp in Sources */,
|
||||
|
@ -1353,13 +1510,17 @@
|
|||
09C81A861657ACAE008539D5 /* openalasync_snddriver.cpp in Sources */,
|
||||
09052B8219053C9F00853FAE /* pngtrans.cpp in Sources */,
|
||||
09C81A871657ACAE008539D5 /* paddles.cpp in Sources */,
|
||||
924A1BAC1CB81B5800D69162 /* GameControllerKeyRemapController.m in Sources */,
|
||||
09C81A881657ACAE008539D5 /* SaveState.cpp in Sources */,
|
||||
09C81A891657ACAE008539D5 /* scc.cpp in Sources */,
|
||||
927E436625C94C12008E5517 /* CPU+Instructions.swift in Sources */,
|
||||
09C81A8A1657ACAE008539D5 /* scc_socket_driver.cpp in Sources */,
|
||||
09052B8319053C9F00853FAE /* pngvcrd.cpp in Sources */,
|
||||
927E436C25C94C12008E5517 /* AddressingMode.swift in Sources */,
|
||||
09C81A8B1657ACAE008539D5 /* sim65816.cpp in Sources */,
|
||||
09C81A8C1657ACAE008539D5 /* smartport.cpp in Sources */,
|
||||
09052B9019053C9F00853FAE /* inffast.cpp in Sources */,
|
||||
927E436A25C94C12008E5517 /* Instruction.swift in Sources */,
|
||||
09C81A8D1657ACAE008539D5 /* sound.cpp in Sources */,
|
||||
09052B7819053C9F00853FAE /* pngerror.cpp in Sources */,
|
||||
09052B7B19053C9F00853FAE /* pngmem.cpp in Sources */,
|
||||
|
@ -1371,14 +1532,19 @@
|
|||
09C81A9C1657ACDD008539D5 /* macdriver_generic.cpp in Sources */,
|
||||
09052B8919053C9F00853FAE /* iowin32.cpp in Sources */,
|
||||
09C81A9F1657ACDD008539D5 /* scc_macdriver.cpp in Sources */,
|
||||
927E436525C94C12008E5517 /* Bus.swift in Sources */,
|
||||
09C81AA71657AD18008539D5 /* emulatorView.mm in Sources */,
|
||||
09C81AA81657AD18008539D5 /* joystick_iphone.cpp in Sources */,
|
||||
09C81AA91657AD18008539D5 /* zoomEmulatorView.mm in Sources */,
|
||||
09052B7F19053C9F00853FAE /* pngrtran.cpp in Sources */,
|
||||
92A9D67925CC5BB6008F5031 /* DebugMemoryCell.swift in Sources */,
|
||||
09052B7A19053C9F00853FAE /* pngget.cpp in Sources */,
|
||||
924A1BB21CB9685700D69162 /* KeyCapView.m in Sources */,
|
||||
09052B7919053C9F00853FAE /* pnggccrd.cpp in Sources */,
|
||||
09052B9219053C9F00853FAE /* inftrees.cpp in Sources */,
|
||||
927E431A25C48592008E5517 /* CheatFinderManager.swift in Sources */,
|
||||
09052B8819053C9F00853FAE /* ioapi.cpp in Sources */,
|
||||
92A9D67F25CC5C66008F5031 /* DebugMemoryActionViewController.swift in Sources */,
|
||||
09052B7C19053C9F00853FAE /* pngpread.cpp in Sources */,
|
||||
09052B8419053C9F00853FAE /* pngwio.cpp in Sources */,
|
||||
09052B8719053C9F00853FAE /* pngwutil.cpp in Sources */,
|
||||
|
@ -1390,6 +1556,7 @@
|
|||
0941E6AF16720886003E0411 /* simplexml.cpp in Sources */,
|
||||
09520D8316AEF8130065E84A /* driver.cpp in Sources */,
|
||||
09520D8916AEF8250065E84A /* activegs_driver.cpp in Sources */,
|
||||
92FA0F2425B52EA200663577 /* EmuWrapper.mm in Sources */,
|
||||
09520D8E16AEF8650065E84A /* apple2e.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1433,12 +1600,13 @@
|
|||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_ENTITLEMENTS = ActiveGS/ActiveGS.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEVELOPMENT_TEAM = TFQRCMRWP4;
|
||||
DEVELOPMENT_TEAM = R72X3BF4KE;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = activegs_Prefix.pch;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
|
@ -1453,16 +1621,19 @@
|
|||
GCC_THUMB_SUPPORT = NO;
|
||||
GCC_VERSION = "";
|
||||
INFOPLIST_FILE = activegs.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks/GraphicsServices.framework\"",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.activeGS.test;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yoshisuga.activeGS;
|
||||
PRODUCT_NAME = ActiveGS;
|
||||
PROVISIONING_PROFILE = "";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "../Common.iphone/ActiveGS-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
@ -1474,12 +1645,13 @@
|
|||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_ENTITLEMENTS = ActiveGS/ActiveGS.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEVELOPMENT_TEAM = TFQRCMRWP4;
|
||||
DEVELOPMENT_TEAM = R72X3BF4KE;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
|
@ -1496,16 +1668,20 @@
|
|||
GCC_THUMB_SUPPORT = NO;
|
||||
GCC_VERSION = "";
|
||||
INFOPLIST_FILE = activegs.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks/GraphicsServices.framework\"",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.activeGS.test;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yoshisuga.activeGS;
|
||||
PRODUCT_NAME = ActiveGS;
|
||||
PROVISIONING_PROFILE = "";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "../Common.iphone/ActiveGS-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -1516,12 +1692,13 @@
|
|||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_ENTITLEMENTS = ActiveGS/ActiveGS.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEVELOPMENT_TEAM = TFQRCMRWP4;
|
||||
DEVELOPMENT_TEAM = R72X3BF4KE;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = activegs_Prefix.pch;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
|
@ -1535,16 +1712,19 @@
|
|||
GCC_THUMB_SUPPORT = NO;
|
||||
GCC_VERSION = "";
|
||||
INFOPLIST_FILE = activegs.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks/GraphicsServices.framework\"",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.activeGS.test;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yoshisuga.activeGS;
|
||||
PRODUCT_NAME = ActiveGS;
|
||||
PROVISIONING_PROFILE = "";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "../Common.iphone/ActiveGS-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -131,91 +131,13 @@ Cg
|
|||
<attributes>
|
||||
<font key="NSFont" size="11" name="CourierNewPSMT"/>
|
||||
<font key="NSOriginalFont" size="11" name="CourierNewPSMT"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content=".zip">
|
||||
<attributes>
|
||||
<font key="NSFont" size="11" name="CourierNewPSMT"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment>
|
||||
|
@ -224,365 +146,53 @@ Cgo
|
|||
</string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="(">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<font key="NSOriginalFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="N">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="ote: ">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<font key="NSOriginalFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="Square-Enix canceled plans to release ">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="Final Fantasy XV ">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<font key="NSOriginalFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="on the Apple IIGS due to excessive shipping costs, therefore it remains unreleased">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content=")">
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<font key="NSOriginalFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment>
|
||||
|
@ -591,46 +201,7 @@ Cgo
|
|||
</string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="10" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment>
|
||||
|
@ -643,92 +214,14 @@ Cgo
|
|||
</string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="Gamepad">
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue-Bold"/>
|
||||
<font key="NSOriginalFont" size="14" name="HelveticaNeue-Bold"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment>
|
||||
|
@ -736,46 +229,7 @@ Cgo
|
|||
</string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue-Bold"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment>
|
||||
|
@ -788,92 +242,14 @@ Cgo
|
|||
</string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment content="Save States">
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue-Bold"/>
|
||||
<font key="NSOriginalFont" size="14" name="HelveticaNeue-Bold"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
<fragment>
|
||||
|
@ -881,46 +257,7 @@ Cgo
|
|||
- Are supported and they work..most of the time! Save states are specific to each program and you have 6 available slots.</string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
|
||||
<tabStops>
|
||||
<textTab alignment="left" location="28">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="56">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="84">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="112">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="140">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="168">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="196">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="224">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="252">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="280">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="308">
|
||||
<options/>
|
||||
</textTab>
|
||||
<textTab alignment="left" location="336">
|
||||
<options/>
|
||||
</textTab>
|
||||
</tabStops>
|
||||
</paragraphStyle>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "KeyMapper.h"
|
||||
|
||||
#import "EmuWrapper.h"
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// BUS.swift
|
||||
// Monitor
|
||||
//
|
||||
// Created by Michał Kałużny on 08.01.18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Bus {
|
||||
func read(from address: UInt16) throws -> UInt8
|
||||
func read(from address: UInt16) throws -> UInt16
|
||||
func write(to address: UInt16, value: UInt8) throws
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// Flags.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 17/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CPU {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
static let negative = Flags(rawValue: 1 << 7)
|
||||
static let overflow = Flags(rawValue: 1 << 6)
|
||||
static let always = Flags(rawValue: 1 << 5)
|
||||
static let `break` = Flags(rawValue: 1 << 4)
|
||||
static let decimal = Flags(rawValue: 1 << 3)
|
||||
static let interrupt = Flags(rawValue: 1 << 2)
|
||||
static let zero = Flags(rawValue: 1 << 1)
|
||||
static let carry = Flags(rawValue: 1 << 0)
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
self.rawValue = rawValue | 0b0010_0000
|
||||
}
|
||||
}
|
||||
|
||||
internal func recalculateStatus(flags: Flags, for value: UInt8) {
|
||||
if flags.contains(.carry) {
|
||||
calculateCarry(value: value)
|
||||
}
|
||||
|
||||
if flags.contains(.overflow) {
|
||||
calculateCarry(value: value)
|
||||
}
|
||||
|
||||
if flags.contains(.negative) {
|
||||
calculateSign(value: value)
|
||||
}
|
||||
|
||||
if flags.contains(.zero) {
|
||||
calculateZero(value: value)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateCarry(value: UInt8) {
|
||||
|
||||
}
|
||||
|
||||
private func calculateOverflow(value: UInt8) {
|
||||
|
||||
}
|
||||
|
||||
private func calculateSign(value: UInt8) {
|
||||
let bit = (value & (1 << 7)) != 0
|
||||
|
||||
if bit {
|
||||
Status.insert(.negative)
|
||||
} else {
|
||||
Status.remove(.negative)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateZero(value: UInt8) {
|
||||
if value != 0 {
|
||||
Status.remove(.zero)
|
||||
} else {
|
||||
Status.insert(.zero)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
// CPU+Instructions.swift
|
||||
// MOS6502PackageDescription
|
||||
//
|
||||
// Created by Michał Kałużny on 09.01.18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CPU {
|
||||
internal func execute(instruction: Instruction) throws {
|
||||
switch instruction.mnemonic {
|
||||
//MARK: Branch Instructions
|
||||
case .BNE:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.zero) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BEQ:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.zero) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BPL:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.negative) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BCC:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.carry) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BCS:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.carry) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BMI:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.negative) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BVC:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.overflow) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BVS:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.overflow) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
//MARK: Stack Operations
|
||||
case .TXS:
|
||||
SP = X
|
||||
PC += instruction.size
|
||||
case .TSX:
|
||||
X = SP
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .PLP:
|
||||
let value = try pop() as UInt8
|
||||
Status = Flags(rawValue: value)
|
||||
PC += instruction.size
|
||||
case .PHP:
|
||||
try push(Status.rawValue | 0b0001_0000)
|
||||
PC += instruction.size
|
||||
case .PHA:
|
||||
try push(A)
|
||||
PC += instruction.size
|
||||
case .PLA:
|
||||
A = try pop()
|
||||
recalculateStatus(flags: [.zero, .negative], for: A)
|
||||
PC += instruction.size
|
||||
case .JSR:
|
||||
// JSR writes the *address of the last byte* of the instruction.
|
||||
try push(PC + instruction.size - 1)
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
case .RTS:
|
||||
PC += instruction.size
|
||||
PC = try pop()
|
||||
// We would land on the *last byte* of the JSR, just before the jump, we need to advance the PC
|
||||
PC += 1
|
||||
//MARK: Register Operations
|
||||
case .INX:
|
||||
X = X &+ 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .DEX:
|
||||
X = X &- 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .INY:
|
||||
Y = Y &+ 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: Y)
|
||||
PC += instruction.size
|
||||
case .DEY:
|
||||
Y = Y &- 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: Y)
|
||||
PC += instruction.size
|
||||
case .TYA:
|
||||
A = Y
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .TXA:
|
||||
A = X
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .TAX:
|
||||
X = A
|
||||
recalculateStatus(flags: [.negative, .zero], for: X)
|
||||
PC += instruction.size
|
||||
case .TAY:
|
||||
Y = A
|
||||
recalculateStatus(flags: [.negative, .zero], for: Y)
|
||||
PC += instruction.size
|
||||
//MARK: Flags Operations:
|
||||
case .SEI:
|
||||
Status.insert(.interrupt)
|
||||
PC += instruction.size
|
||||
case .CLD:
|
||||
Status.remove(.decimal)
|
||||
PC += instruction.size
|
||||
case .CLC:
|
||||
Status.remove(.carry)
|
||||
PC += instruction.size
|
||||
//MARK: Interrupt Operations:
|
||||
case .BRK:
|
||||
try push(PC)
|
||||
try push(Status.rawValue)
|
||||
|
||||
PC = try bus.read(from: CPU.interruptVector)
|
||||
|
||||
Status.insert(.break)
|
||||
//MARK: Other Instructions, clean me up please.
|
||||
case .LDA:
|
||||
A = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
recalculateStatus(flags: [.zero, .negative], for: A)
|
||||
PC += instruction.size
|
||||
case .STA:
|
||||
let address: UInt16 = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
try bus.write(to: address, value: A)
|
||||
PC += instruction.size
|
||||
case .LDX:
|
||||
X = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .STX:
|
||||
let address = try instruction.addressingMode.value(with: self, bus: bus) as UInt16
|
||||
try bus.write(to: address, value: X)
|
||||
PC += instruction.size
|
||||
case .LDY:
|
||||
Y = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
recalculateStatus(flags: [.zero, .negative], for: Y)
|
||||
PC += instruction.size
|
||||
case .STY:
|
||||
let address = try instruction.addressingMode.value(with: self, bus: bus) as UInt16
|
||||
try bus.write(to: address, value: Y)
|
||||
PC += instruction.size
|
||||
case .JMP:
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
case .CMP:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
|
||||
if A >= value {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = A &- value
|
||||
recalculateStatus(flags: [.negative, .zero], for: result)
|
||||
|
||||
PC += instruction.size
|
||||
case .CPY:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
|
||||
if Y >= value {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = Y &- value
|
||||
recalculateStatus(flags: [.negative, .zero], for: result)
|
||||
|
||||
PC += instruction.size
|
||||
case .CPX:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
|
||||
if X >= value {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = X &- value
|
||||
recalculateStatus(flags: [.negative, .zero], for: result)
|
||||
|
||||
PC += instruction.size
|
||||
case .ADC:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
let carry: UInt8 = Status.contains(.carry) ? 1 : 0
|
||||
|
||||
let result = [A, value, carry].map(UInt16.init).reduce(0, +)
|
||||
if result > UInt8.max {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let final: UInt8 = UInt8(result & 0xFF)
|
||||
|
||||
let overflow = (((A ^ final) & 0x80) != 0) && (((UInt16(A) ^ result) & 0x80) == 0)
|
||||
if overflow {
|
||||
Status.insert(.overflow)
|
||||
} else {
|
||||
Status.remove(.overflow)
|
||||
}
|
||||
|
||||
A = final
|
||||
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .EOR:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
A = A ^ value
|
||||
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .NOP:
|
||||
PC += instruction.size
|
||||
case _:
|
||||
throw Error.unimplementedInstruction(instruction: instruction)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// CPU+Stack.swift
|
||||
// MOS6502PackageDescription
|
||||
//
|
||||
// Created by Michał Kałużny on 09.01.18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CPU {
|
||||
static let stackPointerBase: UInt16 = 0x100
|
||||
|
||||
internal func pop() throws -> UInt8 {
|
||||
SP += 1
|
||||
|
||||
let value: UInt8 = try bus.read(from: UInt16(SP) + CPU.stackPointerBase)
|
||||
|
||||
print("Popping value: \(value.hex)")
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
internal func pop() throws -> UInt16 {
|
||||
let low: UInt8 = try pop()
|
||||
let high: UInt8 = try pop()
|
||||
|
||||
return UInt16(low) | UInt16(high) << 8
|
||||
}
|
||||
|
||||
internal func push(_ value: UInt8) throws {
|
||||
try bus.write(to: UInt16(SP) + CPU.stackPointerBase, value: value)
|
||||
|
||||
print("Pushing value: \(value.hex)")
|
||||
|
||||
SP -= 1
|
||||
}
|
||||
|
||||
internal func push(_ value: UInt16) throws {
|
||||
let low: UInt8 = UInt8(value & 0xFF)
|
||||
let high: UInt8 = UInt8(value >> 8) & 0xFF
|
||||
|
||||
try push(high)
|
||||
try push(low)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// CPU.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 15/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class CPU {
|
||||
static let nmiVector: UInt16 = 0xFFFA
|
||||
static let resetVector: UInt16 = 0xFFFC
|
||||
static let interruptVector: UInt16 = 0xFFFE
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case unimplementedInstruction(instruction: Instruction)
|
||||
}
|
||||
|
||||
public enum Register {
|
||||
case X
|
||||
case Y
|
||||
}
|
||||
|
||||
//MARK: Registers
|
||||
public var PC: UInt16 = 0
|
||||
var A: UInt8 = 0
|
||||
var X: UInt8 = 0
|
||||
var Y: UInt8 = 0
|
||||
var SP: UInt8 = 0
|
||||
var Status: Flags = []
|
||||
|
||||
let bus: Bus
|
||||
|
||||
let breakpoints: [UInt16] = [0x378A]
|
||||
|
||||
public init(bus: Bus) {
|
||||
self.bus = bus
|
||||
}
|
||||
|
||||
public func reset() throws {
|
||||
PC = try bus.read(from: CPU.resetVector)
|
||||
A = 0
|
||||
X = 0
|
||||
Y = 0
|
||||
SP = 0xFF
|
||||
Status = [.break, .interrupt, .always]
|
||||
}
|
||||
|
||||
public func step() throws {
|
||||
if breakpoints.contains(PC) {
|
||||
print("Breakpoint!")
|
||||
}
|
||||
print(self)
|
||||
let instruction = try fetch()
|
||||
print(instruction)
|
||||
try execute(instruction: instruction)
|
||||
print(self)
|
||||
print("=============================")
|
||||
}
|
||||
|
||||
public func run() throws {
|
||||
while true {
|
||||
try step()
|
||||
}
|
||||
}
|
||||
|
||||
public func fetch() throws -> Instruction {
|
||||
return try Instruction(from: bus, PC: PC)
|
||||
}
|
||||
|
||||
subscript(register: Register) -> UInt8 {
|
||||
switch register {
|
||||
case .X:
|
||||
return X
|
||||
case .Y:
|
||||
return Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CPU: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "PC: \(PC.hex) SP: \(SP.hex) A: \(A.hex) X: \(X.hex) Y: \(Y.hex) \nFlags: \(Status.rawValue.bin)"
|
||||
}
|
||||
}
|
||||
|
||||
extension FixedWidthInteger {
|
||||
public var hex: String {
|
||||
return String(self, radix: 16, uppercase: true)
|
||||
}
|
||||
|
||||
var bin: String {
|
||||
return String(self, radix: 2, uppercase: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// Debug6502Interpreter.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/2/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct AddressedInstruction: CustomStringConvertible {
|
||||
let address: UInt16
|
||||
let instruction: Instruction
|
||||
|
||||
public var description: String {
|
||||
return String(format: "$%04X : \(instruction.description)", address)
|
||||
}
|
||||
}
|
||||
|
||||
enum InterpreterError: Error {
|
||||
case instructionError(String)
|
||||
}
|
||||
|
||||
public class Debug6502Interpreter: Bus {
|
||||
public func read(from address: UInt16) throws -> UInt8 {
|
||||
if address >= memory.count {
|
||||
return 0
|
||||
}
|
||||
return memory[Int(address)]
|
||||
}
|
||||
|
||||
public func read(from address: UInt16) throws -> UInt16 {
|
||||
if address + 1 >= memory.count {
|
||||
throw InterpreterError.instructionError("Tried to read beyond memory bus")
|
||||
}
|
||||
let low: UInt8 = try read(from: address)
|
||||
let high: UInt8 = try read(from: address + 1)
|
||||
return (UInt16(high) << 8 | UInt16(low))
|
||||
}
|
||||
|
||||
public func write(to address: UInt16, value: UInt8) throws {
|
||||
// no-op, interpreter only
|
||||
}
|
||||
|
||||
var memory: [UInt8]
|
||||
var cpu: CPU! = nil
|
||||
|
||||
init(memory: [UInt8]) {
|
||||
self.memory = memory
|
||||
self.cpu = CPU(bus: self)
|
||||
}
|
||||
|
||||
public func interpret(startAt address:UInt16 = 0) -> [AddressedInstruction] {
|
||||
cpu.PC = address
|
||||
var instructions = [AddressedInstruction]()
|
||||
while cpu.PC < memory.count {
|
||||
do {
|
||||
let instruction = try cpu.fetch()
|
||||
instructions.append(AddressedInstruction(address: cpu.PC, instruction: instruction))
|
||||
cpu.PC += instruction.size
|
||||
} catch Instruction.Error.unknownOpcode(let opcode) {
|
||||
print("Unknown opcode: \(opcode), skipping...")
|
||||
cpu.PC += 1
|
||||
} catch {
|
||||
print("Error when interpreting, stopping...")
|
||||
break
|
||||
}
|
||||
}
|
||||
return instructions
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// AddressingMode.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 16/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Instruction {
|
||||
enum AddressingMode {
|
||||
enum Error: Swift.Error {
|
||||
case addressingModeNotImplemented
|
||||
}
|
||||
|
||||
case accumulator
|
||||
case immediate(data: UInt8)
|
||||
case implied
|
||||
case relative(data: Int8)
|
||||
case absolute(data: UInt16)
|
||||
case zeroPage(data: UInt8)
|
||||
case indirect(data: UInt16)
|
||||
case indexedIndirect(data: UInt8, register: CPU.Register)
|
||||
case indirectIndexed(data: UInt8, register: CPU.Register)
|
||||
case absoluteIndexed(data: UInt16, register: CPU.Register)
|
||||
case zeroPageIndexed(data: UInt8, register: CPU.Register)
|
||||
|
||||
func value(with cpu: CPU, bus: Bus) throws -> UInt8 {
|
||||
switch self {
|
||||
case .immediate(let data):
|
||||
return data
|
||||
case .absolute(let data):
|
||||
return try bus.read(from: data)
|
||||
case .absoluteIndexed(let data, let register):
|
||||
// Check if we should be overflowing here?
|
||||
return try bus.read(from: data &+ UInt16(cpu[register]))
|
||||
case _: throw Error.addressingModeNotImplemented
|
||||
}
|
||||
}
|
||||
|
||||
func value(with cpu: CPU, bus: Bus) throws -> UInt16 {
|
||||
switch self {
|
||||
case .absolute(let data):
|
||||
return data
|
||||
case .zeroPageIndexed(let base, let register):
|
||||
switch register {
|
||||
case .X:
|
||||
return UInt16(base &+ cpu.X)
|
||||
case .Y:
|
||||
return UInt16(base &+ cpu.Y)
|
||||
}
|
||||
case .indirect(let data):
|
||||
return try bus.read(from: data)
|
||||
case .relative(let data):
|
||||
return UInt16(Int32(cpu.PC) + Int32(data))
|
||||
case .zeroPage(let data):
|
||||
return UInt16(data)
|
||||
case _: throw Error.addressingModeNotImplemented
|
||||
}
|
||||
}
|
||||
|
||||
public var dataSize: UInt16 {
|
||||
switch self {
|
||||
case .absolute: return 2
|
||||
case .absoluteIndexed: return 1
|
||||
case .implied: return 0
|
||||
case .immediate: return 1
|
||||
case .indirect: return 2
|
||||
case .accumulator: return 0
|
||||
case .relative: return 1
|
||||
case .zeroPage: return 1
|
||||
case .indirectIndexed: return 1
|
||||
case .indexedIndirect: return 1
|
||||
case .zeroPageIndexed: return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Instruction.AddressingMode: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .accumulator: return "A"
|
||||
case .implied: return ""
|
||||
case .immediate(let data): return "#$\(String(format: "%02x", data))"
|
||||
case .relative(let data): return "$\(String(format: "%0X", data))"
|
||||
case .zeroPage(let data): return "$\(String(format: "%02x", data))"
|
||||
case .indirect(let data): return "$\(String(format: "%02x", data))"
|
||||
case .absolute(let data): return "$\(String(format: "%04x", data))"
|
||||
case .indirectIndexed(let data, let register): return "($\(String(format: "%02x", data)), \(register))"
|
||||
case .indexedIndirect(let data, let register): return "($\(String(format: "%02x", data))), \(register)"
|
||||
case .absoluteIndexed(let data, let register): return "$\(String(format: "%04x", data)), \(register)"
|
||||
case .zeroPageIndexed(let data, let register): return "$\(String(format: "%02x", data)), \(register)"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
//
|
||||
// Instruction.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 15/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Instruction {
|
||||
public let opcode: UInt8
|
||||
public let mnemonic: Mnemonic
|
||||
public let addressingMode: AddressingMode
|
||||
|
||||
public enum Error: Swift.Error {
|
||||
case unknownOpcode(opcode: UInt8)
|
||||
}
|
||||
|
||||
public init(from bus: Bus, PC: UInt16) throws {
|
||||
opcode = try bus.read(from: PC) as UInt8
|
||||
mnemonic = try Mnemonic(opcode)
|
||||
|
||||
//MARK: Addressing Mode
|
||||
switch opcode {
|
||||
case 0x20: fallthrough
|
||||
case 0x0d: fallthrough
|
||||
case 0x0e: fallthrough
|
||||
case 0x2c: fallthrough
|
||||
case 0x2d: fallthrough
|
||||
case 0x2e: fallthrough
|
||||
case 0x4c: fallthrough
|
||||
case 0x4d: fallthrough
|
||||
case 0x4e: fallthrough
|
||||
case 0x6d: fallthrough
|
||||
case 0x7e: fallthrough
|
||||
case 0x8c: fallthrough
|
||||
case 0x8d: fallthrough
|
||||
case 0x8e: fallthrough
|
||||
case 0xac: fallthrough
|
||||
case 0xad: fallthrough
|
||||
case 0xae: fallthrough
|
||||
case 0xcc: fallthrough
|
||||
case 0xcd: fallthrough
|
||||
case 0xce: fallthrough
|
||||
case 0xec: fallthrough
|
||||
case 0xed: fallthrough
|
||||
case 0xee:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .absolute(data: data)
|
||||
case 0x1d: fallthrough
|
||||
case 0x1e: fallthrough
|
||||
case 0x3d: fallthrough
|
||||
case 0x3e: fallthrough
|
||||
case 0x5d: fallthrough
|
||||
case 0x5e: fallthrough
|
||||
case 0x6e: fallthrough
|
||||
case 0x7d: fallthrough
|
||||
case 0x9d: fallthrough
|
||||
case 0xbc: fallthrough
|
||||
case 0xbd: fallthrough
|
||||
case 0xdd: fallthrough
|
||||
case 0xde: fallthrough
|
||||
case 0xfd: fallthrough
|
||||
case 0xfe:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .absoluteIndexed(data: data, register: .X)
|
||||
case 0x19: fallthrough
|
||||
case 0x39: fallthrough
|
||||
case 0x59: fallthrough
|
||||
case 0x79: fallthrough
|
||||
case 0x99: fallthrough
|
||||
case 0xb9: fallthrough
|
||||
case 0xbe: fallthrough
|
||||
case 0xd9: fallthrough
|
||||
case 0xf9:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .absoluteIndexed(data: data, register: .Y)
|
||||
case 0x0a: fallthrough
|
||||
case 0x2a: fallthrough
|
||||
case 0x4a: fallthrough
|
||||
case 0x6a:
|
||||
addressingMode = .accumulator
|
||||
case 0x09: fallthrough
|
||||
case 0x29: fallthrough
|
||||
case 0x49: fallthrough
|
||||
case 0x69: fallthrough
|
||||
case 0xa0: fallthrough
|
||||
case 0xa2: fallthrough
|
||||
case 0xa9: fallthrough
|
||||
case 0xc0: fallthrough
|
||||
case 0xc9: fallthrough
|
||||
case 0xe0: fallthrough
|
||||
case 0xe9:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .immediate(data: data)
|
||||
case 0x0: fallthrough
|
||||
case 0x8: fallthrough
|
||||
case 0x18: fallthrough
|
||||
case 0x28: fallthrough
|
||||
case 0x38: fallthrough
|
||||
case 0x40: fallthrough
|
||||
case 0x48: fallthrough
|
||||
case 0x58: fallthrough
|
||||
case 0x60: fallthrough
|
||||
case 0x68: fallthrough
|
||||
case 0x78: fallthrough
|
||||
case 0x88: fallthrough
|
||||
case 0x98: fallthrough
|
||||
case 0x8a: fallthrough
|
||||
case 0x9a: fallthrough
|
||||
case 0xa8: fallthrough
|
||||
case 0xaa: fallthrough
|
||||
case 0xb8: fallthrough
|
||||
case 0xba: fallthrough
|
||||
case 0xc8: fallthrough
|
||||
case 0xca: fallthrough
|
||||
case 0xd8: fallthrough
|
||||
case 0xe8: fallthrough
|
||||
case 0xea: fallthrough
|
||||
case 0xf8:
|
||||
addressingMode = .implied
|
||||
case 0x1: fallthrough
|
||||
case 0x21: fallthrough
|
||||
case 0x41: fallthrough
|
||||
case 0x61: fallthrough
|
||||
case 0x81: fallthrough
|
||||
case 0xa1: fallthrough
|
||||
case 0xc1: fallthrough
|
||||
case 0xe1:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .indexedIndirect(data: data, register: .X)
|
||||
case 0x6c:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .indirect(data: data)
|
||||
case 0x11: fallthrough
|
||||
case 0x31: fallthrough
|
||||
case 0x51: fallthrough
|
||||
case 0x71: fallthrough
|
||||
case 0x91: fallthrough
|
||||
case 0xb1: fallthrough
|
||||
case 0xd1: fallthrough
|
||||
case 0xf1:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .indirectIndexed(data: data, register: .Y)
|
||||
case 0x10: fallthrough
|
||||
case 0x30: fallthrough
|
||||
case 0x50: fallthrough
|
||||
case 0x70: fallthrough
|
||||
case 0x90: fallthrough
|
||||
case 0xb0: fallthrough
|
||||
case 0xd0: fallthrough
|
||||
case 0xf0:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .relative(data: Int8(bitPattern: data))
|
||||
case 0x5: fallthrough
|
||||
case 0x6: fallthrough
|
||||
case 0x24: fallthrough
|
||||
case 0x25: fallthrough
|
||||
case 0x26: fallthrough
|
||||
case 0x45: fallthrough
|
||||
case 0x46: fallthrough
|
||||
case 0x65: fallthrough
|
||||
case 0x66: fallthrough
|
||||
case 0x84: fallthrough
|
||||
case 0x85: fallthrough
|
||||
case 0x86: fallthrough
|
||||
case 0xa4: fallthrough
|
||||
case 0xa5: fallthrough
|
||||
case 0xa6: fallthrough
|
||||
case 0xc4: fallthrough
|
||||
case 0xc5: fallthrough
|
||||
case 0xc6: fallthrough
|
||||
case 0xe4: fallthrough
|
||||
case 0xe5: fallthrough
|
||||
case 0xe6:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .zeroPage(data: data)
|
||||
case 0x15: fallthrough
|
||||
case 0x16: fallthrough
|
||||
case 0x35: fallthrough
|
||||
case 0x36: fallthrough
|
||||
case 0x55: fallthrough
|
||||
case 0x56: fallthrough
|
||||
case 0x75: fallthrough
|
||||
case 0x76: fallthrough
|
||||
case 0x94: fallthrough
|
||||
case 0x95: fallthrough
|
||||
case 0xb4: fallthrough
|
||||
case 0xb5: fallthrough
|
||||
case 0xd5: fallthrough
|
||||
case 0xd6: fallthrough
|
||||
case 0xf5: fallthrough
|
||||
case 0xf6:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .zeroPageIndexed(data: data, register: .X)
|
||||
case 0x96: fallthrough
|
||||
case 0xb6:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .zeroPageIndexed(data: data, register: .Y)
|
||||
default:
|
||||
throw Error.unknownOpcode(opcode: opcode)
|
||||
}
|
||||
}
|
||||
|
||||
public var size: UInt16 {
|
||||
return addressingMode.dataSize + 1
|
||||
}
|
||||
}
|
||||
|
||||
extension Instruction: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "\(mnemonic) \(addressingMode)"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// Opcode.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 16/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private let mnemonicTable: [Instruction.Mnemonic?] = [
|
||||
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
|
||||
/* 0 */ .BRK, .ORA, nil, .SLO, nil, .ORA, .ASL, .SLO, .PHP, .ORA, .ASL, nil, nil, .ORA, .ASL, .SLO, /* 0 */
|
||||
/* 1 */ .BPL, .ORA, nil, .SLO, nil, .ORA, .ASL, .SLO, .CLC, .ORA, nil, .SLO, nil, .ORA, .ASL, .SLO, /* 1 */
|
||||
/* 2 */ .JSR, .AND, nil, .RLA, .BIT, .AND, .ROL, .RLA, .PLP, .AND, .ROL, nil, .BIT, .AND, .ROL, .RLA, /* 2 */
|
||||
/* 3 */ .BMI, .AND, nil, .RLA, nil, .AND, .ROL, .RLA, .SEC, .AND, nil, .RLA, nil, .AND, .ROL, .RLA, /* 3 */
|
||||
/* 4 */ .RTI, .EOR, nil, .SRE, nil, .EOR, .LSR, .SRE, .PHA, .EOR, .LSR, nil, .JMP, .EOR, .LSR, .SRE, /* 4 */
|
||||
/* 5 */ .BVC, .EOR, nil, .SRE, nil, .EOR, .LSR, .SRE, .CLI, .EOR, nil, .SRE, nil, .EOR, .LSR, .SRE, /* 5 */
|
||||
/* 6 */ .RTS, .ADC, nil, .RRA, nil, .ADC, .ROR, .RRA, .PLA, .ADC, .ROR, nil, .JMP, .ADC, .ROR, .RRA, /* 6 */
|
||||
/* 7 */ .BVS, .ADC, nil, .RRA, nil, .ADC, .ROR, .RRA, .SEI, .ADC, nil, .RRA, nil, .ADC, .ROR, .RRA, /* 7 */
|
||||
/* 8 */ nil, .STA, nil, .SAX, .STY, .STA, .STX, .SAX, .DEY, nil, .TXA, nil, .STY, .STA, .STX, .SAX, /* 8 */
|
||||
/* 9 */ .BCC, .STA, nil, nil, .STY, .STA, .STX, .SAX, .TYA, .STA, .TXS, nil, nil, .STA, nil, nil, /* 9 */
|
||||
/* A */ .LDY, .LDA, .LDX, .LAX, .LDY, .LDA, .LDX, .LAX, .TAY, .LDA, .TAX, nil, .LDY, .LDA, .LDX, .LAX, /* A */
|
||||
/* B */ .BCS, .LDA, nil, .LAX, .LDY, .LDA, .LDX, .LAX, .CLV, .LDA, .TSX, .LAX, .LDY, .LDA, .LDX, .LAX, /* B */
|
||||
/* C */ .CPY, .CMP, nil, .DCP, .CPY, .CMP, .DEC, .DCP, .INY, .CMP, .DEX, nil, .CPY, .CMP, .DEC, .DCP, /* C */
|
||||
/* D */ .BNE, .CMP, nil, .DCP, nil, .CMP, .DEC, .DCP, .CLD, .CMP, nil, .DCP, nil, .CMP, .DEC, .DCP, /* D */
|
||||
/* E */ .CPX, .SBC, nil, .ISB, .CPX, .SBC, .INC, .ISB, .INX, .SBC, .NOP, .SBC, .CPX, .SBC, .INC, .ISB, /* E */
|
||||
/* F */ .BEQ, .SBC, nil, .ISB, nil, .SBC, .INC, .ISB, .SED, .SBC, nil, .ISB, nil, .SBC, .INC, .ISB /* F */
|
||||
]
|
||||
|
||||
public extension Instruction {
|
||||
enum Mnemonic: String {
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case unknownMnemonic(opcode: String)
|
||||
}
|
||||
|
||||
init(_ opcode: UInt8) throws {
|
||||
guard let mnemonic = mnemonicTable[Int(opcode)] else {
|
||||
throw Instruction.Error.unknownOpcode(opcode: opcode)
|
||||
}
|
||||
self = mnemonic
|
||||
}
|
||||
|
||||
init(_ string: String) throws {
|
||||
guard let value = Mnemonic(rawValue: string) else {
|
||||
throw Error.unknownMnemonic(opcode: string)
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
case ADC
|
||||
case AND
|
||||
case ASL
|
||||
case BCC
|
||||
case BCS
|
||||
case BEQ
|
||||
case BIT
|
||||
case BMI
|
||||
case BNE
|
||||
case BPL
|
||||
case BRK
|
||||
case BVC
|
||||
case BVS
|
||||
case CLC
|
||||
case CLD
|
||||
case CLI
|
||||
case CLV
|
||||
case CMP
|
||||
case CPX
|
||||
case CPY
|
||||
case DCP
|
||||
case DEC
|
||||
case DEX
|
||||
case DEY
|
||||
case EOR
|
||||
case INC
|
||||
case INX
|
||||
case INY
|
||||
case ISB
|
||||
case JMP
|
||||
case JSR
|
||||
case LAX
|
||||
case LDA
|
||||
case LDX
|
||||
case LDY
|
||||
case LSR
|
||||
case NOP
|
||||
case ORA
|
||||
case PHA
|
||||
case PHP
|
||||
case PLA
|
||||
case PLP
|
||||
case RLA
|
||||
case ROL
|
||||
case ROR
|
||||
case RRA
|
||||
case RTI
|
||||
case RTS
|
||||
case SBC
|
||||
case SEC
|
||||
case SED
|
||||
case SEI
|
||||
case SLO
|
||||
case SRE
|
||||
case SAX
|
||||
case STA
|
||||
case STX
|
||||
case STY
|
||||
case TAX
|
||||
case TAY
|
||||
case TSX
|
||||
case TXA
|
||||
case TXS
|
||||
case TYA
|
||||
}
|
||||
}
|
||||
|
||||
extension Instruction.Mnemonic: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "\(rawValue)"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Stream.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 15/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol RawStreamReadable {}
|
||||
|
||||
extension UInt8: RawStreamReadable {}
|
||||
extension UInt16: RawStreamReadable {}
|
||||
|
||||
extension InputStream: Stream {
|
||||
func read(_ length: Int) -> [UInt8] {
|
||||
var buffer = Array<UInt8>(repeating: 0, count: length)
|
||||
_ = read(&buffer, maxLength: length)
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MemoryStream: Stream {
|
||||
let storage: [UInt8]
|
||||
var position: Int = 0
|
||||
|
||||
init(storage: [UInt8]) {
|
||||
self.storage = storage
|
||||
}
|
||||
|
||||
open func read(_ length: Int) -> [UInt8] {
|
||||
if position+length > storage.count {
|
||||
return [UInt8](repeating: 0, count: length)
|
||||
}
|
||||
|
||||
let slice = storage[position..<position+length]
|
||||
position += length
|
||||
return Array(slice)
|
||||
}
|
||||
}
|
||||
|
||||
protocol Stream {
|
||||
func read(_ length: Int) -> [UInt8]
|
||||
}
|
||||
|
||||
extension Stream {
|
||||
func read<T: RawStreamReadable>() -> T {
|
||||
let size = MemoryLayout<T>.size
|
||||
let bytes = read(size)
|
||||
let data = Data(bytes: bytes)
|
||||
let value: T = data.withUnsafeBytes { $0.pointee }
|
||||
return value
|
||||
}
|
||||
|
||||
func skip(_ count: Int) {
|
||||
_ = read(count)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// CheatFinderManager.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/29/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class CheatFinderManager {
|
||||
private(set) var matchedMemoryAddresses = [Int: UInt8]()
|
||||
var comparisonMemory = [UInt8]()
|
||||
|
||||
private let dispatchQueue = DispatchQueue(label: "CheatFinderManager", qos: .background)
|
||||
var timer: DispatchSourceTimer?
|
||||
|
||||
var savedMatches = [Int: (value: UInt8, enabled: Bool)]()
|
||||
|
||||
enum UIState {
|
||||
case initial, startedNewSearch, isSearching, didSearch, showSaved
|
||||
}
|
||||
|
||||
enum SearchMode {
|
||||
case less, greater, same
|
||||
case equalTo(Int)
|
||||
}
|
||||
|
||||
var uiState = UIState.initial
|
||||
|
||||
func start() {
|
||||
if timer == nil {
|
||||
timer = DispatchSource.makeTimerSource(queue: dispatchQueue)
|
||||
timer?.setEventHandler(handler: {
|
||||
self.updateMemoryWithCheats()
|
||||
})
|
||||
timer?.schedule(deadline: .now(), repeating: .seconds(1))
|
||||
timer?.resume()
|
||||
}
|
||||
}
|
||||
|
||||
func updateMemoryWithCheats() {
|
||||
print("CheatFinderManager updating memory...")
|
||||
let memory = EmuWrapper.memory()
|
||||
for (address, memoryValue) in savedMatches {
|
||||
if let memory = memory, memoryValue.enabled {
|
||||
print("updating memory at \(String(format: "%05X",address)) to \(memoryValue.value)")
|
||||
memory[address] = memoryValue.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(with memory: UnsafeMutablePointer<UInt8>) {
|
||||
for address in 0..<EmuMemoryModel.maxMemorySize {
|
||||
matchedMemoryAddresses[address] = memory[address]
|
||||
}
|
||||
}
|
||||
|
||||
func findNewMatches(searchMode: SearchMode) {
|
||||
var newMatches = [Int: UInt8]()
|
||||
for (address, oldValue) in matchedMemoryAddresses {
|
||||
let newValue = comparisonMemory[address]
|
||||
var matched = false
|
||||
switch searchMode {
|
||||
case .less:
|
||||
matched = newValue < oldValue
|
||||
case .greater:
|
||||
matched = newValue > oldValue
|
||||
case .same:
|
||||
matched = newValue == oldValue
|
||||
case .equalTo(let searchValue):
|
||||
matched = newValue == searchValue
|
||||
}
|
||||
if matched {
|
||||
newMatches[address] = newValue
|
||||
}
|
||||
// // avoid having too many matches
|
||||
// if newMatches.count > 10000 {
|
||||
// break
|
||||
// }
|
||||
}
|
||||
matchedMemoryAddresses = newMatches
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// DebuggerUtils.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/4/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Orientation {
|
||||
// indicate current device is in the LandScape orientation
|
||||
static var isLandscape: Bool {
|
||||
get {
|
||||
return UIDevice.current.orientation.isValidInterfaceOrientation
|
||||
? UIDevice.current.orientation.isLandscape
|
||||
: (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape)!
|
||||
}
|
||||
}
|
||||
// indicate current device is in the Portrait orientation
|
||||
static var isPortrait: Bool {
|
||||
get {
|
||||
return UIDevice.current.orientation.isValidInterfaceOrientation
|
||||
? UIDevice.current.orientation.isPortrait
|
||||
: (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isPortrait)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
func getSnapshot() -> UIImage {
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
|
||||
drawHierarchy(in: self.bounds, afterScreenUpdates: true)
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()!
|
||||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// EmuMemoryModel.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/4/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class EmuMemoryModel {
|
||||
let numToDisplayPerCell = 8
|
||||
static let maxMemorySize = 256 * 1024
|
||||
|
||||
private(set) var memory = EmuWrapper.memory()
|
||||
private(set) var referencedAddresses = [UInt16: [AddressedInstruction]]()
|
||||
|
||||
var selectedAddress: Int?
|
||||
|
||||
func offset(for indexPath: IndexPath) -> Int {
|
||||
let memMapSection = EmuMemoryMapSection(rawValue: indexPath.section)!
|
||||
return memMapSection.range.lowerBound + (indexPath.row * numToDisplayPerCell)
|
||||
}
|
||||
|
||||
func hexStrings(for indexPath: IndexPath) -> [String] {
|
||||
let memMapSection = EmuMemoryMapSection(rawValue: indexPath.section)!
|
||||
let startIndex = offset(for: indexPath)
|
||||
let endIndex = min(startIndex + numToDisplayPerCell, memMapSection.range.upperBound)
|
||||
guard startIndex < memMapSection.range.upperBound,
|
||||
endIndex <= memMapSection.range.upperBound else {
|
||||
print("indexes are out of range of this section: startIndex=\(startIndex) endIndex=\(endIndex) section range: \(memMapSection.range.lowerBound) - \(memMapSection.range.upperBound)")
|
||||
return []
|
||||
}
|
||||
var row = [String]()
|
||||
guard let memory = memory else {
|
||||
return row
|
||||
}
|
||||
for i in startIndex..<endIndex {
|
||||
row.append(String(format: "%02X", memory[i]))
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func indexPath(for address: Int) -> IndexPath {
|
||||
let section = EmuMemoryMapSection.section(for: address)
|
||||
let offset = (address - section.range.lowerBound) / numToDisplayPerCell
|
||||
return IndexPath(item: offset, section: section.rawValue)
|
||||
}
|
||||
|
||||
func getMemoryHexString(at address: Int) -> String {
|
||||
guard address > 0 && address < Self.maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(Self.maxMemorySize)")
|
||||
return ""
|
||||
}
|
||||
guard let memory = memory else {
|
||||
return ""
|
||||
}
|
||||
let value = memory[address]
|
||||
return String(format: "%02X", value)
|
||||
}
|
||||
|
||||
func getMemoryHeaderString(for address: Int) -> String {
|
||||
return String(format: "%02X", address)
|
||||
}
|
||||
|
||||
func setMemory(at address:Int, value: UInt8) {
|
||||
guard address > 0 && address < Self.maxMemorySize else {
|
||||
print("Cannot set memory: address out of range \(address) > \(Self.maxMemorySize)")
|
||||
return
|
||||
}
|
||||
guard let memory = memory else {
|
||||
return
|
||||
}
|
||||
print("Setting memory address %04X to %02X",address,value)
|
||||
memory[address] = value
|
||||
refresh()
|
||||
}
|
||||
|
||||
func getMemory(at address:Int) -> UInt8 {
|
||||
guard address > 0 && address < Self.maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(Self.maxMemorySize)")
|
||||
return 0
|
||||
}
|
||||
guard let memory = memory else {
|
||||
return 0
|
||||
}
|
||||
return memory[address]
|
||||
}
|
||||
|
||||
var memoryAsArray: [UInt8] {
|
||||
var buffer = [UInt8]()
|
||||
guard let memory = memory else {
|
||||
return buffer
|
||||
}
|
||||
for address in 0..<0x95ff {
|
||||
buffer.append(memory[address])
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
lazy var interpretedInstructions: [AddressedInstruction] = {
|
||||
return interpreted()
|
||||
}()
|
||||
|
||||
private func interpreted() -> [AddressedInstruction] {
|
||||
let interpreter = Debug6502Interpreter(memory: memoryAsArray)
|
||||
let instructions = interpreter.interpret()
|
||||
referencedAddresses = [UInt16: [AddressedInstruction]]()
|
||||
let updateBlock: ((UInt16, AddressedInstruction) -> Void) = { address, instruction in
|
||||
var instructions: [AddressedInstruction] = {
|
||||
if let existingInstructions = self.referencedAddresses[address] {
|
||||
return existingInstructions
|
||||
}
|
||||
return [AddressedInstruction]()
|
||||
}()
|
||||
instructions.append(instruction)
|
||||
self.referencedAddresses[address] = instructions
|
||||
}
|
||||
for instruction in instructions {
|
||||
switch instruction.instruction.addressingMode {
|
||||
case .absolute(let address):
|
||||
updateBlock(address, instruction)
|
||||
case .zeroPage(let zeroPageAddress):
|
||||
updateBlock(UInt16(zeroPageAddress), instruction)
|
||||
case .indirect(let fromAddress):
|
||||
updateBlock(fromAddress, instruction)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return instructions
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
memory = EmuWrapper.memory()
|
||||
interpretedInstructions = interpreted()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// EmuWrapper.h
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/17/21.
|
||||
//
|
||||
|
||||
#ifndef EmuWrapper_h
|
||||
#define EmuWrapper_h
|
||||
|
||||
|
||||
#endif /* EmuWrapper_h */
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface EmuWrapper: NSObject
|
||||
+(unsigned char*) memory;
|
||||
+(void)pause;
|
||||
+(void)resume;
|
||||
+(UIView*)getEmulatorView;
|
||||
+(unsigned int)cpuGetProgramCounter;
|
||||
|
||||
@end
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// EmuWrapper.m
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/17/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "activegsAppDelegate.h"
|
||||
#import "EmuWrapper.h"
|
||||
|
||||
#include "../../Common.osx/cemulatorctrlmac.h"
|
||||
#include "../../kegs/Src/defc.h"
|
||||
#include "../../kegs/Src/sim65816.h"
|
||||
|
||||
|
||||
@implementation EmuWrapper
|
||||
|
||||
+(unsigned char*) memory {
|
||||
return g_memory_ptr;
|
||||
}
|
||||
|
||||
+(void)pause {
|
||||
r_sim65816.pause();
|
||||
}
|
||||
|
||||
+(void)resume {
|
||||
r_sim65816.resume();
|
||||
}
|
||||
|
||||
+(word32)cpuGetProgramCounter {
|
||||
return g_sim65816.engine.kpc;
|
||||
}
|
||||
|
||||
+(UIView*)getEmulatorView {
|
||||
return [pManager getEmulatorView].zv.ew;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,785 @@
|
|||
//
|
||||
// DebugMemoryActionViewController.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/4/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol DebugMemoryActionViewControllerDelegate: class {
|
||||
func jump(to address: Int)
|
||||
func memoryHex(at address: Int) -> String
|
||||
func updateMemory(at address: Int, with memory:UInt8)
|
||||
var memory: UnsafeMutablePointer<UInt8>? { get }
|
||||
var selectedAddress: Int? { get }
|
||||
var referencedMemoryAddresses: [UInt16: [AddressedInstruction]] { get }
|
||||
}
|
||||
|
||||
class DebugMemoryActionViewController: UIViewController {
|
||||
|
||||
enum Mode {
|
||||
case jumpToAddress, changeMemory, cheat, screen
|
||||
}
|
||||
|
||||
var mode: Mode = .jumpToAddress
|
||||
let cheatFinder: CheatFinderManager
|
||||
|
||||
var matchedInstructions = [AddressedInstruction]()
|
||||
|
||||
weak var delegate:DebugMemoryActionViewControllerDelegate?
|
||||
|
||||
let segmentedControl: UISegmentedControl = {
|
||||
let control = UISegmentedControl(items: ["Jump", "Edit", "Cheat", "Screen"])
|
||||
control.translatesAutoresizingMaskIntoConstraints = false
|
||||
control.tintColor = .orange
|
||||
control.selectedSegmentIndex = 0
|
||||
control.addTarget(self, action: #selector(segmentedControlChanged(_:)), for: .valueChanged)
|
||||
control.setTitleTextAttributes([NSAttributedString.Key.font: UIFont(name: "Print Char 21", size: 14)!], for: .normal)
|
||||
return control
|
||||
}()
|
||||
|
||||
let memoryField: UITextField = {
|
||||
let field = UITextField(frame: .zero)
|
||||
field.translatesAutoresizingMaskIntoConstraints = false
|
||||
field.font = UIFont(name: "Print Char 21", size: 14)
|
||||
field.isUserInteractionEnabled = false
|
||||
field.text = ""
|
||||
field.textColor = .cyan
|
||||
field.layer.borderWidth = 1.0
|
||||
field.layer.borderColor = UIColor.cyan.cgColor
|
||||
field.textAlignment = .center
|
||||
field.widthAnchor.constraint(equalToConstant: 80).isActive = true
|
||||
field.heightAnchor.constraint(equalToConstant: 40).isActive = true
|
||||
return field
|
||||
}()
|
||||
|
||||
let updateMemoryButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Update", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.orange.cgColor
|
||||
button.addTarget(self, action: #selector(updateButtonPressed(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
let resetMemoryButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Reset", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.orange.cgColor
|
||||
return button
|
||||
}()
|
||||
|
||||
let addToCheatsButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Add to Cheats", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.orange.cgColor
|
||||
button.addTarget(self, action: #selector(addToCheatButtonPressed(_:)), for: .touchUpInside )
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var editFieldsStackView: UIStackView = {
|
||||
let stackView = UIStackView(arrangedSubviews: [memoryField, updateMemoryButton, resetMemoryButton, addToCheatsButton])
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 4
|
||||
return stackView
|
||||
}()
|
||||
|
||||
let keyboardModel: EmulatorKeyboardViewModel = {
|
||||
let model = EmulatorKeyboardViewModel(keys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "0", code: 0),
|
||||
AppleIIKey(label: "1", code: 1),
|
||||
AppleIIKey(label: "2", code: 2),
|
||||
AppleIIKey(label: "3", code: 3)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "4", code: 4),
|
||||
AppleIIKey(label: "5", code: 5),
|
||||
AppleIIKey(label: "6", code: 6),
|
||||
AppleIIKey(label: "7", code: 7)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "8", code: 8),
|
||||
AppleIIKey(label: "9", code: 9),
|
||||
AppleIIKey(label: "A", code: 10),
|
||||
AppleIIKey(label: "B", code: 11)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "C", code: 12),
|
||||
AppleIIKey(label: "D", code: 13),
|
||||
AppleIIKey(label: "E", code: 14),
|
||||
AppleIIKey(label: "F", code: 15)
|
||||
]
|
||||
]
|
||||
)
|
||||
model.isDraggable = false
|
||||
return model
|
||||
}()
|
||||
|
||||
lazy var keyboardView: EmulatorKeyboardView = {
|
||||
let view = keyboardModel.createView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
return view
|
||||
}()
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.font = UIFont(name: "Print Char 21", size: 14)
|
||||
label.text = "Memory Tools"
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.textColor = .orange
|
||||
label.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||
return label
|
||||
}()
|
||||
|
||||
let cheatFinderNewSearchButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("New Search", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.addTarget(self, action: #selector(cheatFinderNewSearchButtonPressed(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
let cheatFinderContinueSearchButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Continue Search", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.red.cgColor
|
||||
button.addTarget(self, action: #selector(cheatFinderContinueSearchButtonPressed(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var cheatFinderInitialActionsStackView: UIStackView = {
|
||||
let stackView = UIStackView(arrangedSubviews: [cheatFinderNewSearchButton, cheatFinderContinueSearchButton, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton, cheatFinderSearchEqualButton,
|
||||
cheatFinderShowSavedButton])
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 4
|
||||
return stackView
|
||||
}()
|
||||
|
||||
let cheatFinderSearchLessButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("LT", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.addTarget(self, action: #selector(cheatFinderSearchButtonPressed(_:)), for: .touchUpInside)
|
||||
button.tag = 0
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.widthAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let cheatFinderSearchGreaterButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("GT", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.addTarget(self, action: #selector(cheatFinderSearchButtonPressed(_:)), for: .touchUpInside)
|
||||
button.tag = 1
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.widthAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let cheatFinderSearchEqualButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("EQ", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.addTarget(self, action: #selector(cheatFinderSearchButtonPressed(_:)), for: .touchUpInside)
|
||||
button.tag = 2
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.widthAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var cheatFinderShowSavedButton: ToggleButton = {
|
||||
let button = ToggleButton()
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Matched", for: .normal)
|
||||
button.setTitle("Saved", for: .selected)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.onTapped = { [weak self] wasSelected in
|
||||
if wasSelected {
|
||||
self?.cheatFinder.uiState = .showSaved
|
||||
} else {
|
||||
self?.cheatFinder.uiState = .didSearch
|
||||
}
|
||||
self?.cheatFinderUpdateUI()
|
||||
}
|
||||
button.tag = 3
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
let findCodeButton: DebugMemoryButton = {
|
||||
let button = DebugMemoryButton()
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 9)
|
||||
button.setTitle("Find in Code >", for: .normal)
|
||||
button.setTitle("Cancel", for: .selected)
|
||||
button.setTitleColor(.red, for: .selected)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
// lazy var cheatFinderSearchStackView: UIStackView = {
|
||||
// let stackView = UIStackView(arrangedSubviews: [cheatFinderSearchLessButton, cheatFinderSearchGreaterButton])
|
||||
// stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
// stackView.axis = .horizontal
|
||||
// stackView.spacing = 4
|
||||
// return stackView
|
||||
// }()
|
||||
|
||||
|
||||
let cheatFinderPromptLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.font = UIFont(name: "Print Char 21", size: 11)
|
||||
label.text = "Start a new search to begin!"
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.textColor = .red
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var cheatFinderMatchesTableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.dataSource = self
|
||||
view.delegate = self
|
||||
view.register(UITableViewCell.self, forCellReuseIdentifier: "CheatFinderMatchCell")
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var cheatFinderSavedTableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.dataSource = self
|
||||
view.delegate = self
|
||||
view.register(UITableViewCell.self, forCellReuseIdentifier: "CheatFinderMatchCell")
|
||||
return view
|
||||
}()
|
||||
|
||||
init(cheatFinderManager: CheatFinderManager) {
|
||||
self.cheatFinder = cheatFinderManager
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
lazy var emulatorScreenView: UIImageView = {
|
||||
let screenView = UIImageView(frame: .zero)
|
||||
screenView.translatesAutoresizingMaskIntoConstraints = false
|
||||
return screenView
|
||||
}()
|
||||
|
||||
@objc func cheatFinderNewSearchButtonPressed(_ sender: UIButton) {
|
||||
if let memory = delegate?.memory {
|
||||
cheatFinder.update(with: memory)
|
||||
}
|
||||
cheatFinder.uiState = .startedNewSearch
|
||||
cheatFinderUpdateUI()
|
||||
}
|
||||
|
||||
@objc func cheatFinderContinueSearchButtonPressed(_ sender: UIButton) {
|
||||
cheatFinder.uiState = .isSearching
|
||||
cheatFinderUpdateUI()
|
||||
}
|
||||
|
||||
func cheatFinderSetupView() {
|
||||
view.addSubview(cheatFinderInitialActionsStackView)
|
||||
// view.addSubview(cheatFinderSearchStackView)
|
||||
view.addSubview(cheatFinderPromptLabel)
|
||||
view.addSubview(cheatFinderMatchesTableView)
|
||||
view.addSubview(cheatFinderSavedTableView)
|
||||
cheatFinderInitialActionsStackView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
cheatFinderInitialActionsStackView.centerXAnchor.constraint(equalTo: segmentedControl.centerXAnchor).isActive = true
|
||||
// cheatFinderInitialActionsStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
|
||||
// cheatFinderInitialActionsStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
|
||||
|
||||
// cheatFinderSearchStackView.topAnchor.constraint(equalTo: cheatFinderInitialActionsStackView.bottomAnchor, constant: 8).isActive = true
|
||||
// cheatFinderSearchStackView.centerXAnchor.constraint(equalTo: segmentedControl.centerXAnchor).isActive = true
|
||||
cheatFinderPromptLabel.topAnchor.constraint(equalTo: cheatFinderInitialActionsStackView.bottomAnchor, constant: 16).isActive = true
|
||||
cheatFinderPromptLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 48).isActive = true
|
||||
cheatFinderPromptLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -48).isActive = true
|
||||
cheatFinderMatchesTableView.topAnchor.constraint(equalTo: cheatFinderPromptLabel.bottomAnchor, constant: 4).isActive = true
|
||||
cheatFinderMatchesTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24).isActive = true
|
||||
cheatFinderMatchesTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24).isActive = true
|
||||
cheatFinderMatchesTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 8).isActive = true
|
||||
cheatFinderSavedTableView.topAnchor.constraint(equalTo: cheatFinderMatchesTableView.topAnchor).isActive = true
|
||||
cheatFinderSavedTableView.leadingAnchor.constraint(equalTo: cheatFinderMatchesTableView.leadingAnchor).isActive = true
|
||||
cheatFinderSavedTableView.trailingAnchor.constraint(equalTo: cheatFinderMatchesTableView.trailingAnchor).isActive = true
|
||||
cheatFinderSavedTableView.bottomAnchor.constraint(equalTo: cheatFinderMatchesTableView.bottomAnchor).isActive = true
|
||||
[cheatFinderInitialActionsStackView, cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderSavedTableView].forEach{ $0.isHidden = true }
|
||||
}
|
||||
|
||||
func cheatFinderHide() {
|
||||
[cheatFinderInitialActionsStackView, cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton, cheatFinderNewSearchButton, cheatFinderContinueSearchButton, cheatFinderSearchEqualButton, cheatFinderShowSavedButton, cheatFinderSavedTableView].forEach{ $0.isHidden = true }
|
||||
}
|
||||
|
||||
func cheatFinderUpdateUI() {
|
||||
cheatFinderHide()
|
||||
switch cheatFinder.uiState {
|
||||
case .initial:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton, cheatFinderPromptLabel].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "Start a new search to begin!"
|
||||
case .startedNewSearch:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton,
|
||||
cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton,
|
||||
cheatFinderPromptLabel, cheatFinderShowSavedButton].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "New search started! Search to find matches..."
|
||||
case .isSearching:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton, cheatFinderPromptLabel, cheatFinderShowSavedButton].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "Search for values..."
|
||||
case .didSearch:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton,
|
||||
cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton,
|
||||
cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderShowSavedButton].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "Number of matches: \(cheatFinder.matchedMemoryAddresses.count)"
|
||||
cheatFinderMatchesTableView.reloadData()
|
||||
case .showSaved:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton,
|
||||
cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton,
|
||||
cheatFinderPromptLabel, cheatFinderShowSavedButton, cheatFinderSavedTableView].forEach{ $0.isHidden = false }
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func cheatFinderSearchButtonPressed(_ sender: UIButton) {
|
||||
guard let memory = delegate?.memory else {
|
||||
print("Cannot proceed cheat search without reference to memory!")
|
||||
return
|
||||
}
|
||||
cheatFinder.comparisonMemory = [UInt8]()
|
||||
for address in 0..<EmuMemoryModel.maxMemorySize {
|
||||
cheatFinder.comparisonMemory.append(memory[address])
|
||||
}
|
||||
switch sender.tag {
|
||||
case 0:
|
||||
cheatFinder.findNewMatches(searchMode: CheatFinderManager.SearchMode.less)
|
||||
case 1:
|
||||
cheatFinder.findNewMatches(searchMode: CheatFinderManager.SearchMode.greater)
|
||||
case 2:
|
||||
cheatFinder.findNewMatches(searchMode: CheatFinderManager.SearchMode.same)
|
||||
default:
|
||||
break
|
||||
}
|
||||
cheatFinder.uiState = .didSearch
|
||||
cheatFinderUpdateUI()
|
||||
}
|
||||
|
||||
func setupView() {
|
||||
view.addSubview(titleLabel)
|
||||
view.addSubview(segmentedControl)
|
||||
|
||||
view.addSubview(editFieldsStackView)
|
||||
|
||||
view.addSubview(emulatorScreenView)
|
||||
|
||||
// view.addSubview(memoryField)
|
||||
|
||||
view.addSubview(keyboardView)
|
||||
titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 16).isActive = true
|
||||
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
|
||||
segmentedControl.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8).isActive = true
|
||||
segmentedControl.centerXAnchor.constraint(equalTo: titleLabel.centerXAnchor).isActive = true
|
||||
editFieldsStackView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
// memoryField.widthAnchor.constraint(equalToConstant: 80).isActive = true
|
||||
// memoryField.heightAnchor.constraint(equalToConstant: 40).isActive = true
|
||||
editFieldsStackView.centerXAnchor.constraint(equalTo: keyboardView.centerXAnchor).isActive = true
|
||||
keyboardView.topAnchor.constraint(equalTo: editFieldsStackView.bottomAnchor, constant: 8).isActive = true
|
||||
// keyboardView.widthAnchor.constraint(equalToConstant: 200).isActive = true
|
||||
keyboardView.heightAnchor.constraint(equalToConstant: 200).isActive = true
|
||||
keyboardView.viewModel.delegate = self
|
||||
|
||||
emulatorScreenView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
emulatorScreenView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 4.0).isActive = true
|
||||
emulatorScreenView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -4.0).isActive = true
|
||||
emulatorScreenView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16).isActive = true
|
||||
emulatorScreenView.isHidden = true
|
||||
|
||||
view.backgroundColor = UIColor.init(red: 0.14, green: 0.13, blue: 0.14, alpha: 1)
|
||||
}
|
||||
|
||||
// Jump tab: normal constraints
|
||||
private var keyboardViewCenterXConstraint: NSLayoutConstraint!
|
||||
private var findCodeButtonLeadingConstraint: NSLayoutConstraint!
|
||||
private var findCodeButtonCenterYConstraint: NSLayoutConstraint!
|
||||
private var findInCodeTableViewLeadingConstraintOutOfView: NSLayoutConstraint!
|
||||
private var findInCodeTableViewBottomConstraintOutOfView: NSLayoutConstraint!
|
||||
var jumpTabNormalModeConstraints: [NSLayoutConstraint] {
|
||||
[
|
||||
keyboardViewCenterXConstraint,
|
||||
findCodeButtonLeadingConstraint,
|
||||
findCodeButtonCenterYConstraint,
|
||||
findInCodeTableViewLeadingConstraintOutOfView,
|
||||
findInCodeTableViewBottomConstraintOutOfView
|
||||
]
|
||||
}
|
||||
|
||||
// Jump tab: find in code constraints
|
||||
private var keyboardViewLeadingConstraint: NSLayoutConstraint!
|
||||
private var findCodeButtonBottomConstraint: NSLayoutConstraint!
|
||||
private var findCodeButtonCenterXConstraint: NSLayoutConstraint!
|
||||
private var findInCodeTableViewLeadingConstraint: NSLayoutConstraint!
|
||||
private var findInCodeTableViewTrailingConstraint: NSLayoutConstraint!
|
||||
private var findInCodeTableViewBottomConstraint: NSLayoutConstraint!
|
||||
var jumpTabFindInCodeConstraints: [NSLayoutConstraint] {
|
||||
[
|
||||
keyboardViewLeadingConstraint,
|
||||
findCodeButtonBottomConstraint,
|
||||
findInCodeTableViewLeadingConstraint,
|
||||
findInCodeTableViewTrailingConstraint,
|
||||
findCodeButtonCenterXConstraint,
|
||||
findInCodeTableViewBottomConstraint
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Jump tab: find in code table
|
||||
lazy var findInCodeResultsTableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.dataSource = self
|
||||
view.delegate = self
|
||||
view.register(UITableViewCell.self, forCellReuseIdentifier: "FindInCodeResultsCell")
|
||||
return view
|
||||
}()
|
||||
|
||||
func findInCodeSetupView() {
|
||||
view.addSubview(findCodeButton)
|
||||
view.addSubview(findInCodeResultsTableView)
|
||||
|
||||
// Find Code Button Normal config
|
||||
keyboardViewCenterXConstraint = keyboardView.centerXAnchor.constraint(equalTo: titleLabel.centerXAnchor)
|
||||
findCodeButtonLeadingConstraint = findCodeButton.leadingAnchor.constraint(equalTo: keyboardView.trailingAnchor, constant: 8)
|
||||
findCodeButtonCenterYConstraint = findCodeButton.centerYAnchor.constraint(equalTo: keyboardView.centerYAnchor)
|
||||
findInCodeTableViewLeadingConstraintOutOfView = findInCodeResultsTableView.leadingAnchor.constraint(equalTo: view.trailingAnchor, constant: 50)
|
||||
findInCodeTableViewBottomConstraintOutOfView = findInCodeResultsTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -8)
|
||||
|
||||
// Find Code Button Searching Config
|
||||
keyboardViewLeadingConstraint = keyboardView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8)
|
||||
findCodeButtonBottomConstraint = findCodeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 4)
|
||||
findCodeButtonCenterXConstraint = findCodeButton.centerXAnchor.constraint(equalTo: findInCodeResultsTableView.centerXAnchor)
|
||||
findInCodeTableViewBottomConstraint = findInCodeResultsTableView.bottomAnchor.constraint(equalTo: findCodeButton.topAnchor, constant: -4)
|
||||
findInCodeResultsTableView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
findInCodeTableViewLeadingConstraint = findInCodeResultsTableView.leadingAnchor.constraint(equalTo: keyboardView.trailingAnchor, constant: 8)
|
||||
findInCodeTableViewTrailingConstraint = findInCodeResultsTableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8)
|
||||
|
||||
NSLayoutConstraint.deactivate(jumpTabFindInCodeConstraints)
|
||||
NSLayoutConstraint.activate(jumpTabNormalModeConstraints)
|
||||
|
||||
findCodeButton.onTapped = { isSelected in
|
||||
self.updateKeyboardPosition(isFindingCode: isSelected)
|
||||
self.updateMatchedInstructions()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateMatchedInstructions() {
|
||||
if let delegate = delegate,
|
||||
let text = memoryField.text,
|
||||
let address = getAddressFromText(text),
|
||||
address < 0x10000 {
|
||||
matchedInstructions = delegate.referencedMemoryAddresses[UInt16(address)] ?? [AddressedInstruction]()
|
||||
findInCodeResultsTableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateKeyboardPosition(isFindingCode: Bool = false) {
|
||||
let animator = UIViewPropertyAnimator(duration: 0.2, curve: .linear) {
|
||||
if isFindingCode {
|
||||
NSLayoutConstraint.deactivate(self.jumpTabNormalModeConstraints)
|
||||
NSLayoutConstraint.activate(self.jumpTabFindInCodeConstraints)
|
||||
} else {
|
||||
NSLayoutConstraint.deactivate(self.jumpTabFindInCodeConstraints)
|
||||
NSLayoutConstraint.activate(self.jumpTabNormalModeConstraints)
|
||||
}
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
animator.startAnimation()
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
cheatFinderSetupView()
|
||||
findInCodeSetupView()
|
||||
update()
|
||||
}
|
||||
|
||||
func getAddressFromText(_ text: String?) -> UInt64? {
|
||||
guard let text = text else {
|
||||
return nil
|
||||
}
|
||||
let scanner = Scanner(string: text)
|
||||
var address: UInt64 = 0
|
||||
if scanner.scanHexInt64(&address) && address < EmuMemoryModel.maxMemorySize {
|
||||
return address
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateTextField(with keyCode: Int) {
|
||||
guard var text = memoryField.text else {
|
||||
print("no text in address field!")
|
||||
return
|
||||
}
|
||||
text.append(String(format: "%1X",keyCode))
|
||||
let charLimit: Int = {
|
||||
switch mode {
|
||||
case .jumpToAddress:
|
||||
return 5
|
||||
case .changeMemory:
|
||||
return 2
|
||||
default:
|
||||
return 5
|
||||
}
|
||||
}()
|
||||
if text.count > charLimit {
|
||||
text.removeFirst()
|
||||
}
|
||||
if mode == .jumpToAddress, let address = getAddressFromText(text) {
|
||||
delegate?.jump(to: Int(address))
|
||||
if findCodeButton.isSelected {
|
||||
updateMatchedInstructions()
|
||||
}
|
||||
}
|
||||
memoryField.text = text
|
||||
}
|
||||
|
||||
@objc func segmentedControlChanged(_ sender: UISegmentedControl) {
|
||||
switch sender.selectedSegmentIndex {
|
||||
case 0:
|
||||
mode = .jumpToAddress
|
||||
case 1:
|
||||
mode = .changeMemory
|
||||
case 2:
|
||||
mode = .cheat
|
||||
case 3:
|
||||
mode = .screen
|
||||
default:
|
||||
mode = .jumpToAddress
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
func update() {
|
||||
cheatFinderHide()
|
||||
emulatorScreenView.isHidden = true
|
||||
findCodeButton.isHidden = true
|
||||
findInCodeResultsTableView.isHidden = true
|
||||
switch mode {
|
||||
case .jumpToAddress:
|
||||
memoryField.isHidden = false
|
||||
memoryField.layer.borderColor = UIColor.cyan.cgColor
|
||||
memoryField.textColor = .cyan
|
||||
editFieldsStackView.isHidden = false
|
||||
updateMemoryButton.isHidden = true
|
||||
resetMemoryButton.isHidden = true
|
||||
keyboardView.isHidden = false
|
||||
findCodeButton.isHidden = false
|
||||
findInCodeResultsTableView.isHidden = false
|
||||
if let selectedAddress = delegate?.selectedAddress {
|
||||
memoryField.text = String(format: "%04X", selectedAddress)
|
||||
}
|
||||
updateKeyboardPosition(isFindingCode: findCodeButton.isSelected)
|
||||
case .changeMemory:
|
||||
memoryField.isHidden = false
|
||||
memoryField.layer.borderColor = UIColor.orange.cgColor
|
||||
memoryField.textColor = .orange
|
||||
editFieldsStackView.isHidden = false
|
||||
updateMemoryButton.isHidden = false
|
||||
resetMemoryButton.isHidden = false
|
||||
keyboardView.isHidden = false
|
||||
if let selectedAddress = delegate?.selectedAddress {
|
||||
memoryField.text = delegate?.memoryHex(at: selectedAddress)
|
||||
}
|
||||
updateKeyboardPosition()
|
||||
case .cheat:
|
||||
memoryField.isHidden = true
|
||||
editFieldsStackView.isHidden = true
|
||||
keyboardView.isHidden = true
|
||||
cheatFinderUpdateUI()
|
||||
case .screen:
|
||||
memoryField.isHidden = true
|
||||
editFieldsStackView.isHidden = true
|
||||
keyboardView.isHidden = true
|
||||
emulatorScreenView.isHidden = false
|
||||
updateEmulatorScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateButtonPressed(_ sender: UIButton) {
|
||||
guard let selectedAddress = delegate?.selectedAddress,
|
||||
let enteredText = memoryField.text,
|
||||
let memory = UInt8(enteredText, radix: 16) else {
|
||||
print("Could not get memory to update!")
|
||||
return
|
||||
}
|
||||
delegate?.updateMemory(at: selectedAddress, with: memory)
|
||||
}
|
||||
|
||||
@objc func addToCheatButtonPressed(_ sender: UIButton) {
|
||||
guard let selectedAddress = delegate?.selectedAddress,
|
||||
let enteredText = memoryField.text,
|
||||
let memory = UInt8(enteredText, radix: 16) else {
|
||||
print("Could not get memory to update!")
|
||||
return
|
||||
}
|
||||
cheatFinder.savedMatches[selectedAddress] = (value: memory, enabled: true)
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func cheatTableCellActionButtonPressed(_ sender: UIButton) {
|
||||
let address = sender.tag
|
||||
guard let matched = cheatFinder.matchedMemoryAddresses[address] else {
|
||||
print("Could not find matched address: \(address)")
|
||||
return
|
||||
}
|
||||
if cheatFinder.savedMatches[address] != nil {
|
||||
cheatFinder.savedMatches.removeValue(forKey: address)
|
||||
} else {
|
||||
cheatFinder.savedMatches[address] = (value: matched, enabled: true)
|
||||
}
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func cheatSavedTableSwitchPressed(_ sender: UISwitch) {
|
||||
let address = sender.tag
|
||||
guard let matched = cheatFinder.savedMatches[address] else {
|
||||
print("could not find saved entry!")
|
||||
return
|
||||
}
|
||||
cheatFinder.savedMatches[address] = (value: matched.value, enabled: sender.isOn)
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: EmulatorKeyboardKeyPressedDelegate {
|
||||
func keyDown(_ key: KeyCoded) {
|
||||
print("DebugMemoryActionViewController keydown: \(key.keyLabel) ( \(key.keyCode) )")
|
||||
}
|
||||
|
||||
func keyUp(_ key: KeyCoded) {
|
||||
print("DebugMemoryActionViewController keyUp: \(key.keyLabel) ( \(key.keyCode) )")
|
||||
updateTextField(with: key.keyCode)
|
||||
}
|
||||
|
||||
func updateTransparency(toAlpha alpha: CGFloat) {
|
||||
// no op
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: DebugMemoryViewControllerDelegate {
|
||||
func refreshActionController() {
|
||||
update()
|
||||
updateMatchedInstructions()
|
||||
}
|
||||
func updateEmulatorScreen() {
|
||||
let emulatorView = EmuWrapper.getEmulatorView()
|
||||
if let snapshot = emulatorView?.getSnapshot() {
|
||||
let flipped = UIImage(cgImage: snapshot.cgImage!, scale: 1.0, orientation: .downMirrored)
|
||||
emulatorScreenView.image = flipped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if tableView == findInCodeResultsTableView {
|
||||
return matchedInstructions.count
|
||||
} else if tableView == cheatFinderSavedTableView {
|
||||
return cheatFinder.savedMatches.keys.count
|
||||
} else {
|
||||
return cheatFinder.matchedMemoryAddresses.keys.count
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
if tableView == findInCodeResultsTableView {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "FindInCodeResultsCell", for: indexPath)
|
||||
let instruction = matchedInstructions[indexPath.row]
|
||||
cell.textLabel?.text = instruction.description
|
||||
cell.textLabel?.font = UIFont(name: "Print Char 21", size: 11)
|
||||
cell.textLabel?.textColor = .yellow
|
||||
cell.textLabel?.textAlignment = .center
|
||||
return cell
|
||||
} else if tableView == cheatFinderSavedTableView {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "CheatFinderMatchCell", for: indexPath)
|
||||
let addresses = cheatFinder.savedMatches.keys.sorted()
|
||||
let index = addresses.index(addresses.startIndex, offsetBy: indexPath.row)
|
||||
let address = addresses[index]
|
||||
let saved = cheatFinder.savedMatches[address]!
|
||||
cell.textLabel?.text = String(format: "%04X: %02X",address,saved.value)
|
||||
cell.textLabel?.font = UIFont(name: "Print Char 21", size: 14)
|
||||
cell.textLabel?.textColor = .red
|
||||
cell.textLabel?.textAlignment = .left
|
||||
let enableSwitch = UISwitch(frame: .zero)
|
||||
enableSwitch.isOn = saved.enabled
|
||||
enableSwitch.addTarget(self, action: #selector(cheatSavedTableSwitchPressed(_:)), for: .valueChanged)
|
||||
enableSwitch.tag = address
|
||||
cell.accessoryView = enableSwitch
|
||||
return cell
|
||||
} else {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "CheatFinderMatchCell", for: indexPath)
|
||||
let addresses = cheatFinder.matchedMemoryAddresses.keys.sorted()
|
||||
let index = addresses.index(addresses.startIndex, offsetBy: indexPath.row)
|
||||
let address = addresses[index]
|
||||
cell.textLabel?.text = String(format: "%04X: %02X",address,cheatFinder.matchedMemoryAddresses[address]!)
|
||||
cell.textLabel?.font = UIFont(name: "Print Char 21", size: 14)
|
||||
cell.textLabel?.textColor = .red
|
||||
cell.textLabel?.textAlignment = .center
|
||||
let isSaved = cheatFinder.savedMatches[address] != nil
|
||||
let actionButton = isSaved ? UIButton(type: .custom) : UIButton(type: .contactAdd)
|
||||
actionButton.tag = address
|
||||
if isSaved {
|
||||
actionButton.setTitle("Remove", for: .normal)
|
||||
}
|
||||
actionButton.addTarget(self, action: #selector(cheatTableCellActionButtonPressed(_:)), for: .touchUpInside)
|
||||
cell.accessoryView = actionButton
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
if tableView == findInCodeResultsTableView {
|
||||
let instruction = matchedInstructions[indexPath.row]
|
||||
let address = instruction.address
|
||||
delegate?.jump(to: Int(address))
|
||||
} else {
|
||||
let addresses = tableView == cheatFinderSavedTableView ? cheatFinder.savedMatches.keys.sorted() : cheatFinder.matchedMemoryAddresses.keys.sorted()
|
||||
let index = addresses.index(addresses.startIndex, offsetBy: indexPath.row)
|
||||
let address = addresses[index]
|
||||
delegate?.jump(to: address)
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,519 @@
|
|||
//
|
||||
// DebugMemoryViewController.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/17/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
enum EmuMemoryMapSection: Int {
|
||||
case zeroPage = 0
|
||||
case processorStack
|
||||
case getlnBuffer
|
||||
case freeSpace
|
||||
case dosProdosInterruptVectors
|
||||
case textVideoPageAndPeripheralScreenholes
|
||||
case textVideoPageTwoOrApplesoftProgramVariables
|
||||
case freespace2
|
||||
case highResGraphicsPage1
|
||||
case highResGraphicsPage2
|
||||
case applesoftStringData
|
||||
case ioArea
|
||||
case bankSwitched
|
||||
case auxBanks
|
||||
|
||||
var range:Range<Int> {
|
||||
switch self {
|
||||
case .zeroPage: return 0..<0x100
|
||||
case .processorStack: return 0x100..<0x200
|
||||
case .getlnBuffer: return 0x200..<0x300
|
||||
case .freeSpace: return 0x300..<0x3d0
|
||||
case .dosProdosInterruptVectors: return 0x3d0..<0x400
|
||||
case .textVideoPageAndPeripheralScreenholes: return 0x400..<0x800
|
||||
case .textVideoPageTwoOrApplesoftProgramVariables: return 0x800..<0xc00
|
||||
case .freespace2: return 0xc00..<0x2000
|
||||
case .highResGraphicsPage1: return 0x2000..<0x4000
|
||||
case .highResGraphicsPage2: return 0x4000..<0x6000
|
||||
case .applesoftStringData: return 0x6000..<0xc000
|
||||
case .ioArea: return 0xc000..<0xd000
|
||||
case .bankSwitched: return 0xd000..<0x10000
|
||||
case .auxBanks: return 0x10000..<0x30000
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfItems: Int {
|
||||
self.range.count
|
||||
}
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .zeroPage: return "Zero Page"
|
||||
case .processorStack: return "6502 Processor Stack"
|
||||
case .getlnBuffer: return "GETLN Line Input Buffer"
|
||||
case .freeSpace: return "Free Space for Machine Language, Shape Table, etc."
|
||||
case .dosProdosInterruptVectors: return "DOS, ProDOS, and Interrupt Vectors"
|
||||
case .textVideoPageAndPeripheralScreenholes: return "Text Video Page and Peripheral Screenholes"
|
||||
case .textVideoPageTwoOrApplesoftProgramVariables: return "Text Video Page 2 or Applesoft Program and Variables"
|
||||
case .freespace2: return "Free Space for Machine Language, Shapes, etc."
|
||||
case .highResGraphicsPage1: return "High Resolution Graphics Page 1"
|
||||
case .highResGraphicsPage2: return "High Resolution Graphics Page 2"
|
||||
case .applesoftStringData: return "Applesoft String Data"
|
||||
case .ioArea: return "IO Area"
|
||||
case .bankSwitched: return "Bank Switched"
|
||||
case .auxBanks: return "Auxilliary Memory? 😅"
|
||||
}
|
||||
}
|
||||
|
||||
static func section(for address:Int) -> EmuMemoryMapSection {
|
||||
if EmuMemoryMapSection.zeroPage.range.contains(address) {
|
||||
return .zeroPage
|
||||
} else if EmuMemoryMapSection.processorStack.range.contains(address) {
|
||||
return .processorStack
|
||||
} else if EmuMemoryMapSection.getlnBuffer.range.contains(address) {
|
||||
return .getlnBuffer
|
||||
} else if EmuMemoryMapSection.freeSpace.range.contains(address) {
|
||||
return .freeSpace
|
||||
} else if EmuMemoryMapSection.dosProdosInterruptVectors.range.contains(address) {
|
||||
return .dosProdosInterruptVectors
|
||||
} else if EmuMemoryMapSection.textVideoPageAndPeripheralScreenholes.range.contains(address) {
|
||||
return .textVideoPageAndPeripheralScreenholes
|
||||
} else if EmuMemoryMapSection.textVideoPageTwoOrApplesoftProgramVariables.range.contains(address) {
|
||||
return .textVideoPageTwoOrApplesoftProgramVariables
|
||||
} else if EmuMemoryMapSection.freespace2.range.contains(address) {
|
||||
return .freespace2
|
||||
} else if EmuMemoryMapSection.highResGraphicsPage1.range.contains(address) {
|
||||
return .highResGraphicsPage1
|
||||
} else if EmuMemoryMapSection.highResGraphicsPage2.range.contains(address) {
|
||||
return .highResGraphicsPage2
|
||||
} else if EmuMemoryMapSection.applesoftStringData.range.contains(address) {
|
||||
return .applesoftStringData
|
||||
} else if EmuMemoryMapSection.ioArea.range.contains(address) {
|
||||
return .ioArea
|
||||
} else if EmuMemoryMapSection.bankSwitched.range.contains(address) {
|
||||
return .bankSwitched
|
||||
} else if EmuMemoryMapSection.auxBanks.range.contains(address) {
|
||||
return .auxBanks
|
||||
} else {
|
||||
return .auxBanks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol DebugMemoryViewControllerDelegate: class {
|
||||
func refreshActionController()
|
||||
func updateEmulatorScreen()
|
||||
}
|
||||
|
||||
@objc class DebugMemoryViewController: UIViewController {
|
||||
let memoryModel = EmuMemoryModel()
|
||||
|
||||
weak var delegate: DebugMemoryViewControllerDelegate?
|
||||
|
||||
// Landscape constraints
|
||||
var actionControllerTopToViewTopConstraint: NSLayoutConstraint?
|
||||
var actionControllerWidthConstraint: NSLayoutConstraint?
|
||||
var actionControllerLeadingToTableViewTrailingConstraint: NSLayoutConstraint?
|
||||
var tableViewToViewBottomConstraint: NSLayoutConstraint?
|
||||
|
||||
// Portrait constraints
|
||||
var actionControllerLeadingToViewLeadingConstraint: NSLayoutConstraint?
|
||||
var actionControllerTopToTableViewBottomConstraint: NSLayoutConstraint?
|
||||
var actionControllerHeightConstraint: NSLayoutConstraint?
|
||||
var tableViewTrailingToViewTrailingConstraint: NSLayoutConstraint?
|
||||
|
||||
var actionControllerBottomConstraint: NSLayoutConstraint?
|
||||
|
||||
var isShowingActionController = false
|
||||
var actionControllerAnimatorProgress = 0.0
|
||||
var animator: UIViewPropertyAnimator?
|
||||
|
||||
var cheatFinder = CheatFinderManager()
|
||||
|
||||
private var displayLink: CADisplayLink?
|
||||
private var framesSince = 0
|
||||
private var framesSinceUpdateScreen = 0
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = UIFont(name: "Print Char 21", size: 14)
|
||||
label.textColor = .white
|
||||
label.text = "Apple II Memory Debugger"
|
||||
return label
|
||||
}()
|
||||
|
||||
let dismissButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setTitle("[X]", for: .normal)
|
||||
button.addTarget(self, action: #selector(closeTapped(_:)), for: .touchUpInside)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitleColor(.red, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let dataTableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.separatorStyle = .none
|
||||
return view
|
||||
}()
|
||||
|
||||
let codeTableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.separatorStyle = .none
|
||||
return view
|
||||
}()
|
||||
|
||||
let resumePauseEmulationButton: DebugPauseResumeButton = {
|
||||
let button = DebugPauseResumeButton()
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
let memoryViewModeControl: UISegmentedControl = {
|
||||
let control = UISegmentedControl(items: ["Data","Code"])
|
||||
control.translatesAutoresizingMaskIntoConstraints = false
|
||||
control.tintColor = .green
|
||||
control.setTitleTextAttributes([NSAttributedString.Key.font: UIFont(name: "Print Char 21", size: 14)!], for: .normal)
|
||||
control.addTarget(self, action: #selector(memoryViewModeControlChanged(_:)), for: .valueChanged)
|
||||
control.selectedSegmentIndex = 0
|
||||
return control
|
||||
}()
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setupView() {
|
||||
view.addSubview(titleLabel)
|
||||
view.addSubview(memoryViewModeControl)
|
||||
view.addSubview(dataTableView)
|
||||
view.addSubview(codeTableView)
|
||||
view.addSubview(dismissButton)
|
||||
view.addSubview(resumePauseEmulationButton)
|
||||
titleLabel.centerXAnchor.constraint(equalTo: dataTableView.centerXAnchor).isActive = true
|
||||
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16).isActive = true
|
||||
memoryViewModeControl.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8).isActive = true
|
||||
memoryViewModeControl.centerXAnchor.constraint(equalTo: dataTableView.centerXAnchor).isActive = true
|
||||
dataTableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
|
||||
// tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
|
||||
dataTableView.topAnchor.constraint(equalTo: memoryViewModeControl.bottomAnchor, constant: 16).isActive = true
|
||||
codeTableView.leadingAnchor.constraint(equalTo: dataTableView.leadingAnchor).isActive = true
|
||||
codeTableView.trailingAnchor.constraint(equalTo: dataTableView.trailingAnchor).isActive = true
|
||||
codeTableView.topAnchor.constraint(equalTo: dataTableView.topAnchor).isActive = true
|
||||
codeTableView.bottomAnchor.constraint(equalTo: dataTableView.bottomAnchor).isActive = true
|
||||
// tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
dismissButton.trailingAnchor.constraint(equalTo: dataTableView.trailingAnchor, constant: -8).isActive = true
|
||||
dismissButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8.0).isActive = true
|
||||
resumePauseEmulationButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
|
||||
resumePauseEmulationButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8).isActive = true
|
||||
view.backgroundColor = .black
|
||||
}
|
||||
|
||||
func setupTableView() {
|
||||
dataTableView.dataSource = self
|
||||
dataTableView.delegate = self
|
||||
dataTableView.register(DebugMemoryCell.self, forCellReuseIdentifier: DebugMemoryCell.identifier)
|
||||
codeTableView.dataSource = self
|
||||
codeTableView.delegate = self
|
||||
codeTableView.register(DebugMemoryCell.self, forCellReuseIdentifier: DebugMemoryCell.identifier)
|
||||
}
|
||||
|
||||
func setupActionController() {
|
||||
let actionController = DebugMemoryActionViewController(cheatFinderManager: cheatFinder)
|
||||
delegate = actionController
|
||||
actionController.delegate = self
|
||||
addChild(actionController)
|
||||
actionController.didMove(toParent: self)
|
||||
actionController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(actionController.view)
|
||||
actionController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
actionControllerBottomConstraint = actionController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 250)
|
||||
actionControllerBottomConstraint?.isActive = true
|
||||
|
||||
actionControllerHeightConstraint = actionController.view.heightAnchor.constraint(equalToConstant: 380)
|
||||
actionControllerLeadingToViewLeadingConstraint = actionController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor)
|
||||
actionControllerTopToTableViewBottomConstraint = actionController.view.topAnchor.constraint(equalTo: dataTableView.bottomAnchor)
|
||||
tableViewTrailingToViewTrailingConstraint = dataTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8)
|
||||
|
||||
actionControllerWidthConstraint = actionController.view.widthAnchor.constraint(equalToConstant: 400)
|
||||
actionControllerTopToViewTopConstraint = actionController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
|
||||
actionControllerLeadingToTableViewTrailingConstraint = actionController.view.leadingAnchor.constraint(equalTo: dataTableView.trailingAnchor)
|
||||
tableViewToViewBottomConstraint = dataTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
|
||||
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
||||
actionController.view.addGestureRecognizer(pan)
|
||||
}
|
||||
|
||||
func setupActionControllerOrientationConstraints(orientation: UIInterfaceOrientation? = nil) {
|
||||
actionControllerLeadingToViewLeadingConstraint?.isActive = false
|
||||
actionControllerTopToTableViewBottomConstraint?.isActive = false
|
||||
actionControllerHeightConstraint?.isActive = false
|
||||
actionControllerTopToViewTopConstraint?.isActive = false
|
||||
actionControllerWidthConstraint?.isActive = false
|
||||
actionControllerLeadingToTableViewTrailingConstraint?.isActive = false
|
||||
tableViewToViewBottomConstraint?.isActive = false
|
||||
tableViewTrailingToViewTrailingConstraint?.isActive = false
|
||||
|
||||
let isPortrait = orientation != nil ? orientation!.isPortrait : Orientation.isPortrait
|
||||
|
||||
if isPortrait {
|
||||
actionControllerLeadingToViewLeadingConstraint?.isActive = true
|
||||
actionControllerTopToTableViewBottomConstraint?.isActive = true
|
||||
actionControllerHeightConstraint?.isActive = true
|
||||
tableViewTrailingToViewTrailingConstraint?.isActive = true
|
||||
} else {
|
||||
actionControllerTopToViewTopConstraint?.isActive = true
|
||||
actionControllerWidthConstraint?.isActive = true
|
||||
actionControllerLeadingToTableViewTrailingConstraint?.isActive = true
|
||||
tableViewToViewBottomConstraint?.isActive = true
|
||||
actionControllerBottomConstraint?.constant = 0
|
||||
}
|
||||
}
|
||||
|
||||
func setupPauseResumeButton() {
|
||||
resumePauseEmulationButton.onTapped = { selected in
|
||||
if selected {
|
||||
EmuWrapper.resume()
|
||||
self.displayLink = CADisplayLink(target: self, selector: #selector(self.updateMemoryIfNeeded))
|
||||
self.displayLink?.isPaused = false
|
||||
self.displayLink?.add(to: RunLoop.main, forMode: .common)
|
||||
} else {
|
||||
EmuWrapper.pause()
|
||||
self.displayLink?.isPaused = true
|
||||
self.displayLink = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func memoryViewModeControlChanged(_ sender: UISegmentedControl) {
|
||||
switch sender.selectedSegmentIndex {
|
||||
case 1:
|
||||
codeTableView.isHidden = false
|
||||
dataTableView.isHidden = true
|
||||
default:
|
||||
codeTableView.isHidden = true
|
||||
dataTableView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateMemoryIfNeeded() {
|
||||
// maybe apply cheats here?
|
||||
if framesSinceUpdateScreen > 3 {
|
||||
delegate?.updateEmulatorScreen()
|
||||
framesSinceUpdateScreen = 1
|
||||
}
|
||||
if framesSince > 30 {
|
||||
memoryModel.refresh()
|
||||
dataTableView.reloadData()
|
||||
delegate?.refreshActionController()
|
||||
framesSince = 1
|
||||
}
|
||||
framesSince += 1
|
||||
framesSinceUpdateScreen += 1
|
||||
}
|
||||
|
||||
func registerAppleIIFont() {
|
||||
let fontUrl = Bundle.main.url(forResource: "PrintChar21", withExtension: "ttf")
|
||||
CTFontManagerRegisterFontURLs([fontUrl] as CFArray, .persistent, true) { errors, done -> Bool in
|
||||
if(done) {
|
||||
print("Done installing custom font!")
|
||||
}
|
||||
print(errors as Array)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
setupActionController()
|
||||
setupTableView()
|
||||
setupPauseResumeButton()
|
||||
memoryViewModeControlChanged(memoryViewModeControl)
|
||||
// registerAppleIIFont()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
memoryModel.refresh()
|
||||
setupActionControllerOrientationConstraints()
|
||||
resumePauseEmulationButton.isSelected = false
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
dataTableView.reloadData()
|
||||
delegate?.refreshActionController()
|
||||
cheatFinder.start()
|
||||
}
|
||||
|
||||
@objc func handlePan(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
animator = UIViewPropertyAnimator(duration: 0.2, curve: .linear, animations: {
|
||||
self.actionControllerBottomConstraint?.constant = self.isShowingActionController ? 250 : 0
|
||||
self.view.layoutIfNeeded()
|
||||
})
|
||||
animator?.addCompletion { _ in
|
||||
self.isShowingActionController = !self.isShowingActionController
|
||||
}
|
||||
animator?.pauseAnimation()
|
||||
case .changed:
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var fraction = -translation.y / 300
|
||||
if self.isShowingActionController { fraction *= -1 }
|
||||
animator?.fractionComplete = fraction
|
||||
case .ended:
|
||||
animator?.continueAnimation(withTimingParameters: nil, durationFactor: 0)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc func closeTapped(_ sender: UIButton) {
|
||||
displayLink?.isPaused = true
|
||||
displayLink = nil
|
||||
dismiss(animated: true) {
|
||||
EmuWrapper.resume()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
setupActionControllerOrientationConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if tableView == codeTableView {
|
||||
return memoryModel.interpretedInstructions.count
|
||||
} else {
|
||||
let numItems = EmuMemoryMapSection(rawValue: section)!.numberOfItems
|
||||
var numRows = numItems / memoryModel.numToDisplayPerCell
|
||||
if numItems % memoryModel.numToDisplayPerCell > 0 {
|
||||
numRows += 1
|
||||
}
|
||||
return numRows
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
if tableView == codeTableView {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: DebugMemoryCell.identifier, for: indexPath) as! DebugMemoryCell
|
||||
if indexPath.row >= memoryModel.interpretedInstructions.count {
|
||||
return cell
|
||||
}
|
||||
let instruction = memoryModel.interpretedInstructions[indexPath.row]
|
||||
let values = [
|
||||
instruction.instruction.mnemonic.description,
|
||||
instruction.instruction.addressingMode.description
|
||||
]
|
||||
cell.updateWith(delegate: self, offset: Int(instruction.address), hexMemoryValues: values)
|
||||
return cell
|
||||
} else {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: DebugMemoryCell.identifier, for: indexPath) as! DebugMemoryCell
|
||||
let memoryVals = memoryModel.hexStrings(for: indexPath)
|
||||
let offset = memoryModel.offset(for: indexPath)
|
||||
cell.updateWith(delegate: self, offset: offset, hexMemoryValues: memoryVals)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
if tableView == codeTableView {
|
||||
return 1
|
||||
} else {
|
||||
return EmuMemoryMapSection.auxBanks.rawValue + 1
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
if tableView == dataTableView {
|
||||
return EmuMemoryMapSection.init(rawValue: section)!.title
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if tableView == dataTableView {
|
||||
let title = self.tableView(tableView, titleForHeaderInSection: section)
|
||||
let label = UILabel(frame: .zero)
|
||||
label.text = title
|
||||
label.font = UIFont(name: "Print Char 21", size: 12)
|
||||
label.textColor = .cyan
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
let view = UIView(frame: .zero)
|
||||
view.addSubview(label)
|
||||
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
|
||||
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8).isActive = true
|
||||
view.backgroundColor = .black
|
||||
view.layer.borderColor = UIColor.cyan.cgColor
|
||||
view.layer.borderWidth = 1.0
|
||||
view.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
return view
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: DebugMemoryCellDelegate {
|
||||
func updateSelection(to address: Int) {
|
||||
memoryModel.selectedAddress = address
|
||||
self.dataTableView.reloadData()
|
||||
// communicate selected address to action controller
|
||||
delegate?.refreshActionController()
|
||||
}
|
||||
|
||||
func isAddressSelected(_ address: Int) -> Bool {
|
||||
if let selectedAddress = memoryModel.selectedAddress,
|
||||
selectedAddress == address {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: DebugMemoryActionViewControllerDelegate {
|
||||
func updateMemory(at address: Int, with memory:UInt8) {
|
||||
memoryModel.setMemory(at: address, value: memory)
|
||||
self.dataTableView.reloadData()
|
||||
}
|
||||
|
||||
func jump(to address: Int) {
|
||||
print("jumping to address: \(String(format: "%04X",address)) decimal: \(address)")
|
||||
let indexPath = memoryModel.indexPath(for: address)
|
||||
dataTableView.scrollToRow(at: indexPath, at: .middle, animated: true)
|
||||
|
||||
if let codeIndex = memoryModel.interpretedInstructions.firstIndex(where: { $0.address == address }) {
|
||||
let indexPath = IndexPath(row: codeIndex, section: 0)
|
||||
codeTableView.scrollToRow(at: indexPath, at: .middle, animated: true)
|
||||
}
|
||||
|
||||
updateSelection(to: address)
|
||||
}
|
||||
func memoryHex(at address: Int) -> String {
|
||||
return memoryModel.getMemoryHexString(at: address)
|
||||
}
|
||||
var memory: UnsafeMutablePointer<UInt8>? {
|
||||
return memoryModel.memory
|
||||
}
|
||||
var selectedAddress: Int? {
|
||||
return memoryModel.selectedAddress
|
||||
}
|
||||
var referencedMemoryAddresses: [UInt16: [AddressedInstruction]] {
|
||||
return memoryModel.referencedAddresses
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// DebugMemoryButton.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/4/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ToggleButton: UIButton {
|
||||
var onTapped: ((Bool) -> Void)?
|
||||
|
||||
convenience init() {
|
||||
self.init(type: .custom)
|
||||
addTarget(self, action: #selector(tapped(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc func tapped(_ sender: UIButton) {
|
||||
isSelected.toggle()
|
||||
onTapped?(isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
class DebugMemoryButton: ToggleButton {
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .white : .clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DebugPauseResumeButton: ToggleButton {
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .red : .clear
|
||||
}
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(type: .custom)
|
||||
addTarget(self, action: #selector(tapped(_:)), for: .touchUpInside)
|
||||
setTitle("RESUME", for: .normal)
|
||||
setTitleColor(.green, for: .normal)
|
||||
setTitle("PAUSE", for: .selected)
|
||||
setTitleColor(.white, for: .selected)
|
||||
titleLabel?.font = UIFont(name: "Print Char 21", size: 11)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// DebugMemoryCell.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/4/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol DebugMemoryCellDelegate: class {
|
||||
func updateSelection(to address: Int)
|
||||
func isAddressSelected(_ address: Int) -> Bool
|
||||
}
|
||||
|
||||
class DebugMemoryCell: UITableViewCell {
|
||||
static let identifier = "DebugMemoryCell"
|
||||
weak var delegate: DebugMemoryCellDelegate?
|
||||
|
||||
var updateSelection: ((Int) -> Void)?
|
||||
|
||||
var offset: Int?
|
||||
|
||||
lazy var stackView: UIStackView = {
|
||||
let view = UIStackView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.axis = .horizontal
|
||||
view.spacing = 8.0
|
||||
view.alignment = .center
|
||||
return view
|
||||
}()
|
||||
|
||||
let addressLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = UIFont(name: "Print Char 21", size: 16)
|
||||
label.textColor = .yellow
|
||||
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
contentView.addSubview(stackView)
|
||||
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0).isActive = true
|
||||
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 8.0).isActive = true
|
||||
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8.0).isActive = true
|
||||
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 8.0).isActive = true
|
||||
backgroundColor = .clear
|
||||
selectionStyle = .none
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updateWith(
|
||||
delegate: DebugMemoryCellDelegate,
|
||||
offset: Int,
|
||||
hexMemoryValues: [String]
|
||||
) {
|
||||
self.delegate = delegate
|
||||
self.offset = offset
|
||||
stackView.arrangedSubviews.forEach{ $0.removeFromSuperview() }
|
||||
addressLabel.text = String(format: "%05X:", offset)
|
||||
stackView.addArrangedSubview(addressLabel)
|
||||
stackView.setCustomSpacing(3, after: addressLabel)
|
||||
for (index, hexValue) in hexMemoryValues.enumerated() {
|
||||
let button = DebugMemoryButton()
|
||||
let address = offset + index
|
||||
button.setTitle(hexValue, for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 16)
|
||||
button.setTitleColor(.green, for: .normal)
|
||||
button.tag = index
|
||||
button.setTitleColor(.black, for: .selected)
|
||||
button.onTapped = { didSelect in
|
||||
if didSelect {
|
||||
self.delegate?.updateSelection(to: address)
|
||||
}
|
||||
}
|
||||
button.isSelected = self.delegate?.isAddressSelected(address) ?? false
|
||||
// button.addTarget(self, action: #selector(didTapOnButton(_:)), for: .touchUpInside)
|
||||
stackView.addArrangedSubview(button)
|
||||
}
|
||||
let spacer = UIView()
|
||||
spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
stackView.addArrangedSubview(spacer)
|
||||
}
|
||||
|
||||
@objc func didTapOnButton(_ button: UIButton) {
|
||||
let memoryAddr = String(format:"%04X",(offset ?? 0) + button.tag)
|
||||
print("tapped on button index \(button.tag), address: \(memoryAddr) ")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,888 @@
|
|||
//
|
||||
// EmulatorKeyboard.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 7/30/20.
|
||||
//
|
||||
|
||||
// TODO: shift key should change the label of the keys to uppercase (need callback mechanism?)
|
||||
// pan gesture to outer edges of keyboard view for better dragging
|
||||
// double tap modifier to toggle
|
||||
// taller keys?
|
||||
// alt key: add shift to left
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class KeyboardButton: UIButton {
|
||||
let key: KeyCoded
|
||||
var toggleState = false
|
||||
|
||||
// MARK: - Functions
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
let newArea = CGRect(
|
||||
x: self.bounds.origin.x - 5.0,
|
||||
y: self.bounds.origin.y - 5.0,
|
||||
width: self.bounds.size.width + 20.0,
|
||||
height: self.bounds.size.height + 20.0
|
||||
)
|
||||
return newArea.contains(point)
|
||||
}
|
||||
|
||||
override open var isHighlighted: Bool {
|
||||
didSet {
|
||||
// this is the problem - the isHighlighted sets to false for the touchesEnded callback
|
||||
// need to look ask something else
|
||||
// let shouldHighlight = isModifierToggle ? toggleState : isHighlighted
|
||||
if !isHighlighted && toggleState {
|
||||
// don't update the highlught
|
||||
} else {
|
||||
backgroundColor = isHighlighted ? .white : .clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
let shouldHighlight = key.isModifier ? toggleState : isSelected
|
||||
backgroundColor = shouldHighlight ? .red : .clear
|
||||
}
|
||||
}
|
||||
|
||||
required init(key: KeyCoded) {
|
||||
self.key = key
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@objc protocol EmulatorKeyboardKeyPressedDelegate: class {
|
||||
func keyDown(_ key: KeyCoded)
|
||||
func keyUp(_ key: KeyCoded)
|
||||
func updateTransparency(toAlpha alpha: CGFloat)
|
||||
}
|
||||
|
||||
@objc protocol EmulatorKeyboardModifierPressedDelegate: class {
|
||||
func modifierPressedWithKey(_ key: KeyCoded, enable: Bool)
|
||||
func isModifierEnabled(key: KeyCoded) -> Bool
|
||||
}
|
||||
|
||||
protocol EmulatorKeyboardViewDelegate: class {
|
||||
func toggleAlternateKeys()
|
||||
func refreshModifierStates()
|
||||
}
|
||||
|
||||
class EmulatorKeyboardView: UIView {
|
||||
|
||||
var viewModel = EmulatorKeyboardViewModel(keys: [[KeyCoded]]()) {
|
||||
didSet {
|
||||
setupWithModel(viewModel)
|
||||
}
|
||||
}
|
||||
var modifierButtons = Set<KeyboardButton>()
|
||||
|
||||
weak var delegate: EmulatorKeyboardViewDelegate?
|
||||
|
||||
private lazy var keyRowsStackView: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .equalCentering
|
||||
stackView.spacing = 12
|
||||
return stackView
|
||||
}()
|
||||
|
||||
private lazy var alternateKeyRowsStackView: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .equalCentering
|
||||
stackView.spacing = 12
|
||||
stackView.isHidden = true
|
||||
return stackView
|
||||
}()
|
||||
|
||||
let dragMeView: UIView = {
|
||||
let view = UIView(frame: .zero)
|
||||
view.backgroundColor = .white
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.widthAnchor.constraint(equalToConstant: 80).isActive = true
|
||||
view.heightAnchor.constraint(equalToConstant: 2).isActive = true
|
||||
let outerView = UIView(frame: .zero)
|
||||
outerView.backgroundColor = .clear
|
||||
outerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
outerView.addSubview(view)
|
||||
view.centerXAnchor.constraint(equalTo: outerView.centerXAnchor).isActive = true
|
||||
view.centerYAnchor.constraint(equalTo: outerView.centerYAnchor).isActive = true
|
||||
outerView.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||
outerView.widthAnchor.constraint(equalToConstant: 100).isActive = true
|
||||
return outerView
|
||||
}()
|
||||
|
||||
private var pressedKeyLabels = [String: UILabel]()
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: CGRect.zero)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
backgroundColor = .clear
|
||||
// layer.borderColor = UIColor.white.cgColor
|
||||
// layer.borderWidth = 1.0
|
||||
// layer.cornerRadius = 15.0
|
||||
layoutMargins = UIEdgeInsets(top: 16, left: 4, bottom: 16, right: 4)
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
addSubview(keyRowsStackView)
|
||||
keyRowsStackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||
keyRowsStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 4.0).isActive = true
|
||||
keyRowsStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: -4.0).isActive = true
|
||||
addSubview(alternateKeyRowsStackView)
|
||||
alternateKeyRowsStackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||
alternateKeyRowsStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 4.0).isActive = true
|
||||
alternateKeyRowsStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: -4.0).isActive = true
|
||||
addSubview(dragMeView)
|
||||
dragMeView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
||||
dragMeView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
|
||||
@objc private func keyPressed(_ sender: KeyboardButton) {
|
||||
if sender.key.keyCode == AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue {
|
||||
return
|
||||
}
|
||||
if !sender.key.isModifier {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.text = sender.titleLabel?.text
|
||||
// hmm need to convert frame
|
||||
let converted = sender.convert(sender.bounds, to: self)
|
||||
print("sender frame: \(sender.frame), bounds: \(sender.bounds), convertedBounds = \(converted)")
|
||||
var labelFrame = converted.offsetBy(dx: 0, dy: -60)
|
||||
labelFrame = CGRect(x: labelFrame.origin.x, y: labelFrame.origin.y, width: labelFrame.width * 2, height: labelFrame.height * 2)
|
||||
label.backgroundColor = .white
|
||||
label.textColor = .black
|
||||
label.frame = labelFrame
|
||||
label.font = UIFont(name: "Print Char 21", size: 12)
|
||||
label.textAlignment = .center
|
||||
addSubview(label)
|
||||
pressedKeyLabels[label.text ?? "😭"] = label
|
||||
}
|
||||
viewModel.keyPressed(sender.key)
|
||||
}
|
||||
|
||||
@objc private func keyCancelled(_ sender: KeyboardButton) {
|
||||
let title = sender.titleLabel?.text ?? "😭"
|
||||
if let label = pressedKeyLabels[title] {
|
||||
label.removeFromSuperview()
|
||||
pressedKeyLabels.removeValue(forKey: title)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func keyReleased(_ sender: KeyboardButton) {
|
||||
if sender.key.keyCode == AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue {
|
||||
delegate?.toggleAlternateKeys()
|
||||
return
|
||||
}
|
||||
let title = sender.titleLabel?.text ?? "😭"
|
||||
if let label = pressedKeyLabels[title] {
|
||||
label.removeFromSuperview()
|
||||
pressedKeyLabels.removeValue(forKey: title)
|
||||
}
|
||||
let modifierState = viewModel.modifierKeyToggleStateForKey(sender.key)
|
||||
sender.toggleState = modifierState
|
||||
sender.isSelected = modifierState
|
||||
viewModel.keyReleased(sender.key)
|
||||
self.delegate?.refreshModifierStates()
|
||||
}
|
||||
|
||||
func setupWithModel(_ model: EmulatorKeyboardViewModel) {
|
||||
for row in model.keys {
|
||||
let keysInRow = createKeyRow(keys: row)
|
||||
keyRowsStackView.addArrangedSubview(keysInRow)
|
||||
}
|
||||
if let altKeys = model.alternateKeys {
|
||||
for row in altKeys {
|
||||
let keysInRow = createKeyRow(keys: row)
|
||||
alternateKeyRowsStackView.addArrangedSubview(keysInRow)
|
||||
}
|
||||
}
|
||||
if !model.isDraggable {
|
||||
dragMeView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
func toggleKeysStackView() {
|
||||
if viewModel.alternateKeys != nil {
|
||||
keyRowsStackView.isHidden.toggle()
|
||||
alternateKeyRowsStackView.isHidden.toggle()
|
||||
refreshModifierStates()
|
||||
}
|
||||
}
|
||||
|
||||
func refreshModifierStates() {
|
||||
modifierButtons.forEach{ button in
|
||||
button.toggleState = viewModel.modifierKeyToggleStateForKey(button.key)
|
||||
button.isSelected = button.toggleState
|
||||
}
|
||||
}
|
||||
|
||||
private func createKey(_ keyCoded: KeyCoded) -> UIButton {
|
||||
let key = KeyboardButton(key: keyCoded)
|
||||
if let imageName = keyCoded.keyImageName {
|
||||
key.setImage(UIImage(systemName: imageName), for: .normal)
|
||||
if let highlightedImageName = keyCoded.keyImageNameHighlighted {
|
||||
key.setImage(UIImage(systemName: highlightedImageName), for: .highlighted)
|
||||
key.setImage(UIImage(systemName: highlightedImageName), for: .selected)
|
||||
}
|
||||
} else {
|
||||
key.setTitle(keyCoded.keyLabel, for: .normal)
|
||||
key.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
key.setTitleColor(.white, for: .normal)
|
||||
key.setTitleColor(.black, for: .highlighted)
|
||||
}
|
||||
key.translatesAutoresizingMaskIntoConstraints = false
|
||||
key.widthAnchor.constraint(equalToConstant: (25 * CGFloat(keyCoded.keySize.rawValue))).isActive = true
|
||||
key.heightAnchor.constraint(equalToConstant: 35).isActive = true
|
||||
key.layer.borderWidth = 1.0
|
||||
key.layer.borderColor = UIColor.white.cgColor
|
||||
key.layer.cornerRadius = 6.0
|
||||
key.addTarget(self, action: #selector(keyPressed(_:)), for: .touchDown)
|
||||
key.addTarget(self, action: #selector(keyReleased(_:)), for: .touchUpInside)
|
||||
key.addTarget(self, action: #selector(keyReleased(_:)), for: .touchUpOutside)
|
||||
key.addTarget(self, action: #selector(keyCancelled(_:)), for: .touchCancel)
|
||||
if keyCoded.isModifier {
|
||||
modifierButtons.update(with: key)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
private func createKeyRow(keys: [KeyCoded]) -> UIStackView {
|
||||
let subviews: [UIView] = keys.enumerated().map { index, keyCoded -> UIView in
|
||||
if keyCoded is SpacerKey {
|
||||
let spacer = UIView()
|
||||
spacer.widthAnchor.constraint(equalToConstant: 25.0 * CGFloat(keyCoded.keySize.rawValue)).isActive = true
|
||||
spacer.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
|
||||
return spacer
|
||||
} else if let sliderKey = keyCoded as? SliderKey {
|
||||
sliderKey.keyboardViewModel = self.viewModel
|
||||
return sliderKey.createView()
|
||||
}
|
||||
return createKey(keyCoded)
|
||||
}
|
||||
let stack = UIStackView(arrangedSubviews: subviews)
|
||||
stack.axis = .horizontal
|
||||
stack.distribution = .fill
|
||||
stack.spacing = 8
|
||||
return stack
|
||||
}
|
||||
}
|
||||
|
||||
@objc enum KeySize: Int {
|
||||
case standard = 1, wide, wider
|
||||
}
|
||||
|
||||
// represents a key that has an underlying code that gets sent to the emulator
|
||||
@objc protocol KeyCoded: AnyObject {
|
||||
var keyLabel: String { get }
|
||||
var keyImageName: String? { get }
|
||||
var keyImageNameHighlighted: String? { get }
|
||||
var keyCode: Int { get }
|
||||
var keySize: KeySize { get }
|
||||
var isModifier: Bool { get }
|
||||
}
|
||||
|
||||
protocol KeyRowsDataSource {
|
||||
func keyForPositionAt(_ position: KeyPosition) -> KeyCoded?
|
||||
}
|
||||
|
||||
@objc class AppleIIKey: NSObject, KeyCoded {
|
||||
let keyLabel: String
|
||||
var keyImageName: String?
|
||||
var keyImageNameHighlighted: String?
|
||||
let keyCode: Int
|
||||
let keySize: KeySize
|
||||
let isModifier: Bool
|
||||
|
||||
override var description: String {
|
||||
return String(format: "\(keyLabel) (%02X)", keyCode)
|
||||
}
|
||||
init(label: String, code: Int, keySize: KeySize = .standard, isModifier: Bool = false, imageName: String? = nil, imageNameHighlighted: String? = nil) {
|
||||
self.keyLabel = label
|
||||
self.keyCode = code
|
||||
self.keySize = keySize
|
||||
self.isModifier = isModifier
|
||||
self.keyImageName = imageName
|
||||
self.keyImageNameHighlighted = imageNameHighlighted
|
||||
}
|
||||
}
|
||||
|
||||
class SpacerKey: KeyCoded {
|
||||
let keyLabel = ""
|
||||
let keyCode = 0
|
||||
let keySize: KeySize
|
||||
let isModifier = false
|
||||
let keyImageName: String? = nil
|
||||
let keyImageNameHighlighted: String? = nil
|
||||
init(keySize: KeySize = .standard) {
|
||||
self.keySize = keySize
|
||||
}
|
||||
}
|
||||
|
||||
class SliderKey: KeyCoded {
|
||||
let keyLabel = ""
|
||||
let keyCode = 0
|
||||
let keySize: KeySize
|
||||
let isModifier = false
|
||||
let keyImageName: String? = nil
|
||||
let keyImageNameHighlighted: String? = nil
|
||||
weak var keyboardViewModel: EmulatorKeyboardViewModel?
|
||||
init(keySize: KeySize = .standard) {
|
||||
self.keySize = keySize
|
||||
}
|
||||
func createView() -> UIView {
|
||||
let slider = UISlider(frame: .zero)
|
||||
slider.minimumValue = 0.1
|
||||
slider.maximumValue = 1.0
|
||||
slider.addTarget(self, action: #selector(adjustKeyboardAlpha(_:)), for: .valueChanged)
|
||||
slider.value = 1.0
|
||||
return slider
|
||||
}
|
||||
@objc func adjustKeyboardAlpha(_ sender: UISlider) {
|
||||
keyboardViewModel?.delegate?.updateTransparency(toAlpha: CGFloat(sender.value))
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyPosition {
|
||||
let row: Int
|
||||
let column: Int
|
||||
}
|
||||
|
||||
@objc class EmulatorKeyboardViewModel: NSObject, KeyRowsDataSource {
|
||||
var keys = [[KeyCoded]]()
|
||||
var alternateKeys: [[KeyCoded]]?
|
||||
var modifiers: [Int16: KeyCoded]?
|
||||
|
||||
var isDraggable = true
|
||||
|
||||
@objc weak var delegate: EmulatorKeyboardKeyPressedDelegate?
|
||||
@objc weak var modifierDelegate: EmulatorKeyboardModifierPressedDelegate?
|
||||
|
||||
init(keys: [[KeyCoded]], alternateKeys: [[KeyCoded]]? = nil) {
|
||||
self.keys = keys
|
||||
self.alternateKeys = alternateKeys
|
||||
}
|
||||
|
||||
func createView() -> EmulatorKeyboardView {
|
||||
let view = EmulatorKeyboardView()
|
||||
view.viewModel = self
|
||||
return view
|
||||
}
|
||||
|
||||
func keyForPositionAt(_ position: KeyPosition) -> KeyCoded? {
|
||||
guard position.row < keys.count else {
|
||||
return nil
|
||||
}
|
||||
let row = keys[position.row]
|
||||
guard position.column < row.count else {
|
||||
return nil
|
||||
}
|
||||
return row[position.column]
|
||||
}
|
||||
|
||||
func modifierKeyToggleStateForKey(_ key: KeyCoded) -> Bool {
|
||||
return key.isModifier && (modifierDelegate?.isModifierEnabled(key: key) ?? false)
|
||||
}
|
||||
|
||||
func keyPressed(_ key: KeyCoded) {
|
||||
if key.isModifier {
|
||||
let isPressed = modifierDelegate?.isModifierEnabled(key: key) ?? false
|
||||
modifierDelegate?.modifierPressedWithKey(key, enable: !isPressed)
|
||||
return
|
||||
}
|
||||
delegate?.keyDown(key)
|
||||
}
|
||||
|
||||
func keyReleased(_ key: KeyCoded) {
|
||||
if key.isModifier {
|
||||
return
|
||||
}
|
||||
delegate?.keyUp(key)
|
||||
}
|
||||
|
||||
// KeyCoded can support a shifted key label
|
||||
// view can update with shifted key labels?
|
||||
// cluster can support alternate keys and view can swap them out?
|
||||
}
|
||||
|
||||
@objc class EmulatorKeyboardController: UIViewController {
|
||||
@objc lazy var leftKeyboardView: EmulatorKeyboardView = {
|
||||
let view = leftKeyboardModel.createView()
|
||||
view.delegate = self
|
||||
return view
|
||||
}()
|
||||
@objc lazy var rightKeyboardView: EmulatorKeyboardView = {
|
||||
let view = rightKeyboardModel.createView()
|
||||
view.delegate = self
|
||||
return view
|
||||
}()
|
||||
var keyboardConstraints = [NSLayoutConstraint]()
|
||||
|
||||
// Global states for all the keyboards
|
||||
// uses bitwise masks for the state of shift keys, control, open-apple keys, etc
|
||||
@objc var modifierState: Int16 = 0
|
||||
|
||||
@objc let leftKeyboardModel = EmulatorKeyboardViewModel(
|
||||
keys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "esc", code: AppleKeyboardKey.KEY_ESC.rawValue),
|
||||
AppleIIKey(label: "tab", code: AppleKeyboardKey.KEY_TAB.rawValue, keySize: .wide),
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "CTRL", code: AppleKeyboardKey.KEY_CTRL.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "control")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "q", code: AppleKeyboardKey.KEY_Q.rawValue),
|
||||
AppleIIKey(label: "w", code: AppleKeyboardKey.KEY_W.rawValue),
|
||||
AppleIIKey(label: "e", code: AppleKeyboardKey.KEY_E.rawValue),
|
||||
AppleIIKey(label: "r", code: AppleKeyboardKey.KEY_R.rawValue),
|
||||
AppleIIKey(label: "t", code: AppleKeyboardKey.KEY_T.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "a", code: AppleKeyboardKey.KEY_A.rawValue),
|
||||
AppleIIKey(label: "s", code: AppleKeyboardKey.KEY_S.rawValue),
|
||||
AppleIIKey(label: "d", code: AppleKeyboardKey.KEY_D.rawValue),
|
||||
AppleIIKey(label: "f", code: AppleKeyboardKey.KEY_F.rawValue),
|
||||
AppleIIKey(label: "g", code: AppleKeyboardKey.KEY_G.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "z", code: AppleKeyboardKey.KEY_Z.rawValue),
|
||||
AppleIIKey(label: "x", code: AppleKeyboardKey.KEY_X.rawValue),
|
||||
AppleIIKey(label: "c", code: AppleKeyboardKey.KEY_C.rawValue),
|
||||
AppleIIKey(label: "v", code: AppleKeyboardKey.KEY_V.rawValue),
|
||||
AppleIIKey(label: "b", code: AppleKeyboardKey.KEY_B.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "123", code: AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue, keySize: .standard, imageName: "textformat.123"),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe081)!)", code: AppleKeyboardKey.KEY_APPLE.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "SPACE", code: AppleKeyboardKey.KEY_SPACE.rawValue, keySize: .wide)
|
||||
]
|
||||
],
|
||||
alternateKeys:
|
||||
[
|
||||
[
|
||||
SliderKey(keySize: .standard)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "1", code: AppleKeyboardKey.KEY_1.rawValue),
|
||||
AppleIIKey(label: "2", code: AppleKeyboardKey.KEY_2.rawValue),
|
||||
AppleIIKey(label: "3", code: AppleKeyboardKey.KEY_3.rawValue),
|
||||
AppleIIKey(label: "4", code: AppleKeyboardKey.KEY_4.rawValue),
|
||||
AppleIIKey(label: "5", code: AppleKeyboardKey.KEY_5.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "-", code: AppleKeyboardKey.KEY_MINUS.rawValue),
|
||||
AppleIIKey(label: "=", code: AppleKeyboardKey.KEY_EQUALS.rawValue),
|
||||
AppleIIKey(label: "/", code: AppleKeyboardKey.KEY_FSLASH.rawValue),
|
||||
AppleIIKey(label: "[", code: AppleKeyboardKey.KEY_LEFT_BRACKET.rawValue),
|
||||
AppleIIKey(label: "]", code: AppleKeyboardKey.KEY_RIGHT_BRACKET.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: ";", code: AppleKeyboardKey.KEY_SEMICOLON.rawValue),
|
||||
AppleIIKey(label: "~", code: AppleKeyboardKey.KEY_TILDE.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "ABC", code: AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue, keySize: .standard, imageName: "textformat.abc"),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe081)!)", code: AppleKeyboardKey.KEY_APPLE.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "SPACE", code: AppleKeyboardKey.KEY_SPACE.rawValue, keySize: .wide)
|
||||
]
|
||||
]
|
||||
)
|
||||
@objc let rightKeyboardModel = EmulatorKeyboardViewModel(
|
||||
keys:
|
||||
[
|
||||
[
|
||||
SpacerKey(),
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe144)!)", code: AppleKeyboardKey.KEY_SPECIAL_DEBUGGER.rawValue),
|
||||
AppleIIKey(label: "RESET", code: AppleKeyboardKey.KEY_RESET.rawValue, keySize: .wide)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "y", code: AppleKeyboardKey.KEY_Y.rawValue),
|
||||
AppleIIKey(label: "u", code: AppleKeyboardKey.KEY_U.rawValue),
|
||||
AppleIIKey(label: "i", code: AppleKeyboardKey.KEY_I.rawValue),
|
||||
AppleIIKey(label: "o", code: AppleKeyboardKey.KEY_O.rawValue),
|
||||
AppleIIKey(label: "p", code: AppleKeyboardKey.KEY_P.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "h", code: AppleKeyboardKey.KEY_H.rawValue),
|
||||
AppleIIKey(label: "j", code: AppleKeyboardKey.KEY_J.rawValue),
|
||||
AppleIIKey(label: "k", code: AppleKeyboardKey.KEY_K.rawValue),
|
||||
AppleIIKey(label: "l", code: AppleKeyboardKey.KEY_L.rawValue),
|
||||
AppleIIKey(label: "'", code: AppleKeyboardKey.KEY_SQUOTE.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "n", code: AppleKeyboardKey.KEY_N.rawValue),
|
||||
AppleIIKey(label: "m", code: AppleKeyboardKey.KEY_M.rawValue),
|
||||
AppleIIKey(label: ",", code: AppleKeyboardKey.KEY_COMMA.rawValue),
|
||||
AppleIIKey(label: ".", code: AppleKeyboardKey.KEY_PERIOD.rawValue),
|
||||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe080)!)", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .wide)
|
||||
]
|
||||
],
|
||||
alternateKeys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "6", code: AppleKeyboardKey.KEY_6.rawValue),
|
||||
AppleIIKey(label: "7", code: AppleKeyboardKey.KEY_7.rawValue),
|
||||
AppleIIKey(label: "8", code: AppleKeyboardKey.KEY_8.rawValue),
|
||||
AppleIIKey(label: "9", code: AppleKeyboardKey.KEY_9.rawValue),
|
||||
AppleIIKey(label: "0", code: AppleKeyboardKey.KEY_0.rawValue),
|
||||
],
|
||||
[
|
||||
SpacerKey(),
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe08b)!)", code: AppleKeyboardKey.KEY_UP_CURSOR.rawValue),
|
||||
SpacerKey(),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe088)!)", code: AppleKeyboardKey.KEY_LEFT_CURSOR.rawValue),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe08a)!)", code: AppleKeyboardKey.KEY_DOWN_CURSOR.rawValue),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe095)!)", code: AppleKeyboardKey.KEY_RIGHT_CURSOR.rawValue),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SPACE", code: AppleKeyboardKey.KEY_SPACE.rawValue, keySize: .wide),
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe080)!)", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .wide)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@objc let leftKeyboardModel2 = EmulatorKeyboardViewModel(
|
||||
keys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "esc", code: AppleKeyboardKey.KEY_ESC.rawValue),
|
||||
AppleIIKey(label: "tab", code: AppleKeyboardKey.KEY_TAB.rawValue),
|
||||
AppleIIKey(label: "CTRL", code: AppleKeyboardKey.KEY_CTRL.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "control")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "q", code: AppleKeyboardKey.KEY_Q.rawValue),
|
||||
AppleIIKey(label: "a", code: AppleKeyboardKey.KEY_A.rawValue),
|
||||
AppleIIKey(label: "z", code: AppleKeyboardKey.KEY_Z.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "w", code: AppleKeyboardKey.KEY_W.rawValue),
|
||||
AppleIIKey(label: "s", code: AppleKeyboardKey.KEY_S.rawValue),
|
||||
AppleIIKey(label: "x", code: AppleKeyboardKey.KEY_X.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "e", code: AppleKeyboardKey.KEY_E.rawValue),
|
||||
AppleIIKey(label: "d", code: AppleKeyboardKey.KEY_D.rawValue),
|
||||
AppleIIKey(label: "c", code: AppleKeyboardKey.KEY_C.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "r", code: AppleKeyboardKey.KEY_R.rawValue),
|
||||
AppleIIKey(label: "f", code: AppleKeyboardKey.KEY_F.rawValue),
|
||||
AppleIIKey(label: "v", code: AppleKeyboardKey.KEY_V.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "t", code: AppleKeyboardKey.KEY_T.rawValue),
|
||||
AppleIIKey(label: "g", code: AppleKeyboardKey.KEY_G.rawValue),
|
||||
AppleIIKey(label: "b", code: AppleKeyboardKey.KEY_B.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .wide, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "123", code: AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue, keySize: .standard, imageName: "textformat.123")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SPACE", code: AppleKeyboardKey.KEY_SPACE.rawValue, keySize: .wide),
|
||||
AppleIIKey(label: "", code: AppleKeyboardKey.KEY_APPLE.rawValue,
|
||||
keySize: .standard, isModifier: true)
|
||||
]
|
||||
],
|
||||
alternateKeys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "esc", code: AppleKeyboardKey.KEY_ESC.rawValue),
|
||||
AppleIIKey(label: "tab", code: AppleKeyboardKey.KEY_TAB.rawValue),
|
||||
AppleIIKey(label: "CTRL", code: AppleKeyboardKey.KEY_CTRL.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "control")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "1", code: AppleKeyboardKey.KEY_1.rawValue),
|
||||
AppleIIKey(label: "-", code: AppleKeyboardKey.KEY_MINUS.rawValue),
|
||||
AppleIIKey(label: "]", code: AppleKeyboardKey.KEY_RIGHT_BRACKET.rawValue)
|
||||
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "2", code: AppleKeyboardKey.KEY_2.rawValue),
|
||||
AppleIIKey(label: "=", code: AppleKeyboardKey.KEY_EQUALS.rawValue),
|
||||
AppleIIKey(label: "~", code: AppleKeyboardKey.KEY_TILDE.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "3", code: AppleKeyboardKey.KEY_3.rawValue),
|
||||
AppleIIKey(label: ";", code: AppleKeyboardKey.KEY_SEMICOLON.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "4", code: AppleKeyboardKey.KEY_4.rawValue),
|
||||
AppleIIKey(label: "/", code: AppleKeyboardKey.KEY_FSLASH.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "5", code: AppleKeyboardKey.KEY_5.rawValue),
|
||||
AppleIIKey(label: "[", code: AppleKeyboardKey.KEY_LEFT_BRACKET.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .wide, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "ABC", code: AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue, keySize: .wide, imageName: "textformat.abc"),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SPC", code: AppleKeyboardKey.KEY_SPACE.rawValue),
|
||||
AppleIIKey(label: "OPT", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
keySize: .standard, isModifier: true)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
@objc let rightKeyboardModel2 = EmulatorKeyboardViewModel(
|
||||
keys:
|
||||
[
|
||||
[
|
||||
SpacerKey(),
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "RESET", code: AppleKeyboardKey.KEY_RESET.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "y", code: AppleKeyboardKey.KEY_Y.rawValue),
|
||||
AppleIIKey(label: "h", code: AppleKeyboardKey.KEY_H.rawValue),
|
||||
AppleIIKey(label: "n", code: AppleKeyboardKey.KEY_N.rawValue),
|
||||
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "u", code: AppleKeyboardKey.KEY_U.rawValue),
|
||||
AppleIIKey(label: "j", code: AppleKeyboardKey.KEY_J.rawValue),
|
||||
AppleIIKey(label: "m", code: AppleKeyboardKey.KEY_M.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "i", code: AppleKeyboardKey.KEY_I.rawValue),
|
||||
AppleIIKey(label: "k", code: AppleKeyboardKey.KEY_K.rawValue),
|
||||
AppleIIKey(label: ",", code: AppleKeyboardKey.KEY_COMMA.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "o", code: AppleKeyboardKey.KEY_O.rawValue),
|
||||
AppleIIKey(label: "l", code: AppleKeyboardKey.KEY_L.rawValue),
|
||||
AppleIIKey(label: ".", code: AppleKeyboardKey.KEY_PERIOD.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "p", code: AppleKeyboardKey.KEY_P.rawValue),
|
||||
AppleIIKey(label: ";", code: AppleKeyboardKey.KEY_SEMICOLON.rawValue),
|
||||
AppleIIKey(label: "/", code: AppleKeyboardKey.KEY_FSLASH.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "]", code: AppleKeyboardKey.KEY_RIGHT_BRACKET.rawValue),
|
||||
AppleIIKey(label: "'", code: AppleKeyboardKey.KEY_SQUOTE.rawValue),
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .wide),
|
||||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
]
|
||||
],
|
||||
alternateKeys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "6", code: AppleKeyboardKey.KEY_6.rawValue),
|
||||
SpacerKey(),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "7", code: AppleKeyboardKey.KEY_7.rawValue),
|
||||
SpacerKey(),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "8", code: AppleKeyboardKey.KEY_8.rawValue),
|
||||
SpacerKey(),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "9", code: AppleKeyboardKey.KEY_9.rawValue),
|
||||
SpacerKey(),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "0", code: AppleKeyboardKey.KEY_0.rawValue),
|
||||
AppleIIKey(label: "⬆", code: AppleKeyboardKey.KEY_UP_CURSOR.rawValue),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "⬅", code: AppleKeyboardKey.KEY_LEFT_CURSOR.rawValue),
|
||||
AppleIIKey(label: "⬇", code: AppleKeyboardKey.KEY_DOWN_CURSOR.rawValue),
|
||||
AppleIIKey(label: "➡", code: AppleKeyboardKey.KEY_RIGHT_CURSOR.rawValue)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .standard)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
// setupViewFrames()
|
||||
// setupKeyModels()
|
||||
|
||||
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
|
||||
leftKeyboardView.dragMeView.isUserInteractionEnabled = true
|
||||
leftKeyboardView.dragMeView.addGestureRecognizer(panGesture)
|
||||
let panGestureRightKeyboard = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
|
||||
rightKeyboardView.dragMeView.isUserInteractionEnabled = true
|
||||
rightKeyboardView.dragMeView.addGestureRecognizer(panGestureRightKeyboard)
|
||||
}
|
||||
|
||||
func setupView() {
|
||||
NSLayoutConstraint.deactivate(keyboardConstraints)
|
||||
keyboardConstraints.removeAll()
|
||||
leftKeyboardView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(leftKeyboardView)
|
||||
leftKeyboardView.heightAnchor.constraint(equalToConstant: 270).isActive = true
|
||||
leftKeyboardView.widthAnchor.constraint(equalToConstant: 180).isActive = true
|
||||
keyboardConstraints.append(contentsOf: [
|
||||
leftKeyboardView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
|
||||
leftKeyboardView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
|
||||
])
|
||||
rightKeyboardView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(rightKeyboardView)
|
||||
keyboardConstraints.append(contentsOf: [
|
||||
rightKeyboardView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
|
||||
rightKeyboardView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
|
||||
])
|
||||
rightKeyboardView.heightAnchor.constraint(equalToConstant: 270).isActive = true
|
||||
rightKeyboardView.widthAnchor.constraint(equalToConstant: 180).isActive = true
|
||||
NSLayoutConstraint.activate(keyboardConstraints)
|
||||
}
|
||||
|
||||
func setupViewFrames() {
|
||||
// initial placement on the bottom corners
|
||||
// since we don't know the frame of this view yet until layout time,
|
||||
// assume it's taking the full screen
|
||||
let screenFrame = UIScreen.main.bounds
|
||||
let keyboardHeight: CGFloat = 250.0
|
||||
let keyboardWidth: CGFloat = 180.0
|
||||
let bottomLeftFrame = CGRect(
|
||||
x: 0,
|
||||
y: screenFrame.size.height - 40 - keyboardHeight - 20,
|
||||
width: keyboardWidth, height: keyboardHeight)
|
||||
let bottomRightFrame = CGRect(
|
||||
x: screenFrame.size.width - 20 - keyboardWidth,
|
||||
y:screenFrame.size.height - 40 - keyboardHeight - 20,
|
||||
width: keyboardWidth, height: keyboardHeight
|
||||
)
|
||||
view.addSubview(leftKeyboardView)
|
||||
view.addSubview(rightKeyboardView)
|
||||
leftKeyboardView.frame = bottomLeftFrame
|
||||
rightKeyboardView.frame = bottomRightFrame
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
// get relative positions of frames to size
|
||||
// for v in [leftKeyboardView, rightKeyboardView] {
|
||||
// let xPercent = v.frame.origin.x / view.frame.size.width
|
||||
// let yPercent = v.frame.origin.y / view.frame.size.height
|
||||
// var newX = size.width * xPercent
|
||||
// var newY = size.height * yPercent
|
||||
// // mmm need to check if the views fit within the frame and adjust
|
||||
// if newX + v.bounds.size.width > size.width {
|
||||
// newX = size.width - v.bounds.size.width
|
||||
// } else if newX < 0 {
|
||||
// newX = 0
|
||||
// }
|
||||
// if newY + v.bounds.size.height > size.height {
|
||||
// newY = size.height - v.bounds.size.height
|
||||
// }
|
||||
// let newFrame = CGRect(x: newX, y: newY, width: v.bounds.size.width, height: v.bounds.size.height)
|
||||
// v.frame = newFrame
|
||||
// }
|
||||
}
|
||||
|
||||
func setupKeyModels() {
|
||||
leftKeyboardView.setupWithModel(leftKeyboardModel)
|
||||
rightKeyboardView.setupWithModel(rightKeyboardModel)
|
||||
}
|
||||
|
||||
@objc func draggedView(_ sender:UIPanGestureRecognizer){
|
||||
guard let keyboardView = sender.view?.superview else {
|
||||
return
|
||||
}
|
||||
// NSLayoutConstraint.deactivate(keyboardConstraints)
|
||||
// self.view.bringSubviewToFront(keyboardView)
|
||||
let translation = sender.translation(in: self.view)
|
||||
keyboardView.center = CGPoint(x: keyboardView.center.x + translation.x, y: keyboardView.center.y + translation.y)
|
||||
sender.setTranslation(CGPoint.zero, in: self.view)
|
||||
}
|
||||
}
|
||||
|
||||
extension EmulatorKeyboardController: EmulatorKeyboardViewDelegate {
|
||||
func toggleAlternateKeys() {
|
||||
for keyboard in [leftKeyboardView, rightKeyboardView] {
|
||||
keyboard.toggleKeysStackView()
|
||||
}
|
||||
}
|
||||
func refreshModifierStates() {
|
||||
for keyboard in [leftKeyboardView, rightKeyboardView] {
|
||||
keyboard.refreshModifierStates()
|
||||
}
|
||||
}
|
||||
func updateTransparency(toAlpha alpha: Float) {
|
||||
for keyboard in [leftKeyboardView, rightKeyboardView] {
|
||||
keyboard.alpha = CGFloat(alpha)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// GameControllerKeyRemapController.h
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 4/8/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "KeyMapper.h"
|
||||
|
||||
@interface GameControllerKeyRemapController : UIViewController
|
||||
|
||||
@property (nonatomic, strong) IBOutlet UIView *keyboardContainerView;
|
||||
@property (nonatomic, strong) IBOutlet UIButton *saveButton;
|
||||
@property (nonatomic, strong) IBOutlet UIButton *cancelButton;
|
||||
@property (nonatomic, strong) IBOutlet UIButton *defaultsButton;
|
||||
@property (nonatomic, strong) KeyMapper *keyMapper;
|
||||
|
||||
@property(nonatomic, copy) void (^onDismissal)();
|
||||
|
||||
@end
|
|
@ -0,0 +1,503 @@
|
|||
//
|
||||
// GameControllerKeyRemapController.m
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 4/8/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import <GameController/GameController.h>
|
||||
#import "GameControllerKeyRemapController.h"
|
||||
#import "KeyCapView.h"
|
||||
#import "KeyMapper.h"
|
||||
#import "MfiGameControllerHandler.h"
|
||||
|
||||
|
||||
const CGFloat NUMBER_OF_KEYS_IN_ROW = 15.0f;
|
||||
|
||||
const CGFloat KEYCAP_WIDTH_PCT = 1.0f / NUMBER_OF_KEYS_IN_ROW;
|
||||
|
||||
const CGFloat KEYCAP_HORIZONTAL_PADDING = 2.0f;
|
||||
const CGFloat KEYCAP_VERTICAL_PADDING = 2.0f;
|
||||
const CGFloat KEYCAP_HEIGHT = 40.0f;
|
||||
|
||||
|
||||
// Using a C struct here for really just convenience of typing the definitions out
|
||||
// This gets converted into an obj-c object later
|
||||
struct KeyCap {
|
||||
CGFloat widthMultiplier;
|
||||
const char* key1;
|
||||
int code1;
|
||||
const char* key2;
|
||||
};
|
||||
|
||||
struct KeyCap keyCapDefinitions[] = {
|
||||
{ 1.2,"caps",KEY_CAPS,0 },
|
||||
{ 1.5,"opt",KEY_APPLE,0 },
|
||||
{ 1.0,"",KEY_OPTION,0 },
|
||||
{ 1.0,"`",KEY_TILDE,0 },
|
||||
{ 6.3," ",KEY_SPACE,0 },
|
||||
{ 1.0,"←",KEY_LEFT_CURSOR,0 },
|
||||
{ 1.0,"→",KEY_RIGHT_CURSOR,0 },
|
||||
{ 1.0,"↑",KEY_UP_CURSOR,0 },
|
||||
{ 1.0,"↓",KEY_DOWN_CURSOR,0 },
|
||||
{ -1,0,0,0 },
|
||||
{ 2.5,"shift",KEY_SHIFT,0 },
|
||||
{ 1.0,"Z",KEY_Z,0 },
|
||||
{ 1.0,"X",KEY_X,0 },
|
||||
{ 1.0,"C",KEY_C,0 },
|
||||
{ 1.0,"V",KEY_V,0 },
|
||||
{ 1.0,"B",KEY_B,0 },
|
||||
{ 1.0,"N",KEY_N,0 },
|
||||
{ 1.0,"M",KEY_M,0 },
|
||||
{ 1.0,",",KEY_COMMA,"<" },
|
||||
{ 1.0,".",KEY_PERIOD,">" },
|
||||
{ 1.0,"/",KEY_FSLASH,"?" },
|
||||
{ 2.5,"shift",KEY_SHIFT,0 },
|
||||
{ -1,0,0,0 },
|
||||
{ 2.0,"control",KEY_CTRL,0 },
|
||||
{ 1.0,"A",KEY_A,0 },
|
||||
{ 1.0,"S",KEY_S,0 },
|
||||
{ 1.0,"D",KEY_D,0 },
|
||||
{ 1.0,"F",KEY_F,0 },
|
||||
{ 1.0,"G",KEY_G,0 },
|
||||
{ 1.0,"H",KEY_H,0 },
|
||||
{ 1.0,"J",KEY_J,0 },
|
||||
{ 1.0,"K",KEY_K,0 },
|
||||
{ 1.0,"L",KEY_L,0 },
|
||||
{ 1.0,";",KEY_SEMICOLON,":" },
|
||||
{ 1.0,"'",KEY_SQUOTE,"\""},
|
||||
{ 2.0,"return",KEY_RETURN,0 },
|
||||
{ -1,0,0,0 },
|
||||
{ 2.0,"tab",KEY_TAB,0 },
|
||||
{ 1.0,"Q",KEY_Q,0 },
|
||||
{ 1.0,"W",KEY_W,0 },
|
||||
{ 1.0,"E",KEY_E,0 },
|
||||
{ 1.0,"R",KEY_R,0 },
|
||||
{ 1.0,"T",KEY_T,0 },
|
||||
{ 1.0,"Y",KEY_Y,0 },
|
||||
{ 1.0,"U",KEY_U,0 },
|
||||
{ 1.0,"I",KEY_I,0 },
|
||||
{ 1.0,"O",KEY_O,0 },
|
||||
{ 1.0,"P",KEY_P,0 },
|
||||
{ 1.5,"[",KEY_LEFT_BRACKET,"{" },
|
||||
{ 1.5,"]",KEY_RIGHT_BRACKET,"}" },
|
||||
{ -1,0,0,0 },
|
||||
{ 1.0,"esc",KEY_ESC,0 },
|
||||
{ 1.0,"1",KEY_1,"!" },
|
||||
{ 1.0,"2",KEY_2,"@" },
|
||||
{ 1.0,"3",KEY_3,"#" },
|
||||
{ 1.0,"4",KEY_4,"$" },
|
||||
{ 1.0,"5",KEY_5,"%" },
|
||||
{ 1.0,"6",KEY_6,"^" },
|
||||
{ 1.0,"7",KEY_7,"&" },
|
||||
{ 1.0,"8",KEY_8,"*" },
|
||||
{ 1.0,"9",KEY_9,"(" },
|
||||
{ 1.0,"0",KEY_0,")" },
|
||||
{ 1.0,"-",KEY_MINUS,"_" },
|
||||
{ 1.0,"=",KEY_EQUALS,"+" },
|
||||
{ 2.0,"delete",KEY_DELETE,0 },
|
||||
{ 0,0,0,0 }
|
||||
};
|
||||
|
||||
@interface GameControllerKeyRemapController () <UIAlertViewDelegate,UITextFieldDelegate>
|
||||
@property (nonatomic, strong) NSMutableArray *keyCapViews;
|
||||
@property (nonatomic, strong) UIAlertView *alertView;
|
||||
@property (nonatomic, strong) KeyMapper *keyMapperWorkingCopy;
|
||||
@property (nonatomic, strong) MfiGameControllerHandler *controllerHandler;
|
||||
@property (nonatomic, strong) UITextField *textFieldForICadeInput;
|
||||
@property (nonatomic, strong) NSNumber *currentlyMappingKey;
|
||||
@end
|
||||
|
||||
@implementation GameControllerKeyRemapController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.controllerHandler = [[MfiGameControllerHandler alloc] init];
|
||||
[self.controllerHandler discoverController:^(GCController *gameController) {
|
||||
} disconnectedCallback:^{
|
||||
}];
|
||||
self.keyCapViews = [NSMutableArray array];
|
||||
self.keyMapperWorkingCopy = [self.keyMapper copy];
|
||||
self.saveButton.layer.borderWidth = 1.0f;
|
||||
self.saveButton.layer.borderColor = [self.view.tintColor CGColor];
|
||||
self.cancelButton.layer.borderWidth = 1.0f;
|
||||
self.cancelButton.layer.borderColor = [[UIColor redColor] CGColor];
|
||||
self.defaultsButton.layer.borderWidth = 1.0f;
|
||||
self.defaultsButton.layer.borderColor = [self.view.tintColor CGColor];
|
||||
|
||||
// for detecting icade input
|
||||
self.textFieldForICadeInput = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
self.textFieldForICadeInput.delegate = self;
|
||||
self.textFieldForICadeInput.autocapitalizationType= UITextAutocorrectionTypeNo;
|
||||
|
||||
self.currentlyMappingKey = nil;
|
||||
[self constructKeyboard];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
// convert the above c struct array into an objective c object to make it easier to deal with (for me, personally)
|
||||
-(NSArray*) objc_keyCapsDefinitions {
|
||||
NSMutableArray *keyDefs = [NSMutableArray array];
|
||||
NSMutableArray *keyDefsRow = [NSMutableArray array];
|
||||
int i = 0;
|
||||
while (keyCapDefinitions[i].widthMultiplier) {
|
||||
struct KeyCap keyCap = keyCapDefinitions[i];
|
||||
if ( keyCap.widthMultiplier == -1.0 ) {
|
||||
[keyDefs addObject:[keyDefsRow copy]];
|
||||
[keyDefsRow removeAllObjects];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
[keyDefsRow addObject:@[ [NSNumber numberWithFloat:keyCap.widthMultiplier],
|
||||
[NSString stringWithUTF8String:keyCap.key1],
|
||||
[NSNumber numberWithInt:keyCap.code1],
|
||||
keyCap.key2 != 0 ? [NSString stringWithUTF8String:keyCap.key2] : @""
|
||||
]];
|
||||
i++;
|
||||
}
|
||||
[keyDefs addObject:keyDefsRow];
|
||||
return [keyDefs copy];
|
||||
}
|
||||
|
||||
// Construct the keyboard key views using auto layout constraints entirely
|
||||
// It gets constructed from the bottom up (pinned to the bottom)
|
||||
-(void) constructKeyboard {
|
||||
|
||||
NSArray *keyDefinitions = [self objc_keyCapsDefinitions];
|
||||
UIView *keyboardContainer = self.keyboardContainerView;
|
||||
UIView *lastVerticalView = nil;
|
||||
UIView *lastHorizontalView = nil;
|
||||
NSUInteger keyboardRow = 0;
|
||||
|
||||
for (NSArray *keyDefsRow in keyDefinitions) {
|
||||
|
||||
KeyCapView *keyCapView = nil;
|
||||
|
||||
NSUInteger keyIndex = 0;
|
||||
|
||||
for (NSArray *keyDef in keyDefsRow) {
|
||||
keyCapView = [KeyCapView createViewWithKeyDef:keyDef];
|
||||
keyCapView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
keyCapView.layer.borderWidth = 1.0f;
|
||||
keyCapView.layer.borderColor = [[UIColor blackColor] CGColor];
|
||||
[keyCapView setupWithKeyMapper:self.keyMapperWorkingCopy];
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onKeyTap:)];
|
||||
[keyCapView addGestureRecognizer:tap];
|
||||
[self.keyCapViews addObject:keyCapView];
|
||||
|
||||
CGFloat widthMultiplier = [[keyDef objectAtIndex:KeyCapIndexWidthMultiplier] floatValue] * KEYCAP_WIDTH_PCT;
|
||||
|
||||
NSDictionary *metrics = @{@"height" : @(KEYCAP_HEIGHT), @"padding" : @(KEYCAP_HORIZONTAL_PADDING)};
|
||||
[keyboardContainer addSubview:keyCapView];
|
||||
|
||||
// vertical
|
||||
if ( keyboardRow == 0 ) {
|
||||
NSDictionary *bindings = NSDictionaryOfVariableBindings(keyCapView);
|
||||
// pin to super view bottom
|
||||
[keyboardContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[keyCapView(height)]-0@750-|" options:0 metrics:metrics views:bindings]];
|
||||
} else {
|
||||
NSDictionary *bindings = NSDictionaryOfVariableBindings(keyCapView,lastVerticalView);
|
||||
// pin bottom to last vertical view
|
||||
[keyboardContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[keyCapView(height)]-4@750-[lastVerticalView]" options:0 metrics:metrics views:bindings]];
|
||||
}
|
||||
|
||||
// horizontal
|
||||
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:keyCapView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:keyboardContainer attribute:NSLayoutAttributeWidth multiplier:widthMultiplier constant:KEYCAP_HORIZONTAL_PADDING * -1.0];
|
||||
|
||||
if ( lastHorizontalView == nil ) {
|
||||
NSDictionary *bindings = NSDictionaryOfVariableBindings(keyCapView);
|
||||
[keyboardContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-padding@750-[keyCapView]" options:0 metrics:metrics views:bindings]];
|
||||
} else {
|
||||
NSDictionary *bindings = NSDictionaryOfVariableBindings(keyCapView,lastHorizontalView);
|
||||
if ( keyIndex == (int)NUMBER_OF_KEYS_IN_ROW-1 ) {
|
||||
// last key in row
|
||||
[keyboardContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[lastHorizontalView]-padding@750-[keyCapView]-padding@750-|" options:0 metrics:metrics views:bindings]];
|
||||
} else {
|
||||
[keyboardContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[lastHorizontalView]-padding@750-[keyCapView]" options:0 metrics:metrics views:bindings]];
|
||||
}
|
||||
}
|
||||
if ( keyIndex != (int)NUMBER_OF_KEYS_IN_ROW-1 ) {
|
||||
[keyboardContainer addConstraint:widthConstraint];
|
||||
}
|
||||
|
||||
keyIndex++;
|
||||
lastHorizontalView = keyCapView;
|
||||
}
|
||||
|
||||
lastVerticalView = keyCapView;
|
||||
lastHorizontalView = nil;
|
||||
keyboardRow++;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) refreshAllKeyCapViews {
|
||||
for (KeyCapView *view in self.keyCapViews) {
|
||||
[view setupWithKeyMapper:self.keyMapperWorkingCopy];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) onKeyTap:(UITapGestureRecognizer*)sender {
|
||||
KeyCapView *view = (KeyCapView*) sender.view;
|
||||
self.alertView = [[UIAlertView alloc] initWithTitle:@"Remap Key" message:[NSString stringWithFormat:@"Press a button to map the [%@] key",[view.keyDef objectAtIndex:KeyCapIndexKey]] delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Unbind",nil];
|
||||
[self.alertView show];
|
||||
self.alertView.tag = [[view.keyDef objectAtIndex:KeyCapIndexCode] integerValue];
|
||||
[self startRemappingControlsForMfiControllerForKey:[view.keyDef objectAtIndex:KeyCapIndexCode]];
|
||||
self.alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
|
||||
[self.alertView textFieldAtIndex:0].delegate = self;
|
||||
[self.alertView textFieldAtIndex:0].autocapitalizationType = UITextAutocorrectionTypeNo;
|
||||
// start icade detection
|
||||
self.currentlyMappingKey = [view.keyDef objectAtIndex:KeyCapIndexCode];
|
||||
}
|
||||
|
||||
- (void) startRemappingControlsForMfiControllerForKey:(NSNumber*)keyCode {
|
||||
AppleKeyboardKey keyboardKey = [keyCode intValue];
|
||||
if ( [[GCController controllers] count] == 0 ) {
|
||||
NSLog(@"Could not find any mfi controllers!");
|
||||
return;
|
||||
}
|
||||
GCController *controller = [[GCController controllers] firstObject];
|
||||
|
||||
if ( controller.extendedGamepad ) {
|
||||
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
|
||||
if ( gamepad.buttonA.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_A];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.buttonB.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_B];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.buttonX.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_X];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.buttonY.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_Y];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.leftShoulder.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_LS];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.rightShoulder.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_RS];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.xAxis.value > 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_RIGHT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.xAxis.value < 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_LEFT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.yAxis.value > 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_UP];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.yAxis.value < 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_DOWN];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.rightTrigger.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_RT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.leftTrigger.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_LT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad, GCControllerElement *element) {
|
||||
if ( gamepad.buttonA.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_A];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.buttonB.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_B];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.buttonX.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_X];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.buttonY.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_Y];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];;
|
||||
return;
|
||||
}
|
||||
if ( gamepad.leftShoulder.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_LS];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.rightShoulder.pressed ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_BUTTON_RS];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.xAxis.value > 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_RIGHT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.xAxis.value < 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_LEFT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.yAxis.value > 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_UP];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
if ( gamepad.dpad.yAxis.value < 0.0f ) {
|
||||
[self.keyMapperWorkingCopy mapKey:keyboardKey ToControl:MFI_DPAD_DOWN];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
-(void) stopRemappingControls {
|
||||
if ( [[GCController controllers] count] == 0 ) {
|
||||
return;
|
||||
}
|
||||
GCController *controller = [[GCController controllers] firstObject];
|
||||
if ( controller.extendedGamepad ) {
|
||||
controller.extendedGamepad.valueChangedHandler = nil;
|
||||
} else {
|
||||
controller.gamepad.valueChangedHandler = nil;
|
||||
}
|
||||
self.currentlyMappingKey = nil;
|
||||
}
|
||||
|
||||
-(IBAction)saveButtonTapped:(id)sender {
|
||||
self.keyMapper = [self.keyMapperWorkingCopy copy];
|
||||
[self.keyMapper saveKeyMapping];
|
||||
self.keyMapperWorkingCopy = nil;
|
||||
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{
|
||||
self.onDismissal();
|
||||
}];
|
||||
}
|
||||
|
||||
-(IBAction)cancelButtonTapped:(id)sender {
|
||||
self.keyMapperWorkingCopy = nil;
|
||||
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{
|
||||
self.onDismissal();
|
||||
}];
|
||||
}
|
||||
|
||||
-(IBAction) defaultsButtonTapped:(id)sender {
|
||||
[self.keyMapperWorkingCopy resetToDefaults];
|
||||
[self refreshAllKeyCapViews];
|
||||
}
|
||||
|
||||
#
|
||||
# pragma mark - UIAlertViewDelegate
|
||||
#
|
||||
|
||||
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
|
||||
[self stopRemappingControls];
|
||||
if ( buttonIndex == 1 ) {
|
||||
AppleKeyboardKey mappedKey = alertView.tag;
|
||||
[self.keyMapperWorkingCopy unmapKey:mappedKey];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {
|
||||
[self stopRemappingControls];
|
||||
[self refreshAllKeyCapViews];
|
||||
}
|
||||
|
||||
#
|
||||
# pragma mark - UITextFieldDelegate
|
||||
#
|
||||
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
|
||||
if ( self.currentlyMappingKey == nil ) {
|
||||
return NO;
|
||||
}
|
||||
const char* s = [string UTF8String];
|
||||
char c;
|
||||
int i=0;
|
||||
while( (c = s[i++]) != 0)
|
||||
{
|
||||
switch(c) {
|
||||
case 'e': // up released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_DPAD_UP];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'z': // down released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_DPAD_DOWN];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'q': // left released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_DPAD_LEFT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'c': // right released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_DPAD_RIGHT];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 't': // button 1 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_1];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'r': // button 2 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_2];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'f': // button 3 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_3];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'n': // button 4 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_4];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'm': // button 5 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_5];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'p': // button 6 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_6];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'g': // button 7 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_7];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
case 'v': // button 8 released
|
||||
[self.keyMapperWorkingCopy mapKey:self.currentlyMappingKey.integerValue ToControl:ICADE_BUTTON_8];
|
||||
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10116" systemVersion="15A284" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="GameControllerKeyRemapController">
|
||||
<connections>
|
||||
<outlet property="cancelButton" destination="zAx-JO-gDf" id="i1M-NV-0UN"/>
|
||||
<outlet property="defaultsButton" destination="Ih9-iG-ECT" id="Ho3-g5-rOQ"/>
|
||||
<outlet property="keyboardContainerView" destination="PbL-95-ilz" id="UPI-70-2J7"/>
|
||||
<outlet property="saveButton" destination="Ihv-aC-sb7" id="Byt-Gy-MVK"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PbL-95-ilz" userLabel="Keyboard Container View">
|
||||
<rect key="frame" x="0.0" y="380" width="600" height="220"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="220" id="QNq-rB-Zxd"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ihv-aC-sb7">
|
||||
<rect key="frame" x="88.5" y="158" width="107" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="107" id="82d-Qy-Xfg"/>
|
||||
<constraint firstAttribute="height" constant="44" id="bOc-8m-OXA"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Save"/>
|
||||
<connections>
|
||||
<action selector="saveButtonTapped:" destination="-1" eventType="touchUpInside" id="dbc-hJ-1z4"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zAx-JO-gDf">
|
||||
<rect key="frame" x="404.5" y="158" width="107" height="44"/>
|
||||
<state key="normal" title="Cancel">
|
||||
<color key="titleColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="cancelButtonTapped:" destination="-1" eventType="touchUpInside" id="byq-hr-jDY"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Remap Keys to Gamepad Buttons" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="x2H-qm-EGT">
|
||||
<rect key="frame" x="172" y="64" width="256" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ih9-iG-ECT">
|
||||
<rect key="frame" x="247" y="158" width="107" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="107" id="8aa-Yx-81a"/>
|
||||
<constraint firstAttribute="height" constant="44" id="qBN-1t-J0B"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Defaults"/>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="8aa-Yx-81a"/>
|
||||
<exclude reference="qBN-1t-J0B"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<connections>
|
||||
<action selector="defaultsButtonTapped:" destination="-1" eventType="touchUpInside" id="reT-vk-ZW4"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="x2H-qm-EGT" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" multiplier="0.25" id="3zs-qM-hjM"/>
|
||||
<constraint firstItem="zAx-JO-gDf" firstAttribute="top" secondItem="Ihv-aC-sb7" secondAttribute="top" id="6TO-tV-bng"/>
|
||||
<constraint firstItem="PbL-95-ilz" firstAttribute="height" secondItem="i5M-Pr-FkT" secondAttribute="height" multiplier="0.4" id="7Tq-sc-r5F"/>
|
||||
<constraint firstItem="PbL-95-ilz" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="ALZ-jN-EW8"/>
|
||||
<constraint firstItem="zAx-JO-gDf" firstAttribute="height" secondItem="Ihv-aC-sb7" secondAttribute="height" id="HFd-QQ-Vpx"/>
|
||||
<constraint firstItem="zAx-JO-gDf" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" multiplier="1.5" constant="8" id="Kdj-w0-y90"/>
|
||||
<constraint firstItem="Ih9-iG-ECT" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="Mgp-p2-fmB"/>
|
||||
<constraint firstItem="Ih9-iG-ECT" firstAttribute="width" secondItem="Ihv-aC-sb7" secondAttribute="width" id="R6y-RO-5t1"/>
|
||||
<constraint firstItem="Ih9-iG-ECT" firstAttribute="top" secondItem="Ihv-aC-sb7" secondAttribute="top" id="XbU-xP-HYw"/>
|
||||
<constraint firstItem="x2H-qm-EGT" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="ZTO-dh-bwz"/>
|
||||
<constraint firstItem="zAx-JO-gDf" firstAttribute="centerX" secondItem="Ihv-aC-sb7" secondAttribute="centerX" multiplier="2" id="hJc-6p-crb"/>
|
||||
<constraint firstAttribute="bottom" secondItem="PbL-95-ilz" secondAttribute="bottom" id="kCc-QA-uYA"/>
|
||||
<constraint firstItem="zAx-JO-gDf" firstAttribute="width" secondItem="Ihv-aC-sb7" secondAttribute="width" id="mPI-1j-gTO"/>
|
||||
<constraint firstItem="Ihv-aC-sb7" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" multiplier="0.5" constant="-8" id="niB-eU-bnx"/>
|
||||
<constraint firstItem="Ihv-aC-sb7" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" multiplier="0.6" id="vLe-Gk-22l"/>
|
||||
<constraint firstAttribute="trailing" secondItem="PbL-95-ilz" secondAttribute="trailing" id="zbm-Dr-7Jd"/>
|
||||
<constraint firstItem="Ih9-iG-ECT" firstAttribute="height" secondItem="Ihv-aC-sb7" secondAttribute="height" id="zd1-zF-ZZI"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="7Tq-sc-r5F"/>
|
||||
<exclude reference="hJc-6p-crb"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -22,6 +22,8 @@
|
|||
#include "../kegs/Src/SaveState.h"
|
||||
#include "../Common/ActiveDownload.h"
|
||||
#import "MfiGameControllerHandler.h"
|
||||
#import "GameControllerKeyRemapController.h"
|
||||
#import "ActiveGS-Swift.h"
|
||||
|
||||
#ifdef ACTIVEGS_CUSTOMKEYS
|
||||
#include "UICustomKey.h"
|
||||
|
@ -281,6 +283,7 @@ int isHardwareKeyboard()
|
|||
UISegmentedControl *saveStateSegmentedControl;
|
||||
}
|
||||
@property (nonatomic,strong) MfiGameControllerHandler *mfiControllerHandler;
|
||||
@property (nonatomic,strong) KeyMapper *keyMapper;
|
||||
@end
|
||||
|
||||
@implementation KBDController
|
||||
|
@ -333,7 +336,6 @@ int isHardwareKeyboard()
|
|||
|
||||
extern int findCode(const char* _s);
|
||||
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
|
||||
|
@ -455,7 +457,7 @@ extern int findCode(const char* _s);
|
|||
self.debugIndicator.hidden = TRUE;
|
||||
self.debugIndicator.backgroundColor = [UIColor lightGrayColor];
|
||||
self.debugIndicator.font = [UIFont systemFontOfSize:(CGFloat)12.0];
|
||||
self.debugIndicator.lineBreakMode=UILineBreakModeClip;
|
||||
self.debugIndicator.lineBreakMode=NSLineBreakByClipping;
|
||||
[self.interfaceView addSubview:self.debugIndicator];
|
||||
|
||||
[self showDebug:FALSE];
|
||||
|
@ -568,6 +570,9 @@ extern int findCode(const char* _s);
|
|||
[self setInputMode:INPUTMODE_ACCESS+INPUTMODE_HIDDEN];
|
||||
[self setMenuBarVisibility:TRUE]; // So First time users are not lost!
|
||||
|
||||
self.keyMapper = [[KeyMapper alloc] init];
|
||||
[self.keyMapper loadFromDefaults];
|
||||
|
||||
self.mfiControllerHandler = [[MfiGameControllerHandler alloc] init];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.mfiControllerHandler discoverController:^(GCController *gameController) {
|
||||
|
@ -578,8 +583,8 @@ extern int findCode(const char* _s);
|
|||
[pManager setNotificationText:@"mFi Controller Disconnected"];
|
||||
}];
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*)keyCommands
|
||||
{
|
||||
if (lastArrowKey != 0)
|
||||
|
@ -645,8 +650,15 @@ extern int findCode(const char* _s);
|
|||
joyX = xvalue;
|
||||
joyY = yvalue * -1.0;
|
||||
};
|
||||
|
||||
GCControllerButtonInput *buttonX = controller.extendedGamepad ? controller.extendedGamepad.buttonX : controller.gamepad.buttonX;
|
||||
GCControllerButtonInput *buttonA = controller.extendedGamepad ? controller.extendedGamepad.buttonA : controller.gamepad.buttonA;
|
||||
GCControllerButtonInput *buttonY = controller.extendedGamepad ? controller.extendedGamepad.buttonY : controller.gamepad.buttonY;
|
||||
GCControllerButtonInput *buttonB = controller.extendedGamepad ? controller.extendedGamepad.buttonB : controller.gamepad.buttonB;
|
||||
GCControllerButtonInput *buttonRS = controller.extendedGamepad ? controller.extendedGamepad.rightShoulder : controller.gamepad.rightShoulder;
|
||||
GCControllerButtonInput *buttonLS = controller.extendedGamepad ? controller.extendedGamepad.leftShoulder : controller.gamepad.leftShoulder;
|
||||
GCControllerButtonInput *buttonRT = controller.extendedGamepad ? controller.extendedGamepad.rightTrigger : nil;
|
||||
GCControllerButtonInput *buttonLT = controller.extendedGamepad ? controller.extendedGamepad.leftTrigger : nil;
|
||||
GCControllerDirectionPad *dpad = controller.extendedGamepad ? controller.extendedGamepad.dpad : controller.gamepad.dpad;
|
||||
|
||||
buttonX.valueChangedHandler = appleJoyButton0Handler;
|
||||
|
@ -656,6 +668,132 @@ extern int findCode(const char* _s);
|
|||
if ( controller.extendedGamepad ) {
|
||||
controller.extendedGamepad.leftThumbstick.valueChangedHandler = appleJoystickhHandler;
|
||||
}
|
||||
|
||||
//
|
||||
// mapped keys
|
||||
AppleKeyboardKey mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_X];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
buttonX.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_Y];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
buttonY.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_A];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
buttonA.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_B];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
buttonB.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_LS];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
buttonLS.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_RS];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
buttonRS.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_LT];
|
||||
if ( mappedKey != NSNotFound && buttonLT != nil ) {
|
||||
buttonLT.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:MFI_BUTTON_RT];
|
||||
if ( mappedKey != NSNotFound && buttonLT != nil ) {
|
||||
buttonRT.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
if ( pressed ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
} else {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
AppleKeyboardKey mappedKeyDpadUp = [self.keyMapper getMappedKeyForControl:MFI_DPAD_UP];
|
||||
AppleKeyboardKey mappedKeyDpadDown = [self.keyMapper getMappedKeyForControl:MFI_DPAD_DOWN];
|
||||
AppleKeyboardKey mappedKeyDpadLeft = [self.keyMapper getMappedKeyForControl:MFI_DPAD_LEFT];
|
||||
AppleKeyboardKey mappedKeyDpadRight = [self.keyMapper getMappedKeyForControl:MFI_DPAD_RIGHT];
|
||||
if ( mappedKeyDpadUp != NSNotFound || mappedKeyDpadDown != NSNotFound || mappedKeyDpadLeft != NSNotFound || mappedKeyDpadRight != NSNotFound ) {
|
||||
dpad.valueChangedHandler = ^(GCControllerDirectionPad *, float xvalue, float yvalue) {
|
||||
if ( mappedKeyDpadUp != NSNotFound && yvalue > 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadUp, 0);
|
||||
} else if ( mappedKeyDpadUp != NSNotFound && yvalue <= 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadUp, 1);
|
||||
}
|
||||
|
||||
if ( mappedKeyDpadDown != NSNotFound && yvalue < 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadDown, 0);
|
||||
} else if ( mappedKeyDpadDown != NSNotFound && yvalue >= 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadDown, 1);
|
||||
}
|
||||
|
||||
if ( mappedKeyDpadRight != NSNotFound && xvalue > 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadRight, 0);
|
||||
} else if ( mappedKeyDpadRight != NSNotFound && xvalue <= 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadRight, 1);
|
||||
}
|
||||
|
||||
if ( mappedKeyDpadLeft != NSNotFound && xvalue < 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadLeft, 0);
|
||||
} else if ( mappedKeyDpadLeft != NSNotFound && xvalue >= 0.0 ) {
|
||||
add_event_key((int)mappedKeyDpadLeft, 1);
|
||||
}
|
||||
|
||||
// pass the joystick input through
|
||||
joyX = xvalue;
|
||||
joyY = yvalue * -1.0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
int hardwarekeyboard= 0;
|
||||
|
@ -1012,6 +1150,25 @@ extern int x_frame_rate ;
|
|||
[pManager setNotificationText:[NSString stringWithFormat:@"Loaded State #%@",segIndex]];
|
||||
}
|
||||
|
||||
-(void) remapControlsButtonPressed:(id)sender {
|
||||
r_sim65816.pause();
|
||||
GameControllerKeyRemapController *remapController = [[GameControllerKeyRemapController alloc] initWithNibName:@"GameControllerKeyRemapController" bundle:nil];
|
||||
remapController.keyMapper = self.keyMapper;
|
||||
remapController.onDismissal = ^{
|
||||
[self.keyMapper loadFromDefaults];
|
||||
[self setupMfiController:[[GCController controllers] firstObject]];
|
||||
r_sim65816.resume();
|
||||
};
|
||||
[self presentViewController:remapController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
-(void) memoryDebuggerButtonPressed:(id)sender {
|
||||
r_sim65816.pause();
|
||||
DebugMemoryViewController *controller = [pManager getEmulatorView].debugMemoryViewController;
|
||||
controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
[self presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
|
||||
//
|
||||
-(void)addRuntimeControls
|
||||
{
|
||||
|
@ -1111,6 +1268,42 @@ extern int x_frame_rate ;
|
|||
[self.runtimeControlsOptions addSubview:loadStateButton];
|
||||
|
||||
l+=LINEHEIGHT;
|
||||
l += 2.0;
|
||||
|
||||
UILabel* remapControlsLabel = [[UILabel alloc] initWithFrame:CGRectMake(OPTIONMARGIN,l,OPTIONWIDTH,LINEHEIGHT)];
|
||||
remapControlsLabel.text = @"Key Bindings";
|
||||
remapControlsLabel.textAlignment = NSTextAlignmentCenter;
|
||||
remapControlsLabel.font = [UIFont systemFontOfSize:12*res];
|
||||
remapControlsLabel.backgroundColor = [UIColor clearColor];
|
||||
[self.runtimeControlsOptions addSubview:remapControlsLabel];
|
||||
l += LINEHEIGHT;
|
||||
|
||||
l += 2.0;
|
||||
UIButton *remapControlsButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
remapControlsButton.frame = CGRectMake(OPTIONMARGIN,l,OPTIONWIDTH,LINEHEIGHT);
|
||||
[remapControlsButton setTitle:@"Remap Controls" forState:UIControlStateNormal];
|
||||
remapControlsButton.titleLabel.font = [UIFont systemFontOfSize:12*res];
|
||||
[remapControlsButton setTitleColor:self.view.tintColor forState:UIControlStateNormal];
|
||||
remapControlsButton.backgroundColor = [UIColor clearColor];
|
||||
remapControlsButton.layer.borderWidth = 1.0f;
|
||||
remapControlsButton.layer.borderColor = [self.view.tintColor CGColor];
|
||||
[remapControlsButton addTarget:self action:@selector(remapControlsButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.runtimeControlsOptions addSubview:remapControlsButton];
|
||||
|
||||
l += LINEHEIGHT;
|
||||
|
||||
l += 2.0;
|
||||
UIButton *memoryDebuggerButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
memoryDebuggerButton.frame = CGRectMake(OPTIONMARGIN,l,OPTIONWIDTH,LINEHEIGHT);
|
||||
[memoryDebuggerButton setTitle:@"Memory Debugger" forState:UIControlStateNormal];
|
||||
memoryDebuggerButton.titleLabel.font = [UIFont systemFontOfSize:12*res];
|
||||
memoryDebuggerButton.backgroundColor = [UIColor clearColor];
|
||||
memoryDebuggerButton.layer.borderWidth = 1.0f;
|
||||
memoryDebuggerButton.layer.borderColor = [self.view.tintColor CGColor];
|
||||
[memoryDebuggerButton addTarget:self action:@selector(memoryDebuggerButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.runtimeControlsOptions addSubview:memoryDebuggerButton];
|
||||
|
||||
l += LINEHEIGHT;
|
||||
nbs++;
|
||||
|
||||
float w = OPTIONWIDTH+OPTIONMARGIN*2;
|
||||
|
@ -2087,7 +2280,7 @@ int x_adb_get_keypad_y()
|
|||
else
|
||||
if (!bForceOnScreenKeyboard)
|
||||
{
|
||||
|
||||
AppleKeyboardKey mappedKey = NSNotFound;
|
||||
char c;
|
||||
int i=0;
|
||||
while( (c = s[i++]) != 0)
|
||||
|
@ -2096,44 +2289,165 @@ int x_adb_get_keypad_y()
|
|||
{
|
||||
case 'w': // up
|
||||
keypad_y = -32767;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_UP];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'e': // !verti
|
||||
case 'z':
|
||||
case 'e': // !verti : up released
|
||||
keypad_y = 0;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_UP];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'z': // down released
|
||||
keypad_y = 0;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_DOWN];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'x': // down
|
||||
keypad_y = 32767;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_DOWN];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'a': // left
|
||||
keypad_x = -32767;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_LEFT];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'q': // !hori
|
||||
case 'c':
|
||||
case 'q': // left released
|
||||
keypad_x = 0;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_LEFT];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
case 'c': // right released
|
||||
keypad_x = 0;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_RIGHT];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'd': // right
|
||||
keypad_x = 32767;
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_DPAD_RIGHT];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'y': // button 1 pressed
|
||||
add_event_key(0x37, 0);
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_1];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 't': // button 1 depressed
|
||||
add_event_key(0x37, 1);
|
||||
// add_event_key(0x37, 1);
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_1];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'h': // button 2 pressed
|
||||
add_event_key(0x3a, 0);
|
||||
// add_event_key(0x3a, 0);
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_2];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'r': // button 2 depressed
|
||||
add_event_key(0x3a, 1);
|
||||
// add_event_key(0x3a, 1);
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_2];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'g': //à faire sur un keydepressed
|
||||
// display keyboard
|
||||
printf("*** forcing on-screeen keyboard");
|
||||
[self OnScreenKeyboard:TRUE];
|
||||
case 'u': // button 3 pressed
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_3];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'f': // button 3 released
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_3];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'j': // button 4 pressed
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_4];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'n': // button 4 released
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_4];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'i': // button 5 pressed
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_5];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'm': // button 5 released
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_5];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'k': // button 6 pressed
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_5];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'p': // button 6 released
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_5];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_6];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'g': //à faire sur un keydepressed : coin
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_6];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
} else {
|
||||
// display keyboard
|
||||
printf("*** forcing on-screeen keyboard");
|
||||
[self OnScreenKeyboard:TRUE];
|
||||
}
|
||||
break;
|
||||
case 'v': //à faire sur un keydepressed
|
||||
// toggle la menu bar
|
||||
[self setMenuBarVisibility:!bMenuBarVisibility];
|
||||
case 'l':
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_7];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 0);
|
||||
}
|
||||
break;
|
||||
case 'v': //à faire sur un keydepressed : start
|
||||
mappedKey = [self.keyMapper getMappedKeyForControl:ICADE_BUTTON_7];
|
||||
if ( mappedKey != NSNotFound ) {
|
||||
add_event_key((int)mappedKey, 1);
|
||||
} else {
|
||||
// toggle la menu bar
|
||||
[self setMenuBarVisibility:!bMenuBarVisibility];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// KeyCapView.h
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 4/9/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "KeyMapper.h"
|
||||
|
||||
@interface KeyCapView : UIView
|
||||
|
||||
@property (nonatomic, strong) IBOutlet UILabel *keyLabel;
|
||||
@property (nonatomic, strong) IBOutlet UILabel *keyLabelAlt;
|
||||
@property (nonatomic, strong) IBOutlet UILabel *mappedButtonLabel;
|
||||
@property (nonatomic, strong) NSArray *keyDef;
|
||||
|
||||
+ (instancetype)createViewWithKeyDef:(NSArray*)keyDef;
|
||||
- (void)setupWithKeyMapper:(KeyMapper*)keyMapper;
|
||||
|
||||
@end
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// KeyCapView.m
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 4/9/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import "KeyCapView.h"
|
||||
|
||||
@implementation KeyCapView
|
||||
|
||||
+ (instancetype)createViewWithKeyDef:(NSArray*)keyDef
|
||||
{
|
||||
KeyCapView *keyCapView = [[[UINib nibWithNibName:@"KeyCapView" bundle:nil] instantiateWithOwner:nil options:nil] lastObject];
|
||||
|
||||
if ([keyCapView isKindOfClass:[KeyCapView class]]) {
|
||||
keyCapView.keyDef = keyDef;
|
||||
return keyCapView;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupWithKeyMapper:(KeyMapper*)keyMapper {
|
||||
if ( ![[self.keyDef objectAtIndex:KeyCapIndexShiftedKey] isEqualToString:@""] ) {
|
||||
self.keyLabel.text = [self.keyDef objectAtIndex:KeyCapIndexShiftedKey];
|
||||
self.keyLabelAlt.text = [self.keyDef objectAtIndex:KeyCapIndexKey];
|
||||
} else {
|
||||
self.keyLabel.text = [self.keyDef objectAtIndex:KeyCapIndexKey];
|
||||
self.keyLabelAlt.text = @"";
|
||||
}
|
||||
self.mappedButtonLabel.text = @"";
|
||||
NSArray *mappedButtons = [keyMapper getControlsForMappedKey:[[self.keyDef objectAtIndex:KeyCapIndexCode] integerValue]];
|
||||
if ( mappedButtons.count > 0 ) {
|
||||
NSMutableString *displayText = [NSMutableString string];
|
||||
int index = 0;
|
||||
for (NSNumber *button in mappedButtons) {
|
||||
if ( index++ > 0 ) {
|
||||
[displayText appendString:@","];
|
||||
}
|
||||
[displayText appendString:[NSString stringWithFormat:@"%@",[KeyMapper controlToDisplayName:button.integerValue]]];
|
||||
}
|
||||
self.mappedButtonLabel.text = displayText;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// KeyMapper.h
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 4/9/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, AppleKeyboardKey) {
|
||||
KEY_CAPS = 0x39,
|
||||
KEY_OPTION = 0x3A,
|
||||
KEY_APPLE = 0x37,
|
||||
KEY_TILDE = 0x12,
|
||||
KEY_SPACE = 0x31,
|
||||
KEY_RIGHT_CURSOR = 0x3C,
|
||||
KEY_LEFT_CURSOR = 0x3B,
|
||||
KEY_UP_CURSOR = 0x3E,
|
||||
KEY_DOWN_CURSOR = 0x3D,
|
||||
KEY_SHIFT = 0x38,
|
||||
KEY_Z = 0x06,
|
||||
KEY_X = 0x07,
|
||||
KEY_C = 0x08,
|
||||
KEY_V = 0x09,
|
||||
KEY_B = 0x0B,
|
||||
KEY_N = 0x2D,
|
||||
KEY_M = 0x2E,
|
||||
KEY_COMMA = 0x2B,
|
||||
KEY_PERIOD = 0x2F,
|
||||
KEY_FSLASH = 0x2C,
|
||||
KEY_CTRL = 0x36,
|
||||
KEY_A = 0x00,
|
||||
KEY_S = 0x01,
|
||||
KEY_D = 0x02,
|
||||
KEY_F = 0x03,
|
||||
KEY_G = 0x05,
|
||||
KEY_H = 0x04,
|
||||
KEY_J = 0x26,
|
||||
KEY_K = 0x28,
|
||||
KEY_L = 0x25,
|
||||
KEY_SEMICOLON = 0x29,
|
||||
KEY_SQUOTE = 0x27,
|
||||
KEY_RETURN = 0x24,
|
||||
KEY_TAB = 0x30,
|
||||
KEY_Q = 0x0C,
|
||||
KEY_W = 0x0D,
|
||||
KEY_E = 0x0E,
|
||||
KEY_R = 0x0F,
|
||||
KEY_T = 0x11,
|
||||
KEY_Y = 0x10,
|
||||
KEY_U = 0x20,
|
||||
KEY_I = 0x22,
|
||||
KEY_O = 0x1F,
|
||||
KEY_P = 0x23,
|
||||
KEY_LEFT_BRACKET = 0x21,
|
||||
KEY_RIGHT_BRACKET = 0x1E,
|
||||
KEY_ESC = 0x35,
|
||||
KEY_1 = 0x12,
|
||||
KEY_2 = 0x13,
|
||||
KEY_3 = 0x14,
|
||||
KEY_4 = 0x15,
|
||||
KEY_5 = 0x17,
|
||||
KEY_6 = 0x16,
|
||||
KEY_7 = 0x1A,
|
||||
KEY_8 = 0x1C,
|
||||
KEY_9 = 0x19,
|
||||
KEY_0 = 0x1D,
|
||||
KEY_MINUS = 0x1B,
|
||||
KEY_EQUALS = 0x18,
|
||||
KEY_DELETE = 0x33,
|
||||
KEY_RESET = 0x7F,
|
||||
KEY_SPECIAL_TOGGLE = 0x50,
|
||||
KEY_SPECIAL_SPACER = 0x51,
|
||||
KEY_SPECIAL_DEBUGGER = 0x52
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, KeyMapMappableButton) {
|
||||
MFI_BUTTON_X,
|
||||
MFI_BUTTON_A,
|
||||
MFI_BUTTON_B,
|
||||
MFI_BUTTON_Y,
|
||||
MFI_BUTTON_LT,
|
||||
MFI_BUTTON_RT,
|
||||
MFI_BUTTON_LS,
|
||||
MFI_BUTTON_RS,
|
||||
MFI_DPAD_UP,
|
||||
MFI_DPAD_DOWN,
|
||||
MFI_DPAD_LEFT,
|
||||
MFI_DPAD_RIGHT,
|
||||
ICADE_BUTTON_1,
|
||||
ICADE_BUTTON_2,
|
||||
ICADE_BUTTON_3,
|
||||
ICADE_BUTTON_4,
|
||||
ICADE_BUTTON_5,
|
||||
ICADE_BUTTON_6,
|
||||
ICADE_BUTTON_7,
|
||||
ICADE_BUTTON_8,
|
||||
ICADE_DPAD_UP,
|
||||
ICADE_DPAD_DOWN,
|
||||
ICADE_DPAD_LEFT,
|
||||
ICADE_DPAD_RIGHT
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, KeyCapIndex) {
|
||||
KeyCapIndexWidthMultiplier = 0,
|
||||
KeyCapIndexKey = 1,
|
||||
KeyCapIndexCode = 2,
|
||||
KeyCapIndexShiftedKey = 3
|
||||
};
|
||||
|
||||
@interface KeyMapper : NSObject<NSCopying>
|
||||
|
||||
-(void)loadFromDefaults;
|
||||
-(void) resetToDefaults;
|
||||
-(void) saveKeyMapping;
|
||||
-(void) mapKey:(AppleKeyboardKey)keyboardKey ToControl:(KeyMapMappableButton)button;
|
||||
-(void) unmapKey:(AppleKeyboardKey)keyboardKey;
|
||||
-(AppleKeyboardKey) getMappedKeyForControl:(KeyMapMappableButton)button;
|
||||
+(NSString*) controlToDisplayName:(KeyMapMappableButton)button;
|
||||
-(NSArray*) getControlsForMappedKey:(AppleKeyboardKey) keyboardKey;
|
||||
|
||||
@end
|
|
@ -0,0 +1,164 @@
|
|||
//
|
||||
// KeyMapper.m
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 4/9/16.
|
||||
//
|
||||
//
|
||||
|
||||
#import "KeyMapper.h"
|
||||
|
||||
@interface KeyMapper()
|
||||
@property (nonatomic, strong) NSMutableDictionary *keyMapping;
|
||||
@end
|
||||
|
||||
@implementation KeyMapper
|
||||
|
||||
-(void)loadFromDefaults {
|
||||
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"keyMapping"];
|
||||
if ( data == nil || ![data isKindOfClass:[NSData class]] ) {
|
||||
self.keyMapping = [self defaultMapping];
|
||||
} else {
|
||||
NSDictionary *fetchedDict = [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
self.keyMapping = [fetchedDict mutableCopy];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
KeyMapper *copy = [[[self class] alloc] init];
|
||||
copy.keyMapping = [self.keyMapping mutableCopy];
|
||||
return copy;
|
||||
}
|
||||
|
||||
-(NSMutableDictionary*) defaultMapping {
|
||||
return [@{ [NSNumber numberWithInteger:MFI_BUTTON_X] : [NSNumber numberWithInteger:KEY_OPTION],
|
||||
[NSNumber numberWithInteger:MFI_BUTTON_A] : [NSNumber numberWithInteger:KEY_APPLE],
|
||||
[NSNumber numberWithInteger:ICADE_BUTTON_1] : [NSNumber numberWithInteger:KEY_OPTION],
|
||||
[NSNumber numberWithInteger:ICADE_BUTTON_2] : [NSNumber numberWithInteger:KEY_APPLE]
|
||||
} mutableCopy];
|
||||
}
|
||||
|
||||
-(void) resetToDefaults {
|
||||
self.keyMapping = [self defaultMapping];
|
||||
}
|
||||
|
||||
-(void) saveKeyMapping {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:self.keyMapping] forKey:@"keyMapping"];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
-(void) mapKey:(AppleKeyboardKey)keyboardKey ToControl:(KeyMapMappableButton)button {
|
||||
NSNumber *buttonKey = [NSNumber numberWithInteger:button];
|
||||
[self.keyMapping setObject:[NSNumber numberWithInteger:keyboardKey] forKey:buttonKey];
|
||||
}
|
||||
|
||||
-(void) unmapKey:(AppleKeyboardKey)keyboardKey {
|
||||
NSArray *mappedButtons = [self getControlsForMappedKey:keyboardKey];
|
||||
for (NSNumber *button in mappedButtons) {
|
||||
[self.keyMapping removeObjectForKey:button];
|
||||
}
|
||||
}
|
||||
|
||||
-(AppleKeyboardKey) getMappedKeyForControl:(KeyMapMappableButton)button {
|
||||
NSNumber *buttonKey = [NSNumber numberWithInteger:button];
|
||||
NSNumber *mappedKey = [self.keyMapping objectForKey:buttonKey];
|
||||
if ( mappedKey != nil ) {
|
||||
return [mappedKey intValue];
|
||||
} else {
|
||||
return NSNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
-(NSArray*) getControlsForMappedKey:(AppleKeyboardKey) keyboardKey {
|
||||
NSMutableArray *foundControls = [NSMutableArray array];
|
||||
for (NSNumber *buttonKey in self.keyMapping) {
|
||||
NSNumber *mappedKey = [self.keyMapping objectForKey:buttonKey];
|
||||
if ( mappedKey != nil && [mappedKey integerValue] == keyboardKey ) {
|
||||
[foundControls addObject:buttonKey];
|
||||
}
|
||||
}
|
||||
return foundControls;
|
||||
}
|
||||
|
||||
+(NSString*) controlToDisplayName:(KeyMapMappableButton)button {
|
||||
switch (button) {
|
||||
case MFI_BUTTON_A:
|
||||
return @"A";
|
||||
break;
|
||||
case MFI_BUTTON_B:
|
||||
return @"B";
|
||||
break;
|
||||
case MFI_BUTTON_X:
|
||||
return @"X";
|
||||
break;
|
||||
case MFI_BUTTON_Y:
|
||||
return @"Y";
|
||||
break;
|
||||
case MFI_BUTTON_LS:
|
||||
return @"LS";
|
||||
break;
|
||||
case MFI_BUTTON_LT:
|
||||
return @"LT";
|
||||
break;
|
||||
case MFI_BUTTON_RS:
|
||||
return @"RS";
|
||||
break;
|
||||
case MFI_BUTTON_RT:
|
||||
return @"RT";
|
||||
break;
|
||||
case MFI_DPAD_UP:
|
||||
return @"⬆️";
|
||||
break;
|
||||
case MFI_DPAD_DOWN:
|
||||
return @"⬇️";
|
||||
break;
|
||||
case MFI_DPAD_LEFT:
|
||||
return @"⬅️";
|
||||
break;
|
||||
case MFI_DPAD_RIGHT:
|
||||
return @"➡️";
|
||||
break;
|
||||
case ICADE_BUTTON_1:
|
||||
return @"i1";
|
||||
break;
|
||||
case ICADE_BUTTON_2:
|
||||
return @"i2";
|
||||
break;
|
||||
case ICADE_BUTTON_3:
|
||||
return @"i3";
|
||||
break;
|
||||
case ICADE_BUTTON_4:
|
||||
return @"i4";
|
||||
break;
|
||||
case ICADE_BUTTON_5:
|
||||
return @"i5";
|
||||
break;
|
||||
case ICADE_BUTTON_6:
|
||||
return @"i6";
|
||||
break;
|
||||
case ICADE_BUTTON_7:
|
||||
return @"i7";
|
||||
break;
|
||||
case ICADE_BUTTON_8:
|
||||
return @"i8";
|
||||
break;
|
||||
case ICADE_DPAD_UP:
|
||||
return @"i⬆️";
|
||||
break;
|
||||
case ICADE_DPAD_DOWN:
|
||||
return @"i⬇️";
|
||||
break;
|
||||
case ICADE_DPAD_LEFT:
|
||||
return @"i⬅️";
|
||||
break;
|
||||
case ICADE_DPAD_RIGHT:
|
||||
return @"i➡️";
|
||||
break;
|
||||
default:
|
||||
return @"?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// PreviewUI.swift
|
||||
// activegs
|
||||
//
|
||||
// Created by Yoshi Sugawara on 1/9/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
//Text("Hello, World!")
|
||||
IntegratedController()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
ContentView().previewLayout(.fixed(width: 568, height: 320))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IntegratedController: UIViewControllerRepresentable {
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<IntegratedController>) -> EmulatorKeyboardController {
|
||||
let controller = EmulatorKeyboardController()
|
||||
controller.view.backgroundColor = .black
|
||||
return controller
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: EmulatorKeyboardController, context: UIViewControllerRepresentableContext<IntegratedController>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +156,8 @@ extern enum machineSpecsEnum machineSpecs;
|
|||
-(void)setNotificationText:(NSString*) _text;
|
||||
-(void)updateNotificationView:(CGRect) newRect;
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
|
||||
- (void) showKeyRemapController;
|
||||
|
||||
@property(nonatomic,strong,getter=getEmulatorView) activegsEmulatorController* emulatorController;
|
||||
@property(nonatomic,strong,getter=getBrowserView) ACTIVEGS_LAUNCHVIEWCONTROLLER* viewController;
|
||||
@property(nonatomic,strong,getter=getInfoView) infoViewController* infoController;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#include "asynccommand.h"
|
||||
#import <sys/utsname.h>
|
||||
#import "GameControllerKeyRemapController.h"
|
||||
|
||||
// Application Singleton
|
||||
activegsAppDelegate* pManager = nil;
|
||||
|
@ -369,6 +370,16 @@ void x_init_persistent_path(MyString& hp)
|
|||
|
||||
[[pManager getBrowserView] updateView ];
|
||||
|
||||
// fonts!
|
||||
for (NSString *family in [UIFont familyNames]) {
|
||||
NSArray *fontNames = [UIFont fontNamesForFamilyName:family];
|
||||
NSLog(@"Family: %@", family);
|
||||
for (NSString *name in fontNames) {
|
||||
NSLog(@"Font name: %@",name);
|
||||
}
|
||||
NSLog(@"");
|
||||
}
|
||||
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
#ifdef HANDLE_URL
|
||||
|
@ -615,6 +626,10 @@ void x_init_persistent_path(MyString& hp)
|
|||
|
||||
}
|
||||
|
||||
- (void) showKeyRemapController {
|
||||
GameControllerKeyRemapController *remapController = [[GameControllerKeyRemapController alloc] initWithNibName:@"GameControllerKeyRemapController" bundle:nil];
|
||||
[self.viewController presentViewController:remapController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void) screenDidConnect:(NSNotification *)notification
|
||||
{
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#import "../kegs/iOS/emulatorView.h"
|
||||
#import "KBDController.h"
|
||||
|
||||
#import "ActiveGS-Swift.h"
|
||||
|
||||
enum gestureModes
|
||||
{
|
||||
|
||||
|
@ -58,7 +60,7 @@ enum attachMode
|
|||
ATTACH_SECONDARY
|
||||
};
|
||||
|
||||
@interface activegsEmulatorController : UIVIEWCONTROLLERROOT
|
||||
@interface activegsEmulatorController : UIVIEWCONTROLLERROOT<EmulatorKeyboardKeyPressedDelegate, EmulatorKeyboardModifierPressedDelegate>
|
||||
{
|
||||
@public
|
||||
int attachedTo;
|
||||
|
@ -74,7 +76,9 @@ enum attachMode
|
|||
@property (nonatomic, strong) customView* contentView;
|
||||
@property (nonatomic, strong) zoomEmulatorView* zv;
|
||||
@property (nonatomic, strong) KBDController* kbdc ;
|
||||
@property (nonatomic, strong) NSString* trackerName ;
|
||||
@property (nonatomic, strong) EmulatorKeyboardController *emuKeyboardController;
|
||||
@property (nonatomic, strong) DebugMemoryViewController *debugMemoryViewController;
|
||||
@property (nonatomic, strong) NSString* trackerName;
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
#import "activegsAppDelegate.h"
|
||||
#include "../kegs/Src/protos_macdriver.h"
|
||||
#include "../kegs/Src/sim65816.h"
|
||||
#include "../kegs/Src/async_event.h"
|
||||
|
||||
#import "KeyMapper.h"
|
||||
#import "ActiveGS-Swift.h"
|
||||
|
||||
|
||||
#define TIME_BEFORE_REENABLING_GESTURES 1.0
|
||||
#define TIME_BEFORE_DISABLING_GESTURES 0.5
|
||||
|
@ -87,8 +92,21 @@ int x_lock_zoom = 0;
|
|||
return curHit;
|
||||
}
|
||||
}
|
||||
|
||||
EmulatorKeyboardView *leftKeyboard = [pManager getEmulatorView].emuKeyboardController.leftKeyboardView;
|
||||
p = [self convertPoint:_point toView:leftKeyboard];
|
||||
curHit = [leftKeyboard hitTest:p withEvent:event];
|
||||
if ([curHit isDescendantOfView:leftKeyboard]) {
|
||||
return curHit;
|
||||
}
|
||||
|
||||
EmulatorKeyboardView *rightKeyboard = [pManager getEmulatorView].emuKeyboardController.rightKeyboardView;
|
||||
p = [self convertPoint:_point toView:rightKeyboard];
|
||||
curHit = [rightKeyboard hitTest:p withEvent:event];
|
||||
if ([curHit isDescendantOfView:rightKeyboard]) {
|
||||
return curHit;
|
||||
}
|
||||
|
||||
|
||||
// renvoie les évenements à l'emulator
|
||||
return [pManager getEmulatorView].zv;
|
||||
}
|
||||
|
@ -133,8 +151,6 @@ int x_lock_zoom = 0;
|
|||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
-(void)invalidateTimers
|
||||
{
|
||||
|
||||
|
@ -413,7 +429,7 @@ int x_lock_zoom = 0;
|
|||
|
||||
attachedTo = ATTACH_NONE;
|
||||
|
||||
CGRect apprect = [[UIScreen mainScreen] applicationFrame];
|
||||
CGRect apprect = [[UIScreen mainScreen] bounds];
|
||||
printf("mainScreen apprect %d x %d\n",(int)apprect.size.width,(int)apprect.size.height);
|
||||
|
||||
self.contentView = [[customView alloc] initWithFrame:apprect];
|
||||
|
@ -428,11 +444,19 @@ int x_lock_zoom = 0;
|
|||
|
||||
[self.zv setUserInteractionEnabled:TRUE];
|
||||
|
||||
self.kbdc = [KBDController alloc];
|
||||
self.kbdc = [[KBDController alloc] initWithNibName:nil bundle:nil];
|
||||
[self.contentView addSubview:self.kbdc.view];
|
||||
|
||||
self.emuKeyboardController = [[EmulatorKeyboardController alloc] init];
|
||||
self.emuKeyboardController.leftKeyboardModel.delegate = self;
|
||||
self.emuKeyboardController.leftKeyboardModel.modifierDelegate = self;
|
||||
self.emuKeyboardController.rightKeyboardModel.delegate = self;
|
||||
self.emuKeyboardController.rightKeyboardModel.modifierDelegate = self;
|
||||
|
||||
self.debugMemoryViewController = [[DebugMemoryViewController alloc] init];
|
||||
self.debugMemoryViewController.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
|
||||
self.view = self.contentView;
|
||||
|
||||
|
||||
[self.zv disableZoom];
|
||||
}
|
||||
|
@ -564,7 +588,7 @@ int x_lock_zoom = 0;
|
|||
|
||||
// l'interface est repositionnées mais non animée
|
||||
|
||||
CGRect uirectrotate = [[UIScreen mainScreen] applicationFrame];
|
||||
CGRect uirectrotate = [[UIScreen mainScreen] bounds];
|
||||
[self.kbdc updateView:uirectrotate];
|
||||
[pManager updateNotificationView:uirectrotate];
|
||||
|
||||
|
@ -609,6 +633,18 @@ int x_lock_zoom = 0;
|
|||
// Release any cached data, images, etc that aren't in use.
|
||||
}
|
||||
|
||||
-(void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self addChildViewController:self.emuKeyboardController];
|
||||
self.emuKeyboardController.view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.view addSubview:self.emuKeyboardController.view];
|
||||
[[self.emuKeyboardController.view.topAnchor constraintEqualToAnchor:self.view.topAnchor] setActive:YES];
|
||||
[[self.emuKeyboardController.view.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor] setActive:YES];
|
||||
[[self.emuKeyboardController.view.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor] setActive:YES];
|
||||
[[self.emuKeyboardController.view.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor] setActive:YES];
|
||||
[self.emuKeyboardController didMoveToParentViewController:self];
|
||||
[self.view bringSubviewToFront:self.emuKeyboardController.view];
|
||||
}
|
||||
|
||||
-(void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
|
@ -681,4 +717,78 @@ int x_lock_zoom = 0;
|
|||
self.zv.crt.hidden = (vfx==VIDEOFX_CRT?0:1);
|
||||
}
|
||||
|
||||
#pragma mark - EmulatorKeyboardKeyPressedDelegate
|
||||
-(void)keyDown:(id<KeyCoded>)key {
|
||||
NSLog(@"key down: %li modifier: %i",(long)key.keyCode, self.emuKeyboardController.modifierState);
|
||||
if (self.emuKeyboardController.modifierState & shiftKey) {
|
||||
add_event_modifier(shiftKey);
|
||||
}
|
||||
add_event_key((int)key.keyCode, 0);
|
||||
}
|
||||
|
||||
-(void)keyUp:(id<KeyCoded>)key {
|
||||
if (key.keyCode == KEY_SPECIAL_DEBUGGER) {
|
||||
[self presentViewController:self.debugMemoryViewController animated:true completion:nil];
|
||||
return;
|
||||
}
|
||||
if (self.emuKeyboardController.modifierState & shiftKey) {
|
||||
add_event_modifier(0);
|
||||
}
|
||||
add_event_key((int)key.keyCode, 1);
|
||||
}
|
||||
|
||||
-(void)updateTransparencyToAlpha:(CGFloat)alpha {
|
||||
self.emuKeyboardController.view.alpha = alpha;
|
||||
}
|
||||
|
||||
#pragma mark - EmulatorKeyboardModifierPressedDelegate
|
||||
-(void)modifierPressedWithKey:(id<KeyCoded>)key enable:(BOOL)enable {
|
||||
int modifierKey;
|
||||
if (key.keyCode == KEY_SHIFT) {
|
||||
modifierKey = shiftKey;
|
||||
} else if (key.keyCode == KEY_CTRL) {
|
||||
modifierKey = controlKey;
|
||||
} else if (key.keyCode == KEY_APPLE) {
|
||||
modifierKey = cmdKey;
|
||||
} else if (key.keyCode == KEY_OPTION) {
|
||||
modifierKey = optionKey;
|
||||
}
|
||||
if (enable) {
|
||||
self.emuKeyboardController.modifierState |= modifierKey;
|
||||
[self keyDown:key];
|
||||
} else {
|
||||
[self keyUp:key];
|
||||
self.emuKeyboardController.modifierState &= ~modifierKey;
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL)isModifierEnabledWithKey:(id<KeyCoded>)key {
|
||||
BOOL enabled;
|
||||
switch (key.keyCode) {
|
||||
case KEY_SHIFT:
|
||||
NSLog(@"shift modifier = %i",self.emuKeyboardController.modifierState);
|
||||
enabled = self.emuKeyboardController.modifierState & shiftKey;
|
||||
NSLog(@"shift modifier enabled? %@",enabled ? @"YES" : @"NO");
|
||||
return enabled;
|
||||
case KEY_CTRL:
|
||||
NSLog(@"ctrl modifier = %i",self.emuKeyboardController.modifierState);
|
||||
enabled = self.emuKeyboardController.modifierState & controlKey;
|
||||
NSLog(@"ctrl modifier enabled? %@",enabled ? @"YES" : @"NO");
|
||||
return enabled;
|
||||
case KEY_APPLE:
|
||||
NSLog(@"open-apple modifier = %i",self.emuKeyboardController.modifierState);
|
||||
enabled = self.emuKeyboardController.modifierState & cmdKey;
|
||||
NSLog(@"open-apple modifier enabled? %@",enabled ? @"YES" : @"NO");
|
||||
return enabled;
|
||||
case KEY_OPTION:
|
||||
NSLog(@"closed-apple modifier = %i",self.emuKeyboardController.modifierState);
|
||||
enabled = self.emuKeyboardController.modifierState & optionKey;
|
||||
NSLog(@"closed-apple modifier enabled? %@",enabled ? @"YES" : @"NO");
|
||||
return enabled;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -226,17 +226,26 @@ static UIImage* defaultImageII = nil;
|
|||
MyString slotstr;
|
||||
slotstr.Format("%d",slot);
|
||||
|
||||
// Some games like Immortal expect 1 disk drive :(
|
||||
// check if name contains "_1dd_"
|
||||
BOOL isIIGSMultiMoreThanTwoDisks = slot != 6 && diskImages.count > 2;
|
||||
NSRange rangeContaining1dd = [firstImage.name rangeOfString:@"_1dd_"];
|
||||
if ( rangeContaining1dd.location != NSNotFound ) {
|
||||
isIIGSMultiMoreThanTwoDisks = YES;
|
||||
}
|
||||
|
||||
[diskImages enumerateObjectsUsingBlock:^(DiskImageInfo *diskImage, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
unsigned long diskIndex = idx + 1;
|
||||
if ( slot == 6 || isIIGSMultiMoreThanTwoDisks ) {
|
||||
int slotNumber = (int) diskImage.slotNumber;
|
||||
if ( slotNumber == 6 || isIIGSMultiMoreThanTwoDisks ) {
|
||||
// for Apple II disks, assume 1 disk drive
|
||||
// for Apple IIGS that have more than 2 disks, use 1 drive
|
||||
diskIndex = 1;
|
||||
}
|
||||
tempXML += "<image slot=\"";
|
||||
tempXML += slotstr;
|
||||
MyString slotNumberStr;
|
||||
slotNumberStr.Format("%d",slotNumber);
|
||||
tempXML += slotNumberStr;
|
||||
tempXML += [[NSString stringWithFormat:@"\" disk=\"%lu\">",diskIndex] UTF8String];
|
||||
tempXML += [diskImage.name UTF8String];
|
||||
tempXML += "</image>";
|
||||
|
@ -247,7 +256,8 @@ static UIImage* defaultImageII = nil;
|
|||
tempXML += "</config>";
|
||||
};
|
||||
|
||||
for (NSString *s in fileList)
|
||||
NSArray *sortedFileList = [fileList sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||
for (NSString *s in sortedFileList)
|
||||
{
|
||||
const char* fn = [s UTF8String];
|
||||
const char* ext = getext(fn);
|
||||
|
@ -259,7 +269,8 @@ static UIImage* defaultImageII = nil;
|
|||
&& strcasecmp(ext,"po")
|
||||
&& strcasecmp(ext,"do")
|
||||
&& strcasecmp(ext,"nib")
|
||||
&& strcasecmp(ext,"bin"))
|
||||
&& strcasecmp(ext,"bin")
|
||||
&& strcasecmp(ext, "hdv"))
|
||||
continue;
|
||||
|
||||
// si le fichier est dans la liste des blacklistée : ignore
|
||||
|
@ -572,7 +583,26 @@ static NSInteger compareImagesUsingSelector(id p1, id p2, void *context)
|
|||
{
|
||||
NSLog(@"activeGSList viewWillAppear %@",self);
|
||||
|
||||
|
||||
// Move files from Documents/Inbox to Documents (Items arriving through iOS "Open In"
|
||||
NSLog(@"Moving files from Documents/Inbox to Documents so they're visible");
|
||||
//Turn every file inside the directory into an array
|
||||
// Note to self: remember to actually put files in the Documents folder. Use the code in the apparopriately marked file
|
||||
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
//strings to actually get the directories
|
||||
NSString *appFolderPath = [path objectAtIndex:0];
|
||||
NSString *inboxAppFolderPath = [appFolderPath stringByAppendingString:@"/Inbox"]; //add ".plist" to the end of the recipe name
|
||||
|
||||
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
|
||||
|
||||
NSArray *inboxContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[NSString stringWithFormat:inboxAppFolderPath, documentsDirectory] error:nil];
|
||||
|
||||
//move all the files over
|
||||
for (int i = 0; i != [inboxContents count]; i++) {
|
||||
NSString *oldPath = [NSString stringWithFormat:@"%@/%@", inboxAppFolderPath, [inboxContents objectAtIndex:i]];
|
||||
NSString *newPath = [NSString stringWithFormat:@"%@/%@", appFolderPath, [inboxContents objectAtIndex:i]];
|
||||
[[NSFileManager defaultManager] moveItemAtPath:oldPath toPath:newPath error:nil];
|
||||
}
|
||||
|
||||
if (self.sourceName)
|
||||
{
|
||||
#ifndef ACTIVEGS_BACKGROUNDIMAGE
|
||||
|
@ -608,6 +638,8 @@ static NSInteger compareImagesUsingSelector(id p1, id p2, void *context)
|
|||
|
||||
[self reloadData:NO];
|
||||
|
||||
UILayoutGuide *lg = self.view.safeAreaLayoutGuide;
|
||||
NSLog(@"safe area layout guide: %@",NSStringFromCGRect(lg.layoutFrame));
|
||||
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -55,7 +55,7 @@ int messageLineVBL=0;
|
|||
|
||||
|
||||
|
||||
void x_async_refresh(CGContextRef myContext,CGRect myBoundingBox)
|
||||
void x_async_refresh(CGContextRef myContext,CGRect myBoundingBox, CGImageRef *imageRef)
|
||||
{
|
||||
|
||||
#ifdef ENABLEQD
|
||||
|
@ -92,10 +92,17 @@ void x_async_refresh(CGContextRef myContext,CGRect myBoundingBox)
|
|||
|
||||
|
||||
CGImageRef myImage = CGBitmapContextCreateImage((CGContextRef)g_kimage_offscreen.dev_handle);
|
||||
|
||||
|
||||
|
||||
CGContextDrawImage(myContext, myBoundingBox, myImage);// 6
|
||||
*imageRef = CGBitmapContextCreateImage((CGContextRef)g_kimage_offscreen.dev_handle);
|
||||
|
||||
// Yoshi debugging: iOS 15 issue
|
||||
// CGContextDrawImage draws a blank if the target rect width values in 233 ... 235
|
||||
// Raw image size of screen is 704x221.
|
||||
// Rect size here is 234.6667 x 77 for 3x retina size display (iPhone)
|
||||
// Calling CGContextDrawImage using this rect results in a blank image
|
||||
// width of 234.0 and 235.0 does not work
|
||||
// adding 3 to the width here renders the image
|
||||
CGRect newRect = CGRectMake(myBoundingBox.origin.x, myBoundingBox.origin.y, myBoundingBox.size.width + 3.0, myBoundingBox.size.height);
|
||||
CGContextDrawImage(myContext, newRect, myImage);// 6
|
||||
|
||||
#ifndef VIDEO_SINGLEVLINE
|
||||
if (r_sim65816.get_video_fx() == VIDEOFX_CRT)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
|
||||
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
|
||||
This code is covered by the GNU GPL licence
|
||||
*/
|
||||
|
||||
/*
|
||||
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
|
||||
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
|
||||
This code is covered by the GNU GPL licence
|
||||
*/
|
||||
|
||||
#include "adb.h"
|
||||
#include "moremem.h"
|
||||
#include "paddles.h"
|
||||
|
@ -1270,16 +1270,17 @@ update_mouse(int x, int y, int button_states, int buttons_valid)
|
|||
|
||||
mouse_compress_fifo(dcycs);
|
||||
|
||||
#if 0
|
||||
//#if 0
|
||||
printf("Update Mouse called with buttons:%d (state:%d) x,y:%d,%d, fifo:%d,%d, "
|
||||
" a2: %d,%d\n", buttons_valid,button_states, x, y,
|
||||
g_adb.g_mouse_fifo[0].x, g_mouse_fifo[0].y,
|
||||
g_adb.g_mouse_fifo[0].x, g_adb.g_mouse_fifo[0].y,
|
||||
g_adb.g_mouse_a2_x, g_adb.g_mouse_a2_y);
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
if((buttons_valid == -1) &&(g_adb.g_warp_pointer==WARP_POINTER)) {
|
||||
/* Warping the pointer causes it to jump here...this is not */
|
||||
/* real motion, just update info and get out */
|
||||
printf("yoshi debug doing warp pointer logic!\n");
|
||||
g_adb.g_mouse_a2_x += (x - g_adb.g_mouse_fifo[0].x);
|
||||
g_adb.g_mouse_a2_y += (y - g_adb.g_mouse_fifo[0].y);
|
||||
g_adb.g_mouse_fifo[0].x = x;
|
||||
|
@ -1287,11 +1288,11 @@ update_mouse(int x, int y, int button_states, int buttons_valid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//#if 0
|
||||
printf("...real move, warp: %d, %d, new x: %d, %d, a2:%d,%d\n",
|
||||
g_adb.g_mouse_warp_x, g_adb.g_mouse_warp_y, g_adb.g_mouse_fifo[0].x,
|
||||
g_adb.g_mouse_fifo[0].y, g_adb.g_mouse_a2_x, g_adb.g_mouse_a2_y);
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
mouse_moved = (g_adb.g_mouse_fifo[0].x != x) || (g_adb.g_mouse_fifo[0].y != y);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#include <QuartzCore/CADisplayLink.h>
|
||||
|
||||
extern void x_async_refresh(CGContextRef myContext,CGRect r);
|
||||
extern void x_async_refresh(CGContextRef myContext,CGRect r, CGImageRef *imageRef);
|
||||
extern void add_event_key(int,int);
|
||||
extern void add_event_mouse(int _x,int _y, int _state, int _button);
|
||||
extern void add_event_modifier(unsigned int state) ;
|
||||
|
|
|
@ -86,8 +86,19 @@ void x_invalidrect()
|
|||
{
|
||||
|
||||
CGContextRef g = UIGraphicsGetCurrentContext();
|
||||
x_async_refresh(g,rect);
|
||||
|
||||
CGImageRef imageRef;
|
||||
x_async_refresh(g,rect,&imageRef);
|
||||
// // yoshi test...
|
||||
// CGImageRef imgRef = CGBitmapContextCreateImage(g);
|
||||
// UIImage *image = [UIImage imageWithCGImage:imgRef];
|
||||
// CGImageRelease(imgRef);
|
||||
// NSLog(@"yoshi buffer image = %@",image);
|
||||
// if ( imageRef != nil ) {
|
||||
// UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
|
||||
// NSLog(@"yoshi debug: image = %@",image.description);
|
||||
// CGContextDrawImage(g, CGRectMake(0, 0, 275, 77), imageRef);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ float refScaleLandscape;
|
|||
self->viewSize = frame.size;
|
||||
self->dpiRatio = ratio;
|
||||
|
||||
CGRect r = CGRectMake(0.0,0.0,X_A2_WINDOW_WIDTH/self->dpiRatio ,X_A2_WINDOW_HEIGHT/self->dpiRatio );
|
||||
CGRect r = CGRectMake(0.0,0.0, X_A2_WINDOW_WIDTH/self->dpiRatio, X_A2_WINDOW_HEIGHT/self->dpiRatio );
|
||||
|
||||
self.contentSize = CGSizeMake(r.size.width,r.size.height);
|
||||
[self.ew setFrame:r];
|
||||
|
@ -552,6 +552,7 @@ float refScaleLandscape;
|
|||
|
||||
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
#ifdef ACTIVEGS
|
||||
if ([[pManager getEmulatorView].kbdc myTouchesBegan:touches])
|
||||
{
|
||||
|
@ -626,7 +627,7 @@ float refScaleLandscape;
|
|||
self.useTouch = touch;
|
||||
|
||||
lastMousePos = [self rotateTouch:touch];
|
||||
|
||||
NSLog(@"last mouse pos = %f , %f",lastMousePos.x, lastMousePos.y);
|
||||
add_event_mouse(lastMousePos.x,lastMousePos.y,0,-1);
|
||||
|
||||
if (self.secondTouch)
|
||||
|
@ -672,6 +673,7 @@ float refScaleLandscape;
|
|||
{
|
||||
|
||||
debug_printf("touchesEnded : %d",[touches count]);
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
|
||||
#ifdef ACTIVEGS
|
||||
if (! [[pManager getEmulatorView].kbdc myTouchesEnded:touches])
|
||||
|
@ -750,7 +752,7 @@ float refScaleLandscape;
|
|||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
// on est ici car lez zoom a démarré => théoriquement, il ne doit y avoir aucun useTouch, secondTouch, ou mouseDown
|
||||
|
||||
if ([touches containsObject:self.useTouch])
|
||||
|
@ -800,6 +802,7 @@ float refScaleLandscape;
|
|||
{
|
||||
|
||||
lastMousePos = [self rotateTouch:self.useTouch];
|
||||
NSLog(@"touchesmoved: last mouse pos = %f , %f",lastMousePos.x, lastMousePos.y);
|
||||
add_event_mouse(lastMousePos.x,lastMousePos.y,mouseDown,1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue