Cheat Search API, Cheat Search in Cocoa, and new cheats window layout

This commit is contained in:
Lior Halphon 2024-08-29 01:11:47 +03:00
parent fc508427b8
commit 6178ff2a78
15 changed files with 995 additions and 208 deletions

254
Cocoa/CheatSearch.xib Normal file
View File

@ -0,0 +1,254 @@
<?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">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="GBCheatSearchController">
<connections>
<outlet property="addCheatButton" destination="o1I-5D-V4k" id="lRE-uQ-vul"/>
<outlet property="conditionField" destination="XN7-BO-THS" id="vzh-y2-CcN"/>
<outlet property="conditionTypeButton" destination="6RO-h8-lZ8" id="5wx-NY-lbK"/>
<outlet property="dataTypeButton" destination="5Db-vP-S60" id="wdR-m7-fEI"/>
<outlet property="operandField" destination="Q42-Vu-TJW" id="hRn-67-UmU"/>
<outlet property="resultsLabel" destination="wbN-MX-lEy" id="gJB-fD-eKK"/>
<outlet property="tableView" destination="fSU-85-fVM" id="1kO-kP-yBb"/>
<outlet property="window" destination="QvC-M9-y7g" id="g09-DO-GsE"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Cheat Search" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="372"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="480" height="372"/>
<value key="maxSize" type="size" width="480" height="99999"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="372"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fvf-Co-5Ga">
<rect key="frame" x="-1" y="155" width="482" height="218"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" id="1kJ-HR-keu">
<rect key="frame" x="1" y="1" width="480" height="216"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" tableStyle="plain" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" headerView="oyv-sh-ulk" id="fSU-85-fVM">
<rect key="frame" x="0.0" y="0.0" width="480" height="188"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="17" height="0.0"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="AutomaticTableColumnIdentifier.0" editable="NO" width="143" minWidth="40" maxWidth="1000" id="8z0-o5-dNI">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Address">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" controlSize="large" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" title="Text Cell" id="bmG-fw-HDR" customClass="GBCenteredTextCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn identifier="AutomaticTableColumnIdentifier.1" editable="NO" width="143" minWidth="40" maxWidth="1000" id="mBc-yv-f00">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Previous Value">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" controlSize="large" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" title="Text Cell" id="BSe-S3-KNH" customClass="GBCenteredTextCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="143" minWidth="10" maxWidth="3.4028234663852886e+38" id="F7j-ck-3H0">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Current Value">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>
<textFieldCell key="dataCell" controlSize="large" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="wjH-Ei-6hv" customClass="GBCenteredTextCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="Yqt-V5-OkG"/>
<outlet property="delegate" destination="-2" id="TRM-gh-TG4"/>
</connections>
</tableView>
</subviews>
<nil key="backgroundColor"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="ZbQ-IG-kyP">
<rect key="frame" x="1" y="201" width="480" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wK3-8v-kAQ">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="oyv-sh-ulk">
<rect key="frame" x="0.0" y="0.0" width="480" height="28"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KdM-lW-WbP">
<rect key="frame" x="371" y="25" 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"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="search:" target="-2" id="7pG-JY-vEF"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="veO-Qn-0Sz">
<rect key="frame" x="60" y="126" width="70" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" 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"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fTv-nr-5FT">
<rect key="frame" x="18" y="96" width="112" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" 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="56" y="67" width="74" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" 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"/>
<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"/>
<menu key="menu" id="e5r-qR-pg7">
<items>
<menuItem title="8-Bit" state="on" id="al4-Jb-OJB"/>
<menuItem title="16-Bit" tag="1" id="G2m-fU-8Lt"/>
<menuItem title="16-Bit (Big Endian)" tag="3" id="JRa-DB-dyG"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6RO-h8-lZ8">
<rect key="frame" x="133" y="89" width="197" height="25"/>
<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"/>
<menu key="menu" id="Kg9-Rd-1GQ">
<items>
<menuItem title="Any" id="cEg-eI-4hb"/>
<menuItem title="Is Equal To..." state="on" id="Vfb-Dg-Jkb"/>
<menuItem title="Is Different From..." id="JDg-d9-5Ux"/>
<menuItem title="Is Greater Than..." id="Rir-w0-737"/>
<menuItem title="Is Equal or Greater Than..." id="Lvo-pV-Syt"/>
<menuItem title="Is Less Than..." id="VUW-DY-2cI"/>
<menuItem title="Is Equal or Less Than..." id="rDN-UF-tIo"/>
<menuItem title="Did Change" id="JdI-CW-E8H"/>
<menuItem title="Did Not Change" id="sWc-Yz-Cve"/>
<menuItem title="Did Increase" id="LHF-Lf-7tK"/>
<menuItem title="Did Decrease" id="8s2-8n-aQO"/>
<menuItem title="Custom..." id="8xc-2v-YQj"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="conditionChanged:" target="-2" id="KF9-vz-yNC"/>
</connections>
</popUpButton>
<textField 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">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="search:" target="-2" id="BDa-5j-qEz"/>
<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"/>
<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"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="search:" target="-2" id="sv5-eX-Kb9"/>
</connections>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tBa-6c-9AY">
<rect key="frame" x="13" y="25" 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"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="reset:" target="-2" id="KCy-Ob-tlg"/>
</connections>
</button>
<textField 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">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="o1I-5D-V4k">
<rect key="frame" x="264" y="25" 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"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="addCheat:" target="-2" id="7ax-kM-TeV"/>
</connections>
</button>
</subviews>
</view>
<contentBorderThickness minY="22"/>
<point key="canvasLocation" x="161" y="182"/>
</window>
</objects>
</document>

