snes9x/macosx/Snes9x/Cheats/S9xCheatFinderViewController.m

929 lines
23 KiB
Mathematica
Raw Normal View History

2023-03-25 14:36:29 +00:00
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
/***********************************************************************************
SNES9X for Mac OS (c) Copyright John Stiles
Snes9x for Mac OS X
(c) Copyright 2001 - 2011 zones
(c) Copyright 2002 - 2005 107
(c) Copyright 2002 PB1400c
(c) Copyright 2004 Alexander and Sander
(c) Copyright 2004 - 2005 Steven Seeger
(c) Copyright 2005 Ryan Vogt
(c) Copyright 2019 - 2023 Michael Donald Buckley
***********************************************************************************/
#import <snes9x_framework/snes9x_framework.h>
#import "S9xCheatFinderViewController.h"
#import "S9xCheatEditViewController.h"
#import "S9xHexNumberFormatter.h"
#define MAIN_MEMORY_SIZE 0x20000
#define MAIN_MEMORY_BASE 0x7E0000
#define MAX_WATCHES 16
typedef NS_ENUM(uint32_t, S9xCheatFinderByteSize) {
S9xCheatFinderByteSize1 = 1,
S9xCheatFinderByteSize2 = 2,
S9xCheatFinderByteSize3 = 3,
S9xCheatFinderByteSize4 = 4
};
typedef NS_ENUM(uint32_t, S9xCheatFinderValueType) {
S9xCheatFinderValueTypeUnsigned = 101,
S9xCheatFinderValueTypeSigned = 102,
S9xCheatFinderValueTypeHexadecimal = 103
};
typedef NS_ENUM(uint32_t, S9xCheatFinderComparisonType) {
S9xCheatFinderComparisonTypeEqual = 0,
S9xCheatFinderComparisonTypeNotEqual = 1,
S9xCheatFinderComparisonTypeGreaterThan = 2,
S9xCheatFinderComparisonTypeGreaterThanOrEqual = 3,
S9xCheatFinderComparisonTypeLessThan = 4,
S9xCheatFinderComparisonTypeLessThanOrEqual = 5
};
typedef NS_ENUM(uint32_t, S9xCheatFinderCompareTo) {
S9xCheatFinderCompareToCustom = 200,
S9xCheatFinderCompareToPrevious = 201,
S9xCheatFinderCompareToStored = 202
};
typedef NS_ENUM(uint32_t, S9xCheatFinderWatchSegments) {
S9xCheatFinderWatchSegmentsAdd = 0,
S9xCheatFinderWatchSegmentsRemove = 1,
S9xCheatFinderWatchSegmentsSave = 2,
S9xCheatFinderWatchSegmentsLoad = 3
};
@interface S9xCheatFinderTableView : NSTableView
@property (nonatomic, strong, nullable) void (^deleteBlock)(NSIndexSet *);
@end
@interface S9xCheatFinderViewController () <NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate, S9xEmulationDelegate>
@property (nonatomic, weak) IBOutlet S9xCheatFinderTableView *addressTableView;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *addressTableWidthConstraint;
@property (nonatomic, weak) IBOutlet NSPopUpButton *byteSizePopUp;
@property (nonatomic, weak) IBOutlet NSPopUpButton *comparisonTypePopUp;
@property (nonatomic, weak) IBOutlet NSTextField *customValueField;
@property (nonatomic, weak) IBOutlet NSButton *resetButon;
@property (nonatomic, weak) IBOutlet NSButton *searchButton;
@property (nonatomic, weak) IBOutlet NSButton *addCheatButton;
@property (nonatomic, weak) IBOutlet S9xCheatFinderTableView *watchTableView;
@property (nonatomic, weak) IBOutlet NSSegmentedControl *watchButtonsSegmentedControl;
@property (nonatomic, readonly, assign) S9xCheatFinderByteSize byteSize;
@property (nonatomic, readonly, assign) S9xCheatFinderValueType valueType;
@property (nonatomic, readonly, assign) S9xCheatFinderComparisonType compareType;
@property (nonatomic, readonly, assign) S9xCheatFinderCompareTo compareTo;
@property (nonatomic, readonly, strong) NSNumber *customValue;
@end
@interface S9xCheatFinderEntry : NSObject
@property (nonatomic, assign) uint32_t address;
@property (nonatomic, assign) uint32_t value;
@property (nonatomic, assign) uint32_t prevValue;
@property (nonatomic, assign) uint32_t storedValue;
@end
@implementation S9xCheatFinderViewController
{
BOOL _hasStored;
BOOL _hasPrevious;
uint8_t _storedRAM[MAIN_MEMORY_SIZE];
uint8_t _previousRAM[MAIN_MEMORY_SIZE];
uint8_t _currentRAM[MAIN_MEMORY_SIZE];
uint8_t _statusFlag[MAIN_MEMORY_SIZE];
NSMutableArray<S9xCheatFinderEntry *> *_addressRows;
NSMutableArray<S9xWatchPoint *> *_watchRows;
NSNumberFormatter *_unsignedFormatter;
NSNumberFormatter *_signedFormatter;
NSNumberFormatter *_hexFormatter;
BOOL _shouldReopen;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_unsignedFormatter = [NSNumberFormatter new];
_unsignedFormatter.minimum = @(0);
_unsignedFormatter.maximum = @(UINT_MAX);
_signedFormatter = [NSNumberFormatter new];
_signedFormatter.minimum = @(INT_MIN);
_signedFormatter.maximum = @(INT_MAX);
_hexFormatter = [S9xHexNumberFormatter new];
[NSNotificationCenter.defaultCenter addObserverForName:NSPreferredScrollerStyleDidChangeNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *notification)
{
[self updateScrollBar];
}];
__weak S9xCheatFinderViewController *weakSelf = self;
self.addressTableView.deleteBlock = ^(NSIndexSet *deletedIndexes)
{
__strong S9xCheatFinderViewController *strongSelf = weakSelf;
[deletedIndexes enumerateIndexesUsingBlock:^(NSUInteger i, BOOL *stop)
{
S9xCheatFinderEntry *entry = strongSelf->_addressRows[i];
strongSelf->_statusFlag[entry.address] = 0;
strongSelf.resetButon.enabled = YES;
}];
[strongSelf reloadData];
};
self.watchTableView.deleteBlock = ^(NSIndexSet *deletedIndexes)
{
__strong S9xCheatFinderViewController *strongSelf = weakSelf;
[strongSelf->_watchRows removeObjectsAtIndexes:deletedIndexes];
[strongSelf.engine setWatchPoints:self->_watchRows];
[strongSelf reloadWatchPoints];
};
[self resetAll];
}
- (void)viewWillAppear
{
[super viewWillAppear];
[self updateScrollBar];
[self.engine copyRAM:(uint8_t *)_currentRAM length:sizeof(_currentRAM)];
[self reloadData];
[self reloadWatchPoints];
}
- (void)viewWillDisappear
{
[super viewWillDisappear];
[self copyPrevious];
}
- (void)resetAll
{
[self.byteSizePopUp selectItemWithTag:S9xCheatFinderByteSize2];
((NSButton *)[self.view viewWithTag:S9xCheatFinderValueTypeUnsigned]).state = NSControlStateValueOn;
((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToCustom]).state = NSControlStateValueOn;
((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToPrevious]).enabled = NO;
((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToStored]).enabled = NO;
self.resetButon.enabled = NO;
self.searchButton.enabled = NO;
self.addCheatButton.enabled = NO;
[self.comparisonTypePopUp selectItemWithTag:S9xCheatFinderComparisonTypeEqual];
self.customValueField.stringValue = @"";
self.customValueField.formatter = _unsignedFormatter;
_hasStored = NO;
_hasPrevious = NO;
_shouldReopen = NO;
self.engine.emulationDelegate = self;
[self.engine copyRAM:(uint8_t *)_currentRAM length:sizeof(_currentRAM)];
[self.engine setWatchPoints:@[]];
[self reset:self];
}
- (void)copyPrevious
{
memcpy(_previousRAM, _currentRAM, MAIN_MEMORY_SIZE);
_hasPrevious = YES;
((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToPrevious]).enabled = YES;
}
- (void)reloadData
{
NSMutableSet<NSNumber *> *selectedAddresses = [NSMutableSet new];
[self.addressTableView.selectedRowIndexes enumerateIndexesUsingBlock:^(NSUInteger i, BOOL *stop)
{
[selectedAddresses addObject:@(_addressRows[i].address)];
}];
_addressRows = [NSMutableArray new];
for ( NSUInteger i = 0; i < MAIN_MEMORY_SIZE; ++i )
{
if ( _statusFlag[i] )
{
S9xCheatFinderEntry *entry = [S9xCheatFinderEntry new];
entry.address = (uint32_t)i;
entry.value = [self copyValueFrom:_currentRAM index:i];
entry.prevValue = [self copyValueFrom:_previousRAM index:i];
entry.storedValue = [self copyValueFrom:_storedRAM index:i];
[_addressRows addObject:entry];
}
}
[self.addressTableView reloadData];
NSIndexSet *newRows = [_addressRows indexesOfObjectsPassingTest:^(S9xCheatFinderEntry *entry, NSUInteger i, BOOL *stop)
{
return [selectedAddresses containsObject:@(entry.address)];
}];
[self.addressTableView selectRowIndexes:newRows byExtendingSelection:NO];
}
- (void)reloadWatchPoints
{
_watchRows = [[self.engine getWatchPoints] mutableCopy];
[self.watchTableView reloadData];
}
- (uint32_t)copyValueFrom:(uint8_t *)buffer index:(NSUInteger)index
{
uint32_t value = 0;
NSInteger size = self.byteSize;
if (size + index >= MAIN_MEMORY_SIZE)
{
size = MAIN_MEMORY_SIZE - index;
}
memcpy(&value, &(buffer[index]), size);
return value;
}
- (NSString *)stringFor:(uint8_t *)buffer index:(NSUInteger)index
{
uint32_t value = [self copyValueFrom:buffer index:index];
NSString *string = nil;
switch (self.valueType)
{
case S9xCheatFinderValueTypeUnsigned:
string = [NSString stringWithFormat:@"%u", value];
break;
case S9xCheatFinderValueTypeSigned:
switch (self.byteSize)
{
case S9xCheatFinderByteSize1:
string = [NSString stringWithFormat:@"%d", (int8_t)value];
break;
case S9xCheatFinderByteSize2:
string = [NSString stringWithFormat:@"%d", (int16_t)value];
break;
case S9xCheatFinderByteSize3:
if (value >= (1 << 23))
{
value = (int32_t)value - (1 << 24);
}
string = [NSString stringWithFormat:@"%d", value];
break;
case S9xCheatFinderByteSize4:
string = [NSString stringWithFormat:@"%d", value];
break;
}
break;
case S9xCheatFinderValueTypeHexadecimal:
{
switch(self.byteSize)
{
case S9xCheatFinderByteSize1:
string = [NSString stringWithFormat:@"%02X", value];
break;
case S9xCheatFinderByteSize2:
string = [NSString stringWithFormat:@"%04X", value];
break;
case S9xCheatFinderByteSize3:
string = [NSString stringWithFormat:@"%06X", value];
break;
case S9xCheatFinderByteSize4:
string = [NSString stringWithFormat:@"%08X", value];
break;
}
break;
}
}
return string;
}
- (void)updateSearchButton
{
switch (self.compareTo)
{
case S9xCheatFinderCompareToCustom:
self.searchButton.enabled = self.customValueField.stringValue.length > 0;
break;
case S9xCheatFinderCompareToPrevious:
self.searchButton.enabled = _hasPrevious;
break;
case S9xCheatFinderCompareToStored:
self.searchButton.enabled = _hasStored;
break;
}
}
- (void)updateWatchButtons
{
NSInteger numAddressesSelected = self.addressTableView.numberOfSelectedRows;
NSUInteger numWatches = _watchRows.count;
NSInteger numWatchesSelected = self.watchTableView.numberOfSelectedRows;
[self.watchButtonsSegmentedControl setEnabled:numAddressesSelected > 0 && numAddressesSelected + numWatches <= MAX_WATCHES forSegment:S9xCheatFinderWatchSegmentsAdd];
[self.watchButtonsSegmentedControl setEnabled:numWatchesSelected > 0 forSegment:(NSInteger)S9xCheatFinderWatchSegmentsRemove];
[self.watchButtonsSegmentedControl setEnabled:numWatches != 0 forSegment:S9xCheatFinderWatchSegmentsSave];
}
- (void)updateScrollBar
{
const CGFloat defaultWidth = 393.0;
const CGFloat scrollbarWidth = 15.0;
if (NSScroller.preferredScrollerStyle == NSScrollerStyleLegacy)
{
self.addressTableWidthConstraint.constant = defaultWidth + scrollbarWidth;
}
else
{
self.addressTableWidthConstraint.constant = defaultWidth;
}
[self.view layout];
[self.addressTableView sizeLastColumnToFit];
[self.watchTableView sizeLastColumnToFit];
}
#pragma mark - Actions
- (IBAction)storeValues:(id)sender
{
memcpy(_storedRAM, _currentRAM, MAIN_MEMORY_SIZE);
_hasStored = YES;
((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToStored]).enabled = YES;
[self reloadData];
[self updateSearchButton];
}
- (IBAction)reset:(id)sender
{
memset(_statusFlag, 1, sizeof(_statusFlag));
self.resetButon.enabled = NO;
[self reloadData];
[self updateSearchButton];
[self.addressTableView deselectAll:self];
[self.watchTableView deselectAll:self];
[self updateWatchButtons];
}
- (IBAction)search:(id)sender
{
NSMutableIndexSet *rowsToRemove = [NSMutableIndexSet new];
NSNumber *customValue = self.customValue;
S9xCheatFinderValueType valueType = self.valueType;
S9xCheatFinderComparisonType comparisonType = self.compareType;
S9xCheatFinderCompareTo compareTo = self.compareTo;
for (S9xCheatFinderEntry *entry in _addressRows)
{
switch(valueType)
{
case S9xCheatFinderValueTypeUnsigned:
case S9xCheatFinderValueTypeHexadecimal:
{
uint32_t value = [self copyValueFrom:_currentRAM index:entry.address];
uint32_t comparedValue;
switch(compareTo)
{
case S9xCheatFinderCompareToCustom:
comparedValue = customValue.unsignedIntValue;
break;
case S9xCheatFinderCompareToStored:
comparedValue = [self copyValueFrom:_storedRAM index:entry.address];
break;
case S9xCheatFinderCompareToPrevious:
comparedValue = [self copyValueFrom:_previousRAM index:entry.address];
break;
}
switch(comparisonType)
{
case S9xCheatFinderComparisonTypeEqual:
_statusFlag[entry.address] = (value == comparedValue);
break;
case S9xCheatFinderComparisonTypeNotEqual:
_statusFlag[entry.address] = (value != comparedValue);
break;
case S9xCheatFinderComparisonTypeGreaterThan:
_statusFlag[entry.address] = (value > comparedValue);
break;
case S9xCheatFinderComparisonTypeGreaterThanOrEqual:
_statusFlag[entry.address] = (value >= comparedValue);
break;
case S9xCheatFinderComparisonTypeLessThan:
_statusFlag[entry.address] = (value < comparedValue);
break;
case S9xCheatFinderComparisonTypeLessThanOrEqual:
_statusFlag[entry.address] = (value <= comparedValue);
break;
}
break;
}
case S9xCheatFinderValueTypeSigned:
{
int32_t value = [self copyValueFrom:_currentRAM index:entry.address];
int32_t comparedValue;
switch(compareTo)
{
case S9xCheatFinderCompareToCustom:
comparedValue = customValue.intValue;
break;
case S9xCheatFinderCompareToStored:
comparedValue = [self copyValueFrom:_storedRAM index:entry.address];
break;
case S9xCheatFinderCompareToPrevious:
comparedValue = [self copyValueFrom:_previousRAM index:entry.address];
break;
}
switch(comparisonType)
{
case S9xCheatFinderComparisonTypeEqual:
_statusFlag[entry.address] = (value == comparedValue);
break;
case S9xCheatFinderComparisonTypeNotEqual:
_statusFlag[entry.address] = (value != comparedValue);
break;
case S9xCheatFinderComparisonTypeGreaterThan:
_statusFlag[entry.address] = (value > comparedValue);
break;
case S9xCheatFinderComparisonTypeGreaterThanOrEqual:
_statusFlag[entry.address] = (value >= comparedValue);
break;
case S9xCheatFinderComparisonTypeLessThan:
_statusFlag[entry.address] = (value < comparedValue);
break;
case S9xCheatFinderComparisonTypeLessThanOrEqual:
_statusFlag[entry.address] = (value <= comparedValue);
break;
}
break;
}
}
}
[_addressRows removeObjectsAtIndexes:rowsToRemove];
[self reloadData];
[self updateWatchButtons];
self.resetButon.enabled = YES;
}
- (IBAction)reload:(id)sender
{
[self reloadData];
}
- (IBAction)setFormat:(id)sender
{
[self reloadData];
S9xCheatFinderValueType valueType = (S9xCheatFinderValueType)[sender tag];
NSNumber *value = self.customValue;
switch (valueType)
{
case S9xCheatFinderValueTypeUnsigned:
self.customValueField.formatter = _unsignedFormatter;
break;
case S9xCheatFinderValueTypeSigned:
self.customValueField.formatter = _signedFormatter;
break;
case S9xCheatFinderValueTypeHexadecimal:
self.customValueField.formatter = _hexFormatter;
break;
}
self.customValueField.stringValue = [self.customValueField.formatter stringForObjectValue:value];
}
- (IBAction)setCompareTarget:(id)sender
{
[self reloadData];
[self updateSearchButton];
[self updateWatchButtons];
}
- (IBAction)addCheat:(id)sender
{
S9xCheatFinderEntry *entry = _addressRows[self.addressTableView.selectedRow];
S9xCheatEditViewController *vc = [[S9xCheatEditViewController alloc] initWithCheatItem:nil saveCallback:^
{
[self.delegate addedCheat];
}];
[self presentViewControllerAsSheet:vc];
vc.addressField.objectValue = @(entry.address + MAIN_MEMORY_BASE);
vc.valueField.objectValue = @(_currentRAM[entry.address]);
[vc.view.window makeFirstResponder:vc.descriptionField];
[vc updateSaveButton];
}
- (IBAction)watchesAction:(NSSegmentedControl *)sender
{
switch((S9xCheatFinderWatchSegments)sender.selectedSegment)
{
case S9xCheatFinderWatchSegmentsAdd:
{
NSIndexSet *selectedIndexes = self.addressTableView.selectedRowIndexes;
[selectedIndexes enumerateIndexesUsingBlock:^(NSUInteger i, BOOL *stop)
{
S9xWatchPoint *watchPoint = [S9xWatchPoint new];
S9xCheatFinderEntry *entry = _addressRows[i];
watchPoint.address = entry.address + MAIN_MEMORY_BASE;
watchPoint.size = self.byteSize;
watchPoint.format = self.valueType - 100;
[_watchRows addObject:watchPoint];
}];
[self.engine setWatchPoints:_watchRows];
[self reloadWatchPoints];
break;
}
case S9xCheatFinderWatchSegmentsRemove:
{
[_watchRows removeObjectsAtIndexes:self.watchTableView.selectedRowIndexes];
2023-03-25 21:14:28 +00:00
[self.engine setWatchPoints:_watchRows];
2023-03-25 14:36:29 +00:00
[self reloadWatchPoints];
break;
}
case S9xCheatFinderWatchSegmentsSave:
{
NSSavePanel *savePanel = [NSSavePanel new];
savePanel.nameFieldStringValue = @"watches.txt";
[savePanel beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse response)
{
if (response == NSModalResponseOK)
{
FILE *file = fopen(savePanel.URL.path.UTF8String, "wb");
if (file != NULL)
{
for ( S9xWatchPoint *watchPoint in self->_watchRows)
{
fprintf(file, "address = 0x%x, name = \"?\", size = %d, format = %d\n", watchPoint.address, watchPoint.size, watchPoint.format);
}
fclose(file);
}
}
}];
break;
}
case S9xCheatFinderWatchSegmentsLoad:
{
NSOpenPanel *openPanel = [NSOpenPanel new];
openPanel.allowsMultipleSelection = NO;
openPanel.canChooseDirectories = NO;
[openPanel beginSheetModalForWindow:self.view.window completionHandler:^(NSModalResponse response)
{
FILE *file = fopen(openPanel.URL.path.UTF8String, "rb");
if (file != NULL)
{
self->_watchRows = [NSMutableArray new];
for (NSUInteger i = 0; i < MAX_WATCHES; ++i)
{
uint32_t address;
uint32_t size;
uint32_t format;
char ignore[32];
fscanf(file, "address = 0x%x, name = \"%31[^\"]\", size = %d, format = %d\n", &address, ignore, &size, &format);
if (ferror(file))
{
break;
}
S9xWatchPoint *watchPoint = [S9xWatchPoint new];
watchPoint.address = address;
watchPoint.size = size;
watchPoint.format = format;
[self->_watchRows addObject:watchPoint];
if(feof(file))
{
break;
}
}
fclose(file);
[self.engine setWatchPoints:self->_watchRows];
[self reloadWatchPoints];
}
}];
}
}
}
#pragma mark - Emulation Delegate
- (void)gameLoaded
{
[self resetAll];
[self emulationResumed];
}
- (void)emulationPaused
{
if (!NSThread.isMainThread)
{
dispatch_sync(dispatch_get_main_queue(), ^
{
[self emulationPaused];
});
}
else if (_shouldReopen)
{
[self.view.window makeKeyAndOrderFront:self];
_shouldReopen = NO;
}
}
- (void)emulationResumed
{
if (!NSThread.isMainThread)
{
dispatch_sync(dispatch_get_main_queue(), ^
{
[self emulationResumed];
});
}
else
{
if ([self.view.window isVisible])
{
[self.view.window close];
_shouldReopen = YES;
}
[self reloadData];
}
}
#pragma mark - Table View Delegate
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
if (tableView == self.addressTableView)
{
return _addressRows.count;
}
else
{
return _watchRows.count;
}
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSString *value = [self tableView:tableView objectValueForTableColumn:tableColumn row:row];
NSTableCellView *cell = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
cell.textField.stringValue = value;
return cell;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if (tableView == self.addressTableView)
{
S9xCheatFinderEntry *entry = _addressRows[row];
if ([tableColumn.identifier isEqualToString:@"address"])
{
return [NSString stringWithFormat:@"%06X", entry.address + MAIN_MEMORY_BASE];
}
else if ([tableColumn.identifier isEqualToString:@"currentValue"])
{
return [self stringFor:_currentRAM index:entry.address];
}
else if ([tableColumn.identifier isEqualToString:@"previousValue"])
{
if (!_hasPrevious)
{
return @"";
}
return [self stringFor:_previousRAM index:entry.address];
}
else if ( [tableColumn.identifier isEqualToString:@"storedValue"])
{
if (!_hasStored)
{
return @"";
}
return [self stringFor:_storedRAM index:entry.address];
}
return @"";
}
else
{
S9xWatchPoint *watchPoint = _watchRows[row];
NSString *formatString;
switch((S9xCheatFinderValueType)(watchPoint.format + 100))
{
case S9xCheatFinderValueTypeSigned:
formatString = @"s";
break;
case S9xCheatFinderValueTypeUnsigned:
formatString = @"u";
break;
case S9xCheatFinderValueTypeHexadecimal:
formatString = @"x";
break;
}
return [NSString stringWithFormat:@"%06X,%d%@", watchPoint.address, watchPoint.size, formatString];
}
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
self.addCheatButton.enabled = self.addressTableView.numberOfSelectedRows == 1;
[self updateWatchButtons];
}
#pragma mark - Text Field Delegate
- (void)controlTextDidChange:(NSNotification *)obj
{
[self updateSearchButton];
}
#pragma mark - Accessors
@dynamic byteSize;
- (S9xCheatFinderByteSize)byteSize
{
return (S9xCheatFinderByteSize)[self.byteSizePopUp selectedTag];
}
- (S9xCheatFinderValueType)valueType
{
S9xCheatFinderValueType valueType;
if (((NSButton *)[self.view viewWithTag:S9xCheatFinderValueTypeUnsigned]).state == NSOnState)
{
valueType = S9xCheatFinderValueTypeUnsigned;
}
else if (((NSButton *)[self.view viewWithTag:S9xCheatFinderValueTypeSigned]).state == NSOnState)
{
valueType = S9xCheatFinderValueTypeSigned;
}
else
{
valueType = S9xCheatFinderValueTypeHexadecimal;
}
return valueType;
}
@dynamic compareType;
- (S9xCheatFinderComparisonType)compareType
{
return (S9xCheatFinderComparisonType)self.comparisonTypePopUp.selectedTag;
}
@dynamic compareTo;
- (S9xCheatFinderCompareTo)compareTo
{
S9xCheatFinderCompareTo compareTo;
if (((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToCustom]).state == NSOnState)
{
compareTo = S9xCheatFinderCompareToCustom;
}
else if (((NSButton *)[self.view viewWithTag:S9xCheatFinderCompareToStored]).state == NSOnState)
{
compareTo = S9xCheatFinderCompareToStored;
}
else
{
compareTo = S9xCheatFinderCompareToPrevious;
}
return compareTo;
}
@dynamic customValue;
- (NSNumber *)customValue
{
return [((NSNumberFormatter *)self.customValueField.formatter) numberFromString:self.customValueField.stringValue];
}
@end
@implementation S9xCheatFinderTableView
- (void)keyDown:(NSEvent *)event
{
if ( [event.characters isEqualToString:@"\b"] || [event.characters isEqualToString:@"\x7f"] )
{
if (self.deleteBlock != nil && self.numberOfSelectedRows > 0)
{
self.deleteBlock(self.selectedRowIndexes);
}
}
else
{
[super keyDown:event];
}
}
@end
@implementation S9xCheatFinderEntry
@end