Compare commits
92 Commits
v1.0.1-lib
...
master
Author | SHA1 | Date |
---|---|---|
![]() |
8a234a25a3 | |
![]() |
5b88346537 | |
![]() |
20e5e18122 | |
![]() |
b948a1f3fd | |
![]() |
8215c03d62 | |
![]() |
8b2af8adf1 | |
![]() |
00f772b29d | |
![]() |
4107198548 | |
![]() |
e997ce0ce7 | |
![]() |
58c946f249 | |
![]() |
0b57886491 | |
![]() |
2db60c2b3f | |
![]() |
f54bfae01f | |
![]() |
c40221abb6 | |
![]() |
eb38034b76 | |
![]() |
c6a968ed74 | |
![]() |
e69f6b8579 | |
![]() |
cfbc7b481a | |
![]() |
1dfcdffa71 | |
![]() |
5b983bc7ad | |
![]() |
634b90e4fc | |
![]() |
b31cca77be | |
![]() |
5b17b41e07 | |
![]() |
239e0462c3 | |
![]() |
8505f00cdf | |
![]() |
08d58aa992 | |
![]() |
8cce6f7b13 | |
![]() |
ec8baa6329 | |
![]() |
9bd84978cf | |
![]() |
aa0fe30d5c | |
![]() |
6d6aafe887 | |
![]() |
003e8914b1 | |
![]() |
d7e1672ae7 | |
![]() |
1157ff0f36 | |
![]() |
bdd4522ca9 | |
![]() |
4abb5f3539 | |
![]() |
aff7f1706c | |
![]() |
33d237706e | |
![]() |
a39efd31cf | |
![]() |
69bb4c3e95 | |
![]() |
ae2d68aaf3 | |
![]() |
e381ebd1ea | |
![]() |
34c29b052e | |
![]() |
4ebb108ae1 | |
![]() |
d1da91da7c | |
![]() |
490d63b26f | |
![]() |
be63d7eaa3 | |
![]() |
15588a065f | |
![]() |
d52a50353d | |
![]() |
5d70f93920 | |
![]() |
f3cbc1990e | |
![]() |
583c234953 | |
![]() |
3f744254fd | |
![]() |
7abedaed4c | |
![]() |
42ffbd18d0 | |
![]() |
8508eb7b7c | |
![]() |
00000971d7 | |
![]() |
58bd40b833 | |
![]() |
cbaf5c4c4a | |
![]() |
67d338164b | |
![]() |
7bf8145a91 | |
![]() |
e043279500 | |
![]() |
9d6f378d21 | |
![]() |
bfb1092cbb | |
![]() |
282140822e | |
![]() |
19a1e3ec1a | |
![]() |
1ad8bad18c | |
![]() |
9577cbce85 | |
![]() |
6dd2f609f2 | |
![]() |
976fe7a337 | |
![]() |
1400bd40e8 | |
![]() |
6a97192e8c | |
![]() |
42732b20eb | |
![]() |
f0a672c39e | |
![]() |
d211120312 | |
![]() |
6ab1be654b | |
![]() |
f706988171 | |
![]() |
bed9f8220c | |
![]() |
750112f6dd | |
![]() |
63a02d90bc | |
![]() |
795fba1320 | |
![]() |
1923c324d9 | |
![]() |
6a24b9206f | |
![]() |
30a8c4bf42 | |
![]() |
152e242485 | |
![]() |
c4e6161959 | |
![]() |
1951df3476 | |
![]() |
1cf84a5436 | |
![]() |
81c29fa371 | |
![]() |
8b27952680 | |
![]() |
3c58deb46f | |
![]() |
d267d83cec |
|
@ -11,7 +11,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, ubuntu-20.04]
|
||||
os: [macos-latest, ubuntu-latest, ubuntu-22.04]
|
||||
cc: [gcc, clang]
|
||||
include:
|
||||
- os: macos-latest
|
||||
|
|
182
.gitlab-ci.yml
|
@ -1,182 +0,0 @@
|
|||
# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
|
||||
|
||||
##############################################################################
|
||||
################################# BOILERPLATE ################################
|
||||
##############################################################################
|
||||
|
||||
# Core definitions
|
||||
.core-defs:
|
||||
variables:
|
||||
JNI_PATH: libretro
|
||||
MAKEFILE_PATH: libretro
|
||||
CORENAME: sameboy
|
||||
before_script:
|
||||
- export BOOTROMS_DIR=$(pwd)/BootROMs/prebuilt
|
||||
|
||||
# Inclusion templates, required for the build to work
|
||||
include:
|
||||
################################## DESKTOPS ################################
|
||||
# Windows 64-bit
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/windows-x64-mingw.yml'
|
||||
|
||||
# Windows 32-bit
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/windows-i686-mingw.yml'
|
||||
|
||||
# Linux 64-bit
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/linux-x64.yml'
|
||||
|
||||
# Linux 32-bit
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/linux-i686.yml'
|
||||
|
||||
# MacOS 64-bit
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/osx-x64.yml'
|
||||
|
||||
# MacOS ARM 64-bit
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/osx-arm64.yml'
|
||||
|
||||
################################## CELLULAR ################################
|
||||
# Android
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/android-jni.yml'
|
||||
|
||||
# iOS
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/ios-arm64.yml'
|
||||
|
||||
# iOS (armv7)
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/ios9.yml'
|
||||
|
||||
################################## CONSOLES ################################
|
||||
# Nintendo WiiU
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/wiiu-static.yml'
|
||||
|
||||
# Nintendo Switch
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/libnx-static.yml'
|
||||
|
||||
# PlayStation Vita
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/vita-static.yml'
|
||||
|
||||
# tvOS (AppleTV)
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/tvos-arm64.yml'
|
||||
|
||||
#################################### MISC ##################################
|
||||
|
||||
# Stages for building
|
||||
stages:
|
||||
- build-prepare
|
||||
- build-shared
|
||||
- build-static
|
||||
|
||||
##############################################################################
|
||||
#################################### STAGES ##################################
|
||||
##############################################################################
|
||||
#
|
||||
################################### DESKTOPS #################################
|
||||
# Windows 64-bit
|
||||
libretro-build-windows-x64:
|
||||
extends:
|
||||
- .libretro-windows-x64-mingw-make-default
|
||||
- .core-defs
|
||||
|
||||
# Windows 32-bit
|
||||
libretro-build-windows-i686:
|
||||
extends:
|
||||
- .libretro-windows-i686-mingw-make-default
|
||||
- .core-defs
|
||||
|
||||
# Linux 64-bit
|
||||
libretro-build-linux-x64:
|
||||
extends:
|
||||
- .libretro-linux-x64-make-default
|
||||
- .core-defs
|
||||
|
||||
# Linux 32-bit
|
||||
libretro-build-linux-i686:
|
||||
extends:
|
||||
- .libretro-linux-i686-make-default
|
||||
- .core-defs
|
||||
|
||||
# MacOS 64-bit
|
||||
libretro-build-osx-x64:
|
||||
extends:
|
||||
- .libretro-osx-x64-make-10-7
|
||||
- .core-defs
|
||||
|
||||
# MacOS ARM 64-bit
|
||||
libretro-build-osx-arm64:
|
||||
extends:
|
||||
- .libretro-osx-arm64-make-default
|
||||
- .core-defs
|
||||
|
||||
################################### CELLULAR #################################
|
||||
# Android ARMv7a
|
||||
android-armeabi-v7a:
|
||||
extends:
|
||||
- .libretro-android-jni-armeabi-v7a
|
||||
- .core-defs
|
||||
|
||||
# Android ARMv8a
|
||||
android-arm64-v8a:
|
||||
extends:
|
||||
- .libretro-android-jni-arm64-v8a
|
||||
- .core-defs
|
||||
|
||||
# Android 64-bit x86
|
||||
android-x86_64:
|
||||
extends:
|
||||
- .libretro-android-jni-x86_64
|
||||
- .core-defs
|
||||
|
||||
# Android 32-bit x86
|
||||
android-x86:
|
||||
extends:
|
||||
- .libretro-android-jni-x86
|
||||
- .core-defs
|
||||
|
||||
# iOS
|
||||
libretro-build-ios-arm64:
|
||||
extends:
|
||||
- .libretro-ios-arm64-make-default
|
||||
- .core-defs
|
||||
|
||||
# iOS (armv7) [iOS 9 and up]
|
||||
libretro-build-ios9:
|
||||
extends:
|
||||
- .libretro-ios9-make-default
|
||||
- .core-defs
|
||||
|
||||
# tvOS
|
||||
libretro-build-tvos-arm64:
|
||||
extends:
|
||||
- .libretro-tvos-arm64-make-default
|
||||
- .core-defs
|
||||
|
||||
################################### CONSOLES #################################
|
||||
# Nintendo WiiU
|
||||
libretro-build-wiiu:
|
||||
extends:
|
||||
- .libretro-wiiu-static-retroarch-master
|
||||
- .core-defs
|
||||
|
||||
# Nintendo Switch
|
||||
libretro-build-libnx-aarch64:
|
||||
extends:
|
||||
- .libretro-libnx-static-retroarch-master
|
||||
- .core-defs
|
||||
|
||||
# PlayStation Vita
|
||||
libretro-build-vita:
|
||||
extends:
|
||||
- .libretro-vita-static-retroarch-master
|
||||
- .core-defs
|
8
BESS.md
|
@ -22,9 +22,9 @@ BESS works by appending a detectable footer at the end of an existing save state
|
|||
BESS uses a block format where each block contains the following header:
|
||||
|
||||
| Offset | Content |
|
||||
|--------|---------------------------------------|
|
||||
| 0 | A four-letter ASCII identifier |
|
||||
| 4 | Length of the block, excluding header |
|
||||
|--------|-----------------------------------------------------------|
|
||||
| 0 | A four-letter ASCII identifier |
|
||||
| 4 | Length of the block as a 32-bit integer, excluding header |
|
||||
|
||||
Every block is followed by another block, until the END block is reached. If an implementation encounters an unsupported block, it should be completely ignored (Should not have any effect and should not trigger a failure).
|
||||
|
||||
|
@ -256,4 +256,4 @@ Other than previously specified required fail conditions, an implementation is f
|
|||
* An invalid length of MBC (not a multiple of 3)
|
||||
* A write outside the $0000-$7FFF and $A000-$BFFF ranges in the MBC block
|
||||
* An SGB block on a save state targeting another model
|
||||
* An END block with non-zero length
|
||||
* An END block with non-zero length
|
||||
|
|
BIN
Cocoa/CPU@2x.png
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 199 B |
Before Width: | Height: | Size: 565 B After Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 202 B |
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 420 B |
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -103,7 +103,7 @@
|
|||
</tableHeaderView>
|
||||
</scrollView>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KdM-lW-WbP">
|
||||
<rect key="frame" x="371" y="25" width="96" height="32"/>
|
||||
<rect key="frame" x="371" y="24" width="96" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Search" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="t4Y-Ud-mJm">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -113,30 +113,30 @@
|
|||
<action selector="search:" target="-2" id="7pG-JY-vEF"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fTv-nr-5FT">
|
||||
<rect key="frame" x="6" y="96" width="124" height="16"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fTv-nr-5FT">
|
||||
<rect key="frame" x="6" y="95" width="124" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Search Condition:" id="9C2-Xp-JIA">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" alignment="right" title="Search Condition:" id="9C2-Xp-JIA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vMd-zb-8jT">
|
||||
<rect key="frame" x="6" y="67" width="124" height="16"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vMd-zb-8jT">
|
||||
<rect key="frame" x="6" y="64" width="124" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Expression:" id="Jgg-sA-jjs">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" alignment="right" title="Expression:" id="Jgg-sA-jjs">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5Db-vP-S60">
|
||||
<rect key="frame" x="133" y="119" width="197" height="25"/>
|
||||
<rect key="frame" x="133" y="120" width="197" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="8-Bit" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="al4-Jb-OJB" id="dkg-V5-wsX">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="message"/>
|
||||
<menu key="menu" id="e5r-qR-pg7">
|
||||
<items>
|
||||
<menuItem title="8-Bit" state="on" id="al4-Jb-OJB"/>
|
||||
|
@ -151,7 +151,7 @@
|
|||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Is Equal To…" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Vfb-Dg-Jkb" id="DPm-QO-c64">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="message"/>
|
||||
<menu key="menu" id="Kg9-Rd-1GQ">
|
||||
<items>
|
||||
<menuItem title="Any" id="cEg-eI-4hb"/>
|
||||
|
@ -173,7 +173,7 @@
|
|||
<action selector="conditionChanged:" target="-2" id="KF9-vz-yNC"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q42-Vu-TJW">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q42-Vu-TJW">
|
||||
<rect key="frame" x="334" y="93" width="126" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" continuous="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="$0" drawsBackground="YES" usesSingleLineMode="YES" id="WDs-GG-7Lc">
|
||||
|
@ -189,8 +189,8 @@
|
|||
<outlet property="delegate" destination="-2" id="1bO-hp-igc"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XN7-BO-THS">
|
||||
<rect key="frame" x="136" y="64" width="324" height="21"/>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XN7-BO-THS">
|
||||
<rect key="frame" x="136" y="62" width="324" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" enabled="NO" sendsActionOnEndEditing="YES" borderStyle="bezel" title="new == ($0)" drawsBackground="YES" usesSingleLineMode="YES" id="Krh-8w-4ug">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -205,7 +205,7 @@
|
|||
</connections>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tBa-6c-9AY">
|
||||
<rect key="frame" x="13" y="25" width="96" height="32"/>
|
||||
<rect key="frame" x="13" y="24" width="96" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Pge-SU-Y1n">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -215,7 +215,7 @@
|
|||
<action selector="reset:" target="-2" id="KCy-Ob-tlg"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wbN-MX-lEy">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wbN-MX-lEy">
|
||||
<rect key="frame" x="-3" y="4" width="486" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="Status" id="CM3-4U-qao">
|
||||
|
@ -225,7 +225,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="o1I-5D-V4k">
|
||||
<rect key="frame" x="264" y="25" width="110" height="32"/>
|
||||
<rect key="frame" x="264" y="24" width="110" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Add Cheat" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="GGV-nm-ASn">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -235,10 +235,10 @@
|
|||
<action selector="addCheat:" target="-2" id="7ax-kM-TeV"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="veO-Qn-0Sz">
|
||||
<rect key="frame" x="6" y="126" width="124" height="16"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="veO-Qn-0Sz">
|
||||
<rect key="frame" x="6" y="126" width="124" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Data Type:" id="KuT-rz-eHm">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Data Type:" id="KuT-rz-eHm">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
Before Width: | Height: | Size: 231 B After Width: | Height: | Size: 132 B |
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 461 B |
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 120 B |
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 448 B |
|
@ -3,6 +3,7 @@
|
|||
#import "GBImageView.h"
|
||||
#import "GBSplitView.h"
|
||||
#import "GBVisualizerView.h"
|
||||
#import "GBCPUView.h"
|
||||
#import "GBOSDView.h"
|
||||
#import "GBDebuggerButton.h"
|
||||
|
||||
|
@ -84,6 +85,8 @@ enum model {
|
|||
@property IBOutlet NSScrollView *debuggerScrollView;
|
||||
@property IBOutlet NSView *debugBar;
|
||||
|
||||
@property IBOutlet GBCPUView *cpuView;
|
||||
@property IBOutlet NSTextField *cpuCounter;
|
||||
|
||||
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale;
|
||||
- (void) performAtomicBlock: (void (^)())block;
|
||||
|
|
293
Cocoa/Document.m
|
@ -288,6 +288,7 @@ static void debuggerReloadCallback(GB_gameboy_t *gb)
|
|||
GB_set_user_data(&_gb, (__bridge void *)(self));
|
||||
GB_set_boot_rom_load_callback(&_gb, (GB_boot_rom_load_callback_t)boot_rom_load);
|
||||
GB_set_vblank_callback(&_gb, (GB_vblank_callback_t) vblank);
|
||||
GB_set_enable_skipped_frame_vblank_callbacks(&_gb, true);
|
||||
GB_set_log_callback(&_gb, (GB_log_callback_t) consoleLog);
|
||||
GB_set_input_callback(&_gb, (GB_input_callback_t) consoleInput);
|
||||
GB_set_async_input_callback(&_gb, (GB_input_callback_t) asyncConsoleInput);
|
||||
|
@ -345,6 +346,12 @@ static void debuggerReloadCallback(GB_gameboy_t *gb)
|
|||
[self observeStandardDefaultsKey:@"GBDebuggerFontSize" withBlock:^(NSString *value) {
|
||||
[weakSelf updateFonts];
|
||||
}];
|
||||
|
||||
[self observeStandardDefaultsKey:@"GBTurboCap" withBlock:^(NSNumber *value) {
|
||||
if (!_master) {
|
||||
GB_set_turbo_cap(gb, value.doubleValue);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateMinSize
|
||||
|
@ -359,11 +366,29 @@ static void debuggerReloadCallback(GB_gameboy_t *gb)
|
|||
|
||||
- (void)vblankWithType:(GB_vblank_type_t)type
|
||||
{
|
||||
if (type == GB_VBLANK_TYPE_SKIPPED_FRAME) {
|
||||
double frameUsage = GB_debugger_get_frame_cpu_usage(&_gb);
|
||||
[_cpuView addSample:frameUsage];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gbsVisualizer) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_gbsVisualizer setNeedsDisplay:true];
|
||||
});
|
||||
}
|
||||
|
||||
double frameUsage = GB_debugger_get_frame_cpu_usage(&_gb);
|
||||
[_cpuView addSample:frameUsage];
|
||||
|
||||
if (self.consoleWindow.visible) {
|
||||
double secondUsage = GB_debugger_get_second_cpu_usage(&_gb);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_cpuView setNeedsDisplay:true];
|
||||
_cpuCounter.stringValue = [NSString stringWithFormat:@"%.2f%%", secondUsage * 100];
|
||||
});
|
||||
}
|
||||
|
||||
if (type != GB_VBLANK_TYPE_REPEAT) {
|
||||
[self.view flip];
|
||||
if (_borderModeChanged) {
|
||||
|
@ -730,11 +755,12 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
|||
if (old_width != GB_get_screen_width(&_gb)) {
|
||||
[self.view screenSizeChanged];
|
||||
}
|
||||
|
||||
[self updateMinSize];
|
||||
|
||||
|
||||
[self start];
|
||||
if (_gbsTracks) {
|
||||
[self changeGBSTrack:sender];
|
||||
}
|
||||
|
||||
if (_hexController) {
|
||||
/* Verify bank sanity, especially when switching models. */
|
||||
|
@ -859,7 +885,7 @@ again:;
|
|||
{
|
||||
[super windowControllerDidLoadNib:aController];
|
||||
// Interface Builder bug?
|
||||
[self.consoleWindow setContentSize:self.consoleWindow.minSize];
|
||||
[self.consoleWindow setContentSize:self.consoleWindow.frame.size];
|
||||
/* Close Open Panels, if any */
|
||||
for (NSWindow *window in [[NSApplication sharedApplication] windows]) {
|
||||
if ([window isKindOfClass:[NSOpenPanel class]]) {
|
||||
|
@ -897,9 +923,16 @@ again:;
|
|||
[self.vramWindow setFrame:vram_window_rect display:true animate:false];
|
||||
|
||||
|
||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [self.fileURL.path lastPathComponent]];
|
||||
self.memoryWindow.title = [NSString stringWithFormat:@"Memory – %@", [self.fileURL.path lastPathComponent]];
|
||||
self.vramWindow.title = [NSString stringWithFormat:@"VRAM Viewer – %@", [self.fileURL.path lastPathComponent]];
|
||||
if (@available(macOS 11.0, *)) {
|
||||
self.consoleWindow.subtitle = [self.fileURL.path lastPathComponent];
|
||||
self.memoryWindow.subtitle = [self.fileURL.path lastPathComponent];
|
||||
self.vramWindow.subtitle = [self.fileURL.path lastPathComponent];
|
||||
}
|
||||
else {
|
||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [self.fileURL.path lastPathComponent]];
|
||||
self.memoryWindow.title = [NSString stringWithFormat:@"Memory – %@", [self.fileURL.path lastPathComponent]];
|
||||
self.vramWindow.title = [NSString stringWithFormat:@"VRAM Viewer – %@", [self.fileURL.path lastPathComponent]];
|
||||
}
|
||||
|
||||
self.consoleWindow.level = NSNormalWindowLevel;
|
||||
|
||||
|
@ -1152,6 +1185,17 @@ again:;
|
|||
if (@available(macOS 10.10, *)) {
|
||||
_mainWindow.titlebarAppearsTransparent = true;
|
||||
}
|
||||
|
||||
if (@available(macOS 26.0, *)) {
|
||||
// There's a new minimum width for segmented controls in Solarium
|
||||
NSRect frame = _gbsNextPrevButton.frame;
|
||||
frame.origin.x -= 16;
|
||||
_gbsNextPrevButton.frame = frame;
|
||||
|
||||
frame = _gbsTracks.frame;
|
||||
frame.size.width -= 16;
|
||||
_gbsTracks.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)isCartContainer
|
||||
|
@ -1316,6 +1360,7 @@ static bool is_path_writeable(const char *path)
|
|||
[self.vramWindow close];
|
||||
[self.printerFeedWindow close];
|
||||
[self.cheatsWindow close];
|
||||
[_cheatSearchController.window close];
|
||||
[super close];
|
||||
}
|
||||
|
||||
|
@ -1325,6 +1370,8 @@ static bool is_path_writeable(const char *path)
|
|||
GB_debugger_break(&_gb);
|
||||
[self start];
|
||||
[self.consoleWindow makeKeyAndOrderFront:nil];
|
||||
double secondUsage = GB_debugger_get_second_cpu_usage(&_gb);
|
||||
_cpuCounter.stringValue = [NSString stringWithFormat:@"%.2f%%", secondUsage * 100];
|
||||
[self.consoleInput becomeFirstResponder];
|
||||
}
|
||||
|
||||
|
@ -1406,6 +1453,9 @@ static bool is_path_writeable(const char *path)
|
|||
else if ([anItem action] == @selector(decreaseWindowSize:)) {
|
||||
return [self newRect:NULL forWindow:_mainWindow action:GBWindowResizeActionDecrease];
|
||||
}
|
||||
else if ([anItem action] == @selector(reloadROM:)) {
|
||||
return !_gbsTracks;
|
||||
}
|
||||
|
||||
return [super validateUserInterfaceItem:anItem];
|
||||
}
|
||||
|
@ -1547,7 +1597,9 @@ enum GBWindowResizeAction
|
|||
[self reloadVRAMData: nil];
|
||||
|
||||
[textView.textStorage appendAttributedString:_pendingConsoleOutput];
|
||||
[textView scrollToEndOfDocument:nil];
|
||||
if (!_logToSideView) {
|
||||
[textView scrollToEndOfDocument:nil];
|
||||
}
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {
|
||||
[self.consoleWindow orderFront:nil];
|
||||
}
|
||||
|
@ -1605,7 +1657,9 @@ enum GBWindowResizeAction
|
|||
|
||||
- (IBAction)showConsoleWindow:(id)sender
|
||||
{
|
||||
[self.consoleWindow orderBack:nil];
|
||||
[self.consoleWindow orderFront:nil];
|
||||
double secondUsage = GB_debugger_get_second_cpu_usage(&_gb);
|
||||
_cpuCounter.stringValue = [NSString stringWithFormat:@"%.2f%%", secondUsage * 100];
|
||||
}
|
||||
|
||||
- (void)queueDebuggerCommand:(NSString *)command
|
||||
|
@ -1955,124 +2009,130 @@ enum GBWindowResizeAction
|
|||
|
||||
- (IBAction)hexGoTo:(id)sender
|
||||
{
|
||||
NSString *expression = [sender stringValue];
|
||||
__block uint16_t addr = 0;
|
||||
__block uint16_t bank = 0;
|
||||
__block bool fail = false;
|
||||
NSString *error = [self captureOutputForBlock:^{
|
||||
uint16_t addr;
|
||||
uint16_t bank;
|
||||
if (GB_debugger_evaluate(&_gb, [[sender stringValue] UTF8String], &addr, &bank)) {
|
||||
return;
|
||||
if (GB_debugger_evaluate(&_gb, [expression UTF8String], &addr, &bank)) {
|
||||
fail = true;
|
||||
}
|
||||
|
||||
if (bank != (typeof(bank))-1) {
|
||||
GB_memory_mode_t mode = [(GBMemoryByteArray *)(_hexController.byteArray) mode];
|
||||
if (addr < 0x4000) {
|
||||
if (bank == 0) {
|
||||
if (mode != GBMemoryROM && mode != GBMemoryEntireSpace) {
|
||||
mode = GBMemoryEntireSpace;
|
||||
}
|
||||
}
|
||||
else {
|
||||
addr |= 0x4000;
|
||||
mode = GBMemoryROM;
|
||||
}
|
||||
}
|
||||
else if (addr < 0x8000) {
|
||||
mode = GBMemoryROM;
|
||||
}
|
||||
else if (addr < 0xA000) {
|
||||
mode = GBMemoryVRAM;
|
||||
}
|
||||
else if (addr < 0xC000) {
|
||||
mode = GBMemoryExternalRAM;
|
||||
}
|
||||
else if (addr < 0xD000) {
|
||||
if (mode != GBMemoryRAM && mode != GBMemoryEntireSpace) {
|
||||
mode = GBMemoryEntireSpace;
|
||||
}
|
||||
}
|
||||
else if (addr < 0xE000) {
|
||||
mode = GBMemoryRAM;
|
||||
}
|
||||
else {
|
||||
mode = GBMemoryEntireSpace;
|
||||
}
|
||||
[_memorySpaceButton selectItemAtIndex:mode];
|
||||
[self hexUpdateSpace:_memorySpaceButton.cell];
|
||||
[_memoryBankInput setStringValue:[NSString stringWithFormat:@"$%02x", bank]];
|
||||
[self hexUpdateBank:_memoryBankInput];
|
||||
}
|
||||
addr -= _lineRep.valueOffset;
|
||||
if (addr >= _hexController.byteArray.length) {
|
||||
GB_log(&_gb, "Value $%04x is out of range.\n", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_hexController setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(addr, 0)]]];
|
||||
[_hexController _ensureVisibilityOfLocation:addr];
|
||||
for (HFRepresenter *representer in _hexController.representers) {
|
||||
if ([representer isKindOfClass:[HFHexTextRepresenter class]]) {
|
||||
[self.memoryWindow makeFirstResponder:representer.view];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
if (error) {
|
||||
NSBeep();
|
||||
[GBWarningPopover popoverWithContents:error onView:sender];
|
||||
}
|
||||
if (fail) return;
|
||||
|
||||
|
||||
if (bank != (typeof(bank))-1) {
|
||||
GB_memory_mode_t mode = [(GBMemoryByteArray *)(_hexController.byteArray) mode];
|
||||
if (addr < 0x4000) {
|
||||
if (bank == 0) {
|
||||
if (mode != GBMemoryROM && mode != GBMemoryEntireSpace) {
|
||||
mode = GBMemoryEntireSpace;
|
||||
}
|
||||
}
|
||||
else {
|
||||
addr |= 0x4000;
|
||||
mode = GBMemoryROM;
|
||||
}
|
||||
}
|
||||
else if (addr < 0x8000) {
|
||||
mode = GBMemoryROM;
|
||||
}
|
||||
else if (addr < 0xA000) {
|
||||
mode = GBMemoryVRAM;
|
||||
}
|
||||
else if (addr < 0xC000) {
|
||||
mode = GBMemoryExternalRAM;
|
||||
}
|
||||
else if (addr < 0xD000) {
|
||||
if (mode != GBMemoryRAM && mode != GBMemoryEntireSpace) {
|
||||
mode = GBMemoryEntireSpace;
|
||||
}
|
||||
}
|
||||
else if (addr < 0xE000) {
|
||||
mode = GBMemoryRAM;
|
||||
}
|
||||
else {
|
||||
mode = GBMemoryEntireSpace;
|
||||
}
|
||||
[_memorySpaceButton selectItemAtIndex:mode];
|
||||
[self hexUpdateSpace:_memorySpaceButton.cell];
|
||||
[_memoryBankInput setStringValue:[NSString stringWithFormat:@"$%02x", bank]];
|
||||
[self hexUpdateBank:_memoryBankInput];
|
||||
}
|
||||
addr -= _lineRep.valueOffset;
|
||||
if (addr >= _hexController.byteArray.length) {
|
||||
GB_log(&_gb, "Value $%04x is out of range.\n", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
[_hexController setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(addr, 0)]]];
|
||||
[_hexController _ensureVisibilityOfLocation:addr];
|
||||
for (HFRepresenter *representer in _hexController.representers) {
|
||||
if ([representer isKindOfClass:[HFHexTextRepresenter class]]) {
|
||||
[self.memoryWindow makeFirstResponder:representer.view];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hexUpdateBank:(NSControl *)sender ignoreErrors: (bool)ignore_errors
|
||||
{
|
||||
NSString *expression = [sender stringValue];
|
||||
__block uint16_t addr, bank;
|
||||
__block bool fail = false;
|
||||
NSString *error = [self captureOutputForBlock:^{
|
||||
uint16_t addr, bank;
|
||||
if (GB_debugger_evaluate(&_gb, [[sender stringValue] UTF8String], &addr, &bank)) {
|
||||
if (GB_debugger_evaluate(&_gb, [expression UTF8String], &addr, &bank)) {
|
||||
fail = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bank == (uint16_t) -1) {
|
||||
bank = addr;
|
||||
}
|
||||
|
||||
uint16_t n_banks = 1;
|
||||
switch ([(GBMemoryByteArray *)(_hexController.byteArray) mode]) {
|
||||
case GBMemoryROM: {
|
||||
size_t rom_size;
|
||||
GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_ROM, &rom_size, NULL);
|
||||
n_banks = rom_size / 0x4000;
|
||||
break;
|
||||
}
|
||||
case GBMemoryVRAM:
|
||||
n_banks = GB_is_cgb(&_gb) ? 2 : 1;
|
||||
break;
|
||||
case GBMemoryExternalRAM: {
|
||||
size_t ram_size;
|
||||
GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_CART_RAM, &ram_size, NULL);
|
||||
n_banks = (ram_size + 0x1FFF) / 0x2000;
|
||||
break;
|
||||
}
|
||||
case GBMemoryRAM:
|
||||
n_banks = GB_is_cgb(&_gb) ? 8 : 1;
|
||||
break;
|
||||
case GBMemoryEntireSpace:
|
||||
break;
|
||||
}
|
||||
|
||||
bank %= n_banks;
|
||||
|
||||
[sender setStringValue:[NSString stringWithFormat:@"$%x", bank]];
|
||||
[(GBMemoryByteArray *)(_hexController.byteArray) setSelectedBank:bank];
|
||||
_statusRep.bankForDescription = bank;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_hexController reloadData];
|
||||
});
|
||||
}];
|
||||
|
||||
if (error && !ignore_errors) {
|
||||
NSBeep();
|
||||
[GBWarningPopover popoverWithContents:error onView:sender];
|
||||
}
|
||||
|
||||
if (fail) return;
|
||||
|
||||
if (bank == (uint16_t) -1) {
|
||||
bank = addr;
|
||||
}
|
||||
|
||||
uint16_t n_banks = 1;
|
||||
switch ([(GBMemoryByteArray *)(_hexController.byteArray) mode]) {
|
||||
case GBMemoryROM: {
|
||||
size_t rom_size;
|
||||
GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_ROM, &rom_size, NULL);
|
||||
n_banks = rom_size / 0x4000;
|
||||
break;
|
||||
}
|
||||
case GBMemoryVRAM:
|
||||
n_banks = GB_is_cgb(&_gb) ? 2 : 1;
|
||||
break;
|
||||
case GBMemoryExternalRAM: {
|
||||
size_t ram_size;
|
||||
GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_CART_RAM, &ram_size, NULL);
|
||||
n_banks = (ram_size + 0x1FFF) / 0x2000;
|
||||
break;
|
||||
}
|
||||
case GBMemoryRAM:
|
||||
n_banks = GB_is_cgb(&_gb) ? 8 : 1;
|
||||
break;
|
||||
case GBMemoryEntireSpace:
|
||||
break;
|
||||
}
|
||||
|
||||
bank %= n_banks;
|
||||
|
||||
[(GBMemoryByteArray *)(_hexController.byteArray) setSelectedBank:bank];
|
||||
_statusRep.bankForDescription = bank;
|
||||
[sender setStringValue:[NSString stringWithFormat:@"$%x", bank]];
|
||||
[_hexController reloadData];
|
||||
}
|
||||
|
||||
- (IBAction)hexUpdateBank:(NSControl *)sender
|
||||
|
@ -2110,9 +2170,8 @@ enum GBWindowResizeAction
|
|||
}
|
||||
byteArray.selectedBank = bank;
|
||||
_statusRep.bankForDescription = bank;
|
||||
if (bank != (uint16_t)-1) {
|
||||
[self.memoryBankInput setStringValue:[NSString stringWithFormat:@"$%x", byteArray.selectedBank]];
|
||||
}
|
||||
[self.memoryBankInput setStringValue:(bank == (uint16_t)-1)? @"" :
|
||||
[NSString stringWithFormat:@"$%x", byteArray.selectedBank]];
|
||||
|
||||
[_hexController reloadData];
|
||||
for (NSView *view in self.memoryView.subviews) {
|
||||
|
@ -2452,9 +2511,16 @@ enum GBWindowResizeAction
|
|||
- (void)setFileURL:(NSURL *)fileURL
|
||||
{
|
||||
[super setFileURL:fileURL];
|
||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [[fileURL path] lastPathComponent]];
|
||||
self.memoryWindow.title = [NSString stringWithFormat:@"Memory – %@", [[fileURL path] lastPathComponent]];
|
||||
self.vramWindow.title = [NSString stringWithFormat:@"VRAM Viewer – %@", [[fileURL path] lastPathComponent]];
|
||||
if (@available(macOS 11.0, *)) {
|
||||
self.consoleWindow.subtitle = [self.fileURL.path lastPathComponent];
|
||||
self.memoryWindow.subtitle = [self.fileURL.path lastPathComponent];
|
||||
self.vramWindow.subtitle = [self.fileURL.path lastPathComponent];
|
||||
}
|
||||
else {
|
||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [self.fileURL.path lastPathComponent]];
|
||||
self.memoryWindow.title = [NSString stringWithFormat:@"Memory – %@", [self.fileURL.path lastPathComponent]];
|
||||
self.vramWindow.title = [NSString stringWithFormat:@"VRAM Viewer – %@", [self.fileURL.path lastPathComponent]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)splitView:(GBSplitView *)splitView canCollapseSubview:(NSView *)subview;
|
||||
|
@ -2526,6 +2592,8 @@ enum GBWindowResizeAction
|
|||
}
|
||||
GB_set_turbo_mode(&_gb, false, false);
|
||||
GB_set_turbo_mode(&partner->_gb, false, false);
|
||||
GB_set_turbo_cap(&_gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBTurboCap"]);
|
||||
GB_set_turbo_cap(&partner->_gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBTurboCap"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2541,6 +2609,7 @@ enum GBWindowResizeAction
|
|||
GB_set_turbo_mode(&partner->_gb, true, true);
|
||||
_slave = partner;
|
||||
partner->_master = self;
|
||||
GB_set_turbo_cap(&partner->_gb, 0);
|
||||
_linkOffset = 0;
|
||||
GB_set_serial_transfer_bit_start_callback(&_gb, _linkCableBitStart);
|
||||
GB_set_serial_transfer_bit_start_callback(&partner->_gb, _linkCableBitStart);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -15,6 +15,8 @@
|
|||
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
|
||||
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
|
||||
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
|
||||
<outlet property="cpuCounter" destination="xdx-GC-Tb8" id="oSn-U3-qvg"/>
|
||||
<outlet property="cpuView" destination="aBf-yU-8M0" id="RIb-Fj-lvt"/>
|
||||
<outlet property="debugBar" destination="sah-kv-6KJ" id="24d-aM-rs5"/>
|
||||
<outlet property="debuggerBackstepButton" destination="E87-Uq-f2l" id="PI7-Wu-f0v"/>
|
||||
<outlet property="debuggerContinueButton" destination="ybQ-jy-NgI" id="fRF-S3-xSh"/>
|
||||
|
@ -54,7 +56,7 @@
|
|||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" id="xOd-HO-29H" userLabel="Window">
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" id="xOd-HO-29H" userLabel="Window" customClass="GBWindow">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="160" height="144"/>
|
||||
|
@ -91,38 +93,38 @@
|
|||
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="GBPanel">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="921" height="400"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="921" height="480"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||
<value key="minSize" type="size" width="921" height="400"/>
|
||||
<view key="contentView" id="dCP-E5-7Fi" customClass="GBOptionalVisualEffectView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="921" height="400"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="921" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<box horizontalHuggingPriority="750" boxType="separator" id="7bR-gM-1At">
|
||||
<rect key="frame" x="590" y="0.0" width="5" height="401"/>
|
||||
<rect key="frame" x="590" y="0.0" width="5" height="481"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
</box>
|
||||
<splitView dividerStyle="thin" vertical="YES" id="pUc-ZN-vl5" customClass="GBSplitView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="921" height="401"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="921" height="481"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<customView fixedFrame="YES" id="2rj-7i-kxc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="401"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="481"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oTo-zx-o6N">
|
||||
<rect key="frame" x="0.0" y="52" width="591" height="349"/>
|
||||
<rect key="frame" x="0.0" y="52" width="591" height="429"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="349"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="429"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="349"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="429"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.25" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<size key="minSize" width="591" height="349"/>
|
||||
<size key="minSize" width="591" height="429"/>
|
||||
<size key="maxSize" width="1160" height="10000000"/>
|
||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<allowedInputSourceLocales>
|
||||
|
@ -136,7 +138,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="cwi-6E-rbh">
|
||||
<rect key="frame" x="575" y="0.0" width="16" height="349"/>
|
||||
<rect key="frame" x="575" y="0.0" width="16" height="429"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
|
@ -160,7 +162,7 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ybQ-jy-NgI" customClass="GBDebuggerButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="26" height="26"/>
|
||||
<rect key="frame" x="4" y="0.0" width="26" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" title="Interrupt" alternateTitle="interrupt" bezelStyle="rounded" image="InterruptTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="Yd7-kY-21r">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -173,7 +175,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jdD-yP-Nr6" customClass="GBDebuggerButton">
|
||||
<rect key="frame" x="76" y="0.0" width="26" height="26"/>
|
||||
<rect key="frame" x="80" y="0.0" width="26" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" title="Step Out" alternateTitle="finish" bezelStyle="rounded" image="FinishTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="16t-ix-lOh">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -185,7 +187,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tTP-Zs-Ohu" customClass="GBDebuggerButton">
|
||||
<rect key="frame" x="26" y="0.0" width="26" height="26"/>
|
||||
<rect key="frame" x="30" y="0.0" width="26" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" title="Step Over" alternateTitle="next" bezelStyle="rounded" image="NextTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="835-qy-CNq">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -197,7 +199,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="E87-Uq-f2l" customClass="GBDebuggerButton">
|
||||
<rect key="frame" x="100" y="0.0" width="26" height="26"/>
|
||||
<rect key="frame" x="104" y="0.0" width="26" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" title="Step Backward" alternateTitle="backstep" bezelStyle="rounded" image="BackstepTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="yr5-aU-Fli">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -209,7 +211,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fsQ-dD-A8C" customClass="GBDebuggerButton">
|
||||
<rect key="frame" x="52" y="0.0" width="26" height="26"/>
|
||||
<rect key="frame" x="56" y="0.0" width="26" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" title="Step Into" alternateTitle="step" bezelStyle="rounded" image="StepTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="lau-41-TYH">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -229,7 +231,7 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</box>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Jt-TO-8CM" customClass="GBDebuggerButton">
|
||||
<rect key="frame" x="565" y="0.0" width="26" height="26"/>
|
||||
<rect key="frame" x="561" y="0.0" width="26" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" title="Help" alternateTitle="help" bezelStyle="rounded" image="HelpTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="fVh-bT-eYs">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -245,11 +247,11 @@
|
|||
</subviews>
|
||||
</customView>
|
||||
<customView fixedFrame="YES" id="4Z2-33-dYY" customClass="GBOptionalVisualEffectView">
|
||||
<rect key="frame" x="592" y="0.0" width="329" height="401"/>
|
||||
<rect key="frame" x="592" y="0.0" width="329" height="481"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" scrollerKnobStyle="dark" translatesAutoresizingMaskIntoConstraints="NO" id="V9U-Ua-F4z">
|
||||
<rect key="frame" x="0.0" y="338" width="329" height="63"/>
|
||||
<rect key="frame" x="0.0" y="418" width="329" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="63"/>
|
||||
|
@ -279,22 +281,22 @@
|
|||
</scroller>
|
||||
</scrollView>
|
||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="5qI-qZ-nkh">
|
||||
<rect key="frame" x="0.0" y="336" width="329" height="5"/>
|
||||
<rect key="frame" x="0.0" y="415" width="329" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
</box>
|
||||
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vts-CC-ZjQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="338"/>
|
||||
<rect key="frame" x="0.0" y="78" width="329" height="339"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="338"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="339"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="338"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="339"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" red="0.14901960780000001" green="0.14901960780000001" blue="0.14901960780000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<size key="minSize" width="329" height="338"/>
|
||||
<size key="minSize" width="329" height="339"/>
|
||||
<size key="maxSize" width="1160" height="10000000"/>
|
||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<allowedInputSourceLocales>
|
||||
|
@ -309,10 +311,38 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4jm-Gm-D2R">
|
||||
<rect key="frame" x="313" y="0.0" width="16" height="338"/>
|
||||
<rect key="frame" x="313" y="0.0" width="16" height="339"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="wC1-WG-WRf">
|
||||
<rect key="frame" x="0.0" y="75" width="329" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</box>
|
||||
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aBf-yU-8M0" customClass="GBCPUView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="77"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F9b-AT-CdX">
|
||||
<rect key="frame" x="2" y="59" width="100" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="CPU Load" id="ai6-8E-Let">
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xdx-GC-Tb8">
|
||||
<rect key="frame" x="2" y="2" width="100" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="100%" id="set-AM-fVX">
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
</customView>
|
||||
</subviews>
|
||||
</customView>
|
||||
</subviews>
|
||||
|
@ -340,20 +370,20 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
|
||||
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="WUk-8p-S6B"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="E3z-um-6KG"/>
|
||||
<toolbarItem implicitItemIdentifier="4F6AAE25-1E9D-4111-9E5B-91F0792E56CD" label="Address Space" paletteLabel="Address Space" id="VTy-lj-K0H">
|
||||
<nil key="toolTip"/>
|
||||
<size key="minSize" width="100" height="25"/>
|
||||
<size key="maxSize" width="130" height="25"/>
|
||||
<size key="minSize" width="160" height="25"/>
|
||||
<size key="maxSize" width="160" height="25"/>
|
||||
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
|
||||
<rect key="frame" x="0.0" y="14" width="128" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo">
|
||||
<rect key="frame" x="0.0" y="14" width="160" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo" customClass="GBToolbarPopUpButtonCell">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="message"/>
|
||||
<menu key="menu" id="gTX-6Z-mOH">
|
||||
<items>
|
||||
<menuItem title="Entire Space" id="Zp5-J9-dd3"/>
|
||||
|
@ -372,31 +402,29 @@
|
|||
<toolbarItem implicitItemIdentifier="D16C64D2-2F0D-4033-A1EC-A1E699522ECE" label="Bank" paletteLabel="Bank" id="bWC-FW-IYP">
|
||||
<nil key="toolTip"/>
|
||||
<size key="minSize" width="64" height="22"/>
|
||||
<size key="maxSize" width="64" height="22"/>
|
||||
<textField key="view" verticalHuggingPriority="750" id="rdV-q6-hc6">
|
||||
<size key="maxSize" width="80" height="22"/>
|
||||
<textField key="view" focusRingType="none" verticalHuggingPriority="750" id="rdV-q6-hc6">
|
||||
<rect key="frame" x="0.0" y="14" width="64" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" id="JCn-Y1-eHS">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" placeholderString="Bank" id="JCn-Y1-eHS" customClass="GBToolbarFieldCell">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="hexUpdateBank:" target="-2" id="Mx9-WI-wgO"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="F9723DA8-D79F-43AB-876B-783DD0204AA6" label="Go to" paletteLabel="Go to" id="rLO-D7-zRG">
|
||||
<toolbarItem implicitItemIdentifier="F9723DA8-D79F-43AB-876B-783DD0204AA6" label="Go To" paletteLabel="Go To" id="rLO-D7-zRG">
|
||||
<nil key="toolTip"/>
|
||||
<size key="minSize" width="96" height="22"/>
|
||||
<size key="maxSize" width="128" height="22"/>
|
||||
<textField key="view" verticalHuggingPriority="750" id="EJd-jG-hmH">
|
||||
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="vg5-Nn-abb">
|
||||
<size key="minSize" width="160" height="22"/>
|
||||
<size key="maxSize" width="160" height="22"/>
|
||||
<textField key="view" focusRingType="none" verticalHuggingPriority="750" id="EJd-jG-hmH">
|
||||
<rect key="frame" x="0.0" y="14" width="160" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" placeholderString="Address" bezelStyle="round" id="vg5-Nn-abb" customClass="GBToolbarFieldCell">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="hexGoTo:" target="-2" id="7WG-8C-SK8"/>
|
||||
|
@ -422,7 +450,7 @@
|
|||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="512" height="432"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||
<view key="contentView" id="GYW-dv-Um1">
|
||||
<view key="contentView" misplaced="YES" id="GYW-dv-Um1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
|
@ -430,7 +458,7 @@
|
|||
<rect key="frame" x="0.0" y="406" width="512" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</box>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6vK-IP-PmP">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6vK-IP-PmP">
|
||||
<rect key="frame" x="-2" y="4" width="516" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="umk-4r-VNg">
|
||||
|
@ -563,7 +591,7 @@
|
|||
</connections>
|
||||
</popUpButton>
|
||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YIJ-Qc-SIZ">
|
||||
<rect key="frame" x="135" y="412" width="96" height="17"/>
|
||||
<rect key="frame" x="135" y="412" width="100" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="roundRect" title="Effective Tilemap" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="XRF-Vj-3gs" id="3W1-Db-wDn">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -581,7 +609,7 @@
|
|||
</connections>
|
||||
</popUpButton>
|
||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k4c-Vg-MBu">
|
||||
<rect key="frame" x="235" y="412" width="96" height="17"/>
|
||||
<rect key="frame" x="239" y="412" width="100" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="roundRect" title="Effective Tileset" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="CRe-dX-rzY" id="h53-sb-Odg">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -649,21 +677,19 @@
|
|||
</tabView>
|
||||
</subviews>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="7FF8B5E5-F61B-408C-9634-A5D496FE5E70" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconOnly" sizeMode="regular" id="SVR-To-n7n">
|
||||
<toolbar key="toolbar" implicitIdentifier="7FF8B5E5-F61B-408C-9634-A5D496FE5E70" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="SVR-To-n7n">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="hnI-48-dYt"/>
|
||||
<toolbarItem implicitItemIdentifier="B9D3CF98-8020-4389-9372-F99361440794" label="" paletteLabel="" id="fmK-Jl-Koj">
|
||||
<toolbarItem implicitItemIdentifier="B9D3CF98-8020-4389-9372-F99361440794" label="" paletteLabel="" sizingBehavior="auto" id="fmK-Jl-Koj">
|
||||
<nil key="toolTip"/>
|
||||
<size key="minSize" width="100" height="25"/>
|
||||
<size key="maxSize" width="268" height="25"/>
|
||||
<segmentedControl key="view" verticalHuggingPriority="750" id="Aul-vO-dCK">
|
||||
<rect key="frame" x="0.0" y="14" width="268" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedSquare" trackingMode="selectOne" id="HhR-ky-5NN">
|
||||
<font key="font" metaFont="system"/>
|
||||
<segments>
|
||||
<segment label="Tileset" selected="YES"/>
|
||||
<segment label="Tilemap" tag="1"/>
|
||||
<segment label="Tilemap"/>
|
||||
<segment label="Objects"/>
|
||||
<segment label="Palettes"/>
|
||||
</segments>
|
||||
|
@ -686,7 +712,7 @@
|
|||
</connections>
|
||||
<point key="canvasLocation" x="182" y="760"/>
|
||||
</window>
|
||||
<window title="Printer Feed" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="GBPanel">
|
||||
<window title="Printer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="GBPanel">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||
|
@ -702,7 +728,7 @@
|
|||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="1FF86A2B-6637-4EE6-A25A-7298D79AE84E" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="gH3-SH-7il">
|
||||
<toolbar key="toolbar" implicitIdentifier="1FF86A2B-6637-4EE6-A25A-7298D79AE84E" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconAndLabel" sizeMode="regular" id="gH3-SH-7il">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="15EB8D49-8C6E-42F2-9F7F-F7D7A0BBDAAF" label="Save" paletteLabel="Save" tag="-1" image="NSFolder" id="CBz-1N-o0Q">
|
||||
<size key="minSize" width="22" height="22"/>
|
||||
|
@ -754,7 +780,7 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" id="mzf-yu-RID">
|
||||
<rect key="frame" x="1" y="1" width="604" height="257"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="24" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="604" height="240"/>
|
||||
|
@ -834,8 +860,8 @@
|
|||
<rect key="frame" x="-1" y="0.0" width="308" height="135"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="hqi-ob-NW9">
|
||||
<rect key="frame" x="20" y="51" width="176" height="19"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="hqi-ob-NW9">
|
||||
<rect key="frame" x="20" y="52" width="176" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="To value:" id="Ycx-oE-aA4">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -844,7 +870,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kq8-6F-9GK">
|
||||
<rect key="frame" x="42" y="21" width="152" height="19"/>
|
||||
<rect key="frame" x="42" y="22" width="152" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Only if old value was: " bezelStyle="regularSquare" imagePosition="left" alignment="right" state="on" inset="2" id="LkB-WQ-9Qd">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
|
@ -854,8 +880,8 @@
|
|||
<action selector="updateCheat:" target="v7q-gT-jHT" id="kNc-cj-bmF"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C6E-oI-hDC">
|
||||
<rect key="frame" x="22" y="112" width="276" height="21"/>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C6E-oI-hDC">
|
||||
<rect key="frame" x="22" y="113" width="276" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="2uR-9N-hBb">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -866,7 +892,7 @@
|
|||
<outlet property="delegate" destination="v7q-gT-jHT" id="zyw-h0-hRP"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="qHx-1z-daR">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="qHx-1z-daR">
|
||||
<rect key="frame" x="202" y="82" width="96" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="edq-46-JeP" customClass="GBCheatTextFieldCell">
|
||||
|
@ -881,7 +907,7 @@
|
|||
<outlet property="delegate" destination="v7q-gT-jHT" id="79v-33-R1X"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="N3I-PP-X85">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="N3I-PP-X85">
|
||||
<rect key="frame" x="202" y="51" width="96" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="CV2-D9-WsB" customClass="GBCheatTextFieldCell">
|
||||
|
@ -896,7 +922,7 @@
|
|||
<outlet property="delegate" destination="v7q-gT-jHT" id="P69-nT-oOt"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="S6O-LB-gSj">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="S6O-LB-gSj">
|
||||
<rect key="frame" x="202" y="20" width="96" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="tpM-ys-MEO" customClass="GBCheatTextFieldCell">
|
||||
|
@ -911,8 +937,8 @@
|
|||
<outlet property="delegate" destination="v7q-gT-jHT" id="6RH-dg-SL7"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="uFo-ly-Veq">
|
||||
<rect key="frame" x="18" y="82" width="178" height="19"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="uFo-ly-Veq">
|
||||
<rect key="frame" x="18" y="83" width="178" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Change byte at address:" id="xwa-TF-eY1">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -922,8 +948,8 @@
|
|||
</textField>
|
||||
</subviews>
|
||||
</customView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r5T-ol-Dod">
|
||||
<rect key="frame" x="316" y="115" width="270" height="16"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r5T-ol-Dod">
|
||||
<rect key="frame" x="316" y="116" width="270" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Import GameShark or Game Genie cheat:" id="0mf-EN-cKc">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -931,7 +957,7 @@
|
|||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X7K-nJ-alF">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X7K-nJ-alF">
|
||||
<rect key="frame" x="351" y="82" width="233" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="Code" drawsBackground="YES" usesSingleLineMode="YES" id="2bz-dT-7Fi">
|
||||
|
@ -946,7 +972,7 @@
|
|||
<action selector="selectText:" target="KHj-uX-Wbk" id="11z-0U-tMA"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KHj-uX-Wbk">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KHj-uX-Wbk">
|
||||
<rect key="frame" x="351" y="51" width="233" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="50d-va-Cen">
|
||||
|
@ -959,7 +985,7 @@
|
|||
</connections>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
|
||||
<rect key="frame" x="508" y="12" width="83" height="32"/>
|
||||
<rect key="frame" x="508" y="13" width="83" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Import" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mMP-KW-YNy">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
|
@ -970,7 +996,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<box horizontalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="P90-u5-8ko">
|
||||
<rect key="frame" x="304" y="12" width="5" height="123"/>
|
||||
<rect key="frame" x="302" y="12" width="5" height="123"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</box>
|
||||
</subviews>
|
||||
|
@ -1000,7 +1026,7 @@
|
|||
<image name="HelpTemplate" width="14" height="14"/>
|
||||
<image name="InterruptTemplate" width="14" height="14"/>
|
||||
<image name="NSFolder" width="32" height="32"/>
|
||||
<image name="NSStopProgressFreestandingTemplate" width="15" height="15"/>
|
||||
<image name="NSStopProgressFreestandingTemplate" width="20" height="20"/>
|
||||
<image name="NextTemplate" width="14" height="14"/>
|
||||
<image name="StepTemplate" width="14" height="14"/>
|
||||
<image name="printer" catalog="system" width="18" height="16"/>
|
||||
|
|
|
@ -8,9 +8,15 @@
|
|||
#import <JoyKit/JoyKit.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <mach-o/dyld.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#define UPDATE_SERVER "https://sameboy.github.io"
|
||||
|
||||
@interface NSToolbarItem(private)
|
||||
- (NSButton *)_view;
|
||||
@end
|
||||
|
||||
static uint32_t color_to_int(NSColor *color)
|
||||
{
|
||||
color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
||||
|
@ -40,7 +46,14 @@ static uint32_t color_to_int(NSColor *color)
|
|||
- (void) applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
// Refresh icon if launched via a software update
|
||||
[NSApplication sharedApplication].applicationIconImage = [NSImage imageNamed:@"AppIcon"];
|
||||
if (@available(macOS 26.0, *)) {
|
||||
// Severely broken on macOS 26
|
||||
}
|
||||
else {
|
||||
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[[NSBundle mainBundle] bundlePath]];
|
||||
icon.size = [NSApplication sharedApplication].applicationIconImage.size;
|
||||
[NSApplication sharedApplication].applicationIconImage = icon;
|
||||
}
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
for (unsigned i = 0; i < GBKeyboardButtonCount; i++) {
|
||||
|
@ -93,6 +106,9 @@ static uint32_t color_to_int(NSColor *color)
|
|||
@"GBDebuggerFont": hasSFMono? @"SF Mono" : @"Menlo",
|
||||
@"GBDebuggerFontSize": @12,
|
||||
|
||||
@"GBColorPalette": @1,
|
||||
@"GBTurboCap": @0,
|
||||
|
||||
// Default themes
|
||||
@"GBThemes": @{
|
||||
@"Canyon": @{
|
||||
|
@ -326,9 +342,8 @@ static uint32_t color_to_int(NSColor *color)
|
|||
#ifndef UPDATE_SUPPORT
|
||||
[_preferencesWindow.toolbar removeItemAtIndex:4];
|
||||
#endif
|
||||
if (@available(macOS 11.0, *)) {
|
||||
[_preferencesWindow.toolbar insertItemWithItemIdentifier:NSToolbarFlexibleSpaceItemIdentifier atIndex:0];
|
||||
[_preferencesWindow.toolbar insertItemWithItemIdentifier:NSToolbarFlexibleSpaceItemIdentifier atIndex:_preferencesWindow.toolbar.items.count];
|
||||
for (unsigned i = _preferencesWindow.toolbar.items.count; i--;) {
|
||||
[_preferencesWindow.toolbar.items[i] _view].imageScaling = NSImageScaleNone;
|
||||
}
|
||||
}
|
||||
[_preferencesWindow makeKeyAndOrderFront:self];
|
||||
|
@ -388,7 +403,12 @@ static uint32_t color_to_int(NSColor *color)
|
|||
else {
|
||||
self.updateChanges.preferences.standardFontFamily = @"Lucida Grande";
|
||||
}
|
||||
self.updateChanges.preferences.fixedFontFamily = @"Menlo";
|
||||
if (@available(macOS 10.15, *)) {
|
||||
self.updateChanges.preferences.fixedFontFamily = [NSFont monospacedSystemFontOfSize:12 weight:NSFontWeightRegular].displayName;
|
||||
}
|
||||
else {
|
||||
self.updateChanges.preferences.fixedFontFamily = @"Menlo";
|
||||
}
|
||||
self.updateChanges.drawsBackground = false;
|
||||
[self.updateChanges.mainFrame loadHTMLString:html baseURL:nil];
|
||||
});
|
||||
|
@ -644,6 +664,14 @@ static uint32_t color_to_int(NSColor *color)
|
|||
}
|
||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:contentsTempPath error:nil];
|
||||
|
||||
// Remove the quarantine flag so we don't have to escape translocation
|
||||
NSString *bundlePath = [NSBundle mainBundle].bundlePath;
|
||||
removexattr(bundlePath.UTF8String, "com.apple.quarantine", 0);
|
||||
for (NSString *path in [[NSFileManager defaultManager] enumeratorAtPath:bundlePath]) {
|
||||
removexattr([bundlePath stringByAppendingPathComponent:path].UTF8String, "com.apple.quarantine", 0);
|
||||
};
|
||||
|
||||
_downloadDirectory = nil;
|
||||
atexit_b(^{
|
||||
execl(executablePath.UTF8String, executablePath.UTF8String, "--update-launch", NULL);
|
||||
|
@ -763,4 +791,27 @@ static uint32_t color_to_int(NSColor *color)
|
|||
- (IBAction)nop:(id)sender
|
||||
{
|
||||
}
|
||||
|
||||
/* This runs before C constructors. If we need to escape translocation, we should
|
||||
do it ASAP to minimize our launch time. */
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
if (@available(macOS 10.12, *)) {
|
||||
/* Detect and escape translocation so we can safely update ourselves */
|
||||
if ([[[NSBundle mainBundle] bundlePath] containsString:@"/AppTranslocation/"]) {
|
||||
const char *mountPath = [[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent].UTF8String;
|
||||
struct statfs *mntbuf;
|
||||
int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
|
||||
for (unsigned i = 0; i < mntsize; i++) {
|
||||
if (strcmp(mntbuf[i].f_mntonname, mountPath) == 0) {
|
||||
NSBundle *origBundle = [NSBundle bundleWithPath:@(mntbuf[i].f_mntfromname)];
|
||||
|
||||
execl(origBundle.executablePath.UTF8String, origBundle.executablePath.UTF8String, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBCPUView : NSView
|
||||
- (void)addSample:(double)sample;
|
||||
@end
|
|
@ -0,0 +1,126 @@
|
|||
#import "GBCPUView.h"
|
||||
|
||||
#define SAMPLE_COUNT 0x100 // ~4 seconds
|
||||
|
||||
@implementation GBCPUView
|
||||
{
|
||||
double _samples[SAMPLE_COUNT];
|
||||
size_t _position;
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
CGRect bounds = self.bounds;
|
||||
NSSize size = bounds.size;
|
||||
unsigned factor = [[self.window screen] backingScaleFactor];
|
||||
|
||||
NSBitmapImageRep *maskBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:(unsigned)size.width * factor
|
||||
pixelsHigh:(unsigned)size.height * factor
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:2
|
||||
hasAlpha:true
|
||||
isPlanar:false
|
||||
colorSpaceName:NSDeviceWhiteColorSpace
|
||||
bytesPerRow:size.width * 2 * factor
|
||||
bitsPerPixel:16];
|
||||
|
||||
|
||||
|
||||
NSGraphicsContext *mainContext = [NSGraphicsContext currentContext];
|
||||
|
||||
|
||||
NSColor *greenColor, *redColor;
|
||||
if (@available(macOS 10.10, *)) {
|
||||
greenColor = [NSColor systemGreenColor];
|
||||
redColor = [NSColor systemRedColor];
|
||||
}
|
||||
else {
|
||||
greenColor = [NSColor colorWithRed:3.0 / 16 green:0.5 blue:5.0 / 16 alpha:1.0];
|
||||
redColor = [NSColor colorWithRed:13.0 / 16 green:0.25 blue:0.25 alpha:1.0];
|
||||
}
|
||||
|
||||
|
||||
NSBitmapImageRep *colorBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:SAMPLE_COUNT
|
||||
pixelsHigh:1
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:3
|
||||
hasAlpha:false
|
||||
isPlanar:false
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bytesPerRow:SAMPLE_COUNT * 4
|
||||
bitsPerPixel:32];
|
||||
|
||||
unsigned lastFill = 0;
|
||||
NSBezierPath *line = [NSBezierPath bezierPath];
|
||||
bool isRed = false;
|
||||
{
|
||||
double sample = _samples[_position % SAMPLE_COUNT];
|
||||
[line moveToPoint:NSMakePoint(0,
|
||||
(sample * (size.height - 1) + 0.5) * factor)];
|
||||
isRed = sample == 1;
|
||||
}
|
||||
|
||||
|
||||
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:colorBitmap]];
|
||||
for (unsigned i = 1; i < SAMPLE_COUNT; i++) {
|
||||
double sample = _samples[(i + _position) % SAMPLE_COUNT];
|
||||
[line lineToPoint:NSMakePoint(size.width * i * factor / (SAMPLE_COUNT - 1),
|
||||
(sample * (size.height - 1) + 0.5) * factor)];
|
||||
|
||||
if (isRed != (sample == 1)) {
|
||||
// Color changed
|
||||
[(isRed? redColor : greenColor) setFill];
|
||||
NSRectFill(CGRectMake(lastFill, 0, i - lastFill, 1));
|
||||
lastFill = i;
|
||||
|
||||
isRed ^= true;
|
||||
}
|
||||
}
|
||||
[(isRed? redColor : greenColor) setFill];
|
||||
NSRectFill(CGRectMake(lastFill, 0, SAMPLE_COUNT - lastFill, 1));
|
||||
|
||||
NSBezierPath *fill = [line copy];
|
||||
[fill lineToPoint:NSMakePoint(size.width * factor, 0)];
|
||||
[fill lineToPoint:NSMakePoint(0, 0)];
|
||||
|
||||
NSColor *strokeColor = [NSColor whiteColor];
|
||||
NSColor *fillColor = [strokeColor colorWithAlphaComponent:1 / 3.0];
|
||||
|
||||
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:maskBitmap]];
|
||||
[fillColor setFill];
|
||||
[fill fill];
|
||||
|
||||
[strokeColor setStroke];
|
||||
[line setLineWidth:factor];
|
||||
[line stroke];
|
||||
|
||||
CGContextRef maskContext = CGContextRetain([NSGraphicsContext currentContext].graphicsPort);
|
||||
[NSGraphicsContext setCurrentContext:mainContext];
|
||||
CGContextSaveGState(mainContext.graphicsPort);
|
||||
|
||||
CGImageRef maskImage = CGBitmapContextCreateImage(maskContext);
|
||||
CGContextClipToMask(mainContext.graphicsPort, bounds, maskImage);
|
||||
CGImageRelease(maskImage);
|
||||
|
||||
NSImage *colors = [[NSImage alloc] initWithSize:NSMakeSize(SAMPLE_COUNT, 1)];
|
||||
[colors addRepresentation:colorBitmap];
|
||||
[colors drawInRect:bounds];
|
||||
|
||||
CGContextRestoreGState(mainContext.graphicsPort);
|
||||
CGContextRelease(maskContext);
|
||||
|
||||
|
||||
[super drawRect:dirtyRect];
|
||||
}
|
||||
|
||||
- (void)addSample:(double)sample
|
||||
{
|
||||
_samples[_position++] = sample;
|
||||
if (_position == SAMPLE_COUNT) {
|
||||
_position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBColorTextCell : NSTextFieldCell
|
||||
|
||||
@end
|
|
@ -0,0 +1,19 @@
|
|||
#import "GBColorTextCell.h"
|
||||
|
||||
@implementation GBColorTextCell
|
||||
|
||||
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
|
||||
{
|
||||
[self.backgroundColor set];
|
||||
NSRectFill(cellFrame);
|
||||
|
||||
NSBezierPath *path = [NSBezierPath bezierPathWithRect:cellFrame];
|
||||
path.lineWidth = 2;
|
||||
|
||||
[[NSColor colorWithWhite:0 alpha:0.25] setStroke];
|
||||
[path addClip];
|
||||
[path stroke];
|
||||
|
||||
[self drawInteriorWithFrame:cellFrame inView:controlView];
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBDisabledButton : NSButton
|
||||
|
||||
@end
|
|
@ -0,0 +1,8 @@
|
|||
#import "GBDisabledButton.h"
|
||||
|
||||
@implementation GBDisabledButton
|
||||
- (void)mouseDown:(NSEvent *)event
|
||||
{
|
||||
|
||||
}
|
||||
@end
|
|
@ -136,7 +136,7 @@
|
|||
if (!_gb) {
|
||||
return [NSString stringWithFormat:@"$%llX", offset];
|
||||
}
|
||||
return @(GB_debugger_describe_address(_gb, offset + _baseAddress, _bankForDescription, false, isRangeEnd));
|
||||
return @(GB_debugger_describe_address(_gb, offset + _baseAddress, offset < 0x4000? -1 :_bankForDescription, false, isRangeEnd));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -46,17 +46,29 @@
|
|||
-(void)drawKnob:(NSRect)knobRect
|
||||
{
|
||||
[super drawKnob:knobRect];
|
||||
NSRect peekRect = knobRect;
|
||||
peekRect.size.width /= 2;
|
||||
peekRect.size.height = peekRect.size.width;
|
||||
peekRect.origin.x += peekRect.size.width / 2;
|
||||
peekRect.origin.y += peekRect.size.height / 2;
|
||||
NSBezierPath *path = nil;
|
||||
if (@available(macos 26.0, *)) {
|
||||
NSRect peekRect = knobRect;
|
||||
peekRect.size.height /= 2;
|
||||
peekRect.size.width -= peekRect.size.height;
|
||||
peekRect.origin.x += peekRect.size.height / 2;
|
||||
peekRect.origin.y += peekRect.size.height / 2;
|
||||
path = [NSBezierPath bezierPathWithRoundedRect:peekRect xRadius:peekRect.size.height / 2 yRadius:peekRect.size.height / 2];
|
||||
}
|
||||
else {
|
||||
NSRect peekRect = knobRect;
|
||||
peekRect.size.width /= 2;
|
||||
peekRect.size.height = peekRect.size.width;
|
||||
peekRect.origin.x += peekRect.size.width / 2;
|
||||
peekRect.origin.y += peekRect.size.height / 2;
|
||||
path = [NSBezierPath bezierPathWithOvalInRect:peekRect];
|
||||
|
||||
}
|
||||
NSColor *color = self.colorValue;
|
||||
if (!self.enabled) {
|
||||
color = [color colorWithAlphaComponent:0.5];
|
||||
}
|
||||
[color setFill];
|
||||
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:peekRect];
|
||||
[path fill];
|
||||
[[NSColor colorWithWhite:0 alpha:0.25] setStroke];
|
||||
[path setLineWidth:0.5];
|
||||
|
|
|
@ -44,7 +44,8 @@
|
|||
}
|
||||
|
||||
if (parent.displayScrollRect) {
|
||||
NSBezierPath *path = [NSBezierPath bezierPathWithRect:CGRectInfinite];
|
||||
// CGRectInfinite in NSBezierPath is broken in newer macOS versions
|
||||
NSBezierPath *path = [NSBezierPath bezierPathWithRect:CGRectMake(-0x4000, -0x4000, 0x8000, 0x8000)];
|
||||
for (unsigned x = 0; x < 2; x++) {
|
||||
for (unsigned y = 0; y < 2; y++) {
|
||||
NSRect rect = parent.scrollRect;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
static GBJoyConManager *manager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
manager = [[self alloc] _init];
|
||||
manager = [[super allocWithZone:nil] _init];
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
@ -32,6 +32,16 @@
|
|||
return ret;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||
{
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
+ (instancetype)alloc
|
||||
{
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self.class sharedInstance];
|
||||
|
|
|
@ -399,10 +399,16 @@ static double blend(double from, double to, double position)
|
|||
[sender.window.sheetParent endSheet:sender.window];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
+ (instancetype)alloc
|
||||
{
|
||||
static id singleton = nil;
|
||||
if (singleton) return singleton;
|
||||
return (singleton = [super init]);
|
||||
return (singleton = [super allocWithZone:nil]);
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||
{
|
||||
return [self alloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -22,44 +22,44 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="512" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ypt-t4-Mf3">
|
||||
<rect key="frame" x="131" y="0.0" width="96" height="24"/>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ypt-t4-Mf3">
|
||||
<rect key="frame" x="131" y="0.0" width="95" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="sKu-Uy-2LG">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="sKu-Uy-2LG" customClass="GBColorTextCell">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KkX-Z8-Sqi">
|
||||
<rect key="frame" x="226" y="0.0" width="96" height="24"/>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KkX-Z8-Sqi">
|
||||
<rect key="frame" x="226" y="0.0" width="95" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="9LH-TF-W1L">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="9LH-TF-W1L" customClass="GBColorTextCell">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jDk-Ej-4yI">
|
||||
<rect key="frame" x="321" y="0.0" width="96" height="24"/>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jDk-Ej-4yI">
|
||||
<rect key="frame" x="321" y="0.0" width="95" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="arE-i5-zCC">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="arE-i5-zCC" customClass="GBColorTextCell">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7PI-YE-fTk">
|
||||
<rect key="frame" x="416" y="0.0" width="96" height="24"/>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7PI-YE-fTk">
|
||||
<rect key="frame" x="416" y="0.0" width="95" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="ZbU-nE-FsE">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="ZbU-nE-FsE" customClass="GBColorTextCell">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NOK-yI-LKh">
|
||||
<rect key="frame" x="4" y="0.0" width="121" height="20"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NOK-yI-LKh">
|
||||
<rect key="frame" x="8" y="0.0" width="121" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Background 0" id="qM4-cY-SDE">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
@property IBOutlet NSPopUpButton *colorPalettePopupButton;
|
||||
@property IBOutlet NSPopUpButton *hotkey1PopupButton;
|
||||
@property IBOutlet NSPopUpButton *hotkey2PopupButton;
|
||||
@property IBOutlet NSButton *turboCapButton;
|
||||
@property IBOutlet NSSlider *turboCapSlider;
|
||||
@property IBOutlet NSTextField *turboCapLabel;
|
||||
|
||||
@property IBOutlet GBTitledPopUpButton *fontPopupButton;
|
||||
@property IBOutlet NSStepper *fontSizeStepper;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
- (NSWindowToolbarStyle)toolbarStyle
|
||||
{
|
||||
return NSWindowToolbarStyleExpanded;
|
||||
return NSWindowToolbarStylePreference;
|
||||
}
|
||||
|
||||
- (void)close
|
||||
|
@ -342,6 +342,13 @@ static inline NSString *keyEquivalentString(NSMenuItem *item)
|
|||
|
||||
_fontSizeStepper.intValue = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBDebuggerFontSize"];
|
||||
[self updateFonts];
|
||||
|
||||
double cap = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBTurboCap"];
|
||||
if (cap) {
|
||||
_turboCapSlider.intValue = round(cap * 100);
|
||||
_turboCapButton.state = NSOnState;
|
||||
}
|
||||
[self turboCapToggled:_turboCapButton];
|
||||
}
|
||||
|
||||
- (IBAction)fontSizeChanged:(id)sender
|
||||
|
@ -555,4 +562,35 @@ static inline NSString *keyEquivalentString(NSMenuItem *item)
|
|||
[GBJoyConManager sharedInstance].arrangementMode = false;
|
||||
}
|
||||
|
||||
- (IBAction)turboCapToggled:(NSButton *)sender
|
||||
{
|
||||
if (sender.state) {
|
||||
_turboCapSlider.enabled = true;
|
||||
[self turboCapChanged:_turboCapSlider];
|
||||
if (@available(macOS 10.10, *)) {
|
||||
_turboCapLabel.textColor = [NSColor labelColor];
|
||||
}
|
||||
else {
|
||||
_turboCapLabel.textColor = [NSColor blackColor];
|
||||
}
|
||||
}
|
||||
else {
|
||||
_turboCapSlider.enabled = false;
|
||||
_turboCapLabel.enabled = false;
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:0 forKey:@"GBTurboCap"];
|
||||
if (@available(macOS 10.10, *)) {
|
||||
_turboCapLabel.textColor = [NSColor disabledControlTextColor];
|
||||
}
|
||||
else {
|
||||
_turboCapLabel.textColor = [NSColor colorWithWhite:0 alpha:0.25];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)turboCapChanged:(NSSlider *)sender
|
||||
{
|
||||
_turboCapLabel.stringValue = [NSString stringWithFormat:@"%d%%", sender.intValue];
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:sender.doubleValue / 100.0 forKey:@"GBTurboCap"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="332" height="221"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="H3v-X3-48q">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="H3v-X3-48q">
|
||||
<rect key="frame" x="18" y="192" width="296" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Title" id="BwZ-Zj-sP6">
|
||||
|
@ -34,7 +34,7 @@
|
|||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="gaD-ZH-Beh">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="gaD-ZH-Beh">
|
||||
<rect key="frame" x="18" y="166" width="296" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Author" id="IgT-r1-T38">
|
||||
|
@ -59,7 +59,7 @@
|
|||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="YJh-dI-A5D">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="message"/>
|
||||
<menu key="menu" id="Knp-Ok-Pb4"/>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
|
@ -67,7 +67,7 @@
|
|||
</connections>
|
||||
</popUpButton>
|
||||
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SRS-M5-VVL">
|
||||
<rect key="frame" x="247" y="129" width="68" height="24"/>
|
||||
<rect key="frame" x="247" y="128" width="68" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="momentary" id="cmq-I8-cFL">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -94,7 +94,7 @@
|
|||
</customView>
|
||||
</subviews>
|
||||
</customView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="2dl-dH-E3J">
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="2dl-dH-E3J">
|
||||
<rect key="frame" x="18" y="5" width="296" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="Copyright" id="nM9-oF-OV9">
|
||||
|
|
|
@ -11,23 +11,23 @@
|
|||
|
||||
@implementation GBTerminalTextFieldCell
|
||||
{
|
||||
GBTerminalTextView *field_editor;
|
||||
GBTerminalTextView *_fieldEditor;
|
||||
}
|
||||
|
||||
- (NSTextView *)fieldEditorForView:(NSTextField *)controlView
|
||||
{
|
||||
if (field_editor) {
|
||||
field_editor.gb = self.gb;
|
||||
return field_editor;
|
||||
if (_fieldEditor) {
|
||||
_fieldEditor.gb = self.gb;
|
||||
return _fieldEditor;
|
||||
}
|
||||
field_editor = [[GBTerminalTextView alloc] init];
|
||||
[field_editor setFieldEditor:true];
|
||||
field_editor.gb = self.gb;
|
||||
field_editor->_field = (NSTextField *)controlView;
|
||||
_fieldEditor = [[GBTerminalTextView alloc] init];
|
||||
[_fieldEditor setFieldEditor:true];
|
||||
_fieldEditor.gb = self.gb;
|
||||
_fieldEditor->_field = (NSTextField *)controlView;
|
||||
((NSTextFieldCell *)controlView.cell).textInset =
|
||||
field_editor.textContainerInset =
|
||||
NSMakeSize(0, 2);
|
||||
return field_editor;
|
||||
_fieldEditor.textContainerInset =
|
||||
NSMakeSize(7, 2);
|
||||
return _fieldEditor;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBToolbarFieldCell : NSSearchFieldCell
|
||||
|
||||
@end
|
|
@ -0,0 +1,40 @@
|
|||
#import "GBToolbarFieldCell.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface NSTextFieldCell()
|
||||
- (void)textDidChange:(id)sender;
|
||||
@end
|
||||
|
||||
@implementation GBToolbarFieldCell
|
||||
|
||||
- (void)textDidChange:(id)sender
|
||||
{
|
||||
IMP imp = [NSTextFieldCell instanceMethodForSelector:_cmd];
|
||||
method_setImplementation(class_getInstanceMethod([GBToolbarFieldCell class], _cmd), imp);
|
||||
((void(*)(id, SEL, id))imp)(self, _cmd, sender);
|
||||
}
|
||||
|
||||
- (void)endEditing:(NSText *)textObj
|
||||
{
|
||||
IMP imp = [NSTextFieldCell instanceMethodForSelector:_cmd];
|
||||
method_setImplementation(class_getInstanceMethod([GBToolbarFieldCell class], _cmd), imp);
|
||||
((void(*)(id, SEL, id))imp)(self, _cmd, textObj);
|
||||
}
|
||||
|
||||
- (void)resetSearchButtonCell
|
||||
{
|
||||
}
|
||||
|
||||
- (void)resetCancelButtonCell
|
||||
{
|
||||
}
|
||||
|
||||
// We only need this hack on Solarium, avoid regressions
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||
{
|
||||
if (@available(macOS 26.0, *)) {
|
||||
return [super allocWithZone:zone];
|
||||
}
|
||||
return (id)[NSTextFieldCell allocWithZone:zone];
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBToolbarPopUpButtonCell : NSPopUpButtonCell
|
||||
|
||||
@end
|
|
@ -0,0 +1,17 @@
|
|||
#import "GBToolbarPopUpButtonCell.h"
|
||||
|
||||
@implementation GBToolbarPopUpButtonCell
|
||||
|
||||
-(void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
|
||||
{
|
||||
[super drawInteriorWithFrame:CGRectInset(cellFrame, 5, 0) inView:controlView];
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||
{
|
||||
if (@available(macOS 26.0, *)) {
|
||||
return [super allocWithZone:zone];
|
||||
}
|
||||
return (id)[NSPopUpButtonCell allocWithZone:zone];
|
||||
}
|
||||
@end
|
|
@ -148,6 +148,9 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||
[self observeStandardDefaultsKey:@"GBAspectRatioUnkept" withBlock:^(id newValue) {
|
||||
[weakSelf setFrame:weakSelf.superview.frame];
|
||||
}];
|
||||
[self observeStandardDefaultsKey:@"GBForceIntegerScale" withBlock:^(id newValue) {
|
||||
[weakSelf setFrame:weakSelf.superview.frame];
|
||||
}];
|
||||
[self observeStandardDefaultsKey:@"JoyKitDefaultControllers" withBlock:^(id newValue) {
|
||||
[weakSelf reassignControllers];
|
||||
}];
|
||||
|
@ -282,7 +285,9 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||
|
||||
- (void)setFrame:(NSRect)frame
|
||||
{
|
||||
frame = self.superview.frame;
|
||||
NSView *superview = self.superview;
|
||||
if (GB_unlikely(!superview)) return;
|
||||
frame = superview.frame;
|
||||
if (_gb && ![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
|
||||
double ratio = frame.size.width / frame.size.height;
|
||||
double width = GB_get_screen_width(_gb);
|
||||
|
@ -300,6 +305,19 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||
frame.origin.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_gb && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBForceIntegerScale"]) {
|
||||
double factor = self.window.backingScaleFactor;
|
||||
double width = GB_get_screen_width(_gb) / factor;
|
||||
double height = GB_get_screen_height(_gb) / factor;
|
||||
|
||||
double new_width = floor(frame.size.width / width) * width;
|
||||
double new_height = floor(frame.size.height / height) * height;
|
||||
frame.origin.x += floor((frame.size.width - new_width) / 2);
|
||||
frame.origin.y += floor((frame.size.height - new_height) / 2);
|
||||
frame.size.width = new_width;
|
||||
frame.size.height = new_height;
|
||||
}
|
||||
|
||||
[super setFrame:frame];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBWindow : NSWindow
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
#import "GBWindow.h"
|
||||
|
||||
@interface NSWindow(private)
|
||||
- (void)_zoomFill:(id)sender;
|
||||
@end
|
||||
|
||||
/*
|
||||
For some reason, Apple replaced the alt + zoom button behavior to be "fill" rather than zoom.
|
||||
I don't like that. It prevents SameBoy's integer scaling from working. Let's restore it.
|
||||
*/
|
||||
|
||||
@implementation GBWindow
|
||||
- (void)_zoomFill:(id)sender
|
||||
{
|
||||
if (sender == [self standardWindowButton:NSWindowZoomButton] &&
|
||||
((self.currentEvent.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagOption)) {
|
||||
[self zoom:sender];
|
||||
return;
|
||||
}
|
||||
[super _zoomFill:sender];
|
||||
}
|
||||
@end
|
BIN
Cocoa/Icon.png
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
@ -113,7 +113,9 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>SameBoy</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon.icns</string>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.github.liji32.sameboy</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
|
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 281 B |
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 461 B |
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.9" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.9"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
|
@ -82,7 +82,7 @@
|
|||
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hot Swap Cartridge…" keyEquivalent="o" id="Rik-p8-QCf">
|
||||
<menuItem title="Hot Swap Cartridge…" secondaryImage="arrow.down.left.arrow.up.right" catalog="system" keyEquivalent="o" id="Rik-p8-QCf">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="cartSwap:" target="-1" id="iXS-Ol-IS0"/>
|
||||
|
@ -102,7 +102,7 @@
|
|||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
||||
<menuItem title="New Cartridge Instance…" keyEquivalent="n" id="Vld-be-NZu">
|
||||
<menuItem title="New Cartridge Instance…" secondaryImage="plus.square.on.square" catalog="system" keyEquivalent="n" id="Vld-be-NZu">
|
||||
<connections>
|
||||
<action selector="newCartridgeInstance:" target="-1" id="GJc-xU-ZEr"/>
|
||||
</connections>
|
||||
|
@ -158,7 +158,7 @@
|
|||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Aru-vr-frG"/>
|
||||
<menuItem title="Find" id="efg-jw-GVP">
|
||||
<menuItem title="Find" secondaryImage="text.page.badge.magnifyingglass" catalog="system" id="efg-jw-GVP">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Find" id="4R6-IU-Jq6">
|
||||
<items>
|
||||
|
@ -197,23 +197,23 @@
|
|||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Emulation" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Reset" keyEquivalent="r" id="p0i-Lt-sTg">
|
||||
<menuItem title="Reset" secondaryImage="arrow.trianglehead.2.counterclockwise.rotate.90" catalog="system" keyEquivalent="r" id="p0i-Lt-sTg">
|
||||
<connections>
|
||||
<action selector="reset:" target="-1" id="DKW-Bd-h3v"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Quick Reset" tag="-1" alternate="YES" keyEquivalent="r" id="uPG-01-49E">
|
||||
<menuItem title="Quick Reset" secondaryImage="arrow.trianglehead.2.counterclockwise.rotate.90" catalog="system" tag="-1" alternate="YES" keyEquivalent="r" id="uPG-01-49E">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="reset:" target="-1" id="VV1-VP-L7g"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Reload ROM" alternate="YES" keyEquivalent="R" id="eQP-Fb-nkz">
|
||||
<menuItem title="Reload ROM" secondaryImage="arrow.trianglehead.2.counterclockwise.rotate.90" catalog="system" alternate="YES" keyEquivalent="R" id="eQP-Fb-nkz">
|
||||
<connections>
|
||||
<action selector="reloadROM:" target="-1" id="BpN-8V-Csg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Pause" keyEquivalent="p" id="4K4-hw-R7Q">
|
||||
<menuItem title="Pause" secondaryImage="pause" catalog="system" keyEquivalent="p" id="4K4-hw-R7Q">
|
||||
<connections>
|
||||
<action selector="togglePause:" target="-1" id="osW-wt-QAa"/>
|
||||
</connections>
|
||||
|
@ -263,7 +263,7 @@
|
|||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="QIS-av-Byy"/>
|
||||
<menuItem title="Save State" id="Hdz-ut-okE">
|
||||
<menuItem title="Save State" secondaryImage="square.and.arrow.down" catalog="system" id="Hdz-ut-okE">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Save State" id="Mxx-u1-M9D">
|
||||
<items>
|
||||
|
@ -320,7 +320,7 @@
|
|||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Load State" id="EXD-SL-cz4">
|
||||
<menuItem title="Load State" secondaryImage="square.and.arrow.up" catalog="system" id="EXD-SL-cz4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Load State" id="l9D-Ej-sh2">
|
||||
<items>
|
||||
|
@ -388,7 +388,7 @@
|
|||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
|
||||
<menuItem title="Save Screenshot" keyEquivalent="s" id="0J3-yf-iXs">
|
||||
<menuItem title="Save Screenshot" secondaryImage="photo" catalog="system" keyEquivalent="s" id="0J3-yf-iXs">
|
||||
<connections>
|
||||
<action selector="saveScreenshot:" target="-1" id="gJd-ml-J8p"/>
|
||||
</connections>
|
||||
|
@ -405,12 +405,12 @@
|
|||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="DPb-Sh-5tg"/>
|
||||
<menuItem title="Start Audio Recording…" keyEquivalent="A" id="1UK-8n-QPP">
|
||||
<menuItem title="Start Audio Recording…" secondaryImage="record.circle" catalog="system" keyEquivalent="A" id="1UK-8n-QPP">
|
||||
<connections>
|
||||
<action selector="toggleAudioRecording:" target="-1" id="YE5-mi-Yzd"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Mute Sound" keyEquivalent="m" id="zo0-Rh-wTu">
|
||||
<menuItem title="Mute Sound" secondaryImage="speaker.slash" catalog="system" keyEquivalent="m" id="zo0-Rh-wTu">
|
||||
<connections>
|
||||
<action selector="mute:" target="-1" id="eK3-ea-ExJ"/>
|
||||
</connections>
|
||||
|
@ -433,7 +433,7 @@
|
|||
<action selector="showCheats:" target="-1" id="stn-Ei-aFE"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Search Cheats" id="LZV-QK-YXi">
|
||||
<menuItem title="Search Cheats" secondaryImage="text.page.badge.magnifyingglass" catalog="system" id="LZV-QK-YXi">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="showCheatSearch:" target="-1" id="eI1-x0-ykn"/>
|
||||
|
@ -442,9 +442,9 @@
|
|||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Connectivity" id="IcW-ZC-4wb">
|
||||
<menuItem title="Connect" id="IcW-ZC-4wb">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Connectivity" id="BDM-Cv-BOm">
|
||||
<menu key="submenu" title="Connect" id="BDM-Cv-BOm">
|
||||
<items>
|
||||
<menuItem title="None" id="SiH-Q4-OBY">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
|
@ -452,7 +452,7 @@
|
|||
<action selector="disconnectAllAccessories:" target="-1" id="5hY-9U-nRn"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Game Link Cable & Infrared" id="V4S-Fo-xJK">
|
||||
<menuItem title="Game Link Cable & Infrared" secondaryImage="cable.connector.horizontal" catalog="system" id="V4S-Fo-xJK">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Game Link Cable & Infrared" id="6sJ-Wz-QLj">
|
||||
<connections>
|
||||
|
@ -463,13 +463,13 @@
|
|||
<action selector="nop:" target="-3" id="Bpa-0C-lkN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Game Boy Printer" id="zHR-Ha-pOR">
|
||||
<menuItem title="Game Boy Printer" secondaryImage="printer" catalog="system" id="zHR-Ha-pOR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Workboy" id="lo9-CX-BJj">
|
||||
<menuItem title="Workboy" secondaryImage="keyboard" catalog="system" id="lo9-CX-BJj">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="connectWorkboy:" target="-1" id="6vS-bq-wAX"/>
|
||||
|
@ -482,14 +482,14 @@
|
|||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Develop" id="UVb-cc-at0">
|
||||
<items>
|
||||
<menuItem title="Developer Mode" id="Qx6-Tt-zZR">
|
||||
<menuItem title="Developer Mode" secondaryImage="wrench.and.screwdriver" catalog="system" id="Qx6-Tt-zZR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleDeveloperMode:" target="-3" id="PIc-o3-bzb"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="66c-T0-8pW"/>
|
||||
<menuItem title="Show Console" id="Wse-UY-Y9l">
|
||||
<menuItem title="Show Console" secondaryImage="apple.terminal" catalog="system" id="Wse-UY-Y9l">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="showConsoleWindow:" target="-1" id="mFf-4i-jLG"/>
|
||||
|
@ -501,14 +501,14 @@
|
|||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="3If-Yf-U7B"/>
|
||||
<menuItem title="Break Debugger" keyEquivalent="c" id="uBD-GY-Doi">
|
||||
<menuItem title="Break Debugger" secondaryImage="pause" catalog="system" keyEquivalent="c" id="uBD-GY-Doi">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES"/>
|
||||
<connections>
|
||||
<action selector="interrupt:" target="-1" id="ZmB-wG-fTs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="M6n-8G-LZS"/>
|
||||
<menuItem title="Audio Channels" id="Cib-LN-Y4o">
|
||||
<menuItem title="Audio Channels" secondaryImage="speaker.wave.3" catalog="system" id="Cib-LN-Y4o">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Audio Channels" id="l22-4p-yTK">
|
||||
<items>
|
||||
|
@ -540,13 +540,13 @@
|
|||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="v5c-ri-BoZ"/>
|
||||
<menuItem title="Show Background and Window" state="on" id="yfD-Qd-zoz">
|
||||
<menuItem title="Show Background and Window" state="on" secondaryImage="mountain.2" catalog="system" id="yfD-Qd-zoz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleDisplayBackground:" target="-1" id="p5b-1n-SPR"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show Objects" state="on" id="OWx-a0-vQk">
|
||||
<menuItem title="Show Objects" state="on" secondaryImage="cube" catalog="system" id="OWx-a0-vQk">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleDisplayObjects:" target="-1" id="8ie-ey-739"/>
|
||||
|
@ -620,4 +620,24 @@
|
|||
<point key="canvasLocation" x="140" y="260"/>
|
||||
</menu>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="apple.terminal" catalog="system" width="18" height="14"/>
|
||||
<image name="arrow.down.left.arrow.up.right" catalog="system" width="18" height="17"/>
|
||||
<image name="arrow.trianglehead.2.counterclockwise.rotate.90" catalog="system" width="16" height="15"/>
|
||||
<image name="cable.connector.horizontal" catalog="system" width="19" height="7"/>
|
||||
<image name="cube" catalog="system" width="16" height="17"/>
|
||||
<image name="keyboard" catalog="system" width="19" height="13"/>
|
||||
<image name="mountain.2" catalog="system" width="25" height="14"/>
|
||||
<image name="pause" catalog="system" width="9" height="13"/>
|
||||
<image name="photo" catalog="system" width="18" height="14"/>
|
||||
<image name="plus.square.on.square" catalog="system" width="17" height="16"/>
|
||||
<image name="printer" catalog="system" width="18" height="16"/>
|
||||
<image name="record.circle" catalog="system" width="15" height="15"/>
|
||||
<image name="speaker.slash" catalog="system" width="14" height="16"/>
|
||||
<image name="speaker.wave.3" catalog="system" width="22" height="15"/>
|
||||
<image name="square.and.arrow.down" catalog="system" width="15" height="17"/>
|
||||
<image name="square.and.arrow.up" catalog="system" width="15" height="18"/>
|
||||
<image name="text.page.badge.magnifyingglass" catalog="system" width="15" height="18"/>
|
||||
<image name="wrench.and.screwdriver" catalog="system" width="19" height="18"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
#import <objc/runtime.h>
|
||||
|
||||
@interface NSTextFieldCell ()
|
||||
- (CGRect)_textLayerDrawingRectForCellFrame:(CGRect)rect;
|
||||
@property NSSize textInset;
|
||||
- (bool)_isEditingInView:(NSView *)view;
|
||||
@end
|
||||
|
||||
@implementation NSTextFieldCell (Inset)
|
||||
|
@ -19,21 +19,59 @@
|
|||
return [objc_getAssociatedObject(self, _cmd) sizeValue];
|
||||
}
|
||||
|
||||
- (CGRect)_textLayerDrawingRectForCellFrameHook:(CGRect)rect
|
||||
- (void)drawWithFrameHook:(NSRect)cellFrame inView:(NSView *)controlView
|
||||
{
|
||||
CGRect ret = [self _textLayerDrawingRectForCellFrameHook:rect];
|
||||
NSSize inset = self.textInset;
|
||||
ret.origin.x += inset.width;
|
||||
ret.origin.y += inset.height;
|
||||
ret.size.width -= inset.width;
|
||||
ret.size.height -= inset.height;
|
||||
return ret;
|
||||
if (self.drawsBackground) {
|
||||
[self.backgroundColor setFill];
|
||||
if ([self _isEditingInView:controlView]) {
|
||||
NSRectFill(cellFrame);
|
||||
}
|
||||
else {
|
||||
NSRectFill(NSMakeRect(cellFrame.origin.x, cellFrame.origin.y,
|
||||
cellFrame.size.width, inset.height));
|
||||
NSRectFill(NSMakeRect(cellFrame.origin.x, cellFrame.origin.y + cellFrame.size.height - inset.height,
|
||||
cellFrame.size.width, inset.height));
|
||||
|
||||
NSRectFill(NSMakeRect(cellFrame.origin.x, cellFrame.origin.y + inset.height,
|
||||
inset.width, cellFrame.size.height - inset.height * 2));
|
||||
NSRectFill(NSMakeRect(cellFrame.origin.x + cellFrame.size.width - inset.width, cellFrame.origin.y + inset.height,
|
||||
inset.width, cellFrame.size.height - inset.height * 2));
|
||||
}
|
||||
}
|
||||
cellFrame.origin.x += inset.width;
|
||||
cellFrame.origin.y += inset.height;
|
||||
cellFrame.size.width -= inset.width * 2;
|
||||
cellFrame.size.height -= inset.height * 2;
|
||||
[self drawWithFrameHook:cellFrame inView:controlView];
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
method_exchangeImplementations(class_getInstanceMethod(self, @selector(_textLayerDrawingRectForCellFrame:)),
|
||||
class_getInstanceMethod(self, @selector(_textLayerDrawingRectForCellFrameHook:)));
|
||||
method_exchangeImplementations(class_getInstanceMethod(self, @selector(drawWithFrame:inView:)),
|
||||
class_getInstanceMethod(self, @selector(drawWithFrameHook:inView:)));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSTextField (Inset)
|
||||
|
||||
- (bool)wantsUpdateLayerHook
|
||||
{
|
||||
CGSize inset = ((NSTextFieldCell *)self.cell).textInset;
|
||||
if (inset.width || inset.height) return false;
|
||||
return [self wantsUpdateLayerHook];
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
Method method = class_getInstanceMethod(self, @selector(wantsUpdateLayer));
|
||||
if (class_addMethod(self, @selector(wantsUpdateLayer), method_getImplementation(method), method_getTypeEncoding(method))) {
|
||||
method = class_getInstanceMethod(self, @selector(wantsUpdateLayer));
|
||||
}
|
||||
method_exchangeImplementations(method,
|
||||
class_getInstanceMethod(self, @selector(wantsUpdateLayerHook)));
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -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>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,168 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Comment out to debug
|
||||
#define NSLog(...)
|
||||
|
||||
// Solarium has weird proportions, we need to fix them.
|
||||
@implementation NSControl (SolariumFixer)
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
if (@available(macOS 26.0, *)) {
|
||||
if ([self.superview isKindOfClass:objc_getClass("NSToolbarItemViewer")]) return;
|
||||
|
||||
if ([self isKindOfClass:[NSStepper class]]) {
|
||||
NSLog(@"Stepper needs adjustment: %s in window %@", sel_getName(self.action), self.window.title);
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y += 1;
|
||||
self.frame = frame;
|
||||
return;
|
||||
}
|
||||
if (self.controlSize != NSControlSizeRegular) return;
|
||||
|
||||
if ([self isKindOfClass:[NSPopUpButton class]] ) {
|
||||
NSLog(@"Popup button needs adjustment: %@ in window %@", ((NSButton *)self).title, self.window.title);
|
||||
CGRect frame = self.frame;
|
||||
if (frame.size.height != 25) {
|
||||
NSLog(@"%@ in window %@ has the wrong height!", ((NSButton *)self).title, self.window.title);
|
||||
return;
|
||||
}
|
||||
frame.size.width -= 2 + 5; // Remove 5 from the right and 2 from the left
|
||||
frame.origin.x += 2;
|
||||
frame.origin.y += 2;
|
||||
self.frame = frame;
|
||||
}
|
||||
else if ([self isKindOfClass:[NSSegmentedControl class]] ) {
|
||||
NSLog(@"Segmented button needs adjustment: %s in window %@", sel_getName(self.action), self.window.title);
|
||||
CGRect frame = self.frame;
|
||||
if (frame.size.height != 25) {
|
||||
NSLog(@"%s in window %@ has the wrong height!", sel_getName(self.action), self.window.title);
|
||||
return;
|
||||
}
|
||||
frame.origin.x += 8;
|
||||
frame.origin.y += 1;
|
||||
self.frame = frame;
|
||||
}
|
||||
else if ([self isKindOfClass:[NSTextField class]]) {
|
||||
NSTextField *field = (id)self;
|
||||
if (![field isBezeled]) return;
|
||||
NSLog(@"Text field needs adjustment: %@ in window %@", ((NSTextField *)self).placeholderString, self.window.title);
|
||||
CGRect frame = self.frame;
|
||||
if (frame.size.height == 21) {
|
||||
frame.size.height = 24;
|
||||
}
|
||||
else {
|
||||
NSLog(@"%@ in window %@ has the wrong height!", ((NSTextField *)self).placeholderString, self.window.title);
|
||||
return;
|
||||
}
|
||||
frame.size.width -= 1 + 1; // Remove 1 from the right and 1 from the left
|
||||
frame.origin.x += 1;
|
||||
frame.origin.y -= 1;
|
||||
|
||||
self.frame = frame;
|
||||
}
|
||||
else if ([self isKindOfClass:[NSButton class]]) {
|
||||
NSLog(@"Button: %@ in window %@", @(sel_getName(self.action)), self.window.title);
|
||||
NSButton *button = (id)self;
|
||||
if (!button.isBordered) return;
|
||||
if (button.bezelStyle == NSBezelStylePush) {
|
||||
NSLog(@"Button needs adjustment: %@ in window %@", @(sel_getName(self.action)), self.window.title);
|
||||
CGRect frame = self.frame;
|
||||
frame.size.width -= 7 + 7; // Remove 7 from the right and 7 from the left
|
||||
frame.origin.x += 7;
|
||||
frame.origin.y += 5;
|
||||
if (frame.size.height == 32) {
|
||||
frame.size.height = 25;
|
||||
}
|
||||
else {
|
||||
NSLog(@"%@ in window %@ has the wrong height!", @(sel_getName(self.action)), self.window.title);
|
||||
} self.frame = frame;
|
||||
}
|
||||
else if (button.bezelStyle == NSBezelStyleRegularSquare) {
|
||||
CGRect frame = self.frame;
|
||||
if (frame.size.height != 18) return;
|
||||
NSLog(@"Check/Radio needs adjustment: %@ in window %@", ((NSButton *)self).title, self.window.title);
|
||||
frame.size.width -= 2;
|
||||
frame.origin.x += 2;
|
||||
frame.origin.y += 1;
|
||||
frame.size.height = 16;
|
||||
self.frame = frame;
|
||||
}
|
||||
else if (button.bezelStyle == NSBezelStyleHelpButton) {
|
||||
CGRect frame = self.frame;
|
||||
NSLog(@"Help button needs adjustment: %@ in window %@", ((NSButton *)self).title, self.window.title);
|
||||
frame.origin.y += 2;
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
else if ([self isKindOfClass:[NSSlider class]]) {
|
||||
NSLog(@"Slider needs adjustment: %s in window %@", sel_getName(self.action), self.window.title);
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y += 3;
|
||||
self.frame = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSToolbarItem (SolariumFixer)
|
||||
|
||||
static CGSize minSizeHook(id self, SEL _cmd)
|
||||
{
|
||||
return CGSizeMake(8, 0);
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
if (@available(macOS 26.0, *)) {
|
||||
NSLog(@"Toolbar item %@ has view %@", self.label, self.view);
|
||||
if ([self.view isKindOfClass:[NSTextField class]]) {
|
||||
NSLog(@"Handling (Text field)");
|
||||
self.bordered = true;
|
||||
|
||||
NSSize maxSize = self.maxSize;
|
||||
maxSize.height = 36;
|
||||
self.maxSize = maxSize;
|
||||
|
||||
NSSize minSize = self.minSize;
|
||||
minSize.height = 36;
|
||||
self.minSize = minSize;
|
||||
|
||||
((NSTextField *)self.view).backgroundColor = [NSColor clearColor];
|
||||
((NSTextField *)self.view).bezeled = false;
|
||||
((NSTextField *)self.view).bordered = true;
|
||||
|
||||
// Work around even more AppKit bugs
|
||||
self.toolbar.displayMode++;
|
||||
self.toolbar.displayMode--;
|
||||
}
|
||||
else if ([self.view isKindOfClass:[NSPopUpButton class]]) {
|
||||
NSLog(@"Handling (Pop up button)");
|
||||
self.bordered = true;
|
||||
|
||||
NSSize maxSize = self.maxSize;
|
||||
maxSize.height = 28;
|
||||
self.maxSize = maxSize;
|
||||
|
||||
NSSize minSize = self.minSize;
|
||||
minSize.height = 28;
|
||||
self.minSize = minSize;
|
||||
}
|
||||
}
|
||||
else if (@available(macOS 11.0, *)) { // While at it, make macOS 11-15 a bit more consistent
|
||||
if ([self.view isKindOfClass:[NSTextField class]]) {
|
||||
((NSTextField *)self.view).bezelStyle = NSTextFieldRoundedBezel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
if (@available(macOS 26.0, *)) {
|
||||
method_setImplementation(class_getInstanceMethod(objc_getClass("NSToolbarFlexibleSpaceItem"), @selector(minSize)),
|
||||
(void *)minSizeHook);
|
||||
}
|
||||
}
|
||||
@end
|
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 180 B |
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 832 B After Width: | Height: | Size: 569 B |
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 212 B |
Before Width: | Height: | Size: 801 B After Width: | Height: | Size: 543 B |
121
Core/apu.c
|
@ -6,7 +6,7 @@
|
|||
#include <stdlib.h>
|
||||
#include "gb.h"
|
||||
|
||||
/* Band limited synthesis based on: http://www.slack.net/~ant/bl-synth/ */
|
||||
/* Band limited synthesis loosely based on: http://www.slack.net/~ant/bl-synth/ */
|
||||
static int32_t band_limited_steps[GB_BAND_LIMITED_PHASES][GB_BAND_LIMITED_WIDTH];
|
||||
|
||||
static void __attribute__((constructor)) band_limited_init(void)
|
||||
|
@ -15,37 +15,42 @@ static void __attribute__((constructor)) band_limited_init(void)
|
|||
double *master = malloc(master_size * sizeof(*master));
|
||||
memset(master, 0, master_size * sizeof(*master));
|
||||
|
||||
const unsigned sine_size = 256 * GB_BAND_LIMITED_PHASES + 2;
|
||||
const unsigned max_harmonic = sine_size / 2 / GB_BAND_LIMITED_PHASES;
|
||||
nounroll for (unsigned harmonic = 1; harmonic <= max_harmonic; harmonic += 2) {
|
||||
double amplitude = 1.0 / harmonic / 2;
|
||||
double to_angle = M_PI * 2 / sine_size * harmonic;
|
||||
nounroll for (unsigned i = 0; i < master_size; i++) {
|
||||
master[i] += sin(((signed)(i + 1) - (signed)master_size / 2) * to_angle) * amplitude;
|
||||
}
|
||||
const double lowpass = 15.0 / 16.0; // 1.0 means using Nyquist as the exact cutoff
|
||||
const double to_angle = M_PI / GB_BAND_LIMITED_PHASES * lowpass;
|
||||
double sum = 0;
|
||||
nounroll for (signed i = 0; i < master_size; i++) {
|
||||
// Exact Blackman window
|
||||
const double a0 = 7938 / 18608.0;
|
||||
const double a1 = 9240 / 18608.0;
|
||||
const double a2 = 1430 / 18608.0;
|
||||
double window_angle = (2.0 * M_PI * i) / (master_size);
|
||||
double window = a0 - a1 * cos(window_angle) + a2 * cos(2 * window_angle);
|
||||
|
||||
double angle = (i - (signed)master_size / 2) * to_angle;
|
||||
sum += master[i] = (angle == 0? 1 : sin(angle) / angle) * window;
|
||||
}
|
||||
|
||||
// Normalize master waveform
|
||||
nounroll for (unsigned i = 0; i < master_size - 1; i++) {
|
||||
master[i] += master[master_size - 1];
|
||||
master[i] /= master[master_size - 1] * 2;
|
||||
nounroll for (signed i = 0; i < master_size; i++) {
|
||||
master[i] /= sum;
|
||||
}
|
||||
master[master_size - 1] = 1;
|
||||
|
||||
nounroll for (unsigned phase = 0; phase < GB_BAND_LIMITED_PHASES; phase++) {
|
||||
nounroll for (signed phase = 0; phase < GB_BAND_LIMITED_PHASES; phase++) {
|
||||
int32_t error = GB_BAND_LIMITED_ONE;
|
||||
int32_t prev = 0;
|
||||
nounroll for (unsigned i = 0; i < GB_BAND_LIMITED_WIDTH; i++) {
|
||||
int32_t cur = master[(GB_BAND_LIMITED_PHASES - 1 - phase) + i * GB_BAND_LIMITED_PHASES] * GB_BAND_LIMITED_ONE;
|
||||
int32_t delta = cur - prev;
|
||||
error = error - delta;
|
||||
prev = cur;
|
||||
band_limited_steps[phase][i] = delta;
|
||||
nounroll for (signed i = 0; i < GB_BAND_LIMITED_WIDTH; i++) {
|
||||
double sum = 0;
|
||||
nounroll for (signed j = 0; j < GB_BAND_LIMITED_PHASES; j++) {
|
||||
signed index = i * GB_BAND_LIMITED_PHASES - phase + j;
|
||||
if (index >= 0) {
|
||||
sum += master[index];
|
||||
}
|
||||
}
|
||||
int32_t cur = sum * GB_BAND_LIMITED_ONE;
|
||||
error -= cur;
|
||||
band_limited_steps[phase][i] = cur;
|
||||
}
|
||||
|
||||
// Make sure the deltas sum to 1.0
|
||||
band_limited_steps[phase][GB_BAND_LIMITED_WIDTH / 2 - 1] += error / 2;
|
||||
band_limited_steps[phase][0] += error - (error / 2);
|
||||
band_limited_steps[phase][GB_BAND_LIMITED_WIDTH / 2] += error;
|
||||
}
|
||||
free(master);
|
||||
}
|
||||
|
@ -56,7 +61,9 @@ static void band_limited_update(GB_band_limited_t *band_limited, const GB_sample
|
|||
unsigned delay = phase / GB_BAND_LIMITED_PHASES;
|
||||
phase = phase & (GB_BAND_LIMITED_PHASES - 1);
|
||||
|
||||
GB_sample_t delta = {
|
||||
struct {
|
||||
signed left, right;
|
||||
} delta = {
|
||||
.left = input->left - band_limited->input.left,
|
||||
.right = input->right - band_limited->input.right,
|
||||
};
|
||||
|
@ -73,7 +80,9 @@ static void band_limited_update_unfiltered(GB_band_limited_t *band_limited, cons
|
|||
{
|
||||
if (input->packed == band_limited->input.packed) return;
|
||||
|
||||
GB_sample_t delta = {
|
||||
struct {
|
||||
signed left, right;
|
||||
} delta = {
|
||||
.left = input->left - band_limited->input.left,
|
||||
.right = input->right - band_limited->input.right,
|
||||
};
|
||||
|
@ -94,6 +103,39 @@ static void band_limited_read(GB_band_limited_t *band_limited, GB_sample_t *outp
|
|||
|
||||
output->left = band_limited->output.left * multiplier / GB_BAND_LIMITED_ONE;
|
||||
output->right = band_limited->output.right * multiplier / GB_BAND_LIMITED_ONE;
|
||||
|
||||
/* This hueristic will mute the channel if it's only playing an amplitude of 1 or 2 units, usually
|
||||
caused by rounding errors when the channel is playing a single frequency above Nyquist. */
|
||||
|
||||
unsigned diff = abs(output->left - band_limited->last_output.left);
|
||||
if (diff > 4) {
|
||||
band_limited->silence_detection = 0;
|
||||
band_limited->last_output.packed = output->packed;
|
||||
return;
|
||||
}
|
||||
|
||||
diff = abs(output->right - band_limited->last_output.right);
|
||||
if (diff > 4) {
|
||||
band_limited->silence_detection = 0;
|
||||
band_limited->last_output.packed = output->packed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (band_limited->silence_detection == 4000) {
|
||||
output->packed = band_limited->last_output.packed;
|
||||
}
|
||||
else {
|
||||
band_limited->silence_detection++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t sample_fraction_multiply(GB_gameboy_t *gb, unsigned multiplier)
|
||||
{
|
||||
if (unlikely(multiplier == 0)) return 0;
|
||||
if (likely(multiplier < GB_QUICK_MULTIPLY_COUNT + 1)) {
|
||||
return gb->apu_output.quick_fraction_multiply_cache[multiplier - 1];
|
||||
}
|
||||
return gb->apu_output.quick_fraction_multiply_cache[0] * multiplier;
|
||||
}
|
||||
|
||||
static const uint8_t duties[] = {
|
||||
|
@ -164,7 +206,7 @@ static void update_sample(GB_gameboy_t *gb, GB_channel_t index, int8_t value, un
|
|||
if (index == GB_WAVE) {
|
||||
/* For some reason, channel 3 is inverted on the AGB, and has a different "silence" value */
|
||||
value ^= 0xF;
|
||||
silence = 7;
|
||||
silence = 7 * 2;
|
||||
}
|
||||
|
||||
uint8_t bias = agb_bias_for_channel(gb, index);
|
||||
|
@ -173,8 +215,8 @@ static void update_sample(GB_gameboy_t *gb, GB_channel_t index, int8_t value, un
|
|||
bool right = gb->io_registers[GB_IO_NR51] & (1 << index);
|
||||
|
||||
GB_sample_t output = {
|
||||
.left = (0xF - (left? value : silence) * 2 + bias) * left_volume,
|
||||
.right = (0xF - (right? value : silence) * 2 + bias) * right_volume
|
||||
.left = (0xF - (left? value * 2 + bias : silence)) * left_volume,
|
||||
.right = (0xF - (right? value * 2 + bias : silence)) * right_volume
|
||||
};
|
||||
|
||||
if (unlikely(gb->apu_output.channel_muted[index])) {
|
||||
|
@ -187,7 +229,7 @@ static void update_sample(GB_gameboy_t *gb, GB_channel_t index, int8_t value, un
|
|||
else {
|
||||
band_limited_update(&gb->apu_output.band_limited[index],
|
||||
&output,
|
||||
(gb->apu_output.cycles_since_render + cycles_offset) * GB_BAND_LIMITED_PHASES / gb->apu_output.max_cycles_per_sample);
|
||||
(((gb->apu_output.sample_fraction + sample_fraction_multiply(gb, cycles_offset)) >> 8) * GB_BAND_LIMITED_PHASES) >> 20);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +264,7 @@ static void update_sample(GB_gameboy_t *gb, GB_channel_t index, int8_t value, un
|
|||
else {
|
||||
band_limited_update(&gb->apu_output.band_limited[index],
|
||||
&output,
|
||||
(gb->apu_output.cycles_since_render + cycles_offset) * GB_BAND_LIMITED_PHASES / gb->apu_output.max_cycles_per_sample);
|
||||
(((gb->apu_output.sample_fraction + sample_fraction_multiply(gb, cycles_offset)) >> 8) * GB_BAND_LIMITED_PHASES) >> 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,6 +349,12 @@ static void render(GB_gameboy_t *gb)
|
|||
output.right += channel_output.right;
|
||||
}
|
||||
gb->apu_output.cycles_since_render = 0;
|
||||
if (unlikely(gb->apu_output.sample_fraction < (1 << 28))) {
|
||||
gb->apu_output.sample_fraction = 0;
|
||||
}
|
||||
else {
|
||||
gb->apu_output.sample_fraction -= 1 << 28;
|
||||
}
|
||||
|
||||
if (gb->sgb && gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) return;
|
||||
|
||||
|
@ -964,6 +1012,8 @@ restart:;
|
|||
|
||||
if (gb->apu_output.sample_rate) {
|
||||
gb->apu_output.cycles_since_render += cycles;
|
||||
gb->apu_output.sample_fraction += sample_fraction_multiply(gb, cycles);
|
||||
assert(gb->apu_output.sample_fraction < (4 << 28));
|
||||
|
||||
if (gb->apu_output.sample_cycles >= clock_rate) {
|
||||
gb->apu_output.sample_cycles -= clock_rate;
|
||||
|
@ -1760,6 +1810,10 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
|
|||
if (sample_rate) {
|
||||
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
||||
gb->apu_output.max_cycles_per_sample = ceil(GB_get_clock_rate(gb) / 2.0 / sample_rate);
|
||||
gb->apu_output.quick_fraction_multiply_cache[0] = round(sample_rate * 2.0 / GB_get_clock_rate(gb) * (1 << 28));
|
||||
for (unsigned i = 1; i < GB_QUICK_MULTIPLY_COUNT; i++) {
|
||||
gb->apu_output.quick_fraction_multiply_cache[i] = gb->apu_output.quick_fraction_multiply_cache[0] * (i + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
gb->apu_output.max_cycles_per_sample = 0x400;
|
||||
|
@ -1776,6 +1830,11 @@ void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample)
|
|||
gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2;
|
||||
gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample);
|
||||
gb->apu_output.max_cycles_per_sample = ceil(cycles_per_sample / 4);
|
||||
|
||||
gb->apu_output.quick_fraction_multiply_cache[0] = round(gb->apu_output.sample_rate * 2.0 / GB_get_clock_rate(gb) * (1 << 28));
|
||||
for (unsigned i = 1; i < GB_QUICK_MULTIPLY_COUNT; i++) {
|
||||
gb->apu_output.quick_fraction_multiply_cache[i] = gb->apu_output.quick_fraction_multiply_cache[0] * (i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned GB_get_sample_rate(GB_gameboy_t *gb)
|
||||
|
|
11
Core/apu.h
|
@ -5,8 +5,10 @@
|
|||
#include <stdio.h>
|
||||
#include "defs.h"
|
||||
|
||||
#define GB_BAND_LIMITED_WIDTH 16
|
||||
#define GB_BAND_LIMITED_PHASES 512
|
||||
#define GB_BAND_LIMITED_WIDTH 64
|
||||
#define GB_BAND_LIMITED_PHASES 256
|
||||
|
||||
#define GB_QUICK_MULTIPLY_COUNT 64
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
#define GB_BAND_LIMITED_ONE 0x10000 // fixed point value equal to 1
|
||||
|
@ -171,6 +173,8 @@ typedef struct {
|
|||
} buffer[GB_BAND_LIMITED_WIDTH * 2], output;
|
||||
uint8_t pos;
|
||||
GB_sample_t input;
|
||||
GB_sample_t last_output;
|
||||
unsigned silence_detection;
|
||||
} GB_band_limited_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -180,6 +184,9 @@ typedef struct {
|
|||
unsigned max_cycles_per_sample;
|
||||
|
||||
uint32_t cycles_since_render;
|
||||
uint32_t sample_fraction; // Counter in 1 / sample_rate, in 4.28 fixed format
|
||||
uint32_t quick_fraction_multiply_cache[GB_QUICK_MULTIPLY_COUNT];
|
||||
|
||||
GB_band_limited_t band_limited[GB_N_CHANNELS];
|
||||
double dac_discharge[GB_N_CHANNELS];
|
||||
bool channel_muted[GB_N_CHANNELS];
|
||||
|
|
|
@ -73,7 +73,7 @@ bool GB_cheat_search_filter(GB_gameboy_t *gb, const char *expression, GB_cheat_s
|
|||
}
|
||||
skip:;
|
||||
old_data++;
|
||||
if (new_data == gb->ram + gb->ram_size - 1) {
|
||||
if (new_data == gb->ram + gb->ram_size - 1 && gb->mbc_ram_size) {
|
||||
new_data = gb->mbc_ram;
|
||||
}
|
||||
else if (new_data == gb->mbc_ram + gb->mbc_ram_size - 1) {
|
||||
|
|
|
@ -155,15 +155,19 @@ const GB_cheat_t *GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const cha
|
|||
uint8_t dummy;
|
||||
/* GameShark */
|
||||
if (strlen(cheat) == 8) {
|
||||
#ifdef _WIN32
|
||||
// The hh modifier is not supported on old MSVCRT, it's completely ignored
|
||||
uint32_t bank = 0;
|
||||
uint32_t value = 0;
|
||||
#pragma GCC diagnostic ignored "-Wformat"
|
||||
#else
|
||||
uint8_t bank;
|
||||
uint8_t value;
|
||||
#endif
|
||||
uint16_t address;
|
||||
if (sscanf(cheat, "%02hhx%02hhx%04hx%c", &bank, &value, &address, &dummy) == 3) {
|
||||
if (bank >= 0x80) {
|
||||
bank &= 0xF;
|
||||
}
|
||||
address = __builtin_bswap16(address);
|
||||
return GB_add_cheat(gb, description, address, bank, value, 0, false, enabled);
|
||||
return GB_add_cheat(gb, description, address, bank == 1? GB_CHEAT_ANY_BANK : (bank & 0xF), value, 0, false, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,8 +185,13 @@ const GB_cheat_t *GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const cha
|
|||
stripped_cheat[7] = stripped_cheat[8];
|
||||
stripped_cheat[8] = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
uint32_t old_value = 0;
|
||||
uint32_t value = 0;
|
||||
#else
|
||||
uint8_t old_value;
|
||||
uint8_t value;
|
||||
#endif
|
||||
uint16_t address;
|
||||
if (strlen(stripped_cheat) != 8 && strlen(stripped_cheat) != 6) {
|
||||
return NULL;
|
||||
|
|
|
@ -128,7 +128,7 @@ static inline void switch_banking_state(GB_gameboy_t *gb, uint16_t bank)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer_name, bool prefer_local)
|
||||
static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer_name, bool prefer_local, bool prefer_no_padding)
|
||||
{
|
||||
static __thread char output[256];
|
||||
const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, value, prefer_local);
|
||||
|
@ -138,9 +138,13 @@ static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer
|
|||
}
|
||||
|
||||
if (!symbol) {
|
||||
snprintf(output, sizeof(output), "$%04x", value);
|
||||
if (prefer_no_padding) {
|
||||
snprintf(output, sizeof(output), "$%x", value);
|
||||
}
|
||||
else {
|
||||
snprintf(output, sizeof(output), "$%04x", value);
|
||||
}
|
||||
}
|
||||
|
||||
else if (symbol->addr == value) {
|
||||
if (prefer_name) {
|
||||
snprintf(output, sizeof(output), "%s ($%04x)", symbol->name, value);
|
||||
|
@ -171,7 +175,7 @@ static GB_symbol_map_t *get_symbol_map(GB_gameboy_t *gb, uint16_t bank)
|
|||
|
||||
static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, bool prefer_name, bool prefer_local)
|
||||
{
|
||||
if (!value.has_bank) return value_to_string(gb, value.value, prefer_name, prefer_local);
|
||||
if (!value.has_bank) return value_to_string(gb, value.value, prefer_name, prefer_local, false);
|
||||
|
||||
static __thread char output[256];
|
||||
const GB_bank_symbol_t *symbol = GB_map_find_symbol(get_symbol_map(gb, value.bank), value.value, prefer_local);
|
||||
|
@ -901,11 +905,11 @@ static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
|||
(gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
|
||||
(gb->f & GB_SUBTRACT_FLAG)? 'N' : '-',
|
||||
(gb->f & GB_ZERO_FLAG)? 'Z' : '-');
|
||||
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->bc, false, false));
|
||||
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->de, false, false));
|
||||
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->hl, false, false));
|
||||
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->sp, false, false));
|
||||
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false, false));
|
||||
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->bc, false, false, false));
|
||||
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->de, false, false, false));
|
||||
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->hl, false, false, false));
|
||||
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->sp, false, false, false));
|
||||
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false, false, false));
|
||||
GB_log(gb, "IME = %s\n", gb->ime? "Enabled" : "Disabled");
|
||||
return true;
|
||||
}
|
||||
|
@ -1462,10 +1466,10 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!modifiers || !modifiers[0]) {
|
||||
modifiers = "a";
|
||||
if (!modifiers) {
|
||||
modifiers = "";
|
||||
}
|
||||
else if (modifiers[1]) {
|
||||
else if (modifiers[0] && modifiers[1]) {
|
||||
print_usage(gb, command);
|
||||
return true;
|
||||
}
|
||||
|
@ -1474,6 +1478,12 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
|
||||
if (!error) {
|
||||
switch (modifiers[0]) {
|
||||
case '\0':
|
||||
if (!result.has_bank) {
|
||||
GB_log(gb, "=%s\n", value_to_string(gb, result.value, false, false, true));
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
case 'a':
|
||||
GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false, false));
|
||||
break;
|
||||
|
@ -1503,6 +1513,7 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||
break;
|
||||
}
|
||||
default:
|
||||
print_usage(gb, command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1728,6 +1739,49 @@ static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||
return true;
|
||||
}
|
||||
|
||||
double GB_debugger_get_frame_cpu_usage(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->last_frame_busy_cycles || gb->last_frame_idle_cycles) {
|
||||
return (double)gb->last_frame_busy_cycles / (gb->last_frame_busy_cycles + gb->last_frame_idle_cycles);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->last_second_busy_cycles || gb->last_second_idle_cycles) {
|
||||
return (double)gb->last_second_busy_cycles / (gb->last_second_busy_cycles + gb->last_second_idle_cycles);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb);
|
||||
|
||||
static bool usage(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
NO_MODIFIERS
|
||||
|
||||
if (strlen(lstrip(arguments))) {
|
||||
print_usage(gb, command);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gb->last_frame_busy_cycles || gb->last_frame_idle_cycles) {
|
||||
GB_log(gb, "CPU usage (last frame): %.2f%%\n", (double)gb->last_frame_busy_cycles / (gb->last_frame_busy_cycles + gb->last_frame_idle_cycles) * 100);
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "CPU usage (last frame): N/A\n");
|
||||
}
|
||||
|
||||
if (gb->last_second_busy_cycles || gb->last_second_idle_cycles) {
|
||||
GB_log(gb, "CPU usage (last 60 frames): %.2f%%\n", (double)gb->last_second_busy_cycles / (gb->last_second_busy_cycles + gb->last_second_idle_cycles) * 100);
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "CPU usage (last 60 frames): N/A\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
|
@ -2158,7 +2212,7 @@ static const debugger_command_t commands[] = {
|
|||
{"backtrace", 2, backtrace, "Display the current call stack"},
|
||||
{"bt", 2, }, /* Alias */
|
||||
{"print", 1, print, "Evaluate and print an expression. "
|
||||
"Use modifier to format as an address (a, default) or as a number in "
|
||||
"Use modifier to format as an address (a) or as a number in "
|
||||
"decimal (d), hexadecimal (x), octal (o) or binary (b).",
|
||||
"<expression>", "format", .argument_completer = symbol_completer, .modifiers_completer = format_completer},
|
||||
{"eval", 2, }, /* Alias */
|
||||
|
@ -2184,6 +2238,7 @@ static const debugger_command_t commands[] = {
|
|||
{"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was "
|
||||
"used. Use 'keep' as an argument to display ticks without reseeting "
|
||||
"the count.", "(keep)", .argument_completer = keep_completer},
|
||||
{"usage", 2, usage, "Display CPU usage"},
|
||||
{"cartridge", 2, mbc, "Display information about the MBC and cartridge"},
|
||||
{"mbc", 3, }, /* Alias */
|
||||
{"apu", 3, apu, "Display information about the current state of the audio processing unit",
|
||||
|
@ -2330,10 +2385,10 @@ static void test_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t flags, uint
|
|||
condition_ok:
|
||||
GB_debugger_break(gb);
|
||||
if (flags == WATCHPOINT_READ) {
|
||||
GB_log(gb, "Watchpoint %u: [%s]\n", watchpoint->id, value_to_string(gb, addr, true, false));
|
||||
GB_log(gb, "Watchpoint %u: [%s]\n", watchpoint->id, value_to_string(gb, addr, true, false, false));
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "Watchpoint %u: [%s] = $%02x\n", watchpoint->id, value_to_string(gb, addr, true, false), value);
|
||||
GB_log(gb, "Watchpoint %u: [%s] = $%02x\n", watchpoint->id, value_to_string(gb, addr, true, false, false), value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -2533,7 +2588,7 @@ next_command:
|
|||
unsigned breakpoint_id = 0;
|
||||
if (gb->breakpoints && !gb->debug_stopped && (breakpoint_id = should_break(gb, gb->pc, false))) {
|
||||
GB_debugger_break(gb);
|
||||
GB_log(gb, "Breakpoint %u: PC = %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false));
|
||||
GB_log(gb, "Breakpoint %u: PC = %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false, false));
|
||||
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||
}
|
||||
|
||||
|
@ -2544,7 +2599,7 @@ next_command:
|
|||
bool should_delete_state = true;
|
||||
if (jump_to_result == JUMP_TO_BREAK) {
|
||||
GB_debugger_break(gb);
|
||||
GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, address, true, false));
|
||||
GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, address, true, false, false));
|
||||
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||
gb->non_trivial_jump_breakpoint_occured = false;
|
||||
}
|
||||
|
@ -2554,7 +2609,7 @@ next_command:
|
|||
}
|
||||
else {
|
||||
gb->non_trivial_jump_breakpoint_occured = true;
|
||||
GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false));
|
||||
GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false, false));
|
||||
GB_load_state_from_buffer(gb, gb->nontrivial_jump_state, -1);
|
||||
GB_rewind_push(gb);
|
||||
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||
|
@ -2720,6 +2775,9 @@ const char *GB_debugger_describe_address(GB_gameboy_t *gb,
|
|||
if (bank == (uint16_t)-1) {
|
||||
bank = bank_for_addr(gb, addr);
|
||||
}
|
||||
if ((addr >> 12) == 0xC) {
|
||||
bank = 0;
|
||||
}
|
||||
if (exact_match) {
|
||||
const GB_bank_symbol_t *symbol = GB_map_find_symbol(get_symbol_map(gb, bank), addr, prefer_local);
|
||||
if (symbol && symbol->addr == addr) return symbol->name;
|
||||
|
|
|
@ -25,6 +25,9 @@ void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
|
|||
void GB_debugger_clear_symbols(GB_gameboy_t *gb);
|
||||
void GB_debugger_set_reload_callback(GB_gameboy_t *gb, GB_debugger_reload_callback_t callback);
|
||||
|
||||
double GB_debugger_get_frame_cpu_usage(GB_gameboy_t *gb);
|
||||
double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb);
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
internal void GB_debugger_run(GB_gameboy_t *gb);
|
||||
internal void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
|
||||
|
|
|
@ -4,13 +4,18 @@
|
|||
#define GB_unlikely(x) __builtin_expect((bool)(x), 0)
|
||||
#define GB_inline_const(type, ...) (*({static const typeof(type) _= __VA_ARGS__; &_;}))
|
||||
|
||||
#if !defined(typeof)
|
||||
#if defined(__cplusplus) || __STDC_VERSION__ < 202311
|
||||
#define typeof __typeof__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
|
||||
// "Keyword" definitions
|
||||
#define likely(x) GB_likely(x)
|
||||
#define unlikely(x) GB_unlikely(x)
|
||||
#define inline_const GB_inline_const
|
||||
#define typeof __typeof__
|
||||
|
||||
#if !defined(MIN)
|
||||
#define MIN(A, B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
|
||||
|
|
|
@ -176,13 +176,34 @@ void GB_display_vblank(GB_gameboy_t *gb, GB_vblank_type_t type)
|
|||
gb->cycles_since_vblank_callback = 0;
|
||||
gb->lcd_disabled_outside_of_vblank = false;
|
||||
|
||||
#ifndef GB_DISABLE_DEBUGGER
|
||||
gb->last_frame_idle_cycles = gb->current_frame_idle_cycles;
|
||||
gb->last_frame_busy_cycles = gb->current_frame_busy_cycles;
|
||||
gb->current_frame_idle_cycles = 0;
|
||||
gb->current_frame_busy_cycles = 0;
|
||||
|
||||
if (gb->usage_frame_count++ == 60) {
|
||||
gb->last_second_idle_cycles = gb->current_second_idle_cycles;
|
||||
gb->last_second_busy_cycles = gb->current_second_busy_cycles;
|
||||
gb->current_second_idle_cycles = 0;
|
||||
gb->current_second_busy_cycles = 0;
|
||||
gb->usage_frame_count = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* TODO: Slow in turbo mode! */
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
GB_sgb_render(gb);
|
||||
}
|
||||
|
||||
if (gb->turbo) {
|
||||
#ifndef GB_DISABLE_DEBUGGER
|
||||
if (unlikely(gb->backstep_instructions)) return;
|
||||
#endif
|
||||
if (GB_timing_sync_turbo(gb)) {
|
||||
if (gb->vblank_callback && gb->enable_skipped_frame_vblank_callbacks) {
|
||||
gb->vblank_callback(gb, GB_VBLANK_TYPE_SKIPPED_FRAME);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2470,3 +2491,8 @@ double GB_get_usual_frame_rate(GB_gameboy_t *gb)
|
|||
{
|
||||
return GB_get_clock_rate(gb) / (double)LCDC_PERIOD;
|
||||
}
|
||||
|
||||
void GB_set_enable_skipped_frame_vblank_callbacks(GB_gameboy_t *gb, bool enable)
|
||||
{
|
||||
gb->enable_skipped_frame_vblank_callbacks = enable;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ typedef enum {
|
|||
GB_VBLANK_TYPE_LCD_OFF, // An artificial frame pushed while the LCD was off
|
||||
GB_VBLANK_TYPE_ARTIFICIAL, // An artificial frame pushed for some other reason
|
||||
GB_VBLANK_TYPE_REPEAT, // A frame that would not render on actual hardware, but the screen should retain the previous frame
|
||||
GB_VBLANK_TYPE_SKIPPED_FRAME, // If enabled via GB_set_enable_skipped_frame_vblank_callbacks, called on skipped frames during turbo mode
|
||||
} GB_vblank_type_t;
|
||||
|
||||
typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb, GB_vblank_type_t type);
|
||||
|
@ -95,6 +96,7 @@ static const GB_color_correction_mode_t __attribute__((deprecated("Use GB_COLOR_
|
|||
static const GB_color_correction_mode_t __attribute__((deprecated("Use GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST instead"))) GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS = GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST;
|
||||
|
||||
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
|
||||
void GB_set_enable_skipped_frame_vblank_callbacks(GB_gameboy_t *gb, bool enable);
|
||||
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback);
|
||||
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
|
||||
const GB_palette_t *GB_get_palette(GB_gameboy_t *gb);
|
||||
|
|
|
@ -1222,8 +1222,10 @@ uint64_t GB_run_frame(GB_gameboy_t *gb)
|
|||
/* Configure turbo temporarily, the user wants to handle FPS capping manually. */
|
||||
bool old_turbo = gb->turbo;
|
||||
bool old_dont_skip = gb->turbo_dont_skip;
|
||||
double old_turbo_cap = gb->turbo_cap_multiplier;
|
||||
gb->turbo = true;
|
||||
gb->turbo_dont_skip = true;
|
||||
gb->turbo_cap_multiplier = 0;
|
||||
|
||||
gb->cycles_since_last_sync = 0;
|
||||
while (true) {
|
||||
|
@ -1234,6 +1236,7 @@ uint64_t GB_run_frame(GB_gameboy_t *gb)
|
|||
}
|
||||
gb->turbo = old_turbo;
|
||||
gb->turbo_dont_skip = old_dont_skip;
|
||||
gb->turbo_cap_multiplier = old_turbo_cap;
|
||||
return gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
|
||||
}
|
||||
|
||||
|
@ -1418,6 +1421,11 @@ void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip)
|
|||
gb->turbo_dont_skip = no_frame_skip;
|
||||
}
|
||||
|
||||
void GB_set_turbo_cap(GB_gameboy_t *gb, double multiplier)
|
||||
{
|
||||
gb->turbo_cap_multiplier = multiplier;
|
||||
}
|
||||
|
||||
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled)
|
||||
{
|
||||
gb->disable_rendering = disabled;
|
||||
|
|
12
Core/gb.h
|
@ -697,6 +697,7 @@ struct GB_gameboy_internal_s {
|
|||
|
||||
/* Timing */
|
||||
uint64_t last_sync;
|
||||
uint64_t last_render;
|
||||
uint64_t cycles_since_last_sync; // In 8MHz units
|
||||
GB_rtc_mode_t rtc_mode;
|
||||
uint32_t rtc_second_length;
|
||||
|
@ -782,6 +783,14 @@ struct GB_gameboy_internal_s {
|
|||
|
||||
/* Callbacks */
|
||||
GB_debugger_reload_callback_t debugger_reload_callback;
|
||||
|
||||
/* CPU usage */
|
||||
uint32_t current_frame_idle_cycles, current_frame_busy_cycles;
|
||||
uint32_t last_frame_idle_cycles, last_frame_busy_cycles;
|
||||
|
||||
uint32_t current_second_idle_cycles, current_second_busy_cycles;
|
||||
uint32_t last_second_idle_cycles, last_second_busy_cycles;
|
||||
uint8_t usage_frame_count;
|
||||
#endif
|
||||
|
||||
#ifndef GB_DISABLE_REWIND
|
||||
|
@ -822,6 +831,8 @@ struct GB_gameboy_internal_s {
|
|||
/* Misc */
|
||||
bool turbo;
|
||||
bool turbo_dont_skip;
|
||||
double turbo_cap_multiplier;
|
||||
bool enable_skipped_frame_vblank_callbacks;
|
||||
bool disable_rendering;
|
||||
uint8_t boot_rom[0x900];
|
||||
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
|
||||
|
@ -941,6 +952,7 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
|
|||
int GB_load_battery(GB_gameboy_t *gb, const char *path);
|
||||
|
||||
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
|
||||
void GB_set_turbo_cap(GB_gameboy_t *gb, double multiplier); // Use 0 to use no cap
|
||||
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled);
|
||||
|
||||
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
|
||||
|
|
|
@ -551,6 +551,7 @@ static const uint8_t *get_header_bank(GB_gameboy_t *gb)
|
|||
|
||||
static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool append_bess)
|
||||
{
|
||||
errno = 0;
|
||||
if (file->write(file, GB_GET_SECTION(gb, header), GB_SECTION_SIZE(header)) != GB_SECTION_SIZE(header)) goto error;
|
||||
if (!DUMP_SECTION(gb, file, core_state)) goto error;
|
||||
if (!DUMP_SECTION(gb, file, dma )) goto error;
|
||||
|
@ -838,8 +839,10 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
|
|||
goto error;
|
||||
}
|
||||
|
||||
return 0;;
|
||||
error:
|
||||
return 0;
|
||||
if (errno == 0) return EIO;
|
||||
return errno;
|
||||
}
|
||||
|
||||
int GB_save_state(GB_gameboy_t *gb, const char *path)
|
||||
|
|
|
@ -47,10 +47,10 @@ bool GB_timing_sync_turbo(GB_gameboy_t *gb)
|
|||
#endif
|
||||
if (!gb->turbo_dont_skip) {
|
||||
int64_t nanoseconds = get_nanoseconds();
|
||||
if (nanoseconds <= gb->last_sync + (1000000000LL * LCDC_PERIOD / GB_get_clock_rate(gb))) {
|
||||
if (nanoseconds <= gb->last_render + 1000000000 / 60) {
|
||||
return true;
|
||||
}
|
||||
gb->last_sync = nanoseconds;
|
||||
gb->last_render = nanoseconds;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -63,23 +63,32 @@ void GB_timing_sync(GB_gameboy_t *gb)
|
|||
/* Prevent syncing if not enough time has passed.*/
|
||||
if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return;
|
||||
|
||||
unsigned target_clock_rate;
|
||||
if (gb->turbo) {
|
||||
gb->cycles_since_last_sync = 0;
|
||||
if (gb->update_input_hint_callback) {
|
||||
gb->update_input_hint_callback(gb);
|
||||
if (gb->turbo_cap_multiplier) {
|
||||
target_clock_rate = GB_get_clock_rate(gb) * gb->turbo_cap_multiplier;
|
||||
}
|
||||
return;
|
||||
else {
|
||||
gb->cycles_since_last_sync = 0;
|
||||
if (gb->update_input_hint_callback) {
|
||||
gb->update_input_hint_callback(gb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
target_clock_rate = GB_get_clock_rate(gb);
|
||||
}
|
||||
|
||||
uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
|
||||
uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / target_clock_rate; /* / 2 because we use 8MHz units */
|
||||
int64_t nanoseconds = get_nanoseconds();
|
||||
int64_t time_to_sleep = target_nanoseconds + gb->last_sync - nanoseconds;
|
||||
if (time_to_sleep > 0 && time_to_sleep < LCDC_PERIOD * 1200000000LL / GB_get_clock_rate(gb)) { // +20% to be more forgiving
|
||||
if (time_to_sleep > 0 && time_to_sleep < LCDC_PERIOD * 1200000000LL / target_clock_rate) { // +20% to be more forgiving
|
||||
nsleep(time_to_sleep);
|
||||
gb->last_sync += target_nanoseconds;
|
||||
}
|
||||
else {
|
||||
if (time_to_sleep < 0 && -time_to_sleep < LCDC_PERIOD * 1200000000LL / GB_get_clock_rate(gb)) {
|
||||
if (time_to_sleep < 0 && -time_to_sleep < LCDC_PERIOD * 1200000000LL / target_clock_rate) {
|
||||
// We're running a bit too slow, but the difference is small enough,
|
||||
// just skip this sync and let it even out
|
||||
return;
|
||||
|
@ -472,6 +481,8 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
|||
|
||||
#ifndef GB_DISABLE_DEBUGGER
|
||||
gb->absolute_debugger_ticks += cycles;
|
||||
*((gb->halted || gb->stopped)? &gb->current_frame_idle_cycles : &gb->current_frame_busy_cycles) += cycles;
|
||||
*((gb->halted || gb->stopped)? &gb->current_second_idle_cycles : &gb->current_second_busy_cycles) += cycles;
|
||||
#endif
|
||||
|
||||
// Not affected by speed boost
|
||||
|
|
|
@ -473,7 +473,7 @@ static inline Class preferredByteArrayClass(void) {
|
|||
}
|
||||
|
||||
- (void)_setSingleSelectedContentsRange:(HFRange)newSelection {
|
||||
HFASSERT(HFRangeIsSubrangeOfRange(newSelection, HFRangeMake(0, [self contentsLength])));
|
||||
if (!HFRangeIsSubrangeOfRange(newSelection, HFRangeMake(0, [self contentsLength]))) return;
|
||||
BOOL selectionChanged;
|
||||
if ([selectedContentsRanges count] == 1) {
|
||||
selectionChanged = ! HFRangeEqualsRange([selectedContentsRanges[0] HFRange], newSelection);
|
||||
|
|
54
Makefile
|
@ -36,10 +36,14 @@ else
|
|||
DEFAULT := sdl
|
||||
endif
|
||||
|
||||
|
||||
NULL := /dev/null
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
ifneq ($(shell echo /dev/null*),/dev/null)
|
||||
# Windows shell is not "aware" of /dev/null, use NUL and pray
|
||||
NULL := NUL
|
||||
endif
|
||||
endif
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
ifneq ($(shell which xdg-open 2> $(NULL))$(FREEDESKTOP),)
|
||||
|
@ -96,15 +100,8 @@ else
|
|||
CPPP_FLAGS += -UGB_DISABLE_CHEAT_SEARCH
|
||||
endif
|
||||
|
||||
ifneq ($(CORE_FILTER)$(DISABLE_TIMEKEEPING),)
|
||||
ifneq ($(MAKECMDGOALS),lib)
|
||||
$(error SameBoy features can only be disabled when compiling the 'lib' target)
|
||||
endif
|
||||
endif
|
||||
|
||||
CPPP_FLAGS += -UGB_INTERNAL
|
||||
|
||||
|
||||
include version.mk
|
||||
COPYRIGHT_YEAR := $(shell grep -oE "20[2-9][0-9]" LICENSE)
|
||||
export VERSION
|
||||
|
@ -117,6 +114,12 @@ LIBDIR := build/lib
|
|||
PKGCONF_DIR := $(LIBDIR)/pkgconfig
|
||||
PKGCONF_FILE := $(PKGCONF_DIR)/sameboy.pc
|
||||
|
||||
ifneq ($(CORE_FILTER)$(DISABLE_TIMEKEEPING),)
|
||||
ifneq ($(filter-out lib headers $(LIBDIR)/% $(INC)/%,$(MAKECMDGOALS)),)
|
||||
$(error SameBoy features can only be disabled when compiling the 'lib' target)
|
||||
endif
|
||||
endif
|
||||
|
||||
BOOTROMS_DIR ?= $(BIN)/BootROMs
|
||||
|
||||
ifdef DATA_DIR
|
||||
|
@ -132,6 +135,8 @@ CC := clang
|
|||
endif
|
||||
endif
|
||||
|
||||
IBTOOL ?= ibtool
|
||||
|
||||
# Find libraries with pkg-config if available.
|
||||
ifneq (, $(shell which pkg-config 2> $(NULL)))
|
||||
# But not on macOS, it's annoying, and not on Haiku, where OpenGL is broken
|
||||
|
@ -210,9 +215,10 @@ CFLAGS += -DUPDATE_SUPPORT
|
|||
endif
|
||||
|
||||
ifeq (,$(PKG_CONFIG))
|
||||
ifneq ($(PLATFORM),windows32)
|
||||
SDL_CFLAGS := $(shell sdl2-config --cflags)
|
||||
SDL_LDFLAGS := $(shell sdl2-config --libs) -lpthread
|
||||
|
||||
endif
|
||||
ifeq ($(PLATFORM),Darwin)
|
||||
SDL_LDFLAGS += -framework AppKit
|
||||
endif
|
||||
|
@ -282,11 +288,13 @@ sdl: $(BIN)/SDL/xaudio2_9redist.dll
|
|||
endif
|
||||
else
|
||||
LDFLAGS += -lc -lm
|
||||
# libdl is not available as a standalone library in Haiku
|
||||
# libdl is not available as a standalone library in Haiku or OpenBSD
|
||||
ifneq ($(PLATFORM),Haiku)
|
||||
ifneq ($(PLATFORM),OpenBSD)
|
||||
LDFLAGS += -ldl
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),_ios)
|
||||
OBJ := build/obj-ios
|
||||
|
@ -429,7 +437,8 @@ SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES))
|
|||
TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES))
|
||||
XDG_THUMBNAILER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(XDG_THUMBNAILER_SOURCES)) $(OBJ)/XdgThumbnailer/resources.c.o
|
||||
|
||||
lib: $(PUBLIC_HEADERS)
|
||||
lib: headers
|
||||
headers: $(PUBLIC_HEADERS)
|
||||
|
||||
# Automatic dependency generation
|
||||
|
||||
|
@ -541,9 +550,10 @@ $(OBJ)/installer: iOS/installer.m
|
|||
# Cocoa Port
|
||||
|
||||
$(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \
|
||||
$(shell ls Cocoa/*.icns Cocoa/*.png) \
|
||||
$(shell ls Cocoa/*.icns Cocoa/*.png Cocoa/*.car) \
|
||||
Cocoa/License.html \
|
||||
Cocoa/Info.plist \
|
||||
Cocoa/SameBoy.entitlements \
|
||||
Misc/registers.sym \
|
||||
$(BIN)/SameBoy.app/Contents/Resources/dmg_boot.bin \
|
||||
$(BIN)/SameBoy.app/Contents/Resources/mgb_boot.bin \
|
||||
|
@ -558,15 +568,13 @@ $(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \
|
|||
$(BIN)/SameBoy.app/Contents/PlugIns/Previewer.appex \
|
||||
Shaders
|
||||
$(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources
|
||||
cp Cocoa/*.icns Cocoa/*.png Misc/registers.sym $(BIN)/SameBoy.app/Contents/Resources/
|
||||
cp Cocoa/*.icns Cocoa/*.png Cocoa/*.car Misc/registers.sym $(BIN)/SameBoy.app/Contents/Resources/
|
||||
sed "s/@VERSION/$(VERSION)/;s/@COPYRIGHT_YEAR/$(COPYRIGHT_YEAR)/" < Cocoa/Info.plist > $(BIN)/SameBoy.app/Contents/Info.plist
|
||||
sed "s/@COPYRIGHT_YEAR/$(COPYRIGHT_YEAR)/" < Cocoa/License.html > $(BIN)/SameBoy.app/Contents/Resources/Credits.html
|
||||
$(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources/Shaders
|
||||
cp Shaders/*.fsh Shaders/*.metal $(BIN)/SameBoy.app/Contents/Resources/Shaders
|
||||
$(MKDIR) -p $(BIN)/SameBoy.app/Contents/Library/QuickLook/
|
||||
ifeq ($(CONF), release)
|
||||
$(CODESIGN) $@
|
||||
endif
|
||||
$(CODESIGN) $@ --entitlements Cocoa/SameBoy.entitlements
|
||||
|
||||
# We place the dylib inside the Quick Look plugin, because Quick Look plugins run in a very strict sandbox
|
||||
|
||||
|
@ -583,10 +591,10 @@ ifeq ($(CONF), release)
|
|||
endif
|
||||
|
||||
$(BIN)/SameBoy.app/Contents/Resources/%.nib: Cocoa/%.xib
|
||||
ibtool --target-device mac --minimum-deployment-target 10.9 --compile $@ $^ 2>&1 | cat -
|
||||
$(IBTOOL) --target-device mac --minimum-deployment-target 10.9 --compile $@ $^ 2>&1 | cat -
|
||||
|
||||
$(BIN)/SameBoy-iOS.app/%.storyboardc: iOS/%.storyboard
|
||||
ibtool --target-device iphone --target-device ipad --minimum-deployment-target $(IOS_MIN) --compile $@ $^ 2>&1 | cat -
|
||||
$(IBTOOL) --target-device iphone --target-device ipad --minimum-deployment-target $(IOS_MIN) --compile $@ $^ 2>&1 | cat -
|
||||
|
||||
# Quick Look generators
|
||||
|
||||
|
@ -666,11 +674,11 @@ $(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.
|
|||
|
||||
$(BIN)/SDL/sameboy_debugger.txt:
|
||||
echo Looking for sameboy_debugger.exe? > $@
|
||||
echo\>> $@
|
||||
echo >> $@
|
||||
echo Starting with SameBoy v1.0.1, sameboy.exe and sameboy_debugger.exe >> $@
|
||||
echo have been merged into a single executable. You can open a debugger >> $@
|
||||
echo console at any time by pressing Ctrl+C to interrupt the currently >> $@
|
||||
echo open ROM. Once you're done debugging, you can close the debugger >> $@
|
||||
echo open ROM. Once you\'re done debugging, you can close the debugger >> $@
|
||||
echo console and resume normal execution. >> $@
|
||||
|
||||
ifneq ($(USE_WINDRES),)
|
||||
|
@ -683,13 +691,13 @@ $(OBJ)/%.res: %.rc
|
|||
rc /fo $@ /dVERSION=\"$(VERSION)\" /dCOPYRIGHT_YEAR=\"$(COPYRIGHT_YEAR)\" $^
|
||||
|
||||
%.o: %.res
|
||||
cvtres /OUT:"$@" $^
|
||||
cvtres /MACHINE:X64 /OUT:"$@" $^
|
||||
endif
|
||||
|
||||
# Copy required DLL files for the Windows port
|
||||
$(BIN)/SDL/%.dll:
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
@$(eval MATCH := $(shell where $$LIB:$(notdir $@)))
|
||||
@$(eval MATCH := $(shell where "$(lib)":$(notdir $@)))
|
||||
cp "$(MATCH)" $@
|
||||
|
||||
# Tester
|
||||
|
@ -781,7 +789,7 @@ install: $(BIN)/XdgThumbnailer/sameboy-thumbnailer sdl $(shell find FreeDesktop)
|
|||
install -d $(DESTDIR)$(DATA_DIR)/BootROMs
|
||||
install -d $(DESTDIR)$(PREFIX)/bin
|
||||
install -d $(DESTDIR)$(PREFIX)/share/thumbnailers
|
||||
install -d $(DESTDIR)$(PREFIX)/share/mime
|
||||
install -d $(DESTDIR)$(PREFIX)/share/mime/packages
|
||||
install -d $(DESTDIR)$(PREFIX)/share/applications
|
||||
|
||||
(cd $(BIN)/SDL && find . \! -name sameboy -type f -exec install -m 644 {} "$(abspath $(DESTDIR))$(DATA_DIR)/{}" \; )
|
||||
|
@ -797,7 +805,7 @@ ifeq ($(DESTDIR),)
|
|||
xdg-icon-resource install --novendor --theme hicolor --size $$size --context mimetypes FreeDesktop/ColorCartridge/$${size}x$${size}.png x-gameboy-color-rom; \
|
||||
done
|
||||
else
|
||||
install -m 644 FreeDesktop/sameboy.xml $(DESTDIR)$(PREFIX)/share/mime/sameboy.xml
|
||||
install -m 644 FreeDesktop/sameboy.xml $(DESTDIR)$(PREFIX)/share/mime/packages/sameboy.xml
|
||||
install -m 644 FreeDesktop/sameboy.desktop $(DESTDIR)$(PREFIX)/share/applications/sameboy.desktop
|
||||
for size in 16x16 32x32 64x64 128x128 256x256 512x512; do \
|
||||
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/$$size/apps; \
|
||||
|
|
|
@ -47,8 +47,9 @@ SameBoy requires the following tools and libraries to build:
|
|||
|
||||
On Windows, SameBoy also requires:
|
||||
* Visual Studio (For headers, etc.)
|
||||
* [GnuWin](http://gnuwin32.sourceforge.net/)
|
||||
* Running vcvars64 before running make. Make sure all required tools and libraries are in %PATH% and %lib%, respectively. (see [Build FAQ](https://github.com/LIJI32/SameBoy/blob/master/build-faq.md) for more details on Windows compilation)
|
||||
* [Git Bash](https://git-scm.com/downloads/win) or another distribution of basic Unix utilities
|
||||
* Git Bash does not include make, you can get it [here](https://sourceforge.net/projects/ezwinports/files/make-4.4.1-without-guile-w32-bin.zip/download).
|
||||
* Running `vcvars64.bat` or `vcvarsx86_amd64.bat` before running make. Make sure all required tools, libraries, and headers are in %PATH%, %lib%, and %include%`, respectively. (see [Build FAQ](https://github.com/LIJI32/SameBoy/blob/master/build-faq.md) for more details on Windows compilation)
|
||||
|
||||
To compile, simply run `make`. The targets are:
|
||||
* `cocoa` (Default for macOS)
|
||||
|
|
|
@ -161,6 +161,10 @@ typedef struct {
|
|||
/* v1.0.1 */
|
||||
bool disable_rounded_corners; // Windows only
|
||||
bool use_faux_analog_inputs;
|
||||
|
||||
/* v1.0.2 */
|
||||
int8_t vsync_mode;
|
||||
uint8_t turbo_cap;
|
||||
};
|
||||
} configuration_t;
|
||||
|
||||
|
|
95
SDL/gui.c
|
@ -1173,6 +1173,45 @@ static const char *current_agb_revision_string(unsigned index)
|
|||
return "CPU AGB A (AGB)";
|
||||
}
|
||||
|
||||
static void cycle_turbo_cap(unsigned index)
|
||||
{
|
||||
|
||||
if (configuration.turbo_cap >= 16) { // 400%
|
||||
configuration.turbo_cap = 0; // uncapped
|
||||
}
|
||||
else if (configuration.turbo_cap == 0) { // uncapped
|
||||
configuration.turbo_cap = 6; // 150%
|
||||
}
|
||||
else {
|
||||
configuration.turbo_cap++;
|
||||
}
|
||||
}
|
||||
|
||||
static void cycle_turbo_cap_backwards(unsigned index)
|
||||
{
|
||||
|
||||
if (configuration.turbo_cap == 0) { // uncapped
|
||||
configuration.turbo_cap = 16; // 400%
|
||||
}
|
||||
else if (configuration.turbo_cap == 6) { // 150%
|
||||
configuration.turbo_cap = 0; // uncapped
|
||||
}
|
||||
else {
|
||||
configuration.turbo_cap--;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *current_turbo_cap_string(unsigned index)
|
||||
{
|
||||
if (configuration.turbo_cap == 0) {
|
||||
return "Uncapped";
|
||||
}
|
||||
static char ret[5];
|
||||
snprintf(ret, sizeof(ret), "%d%%", configuration.turbo_cap * 25);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct menu_item emulation_menu[] = {
|
||||
{"Emulated Model:", cycle_model, current_model_string, cycle_model_backwards},
|
||||
{"SGB Revision:", cycle_sgb_revision, current_sgb_revision_string, cycle_sgb_revision_backwards},
|
||||
|
@ -1181,6 +1220,7 @@ static const struct menu_item emulation_menu[] = {
|
|||
{"Boot ROMs Folder:", toggle_bootrom, current_bootrom_string, toggle_bootrom},
|
||||
{"Rewind Length:", cycle_rewind, current_rewind_string, cycle_rewind_backwards},
|
||||
{"Real Time Clock:", toggle_rtc_mode, current_rtc_mode_string, toggle_rtc_mode},
|
||||
{"Turbo speed cap:", cycle_turbo_cap, current_turbo_cap_string, cycle_turbo_cap_backwards},
|
||||
{"Back", enter_options_menu},
|
||||
{NULL,}
|
||||
};
|
||||
|
@ -1598,6 +1638,40 @@ static const char *current_osd_mode(unsigned index)
|
|||
return configuration.osd? "Enabled" : "Disabled";
|
||||
}
|
||||
|
||||
static const char *current_vsync_mode(unsigned index)
|
||||
{
|
||||
switch (configuration.vsync_mode) {
|
||||
default:
|
||||
case 0: return "Disabled";
|
||||
case 1: return "Enabled";
|
||||
case -1: return "Adaptive";
|
||||
}
|
||||
}
|
||||
|
||||
static void cycle_vsync(unsigned index)
|
||||
{
|
||||
retry:
|
||||
configuration.vsync_mode++;
|
||||
if (configuration.vsync_mode == 2) {
|
||||
configuration.vsync_mode = -1;
|
||||
}
|
||||
if (SDL_GL_SetSwapInterval(configuration.vsync_mode) && configuration.vsync_mode != 0) {
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
static void cycle_vsync_backwards(unsigned index)
|
||||
{
|
||||
retry:
|
||||
configuration.vsync_mode--;
|
||||
if (configuration.vsync_mode == -2) {
|
||||
configuration.vsync_mode = 1;
|
||||
}
|
||||
if (SDL_GL_SetSwapInterval(configuration.vsync_mode) && configuration.vsync_mode != 0) {
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Don't use the standard header definitions because we might not have the newest headers
|
||||
|
@ -1652,6 +1726,7 @@ static const struct menu_item graphics_menu[] = {
|
|||
{"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards},
|
||||
{"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards},
|
||||
{"On-Screen Display:", toggle_osd, current_osd_mode, toggle_osd},
|
||||
{"Vsync Mode:", cycle_vsync, current_vsync_mode, cycle_vsync_backwards},
|
||||
#ifdef _WIN32
|
||||
{"Window Corners:", toggle_corners, current_corner_mode, toggle_corners},
|
||||
#endif
|
||||
|
@ -2316,6 +2391,10 @@ void run_gui(bool is_running)
|
|||
case SDL_SCANCODE_LEFT:
|
||||
case SDL_SCANCODE_UP:
|
||||
case SDL_SCANCODE_DOWN:
|
||||
case SDL_SCANCODE_H:
|
||||
case SDL_SCANCODE_J:
|
||||
case SDL_SCANCODE_K:
|
||||
case SDL_SCANCODE_L:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -2703,12 +2782,16 @@ void run_gui(bool is_running)
|
|||
}
|
||||
}
|
||||
else if (gui_state == SHOWING_MENU) {
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_DOWN && current_menu[current_selection + 1].string) {
|
||||
if ((event.key.keysym.scancode == SDL_SCANCODE_DOWN ||
|
||||
event.key.keysym.scancode == SDL_SCANCODE_J) &&
|
||||
current_menu[current_selection + 1].string) {
|
||||
current_selection++;
|
||||
mouse_scroling = false;
|
||||
should_render = true;
|
||||
}
|
||||
else if (event.key.keysym.scancode == SDL_SCANCODE_UP && current_selection) {
|
||||
else if ((event.key.keysym.scancode == SDL_SCANCODE_UP ||
|
||||
event.key.keysym.scancode == SDL_SCANCODE_K) &&
|
||||
current_selection) {
|
||||
current_selection--;
|
||||
mouse_scroling = false;
|
||||
should_render = true;
|
||||
|
@ -2732,11 +2815,15 @@ void run_gui(bool is_running)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT && current_menu[current_selection].backwards_handler) {
|
||||
else if ((event.key.keysym.scancode == SDL_SCANCODE_RIGHT ||
|
||||
event.key.keysym.scancode == SDL_SCANCODE_L) &&
|
||||
current_menu[current_selection].backwards_handler) {
|
||||
current_menu[current_selection].handler(current_selection);
|
||||
should_render = true;
|
||||
}
|
||||
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT && current_menu[current_selection].backwards_handler) {
|
||||
else if ((event.key.keysym.scancode == SDL_SCANCODE_LEFT ||
|
||||
event.key.keysym.scancode == SDL_SCANCODE_H) &&
|
||||
current_menu[current_selection].backwards_handler) {
|
||||
current_menu[current_selection].backwards_handler(current_selection);
|
||||
should_render = true;
|
||||
}
|
||||
|
|
26
SDL/main.c
|
@ -277,6 +277,7 @@ static void open_menu(void)
|
|||
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||
GB_set_rewind_length(&gb, configuration.rewind_length);
|
||||
GB_set_rtc_mode(&gb, configuration.rtc_mode);
|
||||
GB_set_turbo_cap(&gb, configuration.turbo_cap / 4.0);
|
||||
if (previous_width != GB_get_screen_width(&gb)) {
|
||||
signed current_window_width, current_window_height;
|
||||
SDL_GetWindowSize(window, ¤t_window_width, ¤t_window_height);
|
||||
|
@ -381,6 +382,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||
GB_audio_clear_queue();
|
||||
turbo_down = event.type == SDL_JOYBUTTONDOWN;
|
||||
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down);
|
||||
SDL_GL_SetSwapInterval(turbo_down? 0 : configuration.vsync_mode);
|
||||
}
|
||||
else if (button == JOYPAD_BUTTON_SLOW_MOTION) {
|
||||
underclock_down = event.type == SDL_JOYBUTTONDOWN;
|
||||
|
@ -391,6 +393,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||
rewind_paused = false;
|
||||
}
|
||||
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down);
|
||||
SDL_GL_SetSwapInterval(turbo_down? 0 : configuration.vsync_mode);
|
||||
}
|
||||
else if (button == JOYPAD_BUTTON_MENU && event.type == SDL_JOYBUTTONDOWN) {
|
||||
open_menu();
|
||||
|
@ -610,6 +613,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||
turbo_down = event.type == SDL_KEYDOWN;
|
||||
GB_audio_clear_queue();
|
||||
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down);
|
||||
SDL_GL_SetSwapInterval(turbo_down? 0 : configuration.vsync_mode);
|
||||
}
|
||||
else if (event.key.keysym.scancode == configuration.keys_2[GB_CONF_KEYS2_REWIND]) {
|
||||
rewind_down = event.type == SDL_KEYDOWN;
|
||||
|
@ -617,6 +621,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||
rewind_paused = false;
|
||||
}
|
||||
GB_set_turbo_mode(gb, turbo_down, turbo_down && rewind_down);
|
||||
SDL_GL_SetSwapInterval(turbo_down? 0 : configuration.vsync_mode);
|
||||
}
|
||||
else if (event.key.keysym.scancode == configuration.keys_2[GB_CONF_KEYS2_UNDERCLOCK]) {
|
||||
underclock_down = event.type == SDL_KEYDOWN;
|
||||
|
@ -737,15 +742,23 @@ static void debugger_reset(int ignore)
|
|||
#endif
|
||||
|
||||
static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||
{
|
||||
{
|
||||
if (turbo_down) {
|
||||
static unsigned skip = 0;
|
||||
skip++;
|
||||
if (skip == GB_audio_get_frequency() / 8) {
|
||||
skip = 0;
|
||||
}
|
||||
if (skip > GB_audio_get_frequency() / 16) {
|
||||
return;
|
||||
if (configuration.turbo_cap) {
|
||||
if (skip > GB_audio_get_frequency() / 8 * 4 / configuration.turbo_cap) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (skip > GB_audio_get_frequency() / 16) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1095,6 +1108,7 @@ restart:;
|
|||
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||
GB_set_rewind_length(&gb, configuration.rewind_length);
|
||||
GB_set_rtc_mode(&gb, configuration.rtc_mode);
|
||||
GB_set_turbo_cap(&gb, configuration.turbo_cap / 4.0);
|
||||
GB_set_update_input_hint_callback(&gb, handle_events);
|
||||
GB_apu_set_sample_callback(&gb, gb_audio_callback);
|
||||
|
||||
|
@ -1382,7 +1396,9 @@ int main(int argc, char **argv)
|
|||
signal(SIGUSR1, debugger_reset);
|
||||
#endif
|
||||
|
||||
SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO);
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO) < 0) {
|
||||
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
||||
}
|
||||
// This is, essentially, best-effort.
|
||||
// This function will not be called if the process is terminated in any way, anyhow.
|
||||
atexit(SDL_Quit);
|
||||
|
@ -1536,6 +1552,8 @@ int main(int argc, char **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
SDL_GL_SetSwapInterval(configuration.vsync_mode);
|
||||
|
||||
if (filename == NULL) {
|
||||
stop_on_start = false;
|
||||
run_gui(false);
|
||||
|
|
19
build-faq.md
|
@ -26,9 +26,9 @@ The following examples will be referenced later:
|
|||
|
||||
After downloading [rgbds](https://github.com/gbdev/rgbds/releases/), ensure that it is added to the `%PATH%`. This may be done by adding it to the user's or SYSTEM's Environment Variables, or may be added to the command line at compilation time via `set path=%path%;C:\path\to\rgbds`.
|
||||
|
||||
### GnuWin
|
||||
### Git Bash & Make
|
||||
|
||||
Ensure that the `gnuwin32\bin\` directory is included in `%PATH%`. Like rgbds above, this may instead be manually included on the command line before installation: `set path=%path%;C:\path\to\gnuwin32\bin`.
|
||||
Ensure that the `Git\usr\bin` directory is included in `%PATH%`. Like rgbds above, this may instead be manually included on the command line before installation: `set path=%path%;C:\path\to\Git\usr\bin`. Similarly, make sure that the directory containing `make.exe` is also included.
|
||||
|
||||
## Building
|
||||
|
||||
|
@ -40,18 +40,5 @@ set lib=%lib%;C:\SDL2\lib\x64
|
|||
set include=%include%;C:\SDL2\include
|
||||
make
|
||||
```
|
||||
Please note that these directories (`C:\SDL2\*`) are the examples given within the "SDL Port" section above. Ensure that your `%PATH%` properly includes `rgbds` and `gnuwin32\bin`, and that the `lib` and `include` paths include the appropriate SDL2 directories.
|
||||
On some versions of Visual Studio, you might need to use `vcvarsx86_amd64` instead of `vcvars64`. Please note that these directories (`C:\SDL2\*`) are the examples given within the "SDL Port" section above. Ensure that your `%PATH%` properly includes `rgbds` and `Git\usr\bin`, and that the `lib` and `include` paths include the appropriate SDL2 directories.
|
||||
|
||||
## Common Errors
|
||||
|
||||
### Error -1073741819
|
||||
|
||||
If encountering an error that appears as follows:
|
||||
|
||||
``` make: *** [build/bin/BootROMs/dmg_boot.bin] Error -1073741819```
|
||||
|
||||
Simply run `make` again, and the process will continue. This appears to happen occasionally with `build/bin/BootROMs/dmg_boot.bin` and `build/bin/BootROMs/sgb2_boot.bin`. It does not affect the compiled output. This appears to be an issue with GnuWin.
|
||||
|
||||
### The system cannot find the file specified (`usr/bin/mkdir`)
|
||||
|
||||
If errors arise (i.e., particularly with the `CREATE_PROCESS('usr/bin/mkdir')` calls, also verify that Git for Windows has not been installed with full Linux support. If it has, remove `C:\Program Files\Git\usr\bin` from the SYSTEM %PATH% until after compilation. This happens because the Git for Windows version of `which` is used instead of the GnuWin one, and it returns a Unix-style path instead of a Windows one.
|
||||
|
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 20 KiB |
BIN
iOS/Assets.car
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 339 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 2.1 KiB |
|
@ -24,7 +24,8 @@
|
|||
if (section == 0) return 1;
|
||||
size_t count;
|
||||
GB_get_cheats(_gb, &count);
|
||||
self.toolbarItems.firstObject.enabled = count;
|
||||
self.toolbarItems[0].enabled = count;
|
||||
((UIButton *)(self.toolbarItems[0].customView.subviews[0])).enabled = count;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -91,11 +92,11 @@
|
|||
}
|
||||
else {
|
||||
alertController.title = @"Invalid cheat code entered";
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
[self presentViewController:alertController animated:true completion:nil];
|
||||
}
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
[self presentViewController:alertController animated:true completion:nil];
|
||||
}
|
||||
|
||||
+ (UIBarButtonItem *)buttonWithLabel:(NSString *)label
|
||||
|
@ -119,9 +120,17 @@
|
|||
[button sizeToFit];
|
||||
CGRect frame = button.frame;
|
||||
frame.size.width = ceil(frame.size.width + (label? 4 : 0));
|
||||
if (@available(iOS 19.0, *)) {
|
||||
if (label) {
|
||||
frame.size.width += 12;
|
||||
}
|
||||
}
|
||||
frame.size.height = 28;
|
||||
button.frame = frame;
|
||||
return [[UIBarButtonItem alloc] initWithCustomView:button];
|
||||
UIView *wrapper = [[UIView alloc] initWithFrame:button.bounds];
|
||||
[wrapper addSubview:button];
|
||||
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:wrapper];
|
||||
return item;
|
||||
}
|
||||
return [[UIBarButtonItem alloc] initWithTitle:label style:UIBarButtonItemStylePlain target:target action:action];
|
||||
}
|
||||
|
@ -153,9 +162,7 @@
|
|||
UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:@[url]
|
||||
applicationActivities:nil];
|
||||
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
controller.popoverPresentationController.barButtonItem = self.toolbarItems.firstObject;
|
||||
}
|
||||
controller.popoverPresentationController.barButtonItem = self.toolbarItems.firstObject;
|
||||
|
||||
[self presentViewController:controller
|
||||
animated:true
|
||||
|
@ -178,28 +185,38 @@
|
|||
hasSFSymbols = true;
|
||||
}
|
||||
|
||||
self.toolbarItems = @[
|
||||
hasSFSymbols?
|
||||
[self.class buttonWithLabel:nil
|
||||
imageWithName:@"square.and.arrow.up"
|
||||
target:self
|
||||
action:@selector(exportCheats)] :
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction
|
||||
target:self
|
||||
action:@selector(exportCheats)],
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil
|
||||
action:NULL],
|
||||
[self.class buttonWithLabel:@"Import"
|
||||
imageWithName:@"square.and.arrow.down"
|
||||
UIBarButtonItem *export = hasSFSymbols?
|
||||
[self.class buttonWithLabel:nil
|
||||
imageWithName:@"square.and.arrow.up"
|
||||
target:self
|
||||
action:@selector(importCheats)],
|
||||
[self.class buttonWithLabel:@"Add"
|
||||
imageWithName:@"plus"
|
||||
target:self
|
||||
action:@selector(addCheat)],
|
||||
|
||||
];
|
||||
action:@selector(exportCheats)] :
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction
|
||||
target:self
|
||||
action:@selector(exportCheats)];
|
||||
|
||||
UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil
|
||||
action:NULL];
|
||||
UIBarButtonItem *import = [self.class buttonWithLabel:@"Import"
|
||||
imageWithName:@"square.and.arrow.down"
|
||||
target:self
|
||||
action:@selector(importCheats)];
|
||||
|
||||
UIBarButtonItem *add = [self.class buttonWithLabel:@"Add"
|
||||
imageWithName:@"plus"
|
||||
target:self
|
||||
action:@selector(addCheat)];
|
||||
|
||||
if (@available(iOS 19.0, *)) {
|
||||
self.toolbarItems = @[export,
|
||||
flexItem,
|
||||
import, [UIBarButtonItem fixedSpaceItemOfWidth:0], add];
|
||||
}
|
||||
else {
|
||||
self.toolbarItems = @[export,
|
||||
flexItem,
|
||||
import, add];
|
||||
}
|
||||
|
||||
_gb = gb;
|
||||
return self;
|
||||
|
|
|
@ -12,6 +12,9 @@ static double StatusBarHeight(void)
|
|||
UIEdgeInsets insets = window.safeAreaInsets;
|
||||
ret = MAX(MAX(insets.left, insets.right), MAX(insets.top, insets.bottom)) ?: 20;
|
||||
[window setHidden:true];
|
||||
if (!ret && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
ret = 32; // iPadOS is buggy af
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
|
@ -47,7 +50,7 @@ static bool HasHomeBar(void)
|
|||
}
|
||||
|
||||
_minY = StatusBarHeight() * _factor;
|
||||
_cutout = _minY <= 24 * _factor? 0 : _minY;
|
||||
_cutout = (_minY <= 24 * _factor || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)? 0 : _minY;
|
||||
|
||||
if (HasHomeBar()) {
|
||||
_homeBar = 21 * _factor;
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
|
||||
@interface GBMenuViewController : UIAlertController
|
||||
+ (instancetype)menu;
|
||||
@property (nonatomic) NSInteger selectedButtonIndex;
|
||||
@property (nonatomic, strong) NSArray<UIButton *> *menuButtons;
|
||||
@end
|
||||
|
|
|
@ -29,6 +29,7 @@ static NSString *const tips[] = {
|
|||
{
|
||||
UILabel *_tipLabel;
|
||||
UIVisualEffectView *_effectView;
|
||||
NSMutableArray<UIButton *> *_buttons;
|
||||
}
|
||||
|
||||
+ (instancetype)menu
|
||||
|
@ -41,6 +42,8 @@ static NSString *const tips[] = {
|
|||
[ret addAction:[UIAlertAction actionWithTitle:@"Close"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil]];
|
||||
ret.selectedButtonIndex = -1;
|
||||
ret->_buttons = [[NSMutableArray alloc] init];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -50,6 +53,7 @@ static NSString *const tips[] = {
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:true];
|
||||
if (_effectView) return;
|
||||
static const struct {
|
||||
NSString *label;
|
||||
NSString *image;
|
||||
|
@ -80,6 +84,7 @@ static NSString *const tips[] = {
|
|||
button.frame = CGRectMake(round(width * x), height * y, round(width), height);
|
||||
button.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
|
||||
[self.view addSubview:button];
|
||||
[_buttons addObject:button];
|
||||
|
||||
if (!buttons[i].selector) {
|
||||
button.enabled = false;
|
||||
|
@ -102,10 +107,23 @@ static NSString *const tips[] = {
|
|||
[button addTarget:block action:@selector(invoke) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
|
||||
_effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]];
|
||||
self.menuButtons = [_buttons copy];
|
||||
[self updateSelectedButton];
|
||||
|
||||
UIVisualEffect *effect = nil;
|
||||
/*
|
||||
// Unfortunately, UIGlassEffect is still very buggy.
|
||||
if (@available(iOS 19.0, *)) {
|
||||
effect = [[objc_getClass("UIGlassEffect") alloc] init];
|
||||
}
|
||||
else */ {
|
||||
effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent];
|
||||
}
|
||||
|
||||
_effectView = [[UIVisualEffectView alloc] initWithEffect:nil];
|
||||
_effectView.layer.cornerRadius = 8;
|
||||
_effectView.layer.masksToBounds = true;
|
||||
[self.view.superview addSubview:_effectView];
|
||||
[self.view.window addSubview:_effectView];
|
||||
_tipLabel = [[UILabel alloc] init];
|
||||
unsigned tipIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBTipIndex"];
|
||||
_tipLabel.text = tips[tipIndex % (sizeof(tips) / sizeof(tips[0]))];
|
||||
|
@ -113,20 +131,23 @@ static NSString *const tips[] = {
|
|||
_tipLabel.textColor = [UIColor labelColor];
|
||||
}
|
||||
_tipLabel.font = [UIFont systemFontOfSize:14];
|
||||
_tipLabel.alpha = 0.8;
|
||||
_tipLabel.alpha = 0;
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:tipIndex + 1 forKey:@"GBTipIndex"];
|
||||
_tipLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
_tipLabel.numberOfLines = 3;
|
||||
[_effectView.contentView addSubview:_tipLabel];
|
||||
[self layoutTip];
|
||||
_effectView.alpha = 0;
|
||||
|
||||
[UIView animateWithDuration:0.25 animations:^{
|
||||
_effectView.alpha = 1.0;
|
||||
_effectView.effect = effect;
|
||||
_tipLabel.alpha = 0.8;
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)layoutTip
|
||||
{
|
||||
[_effectView.superview addSubview:_effectView];
|
||||
UIView *view = self.view.superview;
|
||||
CGSize outerSize = view.frame.size;
|
||||
CGSize size = [_tipLabel textRectForBounds:(CGRect){{0, 0},
|
||||
|
@ -135,8 +156,12 @@ static NSString *const tips[] = {
|
|||
limitedToNumberOfLines:3].size;
|
||||
size.width = ceil(size.width);
|
||||
_tipLabel.frame = (CGRect){{8, 8}, size};
|
||||
unsigned topInset = view.window.safeAreaInsets.top;
|
||||
if (!topInset && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
topInset = 32; // iPadOS is buggy af
|
||||
}
|
||||
_effectView.frame = (CGRect) {
|
||||
{round((outerSize.width - size.width - 16) / 2), view.window.safeAreaInsets.top + 12},
|
||||
{round((outerSize.width - size.width - 16) / 2), topInset + 12},
|
||||
{size.width + 16, size.height + 16}
|
||||
};
|
||||
}
|
||||
|
@ -145,7 +170,10 @@ static NSString *const tips[] = {
|
|||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[UIView animateWithDuration:0.25 animations:^{
|
||||
_effectView.alpha = 0;
|
||||
_effectView.effect = nil;
|
||||
_tipLabel.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
[_effectView removeFromSuperview];
|
||||
}];
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
@ -189,4 +217,116 @@ static NSString *const tips[] = {
|
|||
return [[UIViewController alloc] init];
|
||||
}
|
||||
|
||||
#pragma mark - Vim Navigation
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSArray<UIKeyCommand *> *)keyCommands
|
||||
{
|
||||
return @[
|
||||
[UIKeyCommand keyCommandWithInput:@"h" modifierFlags:0 action:@selector(moveLeft)],
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(moveLeft)],
|
||||
[UIKeyCommand keyCommandWithInput:@"j" modifierFlags:0 action:@selector(moveDown)],
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(moveDown)],
|
||||
[UIKeyCommand keyCommandWithInput:@"k" modifierFlags:0 action:@selector(moveUp)],
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(moveUp)],
|
||||
[UIKeyCommand keyCommandWithInput:@"l" modifierFlags:0 action:@selector(moveRight)],
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(moveRight)],
|
||||
[UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(activateSelected)],
|
||||
[UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(activateSelected)],
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(dismissSelf)],
|
||||
];
|
||||
}
|
||||
|
||||
- (void)moveLeft
|
||||
{
|
||||
if (self.selectedButtonIndex == -1) {
|
||||
self.selectedButtonIndex = 0;
|
||||
}
|
||||
else if (self.selectedButtonIndex % 4 > 0) {
|
||||
self.selectedButtonIndex--;
|
||||
}
|
||||
[self updateSelectedButton];
|
||||
}
|
||||
|
||||
- (void)moveRight
|
||||
{
|
||||
if (self.selectedButtonIndex == -1) {
|
||||
self.selectedButtonIndex = 0;
|
||||
}
|
||||
else if (self.selectedButtonIndex % 4 < 3 && self.selectedButtonIndex + 1 < self.menuButtons.count) {
|
||||
self.selectedButtonIndex++;
|
||||
}
|
||||
[self updateSelectedButton];
|
||||
|
||||
}
|
||||
|
||||
- (void)moveUp
|
||||
{
|
||||
if (self.selectedButtonIndex == -1) {
|
||||
self.selectedButtonIndex = 0;
|
||||
}
|
||||
else if (self.selectedButtonIndex >= 4) {
|
||||
self.selectedButtonIndex -= 4;
|
||||
}
|
||||
[self updateSelectedButton];
|
||||
}
|
||||
|
||||
- (void)moveDown
|
||||
{
|
||||
if (self.selectedButtonIndex == -1) {
|
||||
self.selectedButtonIndex = 0;
|
||||
}
|
||||
else if (self.selectedButtonIndex + 4 < self.menuButtons.count) {
|
||||
self.selectedButtonIndex += 4;
|
||||
}
|
||||
[self updateSelectedButton];
|
||||
}
|
||||
|
||||
- (void)activateSelected
|
||||
{
|
||||
if (self.selectedButtonIndex >= 0 && self.selectedButtonIndex < self.menuButtons.count) {
|
||||
UIButton *button = self.menuButtons[self.selectedButtonIndex];
|
||||
if (button.enabled) {
|
||||
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateSelectedButton
|
||||
{
|
||||
for (NSInteger i = 0; i < self.menuButtons.count; i++) {
|
||||
UIButton *button = self.menuButtons[i];
|
||||
if (i == self.selectedButtonIndex) {
|
||||
button.backgroundColor = [UIColor colorWithWhite:0.5 alpha:0.3];
|
||||
button.layer.borderWidth = 2.0;
|
||||
button.layer.borderColor = [UIColor systemBlueColor].CGColor;
|
||||
if (@available(iOS 19.0, *)) {
|
||||
button.layer.cornerRadius = 32.0;
|
||||
}
|
||||
else {
|
||||
button.layer.cornerRadius = 8.0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
button.backgroundColor = [UIColor clearColor];
|
||||
button.layer.borderWidth = 0.0;
|
||||
button.layer.borderColor = [UIColor clearColor].CGColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
[self becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)dismissSelf
|
||||
{
|
||||
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
@property (readonly) NSString *batterySaveFile;
|
||||
@property (readonly) NSString *autosaveStateFile;
|
||||
@property (readonly) NSString *cheatsFile;
|
||||
@property (readonly) NSArray <NSString *> *forbiddenNames;
|
||||
|
||||
@property (readonly) NSString *localRoot;
|
||||
- (NSString *)stateFile:(unsigned)index;
|
||||
|
|