View File

@ -83,10 +83,11 @@ enum model {
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale;
-(uint8_t) readMemory:(uint16_t) addr;
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
-(void) performAtomicBlock: (void (^)())block;
-(void) connectLinkCable:(NSMenuItem *)sender;
-(int)loadStateFile:(const char *)path noErrorOnNotFound:(bool)noErrorOnFileNotFound;
- (uint8_t) readMemory:(uint16_t) addr;
- (void) writeMemory:(uint16_t) addr value:(uint8_t)value;
- (void) performAtomicBlock: (void (^)())block;
- (void) connectLinkCable:(NSMenuItem *)sender;
- (int)loadStateFile:(const char *)path noErrorOnNotFound:(bool)noErrorOnFileNotFound;
- (NSString *)captureOutputForBlock: (void (^)())block;
@end

View File

@ -11,6 +11,7 @@
#import "GBTerminalTextFieldCell.h"
#import "BigSurToolbar.h"
#import "GBPaletteEditorController.h"
#import "GBCheatSearchController.h"
#import "GBObjectView.h"
#import "GBPaletteView.h"
#import "GBHexStatusBarRepresenter.h"
@ -117,6 +118,8 @@
NSDate *_fileModificationTime;
__weak NSThread *_emulationThread;
GBCheatSearchController *_cheatSearchController;
}
static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
@ -1775,6 +1778,11 @@ enum GBWindowResizeAction
if (self.memoryWindow.isVisible) {
[_hexController reloadData];
}
if (_cheatSearchController.window.isVisible) {
if ([_cheatSearchController.tableView editedColumn] != 2) {
[_cheatSearchController.tableView reloadData];
}
}
}
- (IBAction) reloadVRAMData: (id) sender
@ -2400,6 +2408,14 @@ enum GBWindowResizeAction
[self.cheatsWindow makeKeyAndOrderFront:nil];
}
- (IBAction)showCheatSearch:(id)sender
{
if (!_cheatSearchController) {
_cheatSearchController = [GBCheatSearchController controllerWithDocument:self];
}
[_cheatSearchController.window makeKeyAndOrderFront:sender];
}
- (IBAction)toggleCheats:(id)sender
{
GB_set_cheats_enabled(&_gb, !GB_cheats_enabled(&_gb));

View File

@ -720,165 +720,24 @@
<point key="canvasLocation" x="-159" y="356"/>
</window>
<window title="Cheats" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="4Yb-Np-JrF" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="604" height="400"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="604" height="400"/>
<value key="maxSize" type="size" width="604" height="99999"/>
<view key="contentView" id="gBP-5p-BTh">
<rect key="frame" x="0.0" y="0.0" width="692" height="272"/>
<rect key="frame" x="0.0" y="0.0" width="604" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view id="fWr-0i-K1d" customClass="GBOptionalVisualEffectView">
<rect key="frame" x="0.0" y="0.0" width="294" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="hqi-ob-NW9">
<rect key="frame" x="16" y="174" width="154" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="To value:" id="Ycx-oE-aA4">
<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>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kq8-6F-9GK">
<rect key="frame" x="16" y="142" width="154" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Only if old value was: " bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="LkB-WQ-9Qd">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="updateCheat:" target="v7q-gT-jHT" id="kNc-cj-bmF"/>
</connections>
</button>
<box verticalHuggingPriority="750" boxType="separator" id="D6k-Pe-23u">
<rect key="frame" x="10" y="129" width="274" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r5T-ol-Dod">
<rect key="frame" x="16" y="107" width="270" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Import GameShark or GameGenie cheat:" id="0mf-EN-cKc">
<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 focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X7K-nJ-alF">
<rect key="frame" x="39" y="78" width="233" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Code" drawsBackground="YES" usesSingleLineMode="YES" id="2bz-dT-7Fi">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="selectText:" target="KHj-uX-Wbk" id="11z-0U-tMA"/>
</connections>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KHj-uX-Wbk">
<rect key="frame" x="39" y="47" width="233" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="50d-va-Cen">
<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="performClick:" target="C3V-Ep-bMj" id="kIN-jl-A8d"/>
</connections>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C6E-oI-hDC">
<rect key="frame" x="20" y="233" width="252" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="2uR-9N-hBb">
<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>
<outlet property="delegate" destination="v7q-gT-jHT" id="zyw-h0-hRP"/>
</connections>
</textField>
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
<rect key="frame" x="203" y="12" width="83" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" title="Import" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mMP-KW-YNy">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="importCheat:" target="v7q-gT-jHT" id="lkX-N5-wD1"/>
</connections>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="qHx-1z-daR">
<rect key="frame" x="176" y="204" width="96" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="edq-46-JeP" customClass="GBCheatTextFieldCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<outlet property="delegate" destination="v7q-gT-jHT" id="79v-33-R1X"/>
</connections>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="N3I-PP-X85">
<rect key="frame" x="176" y="174" width="96" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="CV2-D9-WsB" customClass="GBCheatTextFieldCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<outlet property="delegate" destination="v7q-gT-jHT" id="P69-nT-oOt"/>
</connections>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="S6O-LB-gSj">
<rect key="frame" x="176" y="144" width="96" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="tpM-ys-MEO" customClass="GBCheatTextFieldCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<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="16" y="204" width="152" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Change byte at address:" id="xwa-TF-eY1">
<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>
</subviews>
</view>
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="6rU-Xg-KHc">
<rect key="frame" x="293" y="-1" width="400" height="275"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
<scrollView autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="6rU-Xg-KHc">
<rect key="frame" x="-1" y="143" width="606" height="259"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="mzf-yu-RID">
<rect key="frame" x="1" y="1" width="398" height="273"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="1" y="1" width="604" height="257"/>
<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" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
<rect key="frame" x="0.0" y="0.0" width="398" height="256"/>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="24" rowSizeStyle="large" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
<rect key="frame" x="0.0" y="0.0" width="604" height="240"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -895,7 +754,7 @@
</buttonCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="40" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
<tableColumn width="52" minWidth="52" maxWidth="1000" id="9DZ-oW-Scx">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
@ -906,24 +765,24 @@
</buttonCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn editable="NO" width="142" minWidth="40" maxWidth="1000" id="4Qa-FQ-QWY">
<tableColumn editable="NO" width="320" minWidth="320" maxWidth="1000" id="4Qa-FQ-QWY">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Description">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Description" id="1hX-Sr-bGz">
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Description" id="1hX-Sr-bGz" customClass="GBCenteredTextCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn editable="NO" width="134" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
<tableColumn editable="NO" width="172" minWidth="172" maxWidth="1000" id="ACq-gU-K36">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Action">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Action" id="8Sq-h9-eV7">
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Action" id="8Sq-h9-eV7" customClass="GBCenteredTextCell">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -947,13 +806,156 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="pvX-uJ-qK5">
<rect key="frame" x="0.0" y="0.0" width="398" height="17"/>
<rect key="frame" x="0.0" y="0.0" width="604" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TFe-hA-XJc">
<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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="To value:" id="Ycx-oE-aA4">
<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>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kq8-6F-9GK">
<rect key="frame" x="42" y="21" width="152" height="19"/>
<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"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<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"/>
<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"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<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">
<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">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<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">
<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">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<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">
<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">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<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"/>
<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"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</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"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Import GameShark or GameGenie cheat:" id="0mf-EN-cKc">
<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 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">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="selectText:" target="KHj-uX-Wbk" id="11z-0U-tMA"/>
</connections>
</textField>
<textField 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">
<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="performClick:" target="C3V-Ep-bMj" id="kIN-jl-A8d"/>
</connections>
</textField>
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
<rect key="frame" x="508" y="12" 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"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="importCheat:" target="v7q-gT-jHT" id="lkX-N5-wD1"/>
</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"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</box>
</subviews>
</view>
<point key="canvasLocation" x="254" y="-463"/>
<point key="canvasLocation" x="210" y="-399"/>
</window>
<customObject id="v7q-gT-jHT" customClass="GBCheatWindowController">
<connections>

View File

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface GBCenteredTextCell : NSTextFieldCell
@end

View File

@ -0,0 +1,29 @@
#import "GBCenteredTextCell.h"
@implementation GBCenteredTextCell
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
double height = round([self.attributedStringValue size].height);
cellFrame.origin.y += (cellFrame.size.height - height) / 2;
cellFrame.size.height = height;
[super drawInteriorWithFrame:cellFrame inView:controlView];
}
- (void)selectWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate start:(NSInteger)selStart length:(NSInteger)selLength
{
double height = round([self.attributedStringValue size].height);
rect.origin.y += (rect.size.height - height) / 2;
rect.size.height = height;
[super selectWithFrame:rect inView:controlView editor:textObj delegate:delegate start:selStart length:selLength];
}
- (void)editWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate event:(NSEvent *)event
{
double height = round([self.attributedStringValue size].height);
rect.origin.y += (rect.size.height - height) / 2;
rect.size.height = height;
[super editWithFrame:rect inView:controlView editor:textObj delegate:delegate event:event];
}
@end

