-
-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cheat Search API, Cheat Search in Cocoa, and new cheats window layout
- Loading branch information
Showing
15 changed files
with
995 additions
and
208 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#import <Cocoa/Cocoa.h> | ||
|
||
@interface GBCenteredTextCell : NSTextFieldCell | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.