Skip to content

Commit

Permalink
Cheat Search API, Cheat Search in Cocoa, and new cheats window layout
Browse files Browse the repository at this point in the history
  • Loading branch information
LIJI32 committed Aug 28, 2024
1 parent fc50842 commit 6178ff2
Show file tree
Hide file tree
Showing 15 changed files with 995 additions and 208 deletions.
254 changes: 254 additions & 0 deletions Cocoa/CheatSearch.xib

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions Cocoa/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

16 changes: 16 additions & 0 deletions Cocoa/Document.m
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -117,6 +118,8 @@ @implementation Document

NSDate *_fileModificationTime;
__weak NSThread *_emulationThread;

GBCheatSearchController *_cheatSearchController;
}

static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
Expand Down Expand Up @@ -1775,6 +1778,11 @@ - (void) reloadMemoryView
if (self.memoryWindow.isVisible) {
[_hexController reloadData];
}
if (_cheatSearchController.window.isVisible) {
if ([_cheatSearchController.tableView editedColumn] != 2) {
[_cheatSearchController.tableView reloadData];
}
}
}

- (IBAction) reloadVRAMData: (id) sender
Expand Down Expand Up @@ -2400,6 +2408,14 @@ - (IBAction)showCheats:(id)sender
[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));
Expand Down
322 changes: 162 additions & 160 deletions Cocoa/Document.xib

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cocoa/GBCenteredTextCell.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>

@interface GBCenteredTextCell : NSTextFieldCell

@end
29 changes: 29 additions & 0 deletions Cocoa/GBCenteredTextCell.m
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions Cocoa/GBCheatSearchController.h
Original file line number Diff line number Diff line change
@@ -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
231 changes: 231 additions & 0 deletions Cocoa/GBCheatSearchController.m
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions Cocoa/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit 6178ff2

Please sign in to comment.