View File

@ -0,0 +1,8 @@
#import <AppKit/AppKit.h>
#import "Document.h"
@interface GBCheatSearchController<NSTableViewDelegate, NSTableViewDataSource, NSControlTextEditingDelegate> : NSObject
@property IBOutlet NSWindow *window;
@property IBOutlet NSTableView *tableView;
+ (instancetype)controllerWithDocument:(Document *)document;
@end

View File

@ -0,0 +1,231 @@
#import "GBCheatSearchController.h"
#import "GBWarningPopover.h"
#import "GBCheatWindowController.h"
@interface GBCheatSearchController() <NSTableViewDelegate, NSTableViewDataSource>
@property IBOutlet NSPopUpButton *dataTypeButton;
@property IBOutlet NSPopUpButton *conditionTypeButton;
@property IBOutlet NSTextField *operandField;
@property IBOutlet NSTextField *conditionField;
@property IBOutlet NSTextField *resultsLabel;
@property (strong) IBOutlet NSButton *addCheatButton;
@end
@implementation GBCheatSearchController
{
__weak Document *_document;
size_t _resultCount;
GB_cheat_search_result_t *_results;
}
+ (instancetype)controllerWithDocument:(Document *)document
{
GBCheatSearchController *ret = [[self alloc] init];
ret->_document = document;
NSArray *objects;
[[NSBundle mainBundle] loadNibNamed:@"CheatSearch" owner:ret topLevelObjects:&objects];
ret->_resultsLabel.stringValue = @"";
ret->_resultsLabel.cell.backgroundStyle = NSBackgroundStyleRaised;
return ret;
}
- (IBAction)reset:(id)sender
{
_dataTypeButton.enabled = true;
[_document performAtomicBlock:^{
GB_cheat_search_reset(_document.gb);
}];
_resultCount = 0;
if (_results) {
free(_results);
_results = NULL;
}
[_tableView reloadData];
_resultsLabel.stringValue = @"";
}
- (IBAction)search:(id)sender
{
// Dispatch to work around firstResponder oddities
dispatch_async(dispatch_get_main_queue(), ^{
if ([sender isKindOfClass:[NSTextField class]]) {
// Action sent by losing focus rather than pressing enter
if (![sender currentEditor]) return;
}
_dataTypeButton.enabled = false;
[_document performAtomicBlock:^{
__block bool success = false;
NSString *error = [_document captureOutputForBlock:^{
success = GB_cheat_search_filter(_document.gb, _conditionField.stringValue.UTF8String, _dataTypeButton.selectedTag);
}];
if (!success) {
dispatch_async(dispatch_get_main_queue(), ^{
[GBWarningPopover popoverWithContents:error onView:_conditionField];
NSBeep();
});
return;
}
_resultCount = GB_cheat_search_result_count(_document.gb);
_results = malloc(sizeof(*_results) * _resultCount);
GB_cheat_search_get_results(_document.gb, _results);
}];
if (_resultCount == 0) {
_dataTypeButton.enabled = true;
_resultsLabel.stringValue = @"No results.";
}
else {
_resultsLabel.stringValue = [NSString stringWithFormat:@"%@ result%s",
[NSNumberFormatter localizedStringFromNumber:@(_resultCount)
numberStyle:NSNumberFormatterDecimalStyle],
_resultCount > 1? "s" : ""];
}
[_tableView reloadData];
});
}
- (IBAction)conditionChanged:(id)sender
{
unsigned index = [_conditionTypeButton indexOfSelectedItem];
_conditionField.enabled = index == 11;
_operandField.enabled = index >= 1 && index <= 6;
switch ([_conditionTypeButton indexOfSelectedItem]) {
case 0: _conditionField.stringValue = @"1"; break;
case 1: _conditionField.stringValue = [NSString stringWithFormat:@"new == (%@)", _operandField.stringValue]; break;
case 2: _conditionField.stringValue = [NSString stringWithFormat:@"new != (%@)", _operandField.stringValue]; break;
case 3: _conditionField.stringValue = [NSString stringWithFormat:@"new > (%@)", _operandField.stringValue]; break;
case 4: _conditionField.stringValue = [NSString stringWithFormat:@"new >= (%@)", _operandField.stringValue]; break;
case 5: _conditionField.stringValue = [NSString stringWithFormat:@"new < (%@)", _operandField.stringValue]; break;
case 6: _conditionField.stringValue = [NSString stringWithFormat:@"new <= (%@)", _operandField.stringValue]; break;
case 7: _conditionField.stringValue = @"new != old"; break;
case 8: _conditionField.stringValue = @"new == old"; break;
case 9: _conditionField.stringValue = @"new > old"; break;
case 10: _conditionField.stringValue = @"new < old"; break;
}
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return _resultCount;
}
- (uint8_t *)addressForRow:(unsigned)row
{
uint8_t *base;
uint32_t offset;
if (_results[row].addr < 0xc000) {
base = GB_get_direct_access(_document.gb, GB_DIRECT_ACCESS_CART_RAM, NULL, NULL);
offset = (_results[row].addr & 0x1FFF) + _results[row].bank * 0x2000;
}
else if (_results[row].addr < 0xe000) {
base = GB_get_direct_access(_document.gb, GB_DIRECT_ACCESS_RAM, NULL, NULL);
offset = (_results[row].addr & 0xFFF) + _results[row].bank * 0x1000;
}
else {
base = GB_get_direct_access(_document.gb, GB_DIRECT_ACCESS_HRAM, NULL, NULL);
offset = (_results[row].addr & 0x7F);
}
return base + offset;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
switch ([[tableView tableColumns] indexOfObject:tableColumn]) {
case 0:
return [NSString stringWithFormat:@"$%02x:$%04x", _results[row].bank, _results[row].addr];
case 1:
if (_dataTypeButton.selectedTag & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
return [NSString stringWithFormat:@"$%04x", _results[row].value];
}
return [NSString stringWithFormat:@"$%02x", _results[row].value];
default: {
const uint8_t *data = [self addressForRow:row];
GB_cheat_search_data_type_t dataType = _dataTypeButton.selectedTag;
uint16_t value = data[0];
if (!(dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT)) {
return [NSString stringWithFormat:@"$%02x", value];
}
value |= data[1] << 8;
if ((dataType & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT)) {
value = __builtin_bswap16(value);
}
return [NSString stringWithFormat:@"$%04x", value];
}
}
}
- (void)tableView:(NSTableView *)tableView setObjectValue:(NSString *)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
[_document performAtomicBlock:^{
__block bool success = false;
__block uint16_t value;
NSString *error = [_document captureOutputForBlock:^{
success = !GB_debugger_evaluate(_document.gb, object.UTF8String, &value, NULL);
}];
if (!success) {
dispatch_async(dispatch_get_main_queue(), ^{
[GBWarningPopover popoverWithContents:error onView:tableView];
NSBeep();
});
return;
}
uint8_t *dest = [self addressForRow:row];
GB_cheat_search_data_type_t dataType = _dataTypeButton.selectedTag;
if (dataType & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT) {
value = __builtin_bswap16(value);
}
dest[0] = value;
if (dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
dest[1] = value >> 8;
}
dispatch_async(dispatch_get_main_queue(), ^{
[tableView reloadData];
});
}];
}
- (void)controlTextDidChange:(NSNotification *)obj
{
[self conditionChanged:nil];
}
- (IBAction)addCheat:(id)sender
{
GB_cheat_search_result_t *result = _results + _tableView.selectedRow;
uint8_t *data = [self addressForRow:_tableView.selectedRow];
GB_cheat_search_data_type_t dataType = _dataTypeButton.selectedTag;
size_t rowToSelect = 0;
GB_get_cheats(_document.gb, &rowToSelect);
[_document performAtomicBlock:^{
GB_add_cheat(_document.gb,
(dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT)? "New Cheat (Part 1)" : "New Cheat",
result->addr, result->bank,
*data,
0, false,
true);
if (dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
GB_add_cheat(_document.gb,
(dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT)? "New Cheat (Part 2)" : "New Cheat",
result->addr + 1, result->bank,
data[1],
0, false,
true);
}
GB_set_cheats_enabled(_document.gb, true);
}];
[_document.cheatsWindow makeKeyAndOrderFront:nil];
[_document.cheatWindowController.cheatsTable reloadData];
[_document.cheatWindowController.cheatsTable selectRow:rowToSelect byExtendingSelection:false];
[_document.cheatWindowController.cheatsTable.delegate tableViewSelectionDidChange:nil];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
_addCheatButton.enabled = _tableView.numberOfSelectedRows != 0;
}
- (void)dealloc
{
if (_results) free(_results);
}
@end

