SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
Cocoa/GBCheatSearchController.m
1 #import "GBCheatSearchController.h"
2 #import "GBWarningPopover.h"
3 #import "GBCheatWindowController.h"
4 #import "GBPanel.h"
5
6 @interface GBCheatSearchController() <NSTableViewDelegate, NSTableViewDataSource>
7 @property IBOutlet NSPopUpButton *dataTypeButton;
8 @property IBOutlet NSPopUpButton *conditionTypeButton;
9 @property IBOutlet NSTextField *operandField;
10 @property IBOutlet NSTextField *conditionField;
11 @property IBOutlet NSTextField *resultsLabel;
12 @property (strong) IBOutlet NSButton *addCheatButton;
13 @end
14
15 @implementation GBCheatSearchController
16 {
17 __weak Document *_document;
18 size_t _resultCount;
19 GB_cheat_search_result_t *_results;
20 GBPanel *_window;
21 }
22
23 + (instancetype)controllerWithDocument:(Document *)document
24 {
25 GBCheatSearchController *ret = [[self alloc] init];
26 ret->_document = document;
27 NSArray *objects;
28 [[NSBundle mainBundle] loadNibNamed:@"CheatSearch" owner:ret topLevelObjects:&objects];
29 ret->_resultsLabel.stringValue = @"";
30 ret->_resultsLabel.cell.backgroundStyle = NSBackgroundStyleRaised;
31 ret->_window.ownerWindow = document.mainWindow;
32 return ret;
33 }
34
35 - (IBAction)reset:(id)sender
36 {
37 _dataTypeButton.enabled = true;
38 [_document performAtomicBlock:^{
39 GB_cheat_search_reset(_document.gb);
40 }];
41 _resultCount = 0;
42 if (_results) {
43 free(_results);
44 _results = NULL;
45 }
46 [_tableView reloadData];
47 _resultsLabel.stringValue = @"";
48 }
49
50 - (IBAction)search:(id)sender
51 {
52 // Dispatch to work around firstResponder oddities
53 dispatch_async(dispatch_get_main_queue(), ^{
54 if ([sender isKindOfClass:[NSTextField class]]) {
55 // Action sent by losing focus rather than pressing enter
56 if (![sender currentEditor]) return;
57 }
58 _dataTypeButton.enabled = false;
59 [_document performAtomicBlock:^{
60 __block bool success = false;
61 NSString *error = [_document captureOutputForBlock:^{
62 success = GB_cheat_search_filter(_document.gb, _conditionField.stringValue.UTF8String, _dataTypeButton.selectedTag);
63 }];
64 if (!success) {
65 dispatch_async(dispatch_get_main_queue(), ^{
66 [GBWarningPopover popoverWithContents:error onView:_conditionField];
67 NSBeep();
68 });
69 return;
70 }
71 _resultCount = GB_cheat_search_result_count(_document.gb);
72 _results = malloc(sizeof(*_results) * _resultCount);
73 GB_cheat_search_get_results(_document.gb, _results);
74 }];
75 if (_resultCount == 0) {
76 _dataTypeButton.enabled = true;
77 _resultsLabel.stringValue = @"No results.";
78 }
79 else {
80 _resultsLabel.stringValue = [NSString stringWithFormat:@"%@ result%s",
81 [NSNumberFormatter localizedStringFromNumber:@(_resultCount)
82 numberStyle:NSNumberFormatterDecimalStyle],
83 _resultCount > 1? "s" : ""];
84 }
85 [_tableView reloadData];
86 });
87 }
88
89 - (IBAction)conditionChanged:(id)sender
90 {
91 unsigned index = [_conditionTypeButton indexOfSelectedItem];
92 _conditionField.enabled = index == 11;
93 _operandField.enabled = index >= 1 && index <= 6;
94 switch ([_conditionTypeButton indexOfSelectedItem]) {
95 case 0: _conditionField.stringValue = @"1"; break;
96 case 1: _conditionField.stringValue = [NSString stringWithFormat:@"new == (%@)", _operandField.stringValue]; break;
97 case 2: _conditionField.stringValue = [NSString stringWithFormat:@"new != (%@)", _operandField.stringValue]; break;
98 case 3: _conditionField.stringValue = [NSString stringWithFormat:@"new > (%@)", _operandField.stringValue]; break;
99 case 4: _conditionField.stringValue = [NSString stringWithFormat:@"new >= (%@)", _operandField.stringValue]; break;
100 case 5: _conditionField.stringValue = [NSString stringWithFormat:@"new < (%@)", _operandField.stringValue]; break;
101 case 6: _conditionField.stringValue = [NSString stringWithFormat:@"new <= (%@)", _operandField.stringValue]; break;
102 case 7: _conditionField.stringValue = @"new != old"; break;
103 case 8: _conditionField.stringValue = @"new == old"; break;
104 case 9: _conditionField.stringValue = @"new > old"; break;
105 case 10: _conditionField.stringValue = @"new < old"; break;
106 }
107 }
108
109 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
110 {
111 return _resultCount;
112 }
113
114 - (uint8_t *)addressForRow:(unsigned)row
115 {
116 uint8_t *base;
117 uint32_t offset;
118 if (_results[row].addr < 0xc000) {
119 base = GB_get_direct_access(_document.gb, GB_DIRECT_ACCESS_CART_RAM, NULL, NULL);
120 offset = (_results[row].addr & 0x1FFF) + _results[row].bank * 0x2000;
121 }
122 else if (_results[row].addr < 0xe000) {
123 base = GB_get_direct_access(_document.gb, GB_DIRECT_ACCESS_RAM, NULL, NULL);
124 offset = (_results[row].addr & 0xFFF) + _results[row].bank * 0x1000;
125 }
126 else {
127 base = GB_get_direct_access(_document.gb, GB_DIRECT_ACCESS_HRAM, NULL, NULL);
128 offset = (_results[row].addr & 0x7F);
129 }
130 return base + offset;
131 }
132
133 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
134 {
135 switch ([[tableView tableColumns] indexOfObject:tableColumn]) {
136 case 0:
137 return [NSString stringWithFormat:@"$%02x:$%04x", _results[row].bank, _results[row].addr];
138 case 1:
139 if (_dataTypeButton.selectedTag & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
140 return [NSString stringWithFormat:@"$%04x", _results[row].value];
141 }
142 return [NSString stringWithFormat:@"$%02x", _results[row].value];
143 default: {
144 const uint8_t *data = [self addressForRow:row];
145 GB_cheat_search_data_type_t dataType = _dataTypeButton.selectedTag;
146 uint16_t value = data[0];
147 if (!(dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT)) {
148 return [NSString stringWithFormat:@"$%02x", value];
149 }
150 value |= data[1] << 8;
151 if ((dataType & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT)) {
152 value = __builtin_bswap16(value);
153 }
154 return [NSString stringWithFormat:@"$%04x", value];
155 }
156 }
157 }
158
159 - (void)tableView:(NSTableView *)tableView setObjectValue:(NSString *)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
160 {
161 [_document performAtomicBlock:^{
162 __block bool success = false;
163 __block uint16_t value;
164 NSString *error = [_document captureOutputForBlock:^{
165 success = !GB_debugger_evaluate(_document.gb, object.UTF8String, &value, NULL);
166 }];
167 if (!success) {
168 dispatch_async(dispatch_get_main_queue(), ^{
169 [GBWarningPopover popoverWithContents:error onView:tableView];
170 NSBeep();
171 });
172 return;
173 }
174 uint8_t *dest = [self addressForRow:row];
175 GB_cheat_search_data_type_t dataType = _dataTypeButton.selectedTag;
176 if (dataType & GB_CHEAT_SEARCH_DATA_TYPE_BE_BIT) {
177 value = __builtin_bswap16(value);
178 }
179 dest[0] = value;
180 if (dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
181 dest[1] = value >> 8;
182 }
183 dispatch_async(dispatch_get_main_queue(), ^{
184 [tableView reloadData];
185 });
186 }];
187 }
188
189 - (void)controlTextDidChange:(NSNotification *)obj
190 {
191 [self conditionChanged:nil];
192 }
193
194 - (IBAction)addCheat:(id)sender
195 {
196 GB_cheat_search_result_t *result = _results + _tableView.selectedRow;
197 uint8_t *data = [self addressForRow:_tableView.selectedRow];
198 GB_cheat_search_data_type_t dataType = _dataTypeButton.selectedTag;
199 size_t rowToSelect = 0;
200 GB_get_cheats(_document.gb, &rowToSelect);
201 [_document performAtomicBlock:^{
202 GB_add_cheat(_document.gb,
203 (dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT)? "New Cheat (Part 1)" : "New Cheat",
204 result->addr, result->bank,
205 *data,
206 0, false,
207 true);
208 if (dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT) {
209 GB_add_cheat(_document.gb,
210 (dataType & GB_CHEAT_SEARCH_DATA_TYPE_16BIT)? "New Cheat (Part 2)" : "New Cheat",
211 result->addr + 1, result->bank,
212 data[1],
213 0, false,
214 true);
215 }
216 GB_set_cheats_enabled(_document.gb, true);
217 }];
218 [_document.cheatsWindow makeKeyAndOrderFront:nil];
219 [_document.cheatWindowController.cheatsTable reloadData];
220 [_document.cheatWindowController.cheatsTable selectRow:rowToSelect byExtendingSelection:false];
221 [_document.cheatWindowController.cheatsTable.delegate tableViewSelectionDidChange:nil];
222 }
223
224 - (void)tableViewSelectionDidChange:(NSNotification *)notification
225 {
226 _addCheatButton.enabled = _tableView.numberOfSelectedRows != 0;
227 }
228
229 - (void)dealloc
230 {
231 if (_results) free(_results);
232 }
233
234 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.