diff --git a/macosx/Snes9x/AppDelegate.h b/macosx/Snes9x/AppDelegate.h index d8cc147a..eceeb0ad 100644 --- a/macosx/Snes9x/AppDelegate.h +++ b/macosx/Snes9x/AppDelegate.h @@ -15,12 +15,13 @@ (c) Copyright 2004 Alexander and Sander (c) Copyright 2004 - 2005 Steven Seeger (c) Copyright 2005 Ryan Vogt - (c) Copyright 2019 - 2021 Michael Donald Buckley + (c) Copyright 2019 - 2022 Michael Donald Buckley ***********************************************************************************/ #import #import #import "S9xPreferences/S9xPreferencesWindowController.h" +#import "Cheats/S9xCheatsViewController.h" extern NSWindowFrameAutosaveName const kMainWindowIdentifier; @@ -31,6 +32,7 @@ extern NSWindowFrameAutosaveName const kMainWindowIdentifier; @property (nonatomic, strong) NSMutableDictionary *keys; @property (nonatomic, strong) NSWindow *gameWindow; @property (nonatomic, strong) S9xPreferencesWindowController *preferencesWindowController; +@property (nonatomic, strong) NSWindowController *cheatsWindowController; @property (nonatomic, readonly, assign) S9xDeviceSetting deviceSetting; - (void)setButtonCode:(S9xButtonCode)buttonCode forKeyCode:(int16)keyCode player:(int8)player; diff --git a/macosx/Snes9x/AppDelegate.m b/macosx/Snes9x/AppDelegate.m index 8e3de21e..784cc174 100644 --- a/macosx/Snes9x/AppDelegate.m +++ b/macosx/Snes9x/AppDelegate.m @@ -15,7 +15,7 @@ (c) Copyright 2004 Alexander and Sander (c) Copyright 2004 - 2005 Steven Seeger (c) Copyright 2005 Ryan Vogt - (c) Copyright 2019 Michael Donald Buckley + (c) Copyright 2019 - 2022 Michael Donald Buckley ***********************************************************************************/ #import @@ -23,6 +23,7 @@ #import "S9xPreferencesConstants.h" NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow"; +NSWindowFrameAutosaveName const kCheatsWindowIdentifier = @"s9xCheatsWindow"; @implementation AppDelegate @@ -194,6 +195,9 @@ NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow"; [self importKeySettings]; [self importGraphicsSettings]; [self applyEmulationSettings]; + + self.s9xEngine.cheatsEnabled = [defaults boolForKey:kEnableCheatsPref]; + [defaults synchronize]; } @@ -463,6 +467,11 @@ NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow"; [s9xView.leftAnchor constraintGreaterThanOrEqualToAnchor:gameWindow.contentView.leftAnchor].active = YES; [s9xView.rightAnchor constraintLessThanOrEqualToAnchor:gameWindow.contentView.rightAnchor].active = YES; + if (self.cheatsWindowController != nil) + { + [((S9xCheatsViewController *)self.cheatsWindowController.contentViewController) deselectAll]; + [((S9xCheatsViewController *)self.cheatsWindowController.contentViewController) reloadData]; + } [gameWindow makeKeyAndOrderFront:self]; [NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:url]; @@ -481,6 +490,17 @@ NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow"; else if (action == @selector(updateDeviceSetting:)) { menuItem.state = (self.deviceSetting == (S9xDeviceSetting)menuItem.tag) ? NSOnState : NSOffState; } + else if (action == @selector(toggleCheats:)) + { + if (self.s9xEngine.cheatsEnabled) + { + menuItem.title = NSLocalizedString(@"Disable Cheats", nil); + } + else + { + menuItem.title = NSLocalizedString(@"Enable Cheats", nil); + } + } return !self.isRunningEmulation; } @@ -587,4 +607,35 @@ NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow"; } +- (IBAction)openCheatsWindow:(id)sender +{ + if (self.cheatsWindowController == nil) + { + NSWindow *window = [NSWindow windowWithContentViewController:[[S9xCheatsViewController alloc] initWithNibName:@"S9xCheatsViewController" bundle:nil]]; + self.cheatsWindowController = [[NSWindowController alloc] initWithWindow:window]; + + window = self.cheatsWindowController.window; + + window.title = NSLocalizedString(@"Cheats", nil); + window.restorationClass = self.class; + window.frameAutosaveName = kCheatsWindowIdentifier; + window.releasedWhenClosed = NO; + + if ( ![window setFrameUsingName:kCheatsWindowIdentifier] ) + { + [window center]; + } + } + + [self.cheatsWindowController showWindow:nil]; + [self.cheatsWindowController.window makeKeyAndOrderFront:nil]; + [self.cheatsWindowController.window makeKeyWindow]; +} + +- (IBAction)toggleCheats:(id)sender +{ + self.s9xEngine.cheatsEnabled = !self.s9xEngine.cheatsEnabled; + [NSUserDefaults.standardUserDefaults setBool:self.s9xEngine.cheatsEnabled forKey:kEnableCheatsPref]; +} + @end diff --git a/macosx/Snes9x/Base.lproj/MainMenu.xib b/macosx/Snes9x/Base.lproj/MainMenu.xib index 25ecce20..707adc64 100644 --- a/macosx/Snes9x/Base.lproj/MainMenu.xib +++ b/macosx/Snes9x/Base.lproj/MainMenu.xib @@ -1,7 +1,8 @@ - + - + + @@ -94,6 +95,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -174,6 +394,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/macosx/Snes9x/Cheats/S9xCheatEditViewController.h b/macosx/Snes9x/Cheats/S9xCheatEditViewController.h new file mode 100644 index 00000000..cd8c868f --- /dev/null +++ b/macosx/Snes9x/Cheats/S9xCheatEditViewController.h @@ -0,0 +1,33 @@ +/*****************************************************************************\ + 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 - 2022 Michael Donald Buckley + ***********************************************************************************/ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class S9xCheatItem; + +@interface S9xCheatEditViewController : NSViewController + +- (instancetype)initWithCheatItem:(nullable S9xCheatItem *)cheatItem saveCallback:(void (^)(void))saveCallback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/macosx/Snes9x/Cheats/S9xCheatEditViewController.m b/macosx/Snes9x/Cheats/S9xCheatEditViewController.m new file mode 100644 index 00000000..650d1080 --- /dev/null +++ b/macosx/Snes9x/Cheats/S9xCheatEditViewController.m @@ -0,0 +1,150 @@ +/*****************************************************************************\ + 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 - 2022 Michael Donald Buckley + ***********************************************************************************/ + +#import + +#import "S9xCheatEditViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface S9xCheatEditViewController () +@property (nonatomic, weak) IBOutlet NSTextField *codeField; +@property (nonatomic, weak) IBOutlet NSTextField *descriptionField; +@property (nonatomic, weak) IBOutlet NSTextField *addressField; +@property (nonatomic, weak) IBOutlet NSTextField *valueField; +@property (nonatomic, weak) IBOutlet NSTextField *invalidCodeLabel; +@property (nonatomic, weak) IBOutlet NSButton *saveButton; + +@property (nonatomic, strong) S9xCheatItem *cheatItem; +@property (nonatomic, copy) void (^saveBlock)(void); +@end + +@implementation S9xCheatEditViewController + +- (instancetype)initWithCheatItem:(nullable S9xCheatItem *)cheatItem saveCallback:(void (^)(void))saveCallback +{ + if (self = [super initWithNibName:@"S9xCheatEditViewController" bundle:nil]) + { + _cheatItem = cheatItem; + _saveBlock = saveCallback; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + if (self.cheatItem != nil) + { + self.descriptionField.stringValue = self.cheatItem.cheatDescription; + self.addressField.objectValue = self.cheatItem.address; + self.valueField.objectValue = self.cheatItem.value; + } + + [self updateSaveButton]; +} + +- (void)controlTextDidChange:(NSNotification *)notification +{ + if (notification.object == self.codeField) + { + if (self.codeField.stringValue.length > 0 ) + { + uint32 address = 0; + uint8 value = 0; + + if ( CheatValuesFromCode(self.codeField.stringValue, &address, &value) ) + { + self.addressField.objectValue = @(address); + self.valueField.objectValue = @(value); + self.invalidCodeLabel.hidden = YES; + } + else + { + self.addressField.stringValue = @""; + self.valueField.stringValue = @""; + self.invalidCodeLabel.hidden = NO; + } + } + else + { + if (self.cheatItem != nil) + { + self.addressField.objectValue = self.cheatItem.address; + self.valueField.objectValue = self.cheatItem.value; + } + else + { + self.addressField.stringValue = @""; + self.valueField.stringValue = @""; + } + + self.invalidCodeLabel.hidden = YES; + } + } + + else if (notification.object == self.addressField || notification.object == self.valueField) + { + self.codeField.stringValue = @""; + } + + [self updateSaveButton]; +} + +- (void)updateSaveButton +{ + self.saveButton.enabled = (self.addressField.stringValue.length > 0 && self.valueField.stringValue.length > 0); +} + +- (IBAction)save:(id)sender +{ + if ( self.cheatItem != nil ) + { + self.cheatItem.address = self.addressField.objectValue; + self.cheatItem.value = self.valueField.objectValue; + self.cheatItem.cheatDescription = self.descriptionField.stringValue; + } + else + { + if (self.codeField.stringValue.length > 0) + { + CreateCheatFromCode(self.codeField.stringValue, self.descriptionField.stringValue); + } + else + { + CreateCheatFromAddress(self.addressField.objectValue, self.valueField.objectValue, self.descriptionField.stringValue); + } + } + + [self dismissViewController:self]; + + self.saveBlock(); +} + +- (IBAction)cancelOperation:(nullable id)sender +{ + [self dismissViewController:self]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/macosx/Snes9x/Cheats/S9xCheatEditViewController.xib b/macosx/Snes9x/Cheats/S9xCheatEditViewController.xib new file mode 100644 index 00000000..4e82bde9 --- /dev/null +++ b/macosx/Snes9x/Cheats/S9xCheatEditViewController.xib @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macosx/Snes9x/Cheats/S9xCheatsViewController.h b/macosx/Snes9x/Cheats/S9xCheatsViewController.h new file mode 100644 index 00000000..beff0c71 --- /dev/null +++ b/macosx/Snes9x/Cheats/S9xCheatsViewController.h @@ -0,0 +1,30 @@ +/*****************************************************************************\ + 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 - 2022 Michael Donald Buckley + ***********************************************************************************/ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface S9xCheatsViewController : NSViewController +- (void)deselectAll; +- (void)reloadData; +@end + +NS_ASSUME_NONNULL_END diff --git a/macosx/Snes9x/Cheats/S9xCheatsViewController.m b/macosx/Snes9x/Cheats/S9xCheatsViewController.m new file mode 100644 index 00000000..86e3fd00 --- /dev/null +++ b/macosx/Snes9x/Cheats/S9xCheatsViewController.m @@ -0,0 +1,266 @@ +/*****************************************************************************\ + 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 - 2022 Michael Donald Buckley + ***********************************************************************************/ + +#import + +#import "S9xCheatsViewController.h" +#import "S9xCheatEditViewController.h" + +@interface S9xCheatsViewController () + +@property (nonatomic, weak) IBOutlet NSTableView *tableView; +@property (nonatomic, weak) IBOutlet NSSegmentedControl *segmentedControl; + +@property (nonatomic, strong) NSArray *cheats; + +@end + +@implementation S9xCheatsViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self.segmentedControl setEnabled:YES forSegment:0]; + [self.segmentedControl setEnabled:NO forSegment:1]; + [self.segmentedControl setEnabled:NO forSegment:2]; + + self.tableView.doubleAction = @selector(editSelectedCheat); + self.tableView.target = self; + + [self reloadData]; +} + +- (void)deselectAll +{ + [self.tableView deselectAll:nil]; +} + +- (void)reloadData +{ + NSSet *selectedIDs = [NSSet setWithArray:[[self.cheats objectsAtIndexes:self.tableView.selectedRowIndexes] valueForKey:@"cheatID"]]; + + CGPoint scrollPosition = self.tableView.visibleRect.origin; + + self.cheats = [GetAllCheats() sortedArrayUsingDescriptors:self.tableView.sortDescriptors]; + [self.tableView reloadData]; + + NSMutableIndexSet *indexSet = [NSMutableIndexSet new]; + for (NSUInteger i = 0; i < self.tableView.numberOfRows; ++i) + { + if ([selectedIDs containsObject:@(self.cheats[i].cheatID)]) + { + [indexSet addIndex:i]; + } + } + + [self.tableView scrollPoint:scrollPosition]; + + [self.tableView selectRowIndexes:indexSet byExtendingSelection:NO]; +} + +- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors +{ + [self reloadData]; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ + return self.cheats.count; +} + +- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + NSTableCellView *view = [self.tableView makeViewWithIdentifier:tableColumn.identifier owner:self]; + view.objectValue = self.cheats[row]; + return view; +} + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + [self.segmentedControl setEnabled:self.tableView.selectedRowIndexes.count > 0 forSegment:1]; + [self.segmentedControl setEnabled:self.tableView.selectedRowIndexes.count == 1 forSegment:2]; +} + +- (IBAction)segmentedControlAction:(NSSegmentedControl *)sender +{ + switch(sender.selectedSegment) + { + case 0: + [self addNewCheat]; + break; + + case 1: + [self deleteSelectedCheats]; + break; + + case 2: + [self editSelectedCheat]; + break; + } +} + +- (void)addNewCheat +{ + S9xCheatEditViewController *vc = [[S9xCheatEditViewController alloc] initWithCheatItem:nil saveCallback:^ + { + [self reloadData]; + }]; + + [self presentViewControllerAsSheet:vc]; +} + +- (void)deleteSelectedCheats +{ + + NSIndexSet *selectedRows = self.tableView.selectedRowIndexes; + + if (selectedRows.count > 0) + { + NSArray *cheatsToDelete = [[self.cheats objectsAtIndexes:selectedRows] sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"cheatID" ascending:NO]]]; + + for (S9xCheatItem *item in cheatsToDelete) + { + [item delete]; + } + } + + [self.tableView deselectAll:self]; + [self reloadData]; +} + +- (void)editSelectedCheat +{ + NSIndexSet *selectedRows = self.tableView.selectedRowIndexes; + + if (selectedRows.count == 1) + { + S9xCheatItem *item = self.cheats[selectedRows.firstIndex]; + + S9xCheatEditViewController *vc = [[S9xCheatEditViewController alloc] initWithCheatItem:item saveCallback:^ + { + [self reloadData]; + }]; + + [self presentViewControllerAsSheet:vc]; + } +} + +@end + +@interface S9xHexNumberFormatter : NSFormatter +@end + +@implementation S9xHexNumberFormatter + ++ (NSUInteger)maxLength +{ + return NSUIntegerMax; +} + +- (BOOL)getObjectValue:(out id _Nullable __autoreleasing *)obj forString:(NSString *)string errorDescription:(out NSString * _Nullable __autoreleasing *)error +{ + if (string.length > 0) + { + unsigned int value = 0; + NSScanner *scanner = [NSScanner scannerWithString:string]; + [scanner scanHexInt:&value]; + *obj = @(value); + + return YES; + } + + return NO; +} + +- (NSNumber *)numberFromString:(NSString *)string +{ + unsigned int value = 0; + NSScanner *scanner = [NSScanner scannerWithString:string]; + [scanner scanHexInt:&value]; + return @(value); +} + +- (NSString *)stringForObjectValue:(id)obj +{ + if ([obj isKindOfClass:NSNumber.class]) + { + return [NSString stringWithFormat:@"%X", ((NSNumber *)obj).unsignedIntValue]; + } + else if ([obj isKindOfClass:NSString.class]) + { + return obj; + } + + return @""; +} + +- (BOOL)isPartialStringValid:(NSString * _Nonnull * _Nonnull)partialStringPtr proposedSelectedRange:(nullable NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString * _Nullable * _Nullable)error +{ + static NSCharacterSet *hexCharacterSet; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^ + { + hexCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"] invertedSet]; + }); + + if ( [*partialStringPtr rangeOfCharacterFromSet:hexCharacterSet].location != NSNotFound ) + { + *partialStringPtr = nil; + return NO; + } + + *partialStringPtr = (*partialStringPtr).uppercaseString; + + if ( (*partialStringPtr).length > self.class.maxLength ) + { + *partialStringPtr = [*partialStringPtr substringToIndex:self.class.maxLength]; + return NO; + } + + return YES; +} + +@end + +@interface S9xAddressFormatter : S9xHexNumberFormatter +@end + +@implementation S9xAddressFormatter + ++ (NSUInteger)maxLength +{ + return 6; +} + +@end + +@interface S9xValueFormatter : S9xHexNumberFormatter +@end + +@implementation S9xValueFormatter + ++ (NSUInteger)maxLength +{ + return 2; +} + +@end diff --git a/macosx/Snes9x/Cheats/S9xCheatsViewController.xib b/macosx/Snes9x/Cheats/S9xCheatsViewController.xib new file mode 100644 index 00000000..2ef097a6 --- /dev/null +++ b/macosx/Snes9x/Cheats/S9xCheatsViewController.xib @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.h b/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.h index 1ede6fb5..c84e2200 100644 --- a/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.h +++ b/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.h @@ -38,4 +38,6 @@ extern NSString * const kAllowInvalidVRAMAccessPref; extern NSString * const kSeparateEchoBufferFromRAMPref; extern NSString * const kDisableSpriteLimitPref; +extern NSString * const kEnableCheatsPref; + NS_ASSUME_NONNULL_END diff --git a/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.m b/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.m index 343c93fe..5985006e 100644 --- a/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.m +++ b/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.m @@ -35,3 +35,5 @@ NSString * const kApplyGameSpecificHacksPref = @"ApplyGameSpecificHacks"; NSString * const kAllowInvalidVRAMAccessPref = @"AllowInvalidVRAMAccess"; NSString * const kSeparateEchoBufferFromRAMPref = @"SeparateEchoBufferFromRAM"; NSString * const kDisableSpriteLimitPref = @"DisableSpriteLimit"; + +NSString * const kEnableCheatsPref = @"EnableCheats"; diff --git a/macosx/mac-cheat.h b/macosx/mac-cheat.h index bef15b91..db1e0484 100755 --- a/macosx/mac-cheat.h +++ b/macosx/mac-cheat.h @@ -15,13 +15,34 @@ (c) Copyright 2004 Alexander and Sander (c) Copyright 2004 - 2005 Steven Seeger (c) Copyright 2005 Ryan Vogt - (c) Copyright 2019 Michael Donald Buckley + (c) Copyright 2019 - 2022 Michael Donald Buckley ***********************************************************************************/ #ifndef _mac_cheat_h_ #define _mac_cheat_h_ -void ConfigureCheat (void); +#import + +@interface S9xCheatItem : NSView +@property (nonatomic, assign) uint32 cheatID; +@property (nonatomic, strong) NSNumber *address; +@property (nonatomic, strong) NSNumber *value; +@property (nonatomic, strong) NSNumber *enabled; +@property (nonatomic, strong) NSString *cheatDescription; + +- (void)delete; +@end + +#ifdef __cplusplus +extern "C" { +#endif +bool CheatValuesFromCode(NSString *code, uint32 *address, uint8 *value); +void CreateCheatFromAddress(NSNumber *address, NSNumber *value, NSString *cheatDescription); +void CreateCheatFromCode(NSString *code, NSString *cheatDescription); +NSArray *GetAllCheats(void); +#ifdef __cplusplus +} +#endif #endif diff --git a/macosx/mac-cheat.mm b/macosx/mac-cheat.mm index f0baf09a..7dc5be06 100755 --- a/macosx/mac-cheat.mm +++ b/macosx/mac-cheat.mm @@ -15,7 +15,7 @@ (c) Copyright 2004 Alexander and Sander (c) Copyright 2004 - 2005 Steven Seeger (c) Copyright 2005 Ryan Vogt - (c) Copyright 2019 Michael Donald Buckley + (c) Copyright 2019 - 2022 Michael Donald Buckley ***********************************************************************************/ @@ -42,510 +42,137 @@ extern SCheatData Cheat; -typedef struct +@implementation S9xCheatItem + +- (void)setAddress:(uint32)address value:(uint8)value cheatDescription:(const char *)cheatDescription { - uint32 id; - uint32 address; - uint8 value; - bool8 valid; - bool8 enabled; - char description[22]; -} CheatItem; + bool enabled = self.enabled.boolValue; -static WindowRef wRef; -static HIViewRef dbRef; -static CheatItem citem[MAC_MAX_CHEATS]; -static uint32 numofcheats; + char code[256]; + sprintf(code, "%x=%x", address, value); + S9xModifyCheatGroup(self.cheatID, cheatDescription, code); -static void InitCheatItems (void); -static void ImportCheatItems (void); -static void DetachCheatItems (void); -static void AddCheatItem (void); -static void DeleteCheatItem (void); -static void EnableAllCheatItems (void); -static void DBItemNotificationCallBack (HIViewRef, DataBrowserItemID, DataBrowserItemNotification); -static OSStatus CheatEventHandler (EventHandlerCallRef, EventRef, void *); - - -static void InitCheatItems (void) -{ - for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++) + if (enabled) { - citem[i].id = i + 1; - citem[i].valid = false; - citem[i].enabled = false; - citem[i].address = 0; - citem[i].value = 0; - sprintf(citem[i].description, "Cheat %03" PRIu32, citem[i].id); + S9xEnableCheatGroup(self.cheatID); } } -static void ImportCheatItems (void) +@dynamic address; +- (NSNumber *)address { - int cheat_num = std::min((int)Cheat.g.size(), MAC_MAX_CHEATS); - for (unsigned int i = 0; i < cheat_num; i++) + return @(Cheat.g[self.cheatID].c[0].address); +} + +- (void)setAddress:(NSNumber *)address +{ + [self setAddress:address.unsignedIntValue value:Cheat.g[self.cheatID].c[0].byte cheatDescription:self.cheatDescription.UTF8String]; +} + +@dynamic value; +- (NSNumber *)value +{ + return @(Cheat.g[self.cheatID].c[0].byte); +} + +- (void)setValue:(NSNumber *)value +{ + [self setAddress:Cheat.g[self.cheatID].c[0].address value:value.unsignedCharValue cheatDescription:self.cheatDescription.UTF8String]; +} + +@dynamic enabled; +- (NSNumber *)enabled +{ + return @(Cheat.g[self.cheatID].enabled); +} + +- (void)setEnabled:(NSNumber *)enabled +{ + if (enabled.boolValue) { - citem[i].valid = true; - citem[i].enabled = Cheat.g[i].enabled; - citem[i].address = Cheat.g[i].c[0].address; // mac dialog only supports one cheat per group at the moment - citem[i].value = Cheat.g[i].c[0].byte; - strncpy(citem[i].description, Cheat.g[i].name, 21); - citem[i].description[21] = '\0'; + S9xEnableCheatGroup(self.cheatID); + } + else + { + S9xDisableCheatGroup(self.cheatID); } } -static void DetachCheatItems (void) +@dynamic cheatDescription; +- (NSString *)cheatDescription { - S9xDeleteCheats(); + return [NSString stringWithUTF8String:Cheat.g[self.cheatID].name]; +} - for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++) +- (void)setCheatDescription:(NSString *)cheatDescription +{ + [self setAddress:Cheat.g[self.cheatID].c[0].address value:Cheat.g[self.cheatID].c[0].byte cheatDescription:cheatDescription.UTF8String]; +} + +- (void)delete +{ + S9xDeleteCheatGroup(self.cheatID); +} + +@end + +extern "C" +{ + +bool CheatValuesFromCode(NSString *code, uint32 *address, uint8 *value) +{ + const char *text = code.UTF8String; + + if (!S9xGameGenieToRaw (text, *address, *value)) { - if (citem[i].valid) - { - char code[10]; - snprintf(code, 10, "%x=%x", citem[i].address, citem[i].value); - int index = S9xAddCheatGroup(citem[i].description, code); - if(citem[i].enabled && index >= 0) - S9xEnableCheatGroup(index); - } + return true; } + + if (!S9xProActionReplayToRaw (text, *address, *value)) + { + return true; + } + + else if (sscanf (text, "%x = %x", (unsigned int *)address, (unsigned int *)value) == 2) + { + return true; + } + + else if (sscanf (text, "%x / %x", (unsigned int *)address, (unsigned int *)value) == 2) + { + return true; + } + + return false; } -void ConfigureCheat (void) +void CreateCheatFromAddress(NSNumber *address, NSNumber *value, NSString *cheatDescription) { - if (!cartOpen) - return; - -// OSStatus err; -// IBNibRef nibRef; -// -// err = CreateNibReference(kMacS9XCFString, &nibRef); -// if (err == noErr) -// { -// err = CreateWindowFromNib(nibRef, CFSTR("CheatEntry"), &wRef); -// if (err == noErr) -// { -// DataBrowserCallbacks callbacks; -// EventHandlerRef eref; -// EventHandlerUPP eUPP; -// EventTypeSpec events[] = { { kEventClassCommand, kEventCommandProcess }, -// { kEventClassCommand, kEventCommandUpdateStatus }, -// { kEventClassWindow, kEventWindowClose } }; -// HIViewRef ctl, root; -// HIViewID cid; -// -// root = HIViewGetRoot(wRef); -// cid.id = 0; -// cid.signature = kDataBrowser; -// HIViewFindByID(root, cid, &dbRef); -// -// #ifdef MAC_PANTHER_SUPPORT -// if (systemVersion < 0x1040) -// { -// HISize minSize; -// Rect rct; -// -// GetWindowBounds(wRef, kWindowContentRgn, &rct); -// minSize.width = (float) (rct.right - rct.left); -// minSize.height = (float) (rct.bottom - rct.top ); -// err = SetWindowResizeLimits(wRef, &minSize, NULL); -// } -// #endif -// -// callbacks.version = kDataBrowserLatestCallbacks; -// err = InitDataBrowserCallbacks(&callbacks); -// callbacks.u.v1.itemDataCallback = NewDataBrowserItemDataUPP(DBClientDataCallback); -// callbacks.u.v1.itemCompareCallback = NewDataBrowserItemCompareUPP(DBCompareCallBack); -// callbacks.u.v1.itemNotificationCallback = NewDataBrowserItemNotificationUPP(DBItemNotificationCallBack); -// err = SetDataBrowserCallbacks(dbRef, &callbacks); -// -// if (systemVersion >= 0x1040) -// err = DataBrowserChangeAttributes(dbRef, kDataBrowserAttributeListViewAlternatingRowColors, kDataBrowserAttributeNone); -// -// InitCheatItems(); -// ImportCheatItems(); -// -// DataBrowserItemID *id; -// -// id = new DataBrowserItemID[MAC_MAX_CHEATS]; -// if (!id) -// QuitWithFatalError(@"cheat 01"); -// -// numofcheats = 0; -// -// for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++) -// { -// if (citem[i].valid) -// { -// id[numofcheats] = citem[i].id; -// numofcheats++; -// } -// } -// -// if (numofcheats) -// err = AddDataBrowserItems(dbRef, kDataBrowserNoItem, numofcheats, id, kDataBrowserItemNoProperty); -// -// delete [] id; -// -// cid.signature = kNewButton; -// HIViewFindByID(root, cid, &ctl); -// if (numofcheats == MAC_MAX_CHEATS) -// err = DeactivateControl(ctl); -// else -// err = ActivateControl(ctl); -// -// cid.signature = kAllButton; -// HIViewFindByID(root, cid, &ctl); -// if (numofcheats == 0) -// err = DeactivateControl(ctl); -// else -// err = ActivateControl(ctl); -// -// cid.signature = kDelButton; -// HIViewFindByID(root, cid, &ctl); -// err = DeactivateControl(ctl); -// -// eUPP = NewEventHandlerUPP(CheatEventHandler); -// err = InstallWindowEventHandler(wRef, eUPP, GetEventTypeCount(events), events, (void *) wRef, &eref); -// -// err = SetKeyboardFocus(wRef, dbRef, kControlFocusNextPart); -// -// MoveWindowPosition(wRef, kWindowCheatEntry, true); -// ShowWindow(wRef); -// err = RunAppModalLoopForWindow(wRef); -// HideWindow(wRef); -// SaveWindowPosition(wRef, kWindowCheatEntry); -// -// err = RemoveEventHandler(eref); -// DisposeEventHandlerUPP(eUPP); -// -// DisposeDataBrowserItemNotificationUPP(callbacks.u.v1.itemNotificationCallback); -// DisposeDataBrowserItemCompareUPP(callbacks.u.v1.itemCompareCallback); -// DisposeDataBrowserItemDataUPP(callbacks.u.v1.itemDataCallback); -// -// CFRelease(wRef); -// -// DetachCheatItems(); -// } -// -// DisposeNibReference(nibRef); -// } + char code[256]; + sprintf(code, "%x=%x", address.unsignedIntValue, value.unsignedCharValue); + S9xAddCheatGroup(cheatDescription.UTF8String, code); + S9xEnableCheatGroup(Cheat.g.size() - 1); } -static void AddCheatItem (void) +void CreateCheatFromCode(NSString *code, NSString *cheatDescription) { -// OSStatus err; -// HIViewRef ctl, root; -// HIViewID cid; -// DataBrowserItemID id[1]; -// unsigned int i; -// -// if (numofcheats == MAC_MAX_CHEATS) -// return; -// -// for (i = 0; i < MAC_MAX_CHEATS; i++) -// if (citem[i].valid == false) -// break; -// -// if (i == MAC_MAX_CHEATS) -// return; -// -// numofcheats++; -// citem[i].valid = true; -// citem[i].enabled = false; -// citem[i].address = 0; -// citem[i].value = 0; -// sprintf(citem[i].description, "Cheat %03" PRIu32, citem[i].id); -// -// id[0] = citem[i].id; -// err = AddDataBrowserItems(dbRef, kDataBrowserNoItem, 1, id, kDataBrowserItemNoProperty); -// err = RevealDataBrowserItem(dbRef, id[0], kCmAddress, true); -// -// root = HIViewGetRoot(wRef); -// cid.id = 0; -// -// if (numofcheats == MAC_MAX_CHEATS) -// { -// cid.signature = kNewButton; -// HIViewFindByID(root, cid, &ctl); -// err = DeactivateControl(ctl); -// } -// -// if (numofcheats) -// { -// cid.signature = kAllButton; -// HIViewFindByID(root, cid, &ctl); -// err = ActivateControl(ctl); -// } + S9xAddCheatGroup(cheatDescription.UTF8String, code.UTF8String); + S9xEnableCheatGroup(Cheat.g.size() - 1); } -static void DeleteCheatItem (void) +NSArray *GetAllCheats(void) { -// OSStatus err; -// HIViewRef ctl, root; -// HIViewID cid; -// Handle selectedItems; -// ItemCount selectionCount; -// -// selectedItems = NewHandle(0); -// if (!selectedItems) -// return; -// -// err = GetDataBrowserItems(dbRef, kDataBrowserNoItem, true, kDataBrowserItemIsSelected, selectedItems); -// selectionCount = (GetHandleSize(selectedItems) / sizeof(DataBrowserItemID)); -// -// if (selectionCount == 0) -// { -// DisposeHandle(selectedItems); -// return; -// } -// -// err = RemoveDataBrowserItems(dbRef, kDataBrowserNoItem, selectionCount, (DataBrowserItemID *) *selectedItems, kDataBrowserItemNoProperty); -// -// for (unsigned int i = 0; i < selectionCount; i++) -// { -// citem[((DataBrowserItemID *) (*selectedItems))[i] - 1].valid = false; -// citem[((DataBrowserItemID *) (*selectedItems))[i] - 1].enabled = false; -// numofcheats--; -// } -// -// DisposeHandle(selectedItems); -// -// root = HIViewGetRoot(wRef); -// cid.id = 0; -// -// if (numofcheats < MAC_MAX_CHEATS) -// { -// cid.signature = kNewButton; -// HIViewFindByID(root, cid, &ctl); -// err = ActivateControl(ctl); -// } -// -// if (numofcheats == 0) -// { -// cid.signature = kAllButton; -// HIViewFindByID(root, cid, &ctl); -// err = DeactivateControl(ctl); -// } -} + NSMutableArray *cheats = [NSMutableArray new]; + uint32 cheatID = 0; -static void EnableAllCheatItems (void) -{ -// OSStatus err; -// -// for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++) -// if (citem[i].valid) -// citem[i].enabled = true; -// -// err = UpdateDataBrowserItems(dbRef, kDataBrowserNoItem, kDataBrowserNoItem, NULL, kDataBrowserItemNoProperty, kCmCheckBox); -} + for (auto it : Cheat.g) + { + S9xCheatItem *cheat = [S9xCheatItem new]; + cheat.cheatID = cheatID++; -static OSStatus DBClientDataCallback (HIViewRef browser, DataBrowserItemID itemID, DataBrowserPropertyID property, DataBrowserItemDataRef itemData, Boolean changeValue) -{ -// OSStatus err, result; -// CFStringRef str; -// Boolean r; -// uint32 address; -// uint8 value; -// char code[256]; -// -// result = noErr; -// -// switch (property) -// { -// case kCmCheckBox: -// ThemeButtonValue buttonValue; -// -// if (changeValue) -// { -// err = GetDataBrowserItemDataButtonValue(itemData, &buttonValue); -// citem[itemID - 1].enabled = (buttonValue == kThemeButtonOn) ? true : false; -// } -// else -// err = SetDataBrowserItemDataButtonValue(itemData, citem[itemID - 1].enabled ? kThemeButtonOn : kThemeButtonOff); -// -// break; -// -// case kCmAddress: -// if (changeValue) -// { -// err = GetDataBrowserItemDataText(itemData, &str); -// r = CFStringGetCString(str, code, 256, CFStringGetSystemEncoding()); -// CFRelease(str); -// if (r) -// { -// Boolean translated; -// -// if (S9xProActionReplayToRaw(code, address, value) == NULL) -// translated = true; -// else -// if (S9xGameGenieToRaw(code, address, value) == NULL) -// translated = true; -// else -// { -// translated = false; -// if (sscanf(code, "%" SCNx32, &address) != 1) -// address = 0; -// else -// address &= 0xFFFFFF; -// } -// -// citem[itemID - 1].address = address; -// sprintf(code, "%06" PRIX32, address); -// str = CFStringCreateWithCString(kCFAllocatorDefault, code, CFStringGetSystemEncoding()); -// err = SetDataBrowserItemDataText(itemData, str); -// CFRelease(str); -// -// if (translated) -// { -// DataBrowserItemID id[1]; -// -// citem[itemID - 1].value = value; -// id[0] = itemID; -// err = UpdateDataBrowserItems(browser, kDataBrowserNoItem, 1, id, kDataBrowserItemNoProperty, kCmValue); -// } -// } -// } -// else -// { -// sprintf(code, "%06" PRIX32, citem[itemID - 1].address); -// str = CFStringCreateWithCString(kCFAllocatorDefault, code, CFStringGetSystemEncoding()); -// err = SetDataBrowserItemDataText(itemData, str); -// CFRelease(str); -// } -// -// break; -// -// case kCmValue: -// if (changeValue) -// { -// err = GetDataBrowserItemDataText(itemData, &str); -// r = CFStringGetCString(str, code, 256, CFStringGetSystemEncoding()); -// CFRelease(str); -// if (r) -// { -// uint32 byte; -// -// if (sscanf(code, "%" SCNx32, &byte) == 1) -// citem[itemID - 1].value = (uint8) byte; -// else -// { -// citem[itemID - 1].value = 0; -// err = SetDataBrowserItemDataText(itemData, CFSTR("00")); -// } -// } -// } -// else -// { -// sprintf(code, "%02" PRIX8, citem[itemID - 1].value); -// str = CFStringCreateWithCString(kCFAllocatorDefault, code, CFStringGetSystemEncoding()); -// err = SetDataBrowserItemDataText(itemData, str); -// CFRelease(str); -// } -// -// break; -// -// case kCmDescription: -// if (changeValue) -// { -// code[0] = 0; -// err = GetDataBrowserItemDataText(itemData, &str); -// strcpy(code, GetMultiByteCharacters(str, 19)); -// CFRelease(str); -// -// if (code[0] == 0) -// { -// code[0] = ' '; -// code[1] = 0; -// } -// -// strcpy(citem[itemID - 1].description, code); -// } -// else -// { -// str = CFStringCreateWithCString(kCFAllocatorDefault, citem[itemID - 1].description, CFStringGetSystemEncoding()); -// err = SetDataBrowserItemDataText(itemData, str); -// CFRelease(str); -// } -// -// break; -// -// case kDataBrowserItemIsActiveProperty: -// err = SetDataBrowserItemDataBooleanValue(itemData, true); -// break; -// -// case kDataBrowserItemIsEditableProperty: -// err = SetDataBrowserItemDataBooleanValue(itemData, true); -// break; -// -// default: -// result = errDataBrowserPropertyNotSupported; -// } -// -// return (result); - return 0; -} + [cheats addObject:cheat]; + } - -static OSStatus CheatEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) -{ -// OSStatus err, result = eventNotHandledErr; -// WindowRef tWindowRef; -// -// tWindowRef = (WindowRef) inUserData; -// -// switch (GetEventClass(inEvent)) -// { -// case kEventClassWindow: -// switch (GetEventKind(inEvent)) -// { -// case kEventWindowClose: -// QuitAppModalLoopForWindow(tWindowRef); -// result = noErr; -// break; -// } -// -// break; -// -// case kEventClassCommand: -// switch (GetEventKind(inEvent)) -// { -// HICommand tHICommand; -// -// case kEventCommandUpdateStatus: -// err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); -// if (err == noErr && tHICommand.commandID == 'clos') -// { -// UpdateMenuCommandStatus(true); -// result = noErr; -// } -// -// break; -// -// case kEventCommandProcess: -// err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &tHICommand); -// if (err == noErr) -// { -// switch (tHICommand.commandID) -// { -// case kNewButton: -// AddCheatItem(); -// result = noErr; -// break; -// -// case kDelButton: -// DeleteCheatItem(); -// result = noErr; -// break; -// -// case kAllButton: -// EnableAllCheatItems(); -// result = noErr; -// } -// } -// } -// } -// -// return (result); - return 0; + return cheats; +} } diff --git a/macosx/mac-os.h b/macosx/mac-os.h index 18cd2c74..704ab6c7 100644 --- a/macosx/mac-os.h +++ b/macosx/mac-os.h @@ -177,6 +177,8 @@ extern id inputDelegate; @property (nonatomic, weak) id inputDelegate; +@property (nonatomic, assign) BOOL cheatsEnabled; + - (void)recreateS9xView; - (void)start; diff --git a/macosx/mac-os.mm b/macosx/mac-os.mm index 25075627..6f9dda9f 100644 --- a/macosx/mac-os.mm +++ b/macosx/mac-os.mm @@ -3409,6 +3409,17 @@ void QuitWithFatalError ( NSString *message) return inputDelegate; } +@dynamic cheatsEnabled; +- (BOOL)cheatsEnabled +{ + return Cheat.enabled; +} + +- (void)setCheatsEnabled:(BOOL)cheatsEnabled +{ + Cheat.enabled = cheatsEnabled; +} + @end @implementation S9xJoypad diff --git a/macosx/snes9x framework/snes9x_framework.h b/macosx/snes9x framework/snes9x_framework.h index b4c397fd..45db942d 100644 --- a/macosx/snes9x framework/snes9x_framework.h +++ b/macosx/snes9x framework/snes9x_framework.h @@ -16,5 +16,5 @@ FOUNDATION_EXPORT const unsigned char snes9x_frameworkVersionString[]; #import #import - +#import diff --git a/macosx/snes9x.xcodeproj/project.pbxproj b/macosx/snes9x.xcodeproj/project.pbxproj index 0e39acb4..7f4c5604 100755 --- a/macosx/snes9x.xcodeproj/project.pbxproj +++ b/macosx/snes9x.xcodeproj/project.pbxproj @@ -74,6 +74,11 @@ 30A6F62423B2771A00630584 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30A6F62323B2771A00630584 /* MetalKit.framework */; }; 30A6F62623B29EF500630584 /* shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 30A6F62523B29EF500630584 /* shaders.metal */; }; 30A6F62823B29F8200630584 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30A6F62723B29F8200630584 /* Metal.framework */; }; + 30CF849527AEF5AF002B37A9 /* S9xCheatsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 30CF849327AEF5AF002B37A9 /* S9xCheatsViewController.m */; }; + 30CF849627AEF5B0002B37A9 /* S9xCheatsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 30CF849427AEF5AF002B37A9 /* S9xCheatsViewController.xib */; }; + 30CF849727AEFD4F002B37A9 /* mac-cheat.h in Headers */ = {isa = PBXBuildFile; fileRef = EAECB67304AC7FCE00A80003 /* mac-cheat.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30CF849C27AF0C61002B37A9 /* S9xCheatEditViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 30CF849A27AF0C61002B37A9 /* S9xCheatEditViewController.m */; }; + 30CF849D27AF0C61002B37A9 /* S9xCheatEditViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 30CF849B27AF0C61002B37A9 /* S9xCheatEditViewController.xib */; }; 30D15CF322CE6B5A005BC352 /* snes9x_framework.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D15CF122CE6B5A005BC352 /* snes9x_framework.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30D15CFC22CE6B74005BC352 /* sha256.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 85FEF90A20DDB18D00C038E9 /* sha256.cpp */; }; 30D15CFE22CE6B74005BC352 /* bml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 85FEF90620DDB15B00C038E9 /* bml.cpp */; }; @@ -328,6 +333,12 @@ 30AD1D2122FBB2EA000EE989 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/Snes9x.xib; sourceTree = ""; }; 30C49EF6250C0F2B007D04F8 /* Snes9xDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Snes9xDebug.entitlements; sourceTree = ""; }; 30CCAD422290472E00549AED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = /Users/buckley/Projects/snes9x/macosx/Info.plist; sourceTree = ""; }; + 30CF849227AEF5AF002B37A9 /* S9xCheatsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = S9xCheatsViewController.h; sourceTree = ""; }; + 30CF849327AEF5AF002B37A9 /* S9xCheatsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = S9xCheatsViewController.m; sourceTree = ""; }; + 30CF849427AEF5AF002B37A9 /* S9xCheatsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = S9xCheatsViewController.xib; sourceTree = ""; }; + 30CF849927AF0C61002B37A9 /* S9xCheatEditViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = S9xCheatEditViewController.h; sourceTree = ""; }; + 30CF849A27AF0C61002B37A9 /* S9xCheatEditViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = S9xCheatEditViewController.m; sourceTree = ""; }; + 30CF849B27AF0C61002B37A9 /* S9xCheatEditViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = S9xCheatEditViewController.xib; sourceTree = ""; }; 30D15CEF22CE6B5A005BC352 /* snes9x_framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = snes9x_framework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 30D15CF122CE6B5A005BC352 /* snes9x_framework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = snes9x_framework.h; sourceTree = ""; }; 30D15CF222CE6B5A005BC352 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -669,6 +680,7 @@ 30714716230E379500917F82 /* Snes9x */ = { isa = PBXGroup; children = ( + 30CF849827AF0C3E002B37A9 /* Cheats */, 600546BE250F8F1700EC0977 /* S9xPreferences */, 30C49EF6250C0F2B007D04F8 /* Snes9xDebug.entitlements */, 30714717230E379500917F82 /* AppDelegate.h */, @@ -696,6 +708,19 @@ path = ReClassicfication; sourceTree = ""; }; + 30CF849827AF0C3E002B37A9 /* Cheats */ = { + isa = PBXGroup; + children = ( + 30CF849227AEF5AF002B37A9 /* S9xCheatsViewController.h */, + 30CF849327AEF5AF002B37A9 /* S9xCheatsViewController.m */, + 30CF849427AEF5AF002B37A9 /* S9xCheatsViewController.xib */, + 30CF849927AF0C61002B37A9 /* S9xCheatEditViewController.h */, + 30CF849A27AF0C61002B37A9 /* S9xCheatEditViewController.m */, + 30CF849B27AF0C61002B37A9 /* S9xCheatEditViewController.xib */, + ); + path = Cheats; + sourceTree = ""; + }; 30D15CF022CE6B5A005BC352 /* snes9x framework */ = { isa = PBXGroup; children = ( @@ -1118,6 +1143,7 @@ 30D15DD522CE6BC9005BC352 /* 7z.h in Headers */, 30D15DD622CE6BC9005BC352 /* aribitcd.h in Headers */, 30D15DD722CE6BC9005BC352 /* ariconst.h in Headers */, + 30CF849727AEFD4F002B37A9 /* mac-cheat.h in Headers */, 30D15DD822CE6BC9005BC352 /* ariprice.h in Headers */, 30D15DD922CE6BC9005BC352 /* btreecd.h in Headers */, 30D15DDA22CE6BC9005BC352 /* crc32.h in Headers */, @@ -1239,9 +1265,11 @@ 30D709B5236F731B00AAB7C3 /* folder_SRAMs.icns in Resources */, 30D709B2236F731B00AAB7C3 /* CART.icns in Resources */, 30D709B7236F731B00AAB7C3 /* musicbox_ledon.icns in Resources */, + 30CF849D27AF0C61002B37A9 /* S9xCheatEditViewController.xib in Resources */, 30D709B6236F731B00AAB7C3 /* folder_Freezes.icns in Resources */, 30D709C2236F7E3200AAB7C3 /* S9xPreferencesWindowController.xib in Resources */, 306937E32636352100007ABB /* S9xEmulationPreferencesViewController.xib in Resources */, + 30CF849627AEF5B0002B37A9 /* S9xCheatsViewController.xib in Resources */, 3071471E230E379600917F82 /* MainMenu.xib in Resources */, 306937DD26362B2400007ABB /* S9xSoundPreferencesViewController.xib in Resources */, ); @@ -1270,12 +1298,14 @@ 309C54802627F3060055DD95 /* S9xControlsPreferencesViewController.m in Sources */, 30D709C5236F90DF00AAB7C3 /* S9xButtonConfigTextField.m in Sources */, 306937E82636365100007ABB /* S9xFilesPreferencesViewController.m in Sources */, + 30CF849C27AF0C61002B37A9 /* S9xCheatEditViewController.m in Sources */, 306937D02636253900007ABB /* S9xPreferencesTabViewController.m in Sources */, 30714721230E379600917F82 /* main.m in Sources */, 306937CA2635EE5800007ABB /* S9xDisplayPreferencesViewController.m in Sources */, 30714719230E379500917F82 /* AppDelegate.m in Sources */, 306937DC26362B2400007ABB /* S9xSoundPreferencesViewController.m in Sources */, 3082C42A2379199F0081CA7C /* S9xApplication.m in Sources */, + 30CF849527AEF5AF002B37A9 /* S9xCheatsViewController.m in Sources */, 30D709C1236F7E3200AAB7C3 /* S9xPreferencesWindowController.m in Sources */, 304B366C262E82B800F8DC8E /* S9xPreferencesConstants.m in Sources */, );