View File

@ -427,10 +427,16 @@
<action selector="toggleCheats:" target="-1" id="gsw-UY-fhu"/>
</connections>
</menuItem>
<menuItem title="Show Cheats" id="LZV-QK-YXi">
<menuItem title="Show Cheats" id="Nf8-F1-YyD">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showCheats:" target="-1" id="tfr-qM-q8X"/>
<action selector="showCheats:" target="-1" id="stn-Ei-aFE"/>
</connections>
</menuItem>
<menuItem title="Search Cheats" id="LZV-QK-YXi">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showCheatSearch:" target="-1" id="eI1-x0-ykn"/>
</connections>
</menuItem>
</items>

142
Core/cheat_search.c Normal file
View File

@ -0,0 +1,142 @@
#include "gb.h"
void GB_cheat_search_reset(GB_gameboy_t *gb)
{
if (gb->cheat_search_data) {
free(gb->cheat_search_data);
gb->cheat_search_data = NULL;
}
if (gb->cheat_search_bitmap) {
free(gb->cheat_search_bitmap);
gb->cheat_search_bitmap = NULL;
}
gb->cheat_search_count = 0;
}
bool GB_cheat_search_filter(GB_gameboy_t *gb, const char *expression, GB_cheat_search_data_type_t data_type)
{
GB_ASSERT_NOT_RUNNING(gb)
// Make sure the expression is valid first
if (GB_debugger_evaluate_cheat_filter(gb, expression, NULL, 0, 0)) {
return false;
}
gb->cheat_search_data_type = data_type;
if (gb->cheat_search_count == 0) {
GB_cheat_search_reset(gb);
gb->cheat_search_count = gb->ram_size + gb->mbc_ram_size + sizeof(gb->hram);
gb->cheat_search_data = malloc(gb->cheat_search_count);
gb->cheat_search_bitmap = malloc((gb->cheat_search_count + 7) / 8);
memset(gb->cheat_search_data, 0, gb->cheat_search_count);
memset(gb->cheat_search_bitmap, 0, (gb->cheat_search_count + 7) / 8);
}
uint8_t mask = 1;
uint8_t *old_data = gb->cheat_search_data;
uint8_t *bitmap = gb->cheat_search_bitmap;
uint8_t *new_data = gb->ram;
for (unsigned i = gb->ram_size + gb->mbc_ram_size + sizeof(gb->hram); i--;) {
if (*bitmap & mask) {
goto skip;
}
bool result = false;
if (data_type & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
// The last byte of each section always fails on 16-bit searches
if ((new_data != gb->ram + gb->ram_size - 1 &&
new_data != gb->mbc_ram + gb->mbc_ram_size - 1 &&
new_data != gb->hram + sizeof(gb->hram) - 1)) {
uint16_t old = old_data[0] | (old_data[1] << 8);
uint16_t new = new_data[0] | (new_data[1] << 8);
if (data_type & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT) {
old = __builtin_bswap16(old);
new = __builtin_bswap16(new);
}
GB_debugger_evaluate_cheat_filter(gb, expression, &result, old, new);
}
}
else {
GB_debugger_evaluate_cheat_filter(gb, expression, &result, *old_data, *new_data);
}
if (result) {
// Filter passed, update old value
*old_data = *new_data;
if (data_type & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
old_data[1] = new_data[1];
}
}
else {
// Did not pass filter, remove address
*bitmap |= mask;
gb->cheat_search_count--;
}
skip:;
old_data++;
if (new_data == gb->ram + gb->ram_size - 1) {
new_data = gb->mbc_ram;
}
else if (new_data == gb->mbc_ram + gb->mbc_ram_size - 1) {
new_data = gb->hram;
}
else {
new_data++;
}
mask <<= 1;
if (mask == 0) {
mask = 1;
bitmap++;
}
}
return true;
}
size_t GB_cheat_search_result_count(GB_gameboy_t *gb)
{
return gb->cheat_search_count;
}
void GB_cheat_search_get_results(GB_gameboy_t *gb, GB_cheat_search_result_t *results)
{
uint8_t mask = 1;
uint8_t *old_data = gb->cheat_search_data;
uint8_t *bitmap = gb->cheat_search_bitmap;
size_t count = gb->cheat_search_count;
while (count) {
if (!(*bitmap & mask)) {
count--;
if (gb->cheat_search_data_type & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
// Do not check for end of section, data_type is required to be the same as the last filter call
uint16_t old = old_data[0] | (old_data[1] << 8);
if (gb->cheat_search_data_type & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT) {
old = __builtin_bswap16(old);
}
results->value = old;
}
else {
results->value = *old_data;
}
size_t offset = old_data - gb->cheat_search_data;
if (offset < gb->ram_size) {
results->bank = offset / 0x1000;
results->addr = (offset & 0xfff) + (results->bank? 0xd000 : 0xc000);
}
else if (offset < gb->ram_size + gb->mbc_ram_size) {
results->addr = (offset & 0x1fff) + 0xa000;
results->bank = (offset - gb->ram_size) / 0x2000;
}
else {
results->addr = (offset & 0x7f) + 0xff80;
results->bank = 0;
}
results++;
}
old_data++;
mask <<= 1;
if (mask == 0) {
mask = 1;
bitmap++;
}
}
}

