SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit 1931c2830fc46cb648dde4fb0bed4f0345b67b2d parent 8cb94e7a8bce8f012b7e26eca664b725618ad3a6 Author: Lior Halphon <LIJI32@gmail.com> Date: Sat, 14 Sep 2024 23:58:58 +0300 Syncing with the App Store branch (iCloud support) Diffstat:
| M | iOS/GBCheatsController.m | 1 | + |
| M | iOS/GBHubGameViewController.m | 7 | +++---- |
| M | iOS/GBLibraryViewController.h | 2 | +- |
| M | iOS/GBLibraryViewController.m | 10 | +++++++--- |
| D | iOS/GBLoadROMTableViewController.h | 5 | ----- |
| D | iOS/GBLoadROMTableViewController.m | 289 | ------------------------------------------------------------------------------ |
| M | iOS/GBROMManager.h | 3 | +++ |
| M | iOS/GBROMManager.m | 58 | +++++++++++++++++++++++++++++++--------------------------- |
| A | iOS/GBROMViewController.h | 14 | ++++++++++++++ |
| A | iOS/GBROMViewController.m | 322 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | iOS/GBSettingsViewController.m | 6 | +++--- |
| M | iOS/GBStatesViewController.m | 1 | + |
| M | iOS/GBThemePreviewController.m | 12 | ++++++------ |
| M | iOS/GBViewController.h | 3 | ++- |
| M | iOS/GBViewController.m | 119 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
15 files changed, 459 insertions(+), 393 deletions(-)
diff --git a/iOS/GBCheatsController.m b/iOS/GBCheatsController.m @@ -231,6 +231,7 @@ self.navigationController.toolbar.disableCompactLayout = true; } + - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { [url startAccessingSecurityScopedResource]; diff --git a/iOS/GBHubGameViewController.m b/iOS/GBHubGameViewController.m @@ -218,7 +218,6 @@ if ([GBROMManager.sharedManager romFileForROM:_game.title]) { [GBROMManager sharedManager].currentROM = _game.title; [self.navigationController dismissViewControllerAnimated:true completion:nil]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil]; } else { UIActivityIndicatorViewStyle style = UIActivityIndicatorViewStyleWhite; @@ -237,9 +236,9 @@ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Could not download ROM" message:@"Could not download this ROM from Homebrew Hub. Please try again later." preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:nil]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:nil]]; [self presentViewController:alert animated:true completion:nil]; self.navigationItem.rightBarButtonItem.customView = nil; }); diff --git a/iOS/GBLibraryViewController.h b/iOS/GBLibraryViewController.h @@ -1,6 +1,6 @@ #import <UIKit/UIKit.h> @interface GBLibraryViewController : UITabBarController - @end + diff --git a/iOS/GBLibraryViewController.m b/iOS/GBLibraryViewController.m @@ -1,7 +1,9 @@ #import "GBLibraryViewController.h" -#import "GBLoadROMTableViewController.h" +#import "GBROMViewController.h" #import "GBHubViewController.h" #import "GBViewController.h" +#import "GBROMManager.h" + @implementation GBLibraryViewController @@ -19,8 +21,9 @@ - (void)viewDidLoad { [super viewDidLoad]; + self.viewControllers = @[ - [self.class wrapViewController:[[GBLoadROMTableViewController alloc] init]], + [self.class wrapViewController:[[GBROMViewController alloc] init]], [self.class wrapViewController:[[GBHubViewController alloc] init]], ]; if (@available(iOS 13.0, *)) { @@ -42,7 +45,7 @@ } } self.viewControllers[0].tabBarItem.image = [UIImage systemImageNamed:symbol] ?: [UIImage systemImageNamed:@"folder.fill"]; - self.viewControllers[1].tabBarItem.image = [UIImage systemImageNamed:@"globe"]; + self.viewControllers.lastObject.tabBarItem.image = [UIImage systemImageNamed:@"globe"]; } else { self.viewControllers[0].tabBarItem.image = [UIImage imageNamed:@"FolderTemplate"]; @@ -50,4 +53,5 @@ } } + @end diff --git a/iOS/GBLoadROMTableViewController.h b/iOS/GBLoadROMTableViewController.h @@ -1,5 +0,0 @@ -#import <UIKit/UIKit.h> - -@interface GBLoadROMTableViewController : UITableViewController - -@end diff --git a/iOS/GBLoadROMTableViewController.m b/iOS/GBLoadROMTableViewController.m @@ -1,289 +0,0 @@ -#import "GBLoadROMTableViewController.h" -#import "GBROMManager.h" -#import "GBViewController.h" -#import <CoreServices/CoreServices.h> -#import <objc/runtime.h> - -@interface GBLoadROMTableViewController() <UIDocumentPickerDelegate> -@end - -@implementation GBLoadROMTableViewController -{ - NSIndexPath *_renamingPath; -} - -- (instancetype)init -{ - self = [super initWithStyle:UITableViewStyleGrouped]; - self.navigationItem.rightBarButtonItem = self.editButtonItem; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(deselectRow) - name:UIApplicationDidBecomeActiveNotification - object:nil]; - - return self; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 2; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - if (section == 1) return 2; - return [GBROMManager sharedManager].allROMs.count; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section == 1) { - UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; - switch (indexPath.item) { - case 0: cell.textLabel.text = @"Import ROM files"; break; - case 1: cell.textLabel.text = @"Show Library in Files"; break; - } - return cell; - } - UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; - NSString *rom = [GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]; - cell.textLabel.text = rom; - cell.accessoryType = [rom isEqualToString:[GBROMManager sharedManager].currentROM]? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; - - NSString *pngPath = [[[GBROMManager sharedManager] autosaveStateFileForROM:rom] stringByAppendingPathExtension:@"png"]; - UIGraphicsBeginImageContextWithOptions((CGSize){60, 60}, false, self.view.window.screen.scale); - UIBezierPath *mask = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 3, 60, 54) cornerRadius:4]; - [mask addClip]; - UIImage *image = [UIImage imageWithContentsOfFile:pngPath]; - [image drawInRect:mask.bounds]; - if (@available(iOS 13.0, *)) { - [[UIColor tertiaryLabelColor] set]; - } - else { - [[UIColor colorWithWhite:0 alpha:0.5] set]; - } - [mask stroke]; - cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return cell; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section == 1) return [super tableView:tableView heightForRowAtIndexPath:indexPath]; - - return 60; -} - -- (NSString *)title -{ - return @"Local Library"; -} - -- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section -{ - if (section == 0) return nil; - - return @"You can also import ROM files by opening them in SameBoy using the Files app or a web browser, or by sending them over with AirDrop."; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section == 1) { - switch (indexPath.item) { - case 0: { - UIViewController *parent = self.presentingViewController; - NSString *gbUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"gb", NULL); - NSString *gbcUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"gbc", NULL); - NSString *isxUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"isx", NULL); - NSString *zipUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"zip", NULL); - - NSMutableSet *extensions = [NSMutableSet set]; - [extensions addObjectsFromArray:(__bridge_transfer NSArray *)UTTypeCopyAllTagsWithClass((__bridge CFStringRef)gbUTI, kUTTagClassFilenameExtension)]; - [extensions addObjectsFromArray:(__bridge_transfer NSArray *)UTTypeCopyAllTagsWithClass((__bridge CFStringRef)gbcUTI, kUTTagClassFilenameExtension)]; - [extensions addObjectsFromArray:(__bridge_transfer NSArray *)UTTypeCopyAllTagsWithClass((__bridge CFStringRef)isxUTI, kUTTagClassFilenameExtension)]; - - if (extensions.count != 3) { - if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBShownUTIWarning"]) { - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"File Association Conflict" - message:@"Due to a limitation in iOS, the file picker will allow you to select files not supported by SameBoy. SameBoy will only import GB, GBC and ISX files.\n\nIf you have a multi-system emulator installed, updating it could fix this problem." - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *action) { - [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBShownUTIWarning"]; - [self tableView:tableView didSelectRowAtIndexPath:indexPath]; - }]]; - [self presentViewController:alert animated:true completion:nil]; - return; - } - } - - [self.presentingViewController dismissViewControllerAnimated:true completion:^{ - UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.github.liji32.sameboy.gb", - @"com.github.liji32.sameboy.gbc", - @"com.github.liji32.sameboy.isx", - gbUTI ?: @"", - gbcUTI ?: @"", - isxUTI ?: @"", - zipUTI ?: @""] - inMode:UIDocumentPickerModeImport]; - picker.allowsMultipleSelection = true; - if (@available(iOS 13.0, *)) { - picker.shouldShowFileExtensions = true; - } - picker.delegate = self; - objc_setAssociatedObject(picker, @selector(delegate), self, OBJC_ASSOCIATION_RETAIN); - [parent presentViewController:picker animated:true completion:nil]; - }]; - return; - } - case 1: { - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"shareddocuments://%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject]] - options:nil - completionHandler:nil]; - return; - } - } - } - [GBROMManager sharedManager].currentROM = [GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]; - [self.presentingViewController dismissViewControllerAnimated:true completion:^{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil]; - }]; -} - -- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls -{ - [(GBViewController *)[UIApplication sharedApplication] handleOpenURLs:urls openInPlace:false]; -} - -- (UIModalPresentationStyle)modalPresentationStyle -{ - return UIModalPresentationOverFullScreen; -} - -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section == 1) return; - - if (editingStyle != UITableViewCellEditingStyleDelete) return; - NSString *rom = [GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]; - UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Delete ROM “%@”?", rom] - message: @"Save data for this ROM will also be deleted." - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Delete" - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *action) { - [[GBROMManager sharedManager] deleteROM:rom]; - [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; - if ([[GBROMManager sharedManager].currentROM isEqualToString:rom]) { - [GBROMManager sharedManager].currentROM = nil; - [[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil]; - } - }]]; - [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" - style:UIAlertActionStyleCancel - handler:nil]]; - [self presentViewController:alert animated:true completion:nil]; -} - -- (void)renameRow:(NSIndexPath *)indexPath -{ - if (indexPath.section == 1) return; - - UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; - UITextField *field = [[UITextField alloc] initWithFrame:cell.textLabel.frame]; - field.font = cell.textLabel.font; - field.text = cell.textLabel.text; - cell.textLabel.text = @""; - [[cell.textLabel superview] addSubview:field]; - [field becomeFirstResponder]; - [field selectAll:nil]; - _renamingPath = indexPath; - [field addTarget:self action:@selector(doneRename:) forControlEvents:UIControlEventEditingDidEnd | UIControlEventEditingDidEndOnExit]; -} - -- (void)doneRename:(UITextField *)sender -{ - if (!_renamingPath) return; - NSString *newName = sender.text; - NSString *oldName = [GBROMManager sharedManager].allROMs[[_renamingPath indexAtPosition:1]]; - _renamingPath = nil; - if ([newName isEqualToString:oldName]) { - [self.tableView reloadData]; - return; - } - if ([newName containsString:@"/"]) { - [self.tableView reloadData]; - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"You can't use a name that contains “/”. Please choose another name." - message:nil - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleCancel - handler:nil]]; - [self presentViewController:alert animated:true completion:nil]; - return; - } - [[GBROMManager sharedManager] renameROM:oldName toName:newName]; - [self.tableView reloadData]; - _renamingPath = nil; -} - -- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath -{ - return indexPath.section == 0; -} - -// Leave these ROM management to iOS 13.0 and up for now -- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView -contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath - point:(CGPoint)point API_AVAILABLE(ios(13.0)) -{ - if (indexPath.section == 1) return nil; - - return [UIContextMenuConfiguration configurationWithIdentifier:nil - previewProvider:nil - actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) { - UIAction *deleteAction = [UIAction actionWithTitle:@"Delete" - image:[UIImage systemImageNamed:@"trash"] - identifier:nil - handler:^(UIAction *action) { - [self tableView:tableView - commitEditingStyle:UITableViewCellEditingStyleDelete - forRowAtIndexPath:indexPath]; - }]; - deleteAction.attributes = UIMenuElementAttributesDestructive; - return [UIMenu menuWithTitle:nil children:@[ - [UIAction actionWithTitle:@"Rename" - image:[UIImage systemImageNamed:@"pencil"] - identifier:nil - handler:^(__kindof UIAction *action) { - [self renameRow:indexPath]; - }], - [UIAction actionWithTitle:@"Duplicate" - image:[UIImage systemImageNamed:@"plus.square.on.square"] - identifier:nil - handler:^(__kindof UIAction *action) { - [[GBROMManager sharedManager] duplicateROM:[GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]]; - [self.tableView reloadData]; - }], - deleteAction, - ]]; - }]; -} - -- (void)deselectRow -{ - if (self.tableView.indexPathForSelectedRow) { - [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:true]; - } -} - -- (void)viewWillAppear:(BOOL)animated -{ - [self.tableView reloadData]; -} - -@end diff --git a/iOS/GBROMManager.h b/iOS/GBROMManager.h @@ -10,6 +10,8 @@ @property (readonly) NSString *batterySaveFile; @property (readonly) NSString *autosaveStateFile; @property (readonly) NSString *cheatsFile; + +@property (readonly) NSString *localRoot; - (NSString *)stateFile:(unsigned)index; - (NSString *)romFileForROM:(NSString *)rom; @@ -21,4 +23,5 @@ - (NSString *)renameROM:(NSString *)rom toName:(NSString *)newName; - (NSString *)duplicateROM:(NSString *)rom; - (void)deleteROM:(NSString *)rom; + @end diff --git a/iOS/GBROMManager.m b/iOS/GBROMManager.m @@ -4,6 +4,8 @@ @implementation GBROMManager { NSString *_romFile; + NSMutableDictionary<NSString *,NSString *> *_cloudNameToFile; + bool _doneInitializing; } + (instancetype)sharedManager @@ -21,6 +23,7 @@ self = [super init]; if (!self) return nil; self.currentROM = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBLastROM"]; + _doneInitializing = true; return self; } @@ -28,11 +31,16 @@ { _romFile = nil; _currentROM = currentROM; - if (currentROM && !self.romFile) { + bool foundROM = self.romFile; + + if (currentROM && !foundROM) { _currentROM = nil; } [[NSUserDefaults standardUserDefaults] setObject:_currentROM forKey:@"GBLastROM"]; + if (_doneInitializing) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil]; + } } - (NSString *)romFileForDirectory:(NSString *)romDirectory @@ -47,13 +55,17 @@ return nil; } +- (NSString *)romDirectoryForROM:(NSString *)romFile +{ + + return [self.localRoot stringByAppendingPathComponent:romFile]; +} + - (NSString *)romFile { if (_romFile) return _romFile; if (!_currentROM) return nil; - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; - NSString *romDirectory = [root stringByAppendingPathComponent:_currentROM]; - return _romFile = [self romFileForDirectory:romDirectory]; + return _romFile = [self romFileForDirectory:[self romDirectoryForROM:_currentROM]]; } - (NSString *)romFileForROM:(NSString *)rom @@ -63,9 +75,9 @@ if (rom == _currentROM) { return self.romFile; } - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; - NSString *romDirectory = [root stringByAppendingPathComponent:rom]; - return [self romFileForDirectory:romDirectory]; + + + return [self romFileForDirectory:[self romDirectoryForROM:rom]]; } - (NSString *)auxilaryFileForROM:(NSString *)rom withExtension:(NSString *)extension @@ -117,7 +129,7 @@ - (NSArray<NSString *> *)allROMs { NSMutableArray<NSString *> *ret = [NSMutableArray array]; - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; + NSString *root = self.localRoot; for (NSString *romDirectory in [NSFileManager.defaultManager contentsOfDirectoryAtPath:root error:nil]) { if ([romDirectory hasPrefix:@"."] || [romDirectory isEqualToString:@"Inbox"]) continue; @@ -130,7 +142,7 @@ - (NSString *)makeNameUnique:(NSString *)name { - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; + NSString *root = self.localRoot; if (![[NSFileManager defaultManager] fileExistsAtPath:[root stringByAppendingPathComponent:name]]) return name; unsigned i = 2; @@ -150,27 +162,14 @@ NSString *friendlyName = [[romFile lastPathComponent] stringByDeletingPathExtension]; friendlyName = [regex stringByReplacingMatchesInString:friendlyName options:0 range:NSMakeRange(0, [friendlyName length]) withTemplate:@""]; friendlyName = [friendlyName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; - if ([[NSFileManager defaultManager] fileExistsAtPath:[root stringByAppendingPathComponent:friendlyName]]) { - unsigned i = 2; - while (true) { - NSString *attempt = [friendlyName stringByAppendingFormat:@" %u", i]; - if ([[NSFileManager defaultManager] fileExistsAtPath:[root stringByAppendingPathComponent:attempt]]) { - i++; - continue; - } - friendlyName = attempt; - break; - } - } + friendlyName = [self makeNameUnique:friendlyName]; return [self importROM:romFile withName:friendlyName keepOriginal:keep]; } - (NSString *)importROM:(NSString *)romFile withName:(NSString *)friendlyName keepOriginal:(bool)keep { - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; + NSString *root = self.localRoot; NSString *romFolder = [root stringByAppendingPathComponent:friendlyName]; [[NSFileManager defaultManager] createDirectoryAtPath:romFolder withIntermediateDirectories:false @@ -205,9 +204,9 @@ { newName = [self makeNameUnique:newName]; if ([rom isEqualToString:_currentROM]) { - _currentROM = newName; + self.currentROM = newName; } - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; + NSString *root = self.localRoot; [[NSFileManager defaultManager] moveItemAtPath:[root stringByAppendingPathComponent:rom] toPath:[root stringByAppendingPathComponent:newName] error:nil]; @@ -224,9 +223,14 @@ - (void)deleteROM:(NSString *)rom { - NSString *root = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; + NSString *root = self.localRoot; NSString *romDirectory = [root stringByAppendingPathComponent:rom]; [[NSFileManager defaultManager] removeItemAtPath:romDirectory error:nil]; } + +- (NSString *)localRoot +{ + return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject; +} @end diff --git a/iOS/GBROMViewController.h b/iOS/GBROMViewController.h @@ -0,0 +1,14 @@ +#import <UIKit/UIKit.h> + +@interface GBROMViewController : UITableViewController<UIDocumentPickerDelegate> + +/* For inheritance */ +- (void)romSelectedAtIndex:(unsigned)index; +- (void)deleteROMAtIndex:(unsigned)index; +- (void)renameROM:(NSString *)oldName toName:(NSString *)newName; +- (void)duplicateROMAtIndex:(unsigned)index; +- (NSString *)rootPath; + +/* To be used by subclasses */ +- (UITableViewCell *)cellForROM:(NSString *)rom; +@end diff --git a/iOS/GBROMViewController.m b/iOS/GBROMViewController.m @@ -0,0 +1,322 @@ +#import "GBROMViewController.h" +#import "GBROMManager.h" +#import "GBViewController.h" +#import "GBLibraryViewController.h" +#import <CoreServices/CoreServices.h> +#import <objc/runtime.h> + +@implementation GBROMViewController +{ + NSIndexPath *_renamingPath; +} + +- (instancetype)init +{ + self = [super initWithStyle:UITableViewStyleGrouped]; + self.navigationItem.rightBarButtonItem = self.editButtonItem; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deselectRow) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + return self; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return 2; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + if (section == 1) return 2; + return [GBROMManager sharedManager].allROMs.count; +} + +- (UITableViewCell *)cellForROM:(NSString *)rom +{ + UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; + cell.textLabel.text = rom.lastPathComponent; + cell.accessoryType = [rom isEqualToString:[GBROMManager sharedManager].currentROM]? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; + + NSString *pngPath = [[[GBROMManager sharedManager] autosaveStateFileForROM:rom] stringByAppendingPathExtension:@"png"]; + UIGraphicsBeginImageContextWithOptions((CGSize){60, 60}, false, self.view.window.screen.scale); + UIBezierPath *mask = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 3, 60, 54) cornerRadius:4]; + [mask addClip]; + UIImage *image = [UIImage imageWithContentsOfFile:pngPath]; + [image drawInRect:mask.bounds]; + if (@available(iOS 13.0, *)) { + [[UIColor tertiaryLabelColor] set]; + } + else { + [[UIColor colorWithWhite:0 alpha:0.5] set]; + } + [mask stroke]; + cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return cell; + +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.section == 1) { + UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; + switch (indexPath.item) { + case 0: cell.textLabel.text = @"Import ROM files"; break; + case 1: cell.textLabel.text = @"Show Library in Files"; break; + } + return cell; + } + return [self cellForROM:[GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]]; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.section == 1) return [super tableView:tableView heightForRowAtIndexPath:indexPath]; + + return 60; +} + +- (NSString *)title +{ + return @"Local Library"; +} + +- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section +{ + if (section == 0) return nil; + + return @"You can also import ROM files by opening them in SameBoy using the Files app or a web browser, or by sending them over with AirDrop."; +} + +- (void)romSelectedAtIndex:(unsigned)index +{ + [GBROMManager sharedManager].currentROM = [GBROMManager sharedManager].allROMs[index]; + [self.presentingViewController dismissViewControllerAnimated:true completion:nil]; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.section == 1) { + switch (indexPath.item) { + case 0: { + UIViewController *parent = self.presentingViewController; + NSString *gbUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"gb", NULL); + NSString *gbcUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"gbc", NULL); + NSString *isxUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"isx", NULL); + NSString *zipUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"zip", NULL); + + NSMutableSet *extensions = [NSMutableSet set]; + [extensions addObjectsFromArray:(__bridge_transfer NSArray *)UTTypeCopyAllTagsWithClass((__bridge CFStringRef)gbUTI, kUTTagClassFilenameExtension)]; + [extensions addObjectsFromArray:(__bridge_transfer NSArray *)UTTypeCopyAllTagsWithClass((__bridge CFStringRef)gbcUTI, kUTTagClassFilenameExtension)]; + [extensions addObjectsFromArray:(__bridge_transfer NSArray *)UTTypeCopyAllTagsWithClass((__bridge CFStringRef)isxUTI, kUTTagClassFilenameExtension)]; + + if (extensions.count != 3) { + if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBShownUTIWarning"]) { + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"File Association Conflict" + message:@"Due to a limitation in iOS, the file picker will allow you to select files not supported by SameBoy. SameBoy will only import GB, GBC and ISX files.\n\nIf you have a multi-system emulator installed, updating it could fix this problem." + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { + [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBShownUTIWarning"]; + [self tableView:tableView didSelectRowAtIndexPath:indexPath]; + }]]; + [self presentViewController:alert animated:true completion:nil]; + return; + } + } + + [self.presentingViewController dismissViewControllerAnimated:true completion:^{ + UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.github.liji32.sameboy.gb", + @"com.github.liji32.sameboy.gbc", + @"com.github.liji32.sameboy.isx", + gbUTI ?: @"", + gbcUTI ?: @"", + isxUTI ?: @"", + zipUTI ?: @""] + inMode:UIDocumentPickerModeImport]; + picker.allowsMultipleSelection = true; + if (@available(iOS 13.0, *)) { + picker.shouldShowFileExtensions = true; + } + picker.delegate = self; + objc_setAssociatedObject(picker, @selector(delegate), self, OBJC_ASSOCIATION_RETAIN); + [parent presentViewController:picker animated:true completion:nil]; + }]; + return; + } + case 1: { + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"shareddocuments://%@", + [self.rootPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]]; + [[UIApplication sharedApplication] openURL:url + options:nil + completionHandler:nil]; + return; + } + } + } + [self romSelectedAtIndex:indexPath.row]; +} + +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls +{ + [(GBViewController *)[UIApplication sharedApplication].delegate handleOpenURLs:urls + openInPlace:false]; +} + +- (UIModalPresentationStyle)modalPresentationStyle +{ + return UIModalPresentationOverFullScreen; +} + +- (void)deleteROMAtIndex:(unsigned)index +{ + NSString *rom = [GBROMManager sharedManager].allROMs[index]; + + [[GBROMManager sharedManager] deleteROM:rom]; + [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; + if ([[GBROMManager sharedManager].currentROM isEqualToString:rom]) { + [GBROMManager sharedManager].currentROM = nil; + } +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.section == 1) return; + + if (editingStyle != UITableViewCellEditingStyleDelete) return; + NSString *rom = [self.tableView cellForRowAtIndexPath:indexPath].textLabel.text; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Delete “%@”?", rom] + message: @"Save data for this ROM will also be deleted." + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Delete" + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction *action) { + [self deleteROMAtIndex:indexPath.row]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:nil]]; + [self presentViewController:alert animated:true completion:nil]; +} + +- (void)renameRow:(NSIndexPath *)indexPath +{ + if (indexPath.section == 1) return; + + UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; + UITextField *field = [[UITextField alloc] initWithFrame:cell.textLabel.frame]; + field.font = cell.textLabel.font; + field.text = cell.textLabel.text; + cell.textLabel.textColor = [UIColor clearColor]; + [[cell.textLabel superview] addSubview:field]; + [field becomeFirstResponder]; + [field selectAll:nil]; + _renamingPath = indexPath; + [field addTarget:self action:@selector(doneRename:) forControlEvents:UIControlEventEditingDidEnd | UIControlEventEditingDidEndOnExit]; +} + +- (void)renameROM:(NSString *)oldName toName:(NSString *)newName +{ + [[GBROMManager sharedManager] renameROM:oldName toName:newName]; + [self.tableView reloadData]; +} + +- (void)doneRename:(UITextField *)sender +{ + if (!_renamingPath) return; + NSString *newName = sender.text; + NSString *oldName = [self.tableView cellForRowAtIndexPath:_renamingPath].textLabel.text; + + _renamingPath = nil; + if ([newName isEqualToString:oldName]) { + [self.tableView reloadData]; + return; + } + + if ([newName containsString:@"/"]) { + [self.tableView reloadData]; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"You can't use a name that contains “/”. Please choose another name." + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleCancel + handler:nil]]; + [self presentViewController:alert animated:true completion:nil]; + return; + } + [self renameROM:oldName toName:newName]; + _renamingPath = nil; +} + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath +{ + return indexPath.section == 0; +} + +- (void)duplicateROMAtIndex:(unsigned)index +{ + [[GBROMManager sharedManager] duplicateROM:[GBROMManager sharedManager].allROMs[index]]; + [self.tableView reloadData]; +} + + +// Leave these ROM management to iOS 13.0 and up for now +- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView +contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath + point:(CGPoint)point API_AVAILABLE(ios(13.0)) +{ + if (indexPath.section == 1) return nil; + + return [UIContextMenuConfiguration configurationWithIdentifier:nil + previewProvider:nil + actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) { + UIAction *deleteAction = [UIAction actionWithTitle:@"Delete" + image:[UIImage systemImageNamed:@"trash"] + identifier:nil + handler:^(UIAction *action) { + [self tableView:tableView + commitEditingStyle:UITableViewCellEditingStyleDelete + forRowAtIndexPath:indexPath]; + }]; + deleteAction.attributes = UIMenuElementAttributesDestructive; + return [UIMenu menuWithTitle:nil children:@[ + [UIAction actionWithTitle:@"Rename" + image:[UIImage systemImageNamed:@"pencil"] + identifier:nil + handler:^(__kindof UIAction *action) { + [self renameRow:indexPath]; + }], + [UIAction actionWithTitle:@"Duplicate" + image:[UIImage systemImageNamed:@"plus.square.on.square"] + identifier:nil + handler:^(__kindof UIAction *action) { + [self duplicateROMAtIndex:indexPath.row]; + }], + deleteAction, + ]]; + }]; +} + +- (void)deselectRow +{ + if (self.tableView.indexPathForSelectedRow) { + [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:true]; + } +} + +- (void)viewWillAppear:(BOOL)animated +{ + [self.tableView reloadData]; +} + +- (NSString *)rootPath +{ + return [GBROMManager sharedManager].localRoot; +} + +@end diff --git a/iOS/GBSettingsViewController.m b/iOS/GBSettingsViewController.m @@ -696,9 +696,9 @@ static NSString *LocalizedNameForElement(GCControllerElement *element, GBControl UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"No Controllers Connected" message:@"There are no connected game controllers to configure" preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:nil]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:nil]]; [self presentViewController:alert animated:true completion:nil]; return false; diff --git a/iOS/GBStatesViewController.m b/iOS/GBStatesViewController.m @@ -35,6 +35,7 @@ } } + - (void)viewDidLoad { [super viewDidLoad]; diff --git a/iOS/GBThemePreviewController.m b/iOS/GBThemePreviewController.m @@ -57,16 +57,16 @@ // No supporter-only themes outside the App Store release } else { - [alert addAction:[UIAlertAction actionWithTitle:@"Apply Theme" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { + [alert addAction:[UIAlertAction actionWithTitle:@"Apply Theme" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { [[NSUserDefaults standardUserDefaults] setObject:_verticalLayout.theme.name forKey:@"GBInterfaceTheme"]; [[self presentingViewController] dismissViewControllerAnimated:true completion:nil]; }]]; } - [alert addAction:[UIAlertAction actionWithTitle:@"Exit Preview" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { + [alert addAction:[UIAlertAction actionWithTitle:@"Exit Preview" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { [[self presentingViewController] dismissViewControllerAnimated:true completion:nil]; }]]; [self presentViewController:alert animated:true completion:^{ diff --git a/iOS/GBViewController.h b/iOS/GBViewController.h @@ -27,7 +27,8 @@ typedef enum { - (void)emptyPrinterFeed; - (void)saveStateToFile:(NSString *)file; - (bool)loadStateFromFile:(NSString *)file; -- (bool)handleOpenURLs:(NSArray <NSURL *> *)urls openInPlace:(bool)inPlace; +- (bool)handleOpenURLs:(NSArray <NSURL *> *)urls + openInPlace:(bool)inPlace; - (void)dismissViewController; @property (nonatomic) GBRunMode runMode; @end diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m @@ -32,6 +32,7 @@ bool _rewindOver; bool _romLoaded; bool _swappingROM; + bool _loadingState; UIInterfaceOrientation _orientation; GBHorizontalLayout *_horizontalLayout; @@ -265,12 +266,12 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) _audioLock = [[NSCondition alloc] init]; - [self loadROM]; [[NSNotificationCenter defaultCenter] addObserverForName:@"GBROMChanged" object:nil queue:nil usingBlock:^(NSNotification *note) { - [self loadROM]; + _swappingROM = true; + [self stop]; [self start]; }]; @@ -628,9 +629,9 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"SameBoy is not properly signed and might not be able to open ROMs" message:[NSString stringWithFormat:@"The bundle identifier in the Info.plist file (“%@”) does not match the one in the entitlements (“%@”)", plistIdentifier, entIdentifier] preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:nil]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:nil]]; [self presentViewController:alert animated:true completion:nil]; } } @@ -648,7 +649,7 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) - (bool)loadStateFromFile:(NSString *)file { - [self stop]; + _loadingState = true; GB_model_t model; if (!GB_get_state_model(file.fileSystemRepresentation, &model)) { if (GB_get_model(&_gb) != model) { @@ -656,53 +657,56 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) } return GB_load_state(&_gb, file.fileSystemRepresentation) == 0; } + return false; } - (void)loadROM { - _swappingROM = true; - [self stop]; GBROMManager *romManager = [GBROMManager sharedManager]; if (romManager.romFile) { - // Todo: display errors and warnings - if ([romManager.romFile.pathExtension.lowercaseString isEqualToString:@"isx"]) { - _romLoaded = GB_load_isx(&_gb, romManager.romFile.fileSystemRepresentation) == 0; - } - else { - _romLoaded = GB_load_rom(&_gb, romManager.romFile.fileSystemRepresentation) == 0; - } - if (_romLoaded) { - GB_reset(&_gb); - GB_load_battery(&_gb, [GBROMManager sharedManager].batterySaveFile.fileSystemRepresentation); - GB_remove_all_cheats(&_gb); - GB_load_cheats(&_gb, [GBROMManager sharedManager].cheatsFile.UTF8String, false); - if (![self loadStateFromFile:[GBROMManager sharedManager].autosaveStateFile]) { - // Newly played ROM, pick the best model - uint8_t *rom = GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_ROM, NULL, NULL); - - if ((rom[0x143] & 0x80)) { - if (!GB_is_cgb(&_gb)) { - GB_switch_model_and_reset(&_gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBCGBModel"]); + if (!_loadingState) { + // Todo: display errors and warnings + if ([romManager.romFile.pathExtension.lowercaseString isEqualToString:@"isx"]) { + _romLoaded = GB_load_isx(&_gb, romManager.romFile.fileSystemRepresentation) == 0; + } + else { + _romLoaded = GB_load_rom(&_gb, romManager.romFile.fileSystemRepresentation) == 0; + } + if (_romLoaded) { + GB_reset(&_gb); + GB_load_battery(&_gb, [GBROMManager sharedManager].batterySaveFile.fileSystemRepresentation); + GB_remove_all_cheats(&_gb); + GB_load_cheats(&_gb, [GBROMManager sharedManager].cheatsFile.UTF8String, false); + if (![self loadStateFromFile:[GBROMManager sharedManager].autosaveStateFile]) { + // Newly played ROM, pick the best model + uint8_t *rom = GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_ROM, NULL, NULL); + + if ((rom[0x143] & 0x80)) { + if (!GB_is_cgb(&_gb)) { + GB_switch_model_and_reset(&_gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBCGBModel"]); + } + } + else if ((rom[0x146] == 3) && !GB_is_sgb(&_gb)) { + GB_switch_model_and_reset(&_gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBSGBModel"]); } - } - else if ((rom[0x146] == 3) && !GB_is_sgb(&_gb)) { - GB_switch_model_and_reset(&_gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBSGBModel"]); } } + GB_rewind_reset(&_gb); } - GB_rewind_reset(&_gb); } else { _romLoaded = false; } - _gbView.hidden = !_romLoaded; + dispatch_async(dispatch_get_main_queue(), ^{ + _gbView.hidden = !_romLoaded; + }); _swappingROM = false; + _loadingState = false; } - (void)applicationDidBecomeActive:(UIApplication *)application { - if (self.presentedViewController) return; [self start]; } @@ -758,9 +762,9 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"SameBoy is not a Game Boy Advance Emulator" message:@"SameBoy cannot play GBA games. Changing the model to Game Boy Advance lets you play Game Boy games as if on a Game Boy Advance in Game Boy Color mode." preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *action) { + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { [self start]; [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBShownGBAWarning"]; }]]; @@ -836,9 +840,9 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) if (completion) { completion(); } - if (!self.presentedViewController) { + dispatch_async(dispatch_get_main_queue(), ^{ [self start]; - } + }); }]; } @@ -846,9 +850,9 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) { /* Hack. Some view controllers dismiss without calling the method above. */ [super setNeedsUpdateOfSupportedInterfaceOrientations]; - if (!self.presentedViewController) { + dispatch_async(dispatch_get_main_queue(), ^{ [self start]; - } + }); } - (void)dismissViewController @@ -1047,6 +1051,12 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp) - (void)run { + [self loadROM]; + if (!_romLoaded) { + _running = false; + _stopping = false; + return; + } [self preRun]; while (_running) { if (_rewind) { @@ -1168,8 +1178,8 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response - (void)start { - if (!_romLoaded) return; if (_running) return; + if (self.presentedViewController) return; _running = true; [[[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil] start]; } @@ -1289,7 +1299,8 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response GB_set_palette(&_gb, &_palette); } -- (bool)handleOpenURLs:(NSArray <NSURL *> *)urls openInPlace:(bool)inPlace +- (bool)handleOpenURLs:(NSArray <NSURL *> *)urls + openInPlace:(bool)inPlace { NSMutableArray<NSURL *> *validURLs = [NSMutableArray array]; NSMutableArray<NSString *> *skippedBasenames = [NSMutableArray array]; @@ -1335,10 +1346,9 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response message:[NSString stringWithFormat:@"Could not find any Game Boy ROM files in the following archives:\n%@", [unusedZips componentsJoinedByString:@"\n"]] preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:nil]]; - [self stop]; + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:nil]]; [self presentViewController:alert animated:true completion:nil]; } @@ -1347,15 +1357,15 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response message:[NSString stringWithFormat:@"Could not import the following files because they're not supported:\n%@", [skippedBasenames componentsJoinedByString:@"\n"]] preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Close" - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *action) { + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { [[NSUserDefaults standardUserDefaults] setBool:false forKey:@"GBShownUTIWarning"]; // Somebody might need a reminder }]]; - [self stop]; [self presentViewController:alert animated:true completion:nil]; } + if (validURLs.count == 1 && urls.count == 1) { NSURL *url = validURLs.firstObject; NSString *potentialROM = [[url.path stringByDeletingLastPathComponent] lastPathComponent]; @@ -1369,7 +1379,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response keepOriginal:![url.path hasPrefix:tempDir] && !inPlace]; [url stopAccessingSecurityScopedResource]; } - [[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil]; return true; } for (NSURL *url in validURLs) { @@ -1383,7 +1392,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response keepOriginal:![url.path hasPrefix:tempDir] && !inPlace]; [url stopAccessingSecurityScopedResource]; } - [self stop]; [self openLibrary]; return validURLs.count; @@ -1391,15 +1399,18 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { + if (self.presentedViewController && ![self.presentedViewController isKindOfClass:[UIAlertController class]]) { + [self dismissViewController]; + } NSString *potentialROM = [[url.path stringByDeletingLastPathComponent] lastPathComponent]; if ([[[GBROMManager sharedManager] romFileForROM:potentialROM].stringByStandardizingPath isEqualToString:url.path.stringByStandardizingPath]) { [self stop]; [GBROMManager sharedManager].currentROM = potentialROM; - [self loadROM]; [self start]; return [GBROMManager sharedManager].currentROM != nil; } - return [self handleOpenURLs:@[url] openInPlace:[options[UIApplicationOpenURLOptionsOpenInPlaceKey] boolValue]]; + return [self handleOpenURLs:@[url] + openInPlace:[options[UIApplicationOpenURLOptionsOpenInPlaceKey] boolValue]]; } - (void)setRunMode:(GBRunMode)runMode ignoreDynamicSpeed:(bool)ignoreDynamicSpeed
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.