diff --git a/apu/apu.cpp b/apu/apu.cpp index 4c54f2b89..86af9c658 100644 --- a/apu/apu.cpp +++ b/apu/apu.cpp @@ -122,7 +122,10 @@ bool8 S9xMixSamples(uint8 *dest, int sample_count) int S9xGetSampleCount(void) { - return spc::resampler->avail(); + int avail = spc::resampler->avail(); + if (Settings.MSU1) // return minimum available samples, otherwise we can run into the assert above due to partial sample generation in msu1 + avail = Resampler::min(avail, msu::resampler->avail()); + return avail; } void S9xLandSamples(void) diff --git a/cpuaddr.h b/cpuaddr.h index 47e1aa656..bef687e2b 100644 --- a/cpuaddr.h +++ b/cpuaddr.h @@ -513,7 +513,6 @@ static inline uint32 StackRelativeIndirectIndexed (AccessMode a) // (d,S),Y if (a & READ) OpenBus = (uint8) (addr >> 8); addr = (addr + Registers.Y.W + ICPU.ShiftedDB) & 0xffffff; - AddCycles(ONE_CYCLE); return (addr); } diff --git a/cpuops.cpp b/cpuops.cpp index b2342baeb..8d7279b5b 100644 --- a/cpuops.cpp +++ b/cpuops.cpp @@ -1406,11 +1406,13 @@ bOP(70Slow, RelativeSlow, CheckOverflow(), 0, CheckEmulation()) static void Op82 (void) { S9xSetPCBase(ICPU.ShiftedPB + RelativeLong(JUMP)); + AddCycles(ONE_CYCLE); } static void Op82Slow (void) { S9xSetPCBase(ICPU.ShiftedPB + RelativeLongSlow(JUMP)); + AddCycles(ONE_CYCLE); } /* Flag Instructions ******************************************************* */ @@ -2847,6 +2849,7 @@ static void Op22E1 (void) // Note: JSL is a new instruction, // and so doesn't respect the emu-mode stack bounds. uint32 addr = AbsoluteLong(JSR); + AddCycles(ONE_CYCLE); PushB(Registers.PB); PushW(Registers.PCw - 1); Registers.SH = 1; @@ -2856,6 +2859,7 @@ static void Op22E1 (void) static void Op22E0 (void) { uint32 addr = AbsoluteLong(JSR); + AddCycles(ONE_CYCLE); PushB(Registers.PB); PushW(Registers.PCw - 1); S9xSetPCBase(addr); @@ -2864,6 +2868,7 @@ static void Op22E0 (void) static void Op22Slow (void) { uint32 addr = AbsoluteLongSlow(JSR); + AddCycles(ONE_CYCLE); PushB(Registers.PB); PushW(Registers.PCw - 1); if (CheckEmulation()) diff --git a/gtk/src/gtk_file.cpp b/gtk/src/gtk_file.cpp index 6081c4fb9..da59b1f6b 100644 --- a/gtk/src/gtk_file.cpp +++ b/gtk/src/gtk_file.cpp @@ -121,8 +121,15 @@ const char *S9xGetFilename(const char *ex, enum s9x_getdirtype dirtype) static std::string filename; fs::path path(S9xGetDirectory(dirtype)); path /= fs::path(Memory.ROMFilename).filename(); - path.replace_extension(ex); - filename = path.string(); + //Fixes issue with MSU-1 on linux + if(ex[0] == '-') { + path.replace_extension(""); + filename = path.string(); + filename.append(ex); + } else { + path.replace_extension(ex); + filename = path.string(); + } return filename.c_str(); } diff --git a/macosx/Snes9x/AppDelegate.h b/macosx/Snes9x/AppDelegate.h index d8cc147ae..eceeb0ada 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 8e3de21ec..784cc1744 100644 --- a/macosx/Snes9x/AppDelegate.m +++ b/macosx/Snes9x/AppDelegate.m @@ -15,7 +15,7 @@ SNES9X for Mac OS (c) Copyright John Stiles (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 @@ SNES9X for Mac OS (c) Copyright John Stiles #import "S9xPreferencesConstants.h" NSWindowFrameAutosaveName const kMainWindowIdentifier = @"s9xMainWindow"; +NSWindowFrameAutosaveName const kCheatsWindowIdentifier = @"s9xCheatsWindow"; @implementation AppDelegate @@ -194,6 +195,9 @@ - (void)setupDefaults [self importKeySettings]; [self importGraphicsSettings]; [self applyEmulationSettings]; + + self.s9xEngine.cheatsEnabled = [defaults boolForKey:kEnableCheatsPref]; + [defaults synchronize]; } @@ -463,6 +467,11 @@ - (BOOL)openURL:(NSURL *)url [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 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem 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 @@ - (void)deviceSettingChanged:(S9xDeviceSetting)deviceSetting } +- (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 25ecce20f..707adc640 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 000000000..cd8c868f1 --- /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 000000000..650d10803 --- /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 000000000..4e82bde9b --- /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 000000000..beff0c718 --- /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 000000000..86e3fd00a --- /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 000000000..2ef097a68 --- /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 1ede6fb5f..c84e2200a 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 343c93feb..5985006e0 100644 --- a/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.m +++ b/macosx/Snes9x/S9xPreferences/S9xPreferencesConstants.m @@ -35,3 +35,5 @@ SNES9X for Mac OS (c) Copyright John Stiles 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 bef15b919..db1e0484d 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 f0baf09a3..7dc5be06f 100755 --- a/macosx/mac-cheat.mm +++ b/macosx/mac-cheat.mm @@ -15,7 +15,7 @@ SNES9X for Mac OS (c) Copyright John Stiles (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 @@ SNES9X for Mac OS (c) Copyright John Stiles extern SCheatData Cheat; -typedef struct -{ - uint32 id; - uint32 address; - uint8 value; - bool8 valid; - bool8 enabled; - char description[22]; -} CheatItem; - -static WindowRef wRef; -static HIViewRef dbRef; -static CheatItem citem[MAC_MAX_CHEATS]; -static uint32 numofcheats; - -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) +@implementation S9xCheatItem + +- (void)setAddress:(uint32)address value:(uint8)value cheatDescription:(const char *)cheatDescription { - for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++) + bool enabled = self.enabled.boolValue; + + char code[256]; + sprintf(code, "%x=%x", address, value); + S9xModifyCheatGroup(self.cheatID, cheatDescription, code); + + 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++) - { - 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'; - } + 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]; } -static void DetachCheatItems (void) +@dynamic enabled; +- (NSNumber *)enabled { - S9xDeleteCheats(); + return @(Cheat.g[self.cheatID].enabled); +} - for (unsigned int i = 0; i < MAC_MAX_CHEATS; i++) +- (void)setEnabled:(NSNumber *)enabled +{ + if (enabled.boolValue) { - 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); - } + S9xEnableCheatGroup(self.cheatID); + } + else + { + S9xDisableCheatGroup(self.cheatID); } } -void ConfigureCheat (void) +@dynamic cheatDescription; +- (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); -// } + return [NSString stringWithUTF8String:Cheat.g[self.cheatID].name]; } -static void AddCheatItem (void) +- (void)setCheatDescription:(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); -// } + [self setAddress:Cheat.g[self.cheatID].c[0].address value:Cheat.g[self.cheatID].c[0].byte cheatDescription:cheatDescription.UTF8String]; } -static void DeleteCheatItem (void) +- (void)delete { -// 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); -// } + S9xDeleteCheatGroup(self.cheatID); } -static void EnableAllCheatItems (void) +@end + +extern "C" +{ + +bool CheatValuesFromCode(NSString *code, uint32 *address, uint8 *value) { -// 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); + const char *text = code.UTF8String; + + if (!S9xGameGenieToRaw (text, *address, *value)) + { + 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; } -static OSStatus DBClientDataCallback (HIViewRef browser, DataBrowserItemID itemID, DataBrowserPropertyID property, DataBrowserItemDataRef itemData, Boolean changeValue) +void CreateCheatFromAddress(NSNumber *address, NSNumber *value, NSString *cheatDescription) { -// 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; + char code[256]; + sprintf(code, "%x=%x", address.unsignedIntValue, value.unsignedCharValue); + S9xAddCheatGroup(cheatDescription.UTF8String, code); + S9xEnableCheatGroup(Cheat.g.size() - 1); } +void CreateCheatFromCode(NSString *code, NSString *cheatDescription) +{ + S9xAddCheatGroup(cheatDescription.UTF8String, code.UTF8String); + S9xEnableCheatGroup(Cheat.g.size() - 1); +} -static OSStatus CheatEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) +NSArray *GetAllCheats(void) { -// 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; + NSMutableArray *cheats = [NSMutableArray new]; + uint32 cheatID = 0; + + for (auto it : Cheat.g) + { + S9xCheatItem *cheat = [S9xCheatItem new]; + cheat.cheatID = cheatID++; + + [cheats addObject:cheat]; + } + + return cheats; +} } diff --git a/macosx/mac-os.h b/macosx/mac-os.h index 18cd2c741..704ab6c70 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 25075627d..6f9dda9fa 100644 --- a/macosx/mac-os.mm +++ b/macosx/mac-os.mm @@ -3409,6 +3409,17 @@ - (void)setInputDelegate:(id)delegate return inputDelegate; } +@dynamic cheatsEnabled; +- (BOOL)cheatsEnabled +{ + return Cheat.enabled; +} + +- (void)setCheatsEnabled:(BOOL)cheatsEnabled +{ + Cheat.enabled = cheatsEnabled; +} + @end @implementation S9xJoypad diff --git a/macosx/mac-render.mm b/macosx/mac-render.mm index f43f73b14..a04f2a402 100644 --- a/macosx/mac-render.mm +++ b/macosx/mac-render.mm @@ -303,76 +303,80 @@ static void S9xPutImageMetal (int width, int height, uint16 *buffer16) CGSize layerSize = metalLayer.bounds.size; - MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new]; - textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; - textureDescriptor.width = width; - textureDescriptor.height = height; - - metalTexture = [metalDevice newTextureWithDescriptor:textureDescriptor]; - - [metalTexture replaceRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0 withBytes:buffer bytesPerRow:width * 4]; - - float vWidth = layerSize.width / 2.0; - float vHeight = layerSize.height / 2.0; - - const MetalVertex verticies[] = - { - // Pixel positions, Texture coordinates - { { vWidth, -vHeight }, { 1.f, 1.f } }, - { { -vWidth, -vHeight }, { 0.f, 1.f } }, - { { -vWidth, vHeight }, { 0.f, 0.f } }, - - { { vWidth, -vHeight }, { 1.f, 1.f } }, - { { -vWidth, vHeight }, { 0.f, 0.f } }, - { { vWidth, vHeight }, { 1.f, 0.f } }, - }; - - id vertexBuffer = [metalDevice newBufferWithBytes:verticies length:sizeof(verticies) options:MTLResourceStorageModeShared]; - id fragmentBuffer = [metalDevice newBufferWithBytes:&videoMode length:sizeof(videoMode) options:MTLResourceStorageModeShared]; - - id commandBuffer = [metalCommandQueue commandBuffer]; - commandBuffer.label = @"Snes9x command buffer"; - - id drawable = [metalLayer nextDrawable]; - - MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - - renderPassDescriptor.colorAttachments[0].texture = drawable.texture; - renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; - renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,1.0); - - if(renderPassDescriptor != nil) - { - id renderEncoder = - [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - renderEncoder.label = @"Snes9x render encoder"; - - vector_uint2 viewportSize = { static_cast(layerSize.width), static_cast(layerSize.height) }; + @autoreleasepool { + MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new]; + textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; + textureDescriptor.width = width; + textureDescriptor.height = height; - CGFloat scale = metalLayer.contentsScale; - [renderEncoder setViewport:(MTLViewport){0.0, 0.0, layerSize.width * scale, layerSize.height * scale, -1.0, 1.0 }]; - - [renderEncoder setRenderPipelineState:metalPipelineState]; - - [renderEncoder setVertexBuffer:vertexBuffer - offset:0 - atIndex:0]; - - [renderEncoder setVertexBytes:&viewportSize - length:sizeof(viewportSize) - atIndex:1]; - - [renderEncoder setFragmentTexture:metalTexture atIndex:0]; - [renderEncoder setFragmentBuffer:fragmentBuffer offset:0 atIndex:1]; - - [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; - - [renderEncoder endEncoding]; + metalTexture = [metalDevice newTextureWithDescriptor:textureDescriptor]; - [commandBuffer presentDrawable:drawable]; - } - - [commandBuffer commit]; + [metalTexture replaceRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0 withBytes:buffer bytesPerRow:width * 4]; + + float vWidth = layerSize.width / 2.0; + float vHeight = layerSize.height / 2.0; + + const MetalVertex verticies[] = + { + // Pixel positions, Texture coordinates + { { vWidth, -vHeight }, { 1.f, 1.f } }, + { { -vWidth, -vHeight }, { 0.f, 1.f } }, + { { -vWidth, vHeight }, { 0.f, 0.f } }, + + { { vWidth, -vHeight }, { 1.f, 1.f } }, + { { -vWidth, vHeight }, { 0.f, 0.f } }, + { { vWidth, vHeight }, { 1.f, 0.f } }, + }; + + id vertexBuffer = [metalDevice newBufferWithBytes:verticies length:sizeof(verticies) options:MTLResourceStorageModeShared]; + id fragmentBuffer = [metalDevice newBufferWithBytes:&videoMode length:sizeof(videoMode) options:MTLResourceStorageModeShared]; + + id commandBuffer = [metalCommandQueue commandBuffer]; + commandBuffer.label = @"Snes9x command buffer"; + + id drawable = [metalLayer nextDrawable]; + + MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0,0.0,0.0,1.0); + + if(renderPassDescriptor != nil) + { + id renderEncoder = + [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + renderEncoder.label = @"Snes9x render encoder"; + + vector_uint2 viewportSize = { static_cast(layerSize.width), static_cast(layerSize.height) }; + + CGFloat scale = metalLayer.contentsScale; + [renderEncoder setViewport:(MTLViewport){0.0, 0.0, layerSize.width * scale, layerSize.height * scale, -1.0, 1.0 }]; + + [renderEncoder setRenderPipelineState:metalPipelineState]; + + [renderEncoder setVertexBuffer:vertexBuffer + offset:0 + atIndex:0]; + + [renderEncoder setVertexBytes:&viewportSize + length:sizeof(viewportSize) + atIndex:1]; + + [renderEncoder setFragmentTexture:metalTexture atIndex:0]; + [renderEncoder setFragmentBuffer:fragmentBuffer offset:0 atIndex:1]; + + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; + + [renderEncoder endEncoding]; + + [commandBuffer commit]; + + [commandBuffer waitUntilCompleted]; + + [drawable present]; + } + } } void S9xTextMode (void) diff --git a/macosx/snes9x framework/snes9x_framework.h b/macosx/snes9x framework/snes9x_framework.h index b4c397fd2..45db942d1 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 0e39acb45..d04513947 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 */; }; @@ -100,7 +105,6 @@ 30D15D4122CE6B74005BC352 /* gfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA31FE2D05F7743E00E13748 /* gfx.cpp */; }; 30D15D4222CE6B74005BC352 /* globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061830526CCB900A80003 /* globals.cpp */; }; 30D15D4322CE6B74005BC352 /* loadzip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061A90526CCB900A80003 /* loadzip.cpp */; }; - 30D15D4422CE6B74005BC352 /* logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA00D01D0A5A9956000C58E0 /* logger.cpp */; }; 30D15D4522CE6B74005BC352 /* memmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAB7319C0527033000A80003 /* memmap.cpp */; }; 30D15D4622CE6B74005BC352 /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA813E9A066F50A5004F99B5 /* movie.cpp */; }; 30D15D4722CE6B74005BC352 /* msu1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BF0B39DE1FA580F9002B04D3 /* msu1.cpp */; }; @@ -161,7 +165,6 @@ 30D15DA822CE6BC9005BC352 /* getset.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061800526CCB900A80003 /* getset.h */; }; 30D15DA922CE6BC9005BC352 /* gfx.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061820526CCB900A80003 /* gfx.h */; }; 30D15DAA22CE6BC9005BC352 /* language.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9508F8D6E00072CDFB /* language.h */; }; - 30D15DAB22CE6BC9005BC352 /* logger.h in Headers */ = {isa = PBXBuildFile; fileRef = EA00D01F0A5A998F000C58E0 /* logger.h */; }; 30D15DAC22CE6BC9005BC352 /* memmap.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061B10526CCB900A80003 /* memmap.h */; }; 30D15DAD22CE6BC9005BC352 /* messages.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061B20526CCB900A80003 /* messages.h */; }; 30D15DAE22CE6BC9005BC352 /* missing.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061B30526CCB900A80003 /* missing.h */; }; @@ -328,6 +331,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 = ""; }; @@ -393,8 +402,6 @@ CFEFAE8E10EAC92B00FB081A /* snes_ntsc_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = snes_ntsc_config.h; sourceTree = ""; }; CFEFAE8F10EAC92B00FB081A /* snes_ntsc_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = snes_ntsc_impl.h; sourceTree = ""; }; CFEFAE9010EAC92B00FB081A /* snes_ntsc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = snes_ntsc.h; sourceTree = ""; }; - EA00D01D0A5A9956000C58E0 /* logger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logger.cpp; sourceTree = ""; usesTabs = 1; }; - EA00D01F0A5A998F000C58E0 /* logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logger.h; sourceTree = ""; usesTabs = 1; }; EA0C95D808364AAE009307B4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; EA16053E0639E655004412AB /* mac-netplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "mac-netplay.h"; sourceTree = ""; }; EA1605500639E735004412AB /* mac-netplay.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = "mac-netplay.mm"; sourceTree = ""; }; @@ -669,6 +676,7 @@ 30714716230E379500917F82 /* Snes9x */ = { isa = PBXGroup; children = ( + 30CF849827AF0C3E002B37A9 /* Cheats */, 600546BE250F8F1700EC0977 /* S9xPreferences */, 30C49EF6250C0F2B007D04F8 /* Snes9xDebug.entitlements */, 30714717230E379500917F82 /* AppDelegate.h */, @@ -696,6 +704,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 = ( @@ -898,8 +919,6 @@ EAA7B5D807609F76001BAB8B /* jma */, EA809E9508F8D6E00072CDFB /* language.h */, EAE061A90526CCB900A80003 /* loadzip.cpp */, - EA00D01D0A5A9956000C58E0 /* logger.cpp */, - EA00D01F0A5A998F000C58E0 /* logger.h */, EAECB65C04AC7FCD00A80003 /* macosx */, EAB7319C0527033000A80003 /* memmap.cpp */, EAE061B10526CCB900A80003 /* memmap.h */, @@ -1075,7 +1094,6 @@ 30D15DA822CE6BC9005BC352 /* getset.h in Headers */, 30D15DA922CE6BC9005BC352 /* gfx.h in Headers */, 30D15DAA22CE6BC9005BC352 /* language.h in Headers */, - 30D15DAB22CE6BC9005BC352 /* logger.h in Headers */, 30D15DAC22CE6BC9005BC352 /* memmap.h in Headers */, 30D15DAD22CE6BC9005BC352 /* messages.h in Headers */, 30D15DAE22CE6BC9005BC352 /* missing.h in Headers */, @@ -1118,6 +1136,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 +1258,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 +1291,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 */, ); @@ -1333,7 +1356,6 @@ 30D15D4122CE6B74005BC352 /* gfx.cpp in Sources */, 30D15D4222CE6B74005BC352 /* globals.cpp in Sources */, 30D15D4322CE6B74005BC352 /* loadzip.cpp in Sources */, - 30D15D4422CE6B74005BC352 /* logger.cpp in Sources */, 30D15D4522CE6B74005BC352 /* memmap.cpp in Sources */, 30D15D4622CE6B74005BC352 /* movie.cpp in Sources */, 30D15D4722CE6B74005BC352 /* msu1.cpp in Sources */, diff --git a/memmap.cpp b/memmap.cpp index a04bcf0d8..4f0421c97 100644 --- a/memmap.cpp +++ b/memmap.cpp @@ -34,6 +34,7 @@ #include "movie.h" #include "display.h" #include "sha256.h" +#include "snapshot.h" #ifndef SET_UI_COLOR #define SET_UI_COLOR(r, g, b) ; @@ -1397,6 +1398,8 @@ bool8 CMemory::LoadROM (const char *filename) if(!filename || !*filename) return FALSE; + S9xResetSaveTimer(FALSE); // reset oops timer here so that .oops file has rom name of previous rom + int32 totalFileSize; do @@ -1672,6 +1675,8 @@ bool8 CMemory::LoadMultiCartMem (const uint8 *sourceA, uint32 sourceASize, bool8 CMemory::LoadMultiCart (const char *cartA, const char *cartB) { + S9xResetSaveTimer(FALSE); // reset oops timer here so that .oops file has rom name of previous rom + memset(ROM, 0, MAX_ROM_SIZE); memset(&Multi, 0, sizeof(Multi)); diff --git a/msu1.cpp b/msu1.cpp index f6e545ea7..71cb0878f 100644 --- a/msu1.cpp +++ b/msu1.cpp @@ -253,7 +253,14 @@ void S9xMSU1Generate(size_t sample_count) { if (MSU1.MSU1_STATUS & AudioRepeating) { - MSU1.MSU1_AUDIO_POS = audioLoopPos; + if (audioLoopPos < MSU1.MSU1_AUDIO_POS) + { + MSU1.MSU1_AUDIO_POS = audioLoopPos; + } + else // if the loop point is invalid, revert to start + { + MSU1.MSU1_AUDIO_POS = 8; + } REVERT_STREAM(audioStream, MSU1.MSU1_AUDIO_POS, 0); } else diff --git a/win32/wsnes9x.cpp b/win32/wsnes9x.cpp index d0d95701f..658af29fc 100644 --- a/win32/wsnes9x.cpp +++ b/win32/wsnes9x.cpp @@ -7341,6 +7341,7 @@ void MakeExtFile(void) out<<"sfcN"<