25
Core/cheat_search.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#ifndef GB_DISABLE_CHEAT_SEARCH
#include "defs.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
typedef struct {
uint16_t addr;
uint16_t bank;
uint16_t value;
} GB_cheat_search_result_t;
typedef enum {
GB_CHEAT_SEARCH_DATA_TYPE_8BIT = 0,
GB_CHEAT_SEARCH_DATA_TYPE_16BIT = 1,
GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT = 2, // Not used alone
GB_CHEAT_SEARCH_DATA_TYPE_16BIT_BE = GB_CHEAT_SEARCH_DATA_TYPE_16BIT | GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT,
} GB_cheat_search_data_type_t;
void GB_cheat_search_reset(GB_gameboy_t *gb);
bool GB_cheat_search_filter(GB_gameboy_t *gb, const char *expression, GB_cheat_search_data_type_t data_type);
size_t GB_cheat_search_result_count(GB_gameboy_t *gb);
void GB_cheat_search_get_results(GB_gameboy_t *gb, GB_cheat_search_result_t *results);
#endif

View File

@ -369,13 +369,22 @@ static struct {
{":", 3, bank},
};
value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
size_t length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value);
typedef struct {
union {
uint16_t old_address;
uint16_t old_value;
};
uint16_t new_value;
bool old_as_value;
} evaluate_conf_t;
static value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
size_t length, bool *error,
const evaluate_conf_t *conf);
static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
size_t length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
const evaluate_conf_t *conf)
{
*error = false;
// Strip whitespace
@ -403,7 +412,7 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
}
if (string[i] == ')') depth--;
}
if (depth == 0) return debugger_evaluate_lvalue(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value);
if (depth == 0) return debugger_evaluate_lvalue(gb, string + 1, length - 2, error, conf);
}
else if (string[0] == '[' && string[length - 1] == ']') {
// Attempt to strip square parentheses (memory dereference)
@ -418,7 +427,7 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
if (string[i] == ']') depth--;
}
if (depth == 0) {
return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)};
return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, conf)};
}
}
else if (string[0] == '{' && string[length - 1] == '}') {
@ -434,7 +443,7 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
if (string[i] == '}') depth--;
}
if (depth == 0) {
return (lvalue_t){LVALUE_MEMORY16, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)};
return (lvalue_t){LVALUE_MEMORY16, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, conf)};
}
}
@ -473,9 +482,9 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
}
#define ERROR ((value_t){0,})
value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
size_t length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
static value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
size_t length, bool *error,
const evaluate_conf_t *conf)
{
/* Disable watchpoints while evaluating expressions */
uint16_t n_watchpoints = gb->n_watchpoints;
@ -510,7 +519,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
if (string[i] == ')') depth--;
}
if (depth == 0) {
ret = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value);
ret = debugger_evaluate(gb, string + 1, length - 2, error, conf);
goto exit;
}
}
@ -528,7 +537,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
if (depth == 0) {
value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value);
value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, conf);
banking_state_t state;
if (addr.bank) {
save_banking_state(gb, &state);
@ -555,7 +564,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
if (depth == 0) {
value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value);
value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, conf);
banking_state_t state;
if (addr.bank) {
save_banking_state(gb, &state);
@ -600,15 +609,15 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
if (operator_index != -1) {
unsigned right_start = (unsigned)(operator_pos + strlen(operators[operator_index].string));
value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value);
value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, conf);
if (*error) goto exit;
if (operators[operator_index].lvalue_operator) {
lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value);
lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, conf);
if (*error) goto exit;
ret = operators[operator_index].lvalue_operator(gb, left, right.value);
goto exit;
}
value_t left = debugger_evaluate(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value);
value_t left = debugger_evaluate(gb, string, operator_pos, error, conf);
if (*error) goto exit;
ret = operators[operator_index].operator(left, right);
goto exit;
@ -640,22 +649,22 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}; goto exit;}
}
}
else if (length == 3) {
if (watchpoint_address && memcmp(string, "old", 3) == 0) {
ret = VALUE_16(GB_read_memory(gb, *watchpoint_address));
else if (length == 3 && conf) {
if (memcmp(string, "old", 3) == 0) {
if (conf->old_as_value) {
ret = VALUE_16(conf->old_value);
}
else {
ret = VALUE_16(GB_read_memory(gb, conf->old_address));
}
goto exit;
}
if (watchpoint_new_value && memcmp(string, "new", 3) == 0) {
ret = VALUE_16(*watchpoint_new_value);
if (memcmp(string, "new", 3) == 0) {
ret = VALUE_16(conf->new_value);
goto exit;
}
/* $new is identical to $old in read conditions */
if (watchpoint_address && memcmp(string, "new", 3) == 0) {
ret = VALUE_16(GB_read_memory(gb, *watchpoint_address));
goto exit;
}
}
char symbol_name[length + 1];
@ -1036,7 +1045,7 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const
condition += strlen(" if ");
/* Verify condition is sane (Todo: This might have side effects!) */
bool error;
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL, NULL);
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL);
if (error) return true;
}
@ -1050,13 +1059,13 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
if (error) return true;
uint16_t length = 0;
value_t end = result;
if (to) {
end = debugger_evaluate(gb, to, (unsigned)strlen(to), &error, NULL, NULL);
end = debugger_evaluate(gb, to, (unsigned)strlen(to), &error, NULL);
if (error) return true;
if (end.has_bank && result.has_bank && end.bank != result.bank) {
GB_log(gb, "Breakpoint range start and end points have different banks\n");
@ -1211,9 +1220,12 @@ static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
/* Verify condition is sane (Todo: This might have side effects!) */
bool error;
/* To make new and old legal */
uint16_t dummy = 0;
uint8_t dummy2 = 0;
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &dummy, &dummy2);
static const evaluate_conf_t conf = {
.old_as_value = true,
.old_value = 0,
.new_value = 0,
};
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &conf);
if (error) return true;
}
@ -1226,13 +1238,13 @@ static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
uint32_t key = WP_KEY(result);
uint16_t length = 0;
value_t end = result;
if (to) {
end = debugger_evaluate(gb, to, (unsigned)strlen(to), &error, NULL, NULL);
end = debugger_evaluate(gb, to, (unsigned)strlen(to), &error, NULL);
if (error) return true;
if (end.has_bank && result.has_bank && end.bank != result.bank) {
GB_log(gb, "Watchpoint range start and end points have different banks\n");
@ -1413,7 +1425,7 @@ static unsigned should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to)
bool error;
bool condition = debugger_evaluate(gb, breakpoint->condition,
(unsigned)strlen(breakpoint->condition),
&error, NULL, NULL).value;
&error, NULL).value;
if (error) {
GB_log(gb, "The condition for breakpoint %u is no longer a valid expression\n", breakpoint->id);
return breakpoint->id;
@ -1453,7 +1465,7 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
if (!error) {
switch (modifiers[0]) {
case 'a':
@ -1499,7 +1511,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
}
bool error;
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
uint16_t count = 32;
if (modifiers) {
@ -1551,7 +1563,7 @@ static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, cons
}
bool error;
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
uint16_t count = 5;
if (modifiers) {
@ -2320,9 +2332,19 @@ static void test_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t flags, uint
return;
}
bool error;
evaluate_conf_t conf = {
.old_as_value = flags == WATCHPOINT_READ,
.new_value = value,
};
if (flags == WATCHPOINT_READ) {
conf.old_value = value;
}
else {
conf.old_address = addr;
}
bool condition = debugger_evaluate(gb, watchpoint->condition,
(unsigned)strlen(watchpoint->condition),
&error, &addr, flags == WATCHPOINT_WRITE? &value : NULL).value;
&error, &conf).value;
if (error) {
GB_log(gb, "The condition for watchpoint %u is no longer a valid expression\n", watchpoint->id);
GB_debugger_break(gb);
@ -2714,7 +2736,7 @@ bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result
GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
bool error = false;
value_t value = debugger_evaluate(gb, string, strlen(string), &error, NULL, NULL);
value_t value = debugger_evaluate(gb, string, strlen(string), &error, NULL);
if (result) {
*result = value.value;
}
@ -2724,6 +2746,26 @@ bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result
return error;
}
#ifndef GB_DISABLE_CHEAT_SEARCH
internal bool GB_debugger_evaluate_cheat_filter(GB_gameboy_t *gb, const char *string, bool *result, uint16_t old, uint16_t new)
{
GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
bool error = false;
evaluate_conf_t conf = {
.old_as_value = true,
.old_value = old,
.new_value = new,
};
value_t value = debugger_evaluate(gb, string, strlen(string), &error, &conf);
if (result) {
*result = value.value;
}
return error;
}
#endif
void GB_debugger_break(GB_gameboy_t *gb)
{
gb->debug_stopped = true;

View File

@ -31,6 +31,9 @@ internal void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr,
internal void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
internal const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr, bool prefer_local);
internal void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol);
#ifndef GB_DISABLE_CHEAT_SEARCH
internal bool GB_debugger_evaluate_cheat_filter(GB_gameboy_t *gb, const char *string, bool *result, uint16_t old, uint16_t new);
#endif
#endif
#else // GB_DISABLE_DEBUGGER

View File

@ -229,6 +229,9 @@ void GB_free(GB_gameboy_t *gb)
while (gb->cheats) {
GB_remove_cheat(gb, gb->cheats[0]);
}
#endif
#ifndef GB_DISABLE_CHEAT_SEARCH
GB_cheat_search_reset(gb);
#endif
GB_stop_audio_recording(gb);
memset(gb, 0, sizeof(*gb));
@ -1690,7 +1693,7 @@ static void GB_reset_internal(GB_gameboy_t *gb, bool quick)
uint8_t extra_oam[sizeof(gb->extra_oam)];
uint8_t dma, obp0, obp1;
} *preserved_state = NULL;
if (quick) {
preserved_state = alloca(sizeof(*preserved_state));
memcpy(preserved_state->hram, gb->hram, sizeof(gb->hram));
@ -1810,6 +1813,11 @@ void GB_quick_reset(GB_gameboy_t *gb)
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
{
GB_ASSERT_NOT_RUNNING(gb)
#ifndef GB_DISABLE_CHEAT_SEARCH
GB_cheat_search_reset(gb);
#endif
gb->model = model;
if (GB_is_cgb(gb)) {
gb->ram = realloc(gb->ram, gb->ram_size = 0x1000 * 8);

View File

@ -9,6 +9,14 @@ extern "C" {
#include <stdalign.h>
#include <time.h>
#ifdef GB_DISABLE_CHEATS
#define GB_DISABLE_CHEAT_SEARCH
#else
#ifdef GB_DISABLE_DEBUGGER
#define GB_DISABLE_CHEAT_SEARCH
#endif
#endif
#include "model.h"
#include "defs.h"
#include "save_state.h"
@ -27,6 +35,7 @@ extern "C" {
#include "symbol_hash.h"
#include "sgb.h"
#include "cheats.h"
#include "cheat_search.h"
#include "rumble.h"
#include "workboy.h"
#include "random.h"
@ -819,6 +828,12 @@ struct GB_gameboy_internal_s {
GB_cheat_t **cheats;
GB_cheat_hash_t *cheat_hash[256];
#endif
#ifndef GB_DISABLE_CHEAT_SEARCH
uint8_t *cheat_search_data;
uint8_t *cheat_search_bitmap;
size_t cheat_search_count;
GB_cheat_search_data_type_t cheat_search_data_type;
#endif
/* Misc */
bool turbo;