diff --git a/iOS/GBCheatsController.m b/iOS/GBCheatsController.m index 98c7c6f41..422d5b44b 100644 --- a/iOS/GBCheatsController.m +++ b/iOS/GBCheatsController.m @@ -231,6 +231,7 @@ - (void)viewWillAppear:(BOOL)animated self.navigationController.toolbar.disableCompactLayout = true; } + - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { [url startAccessingSecurityScopedResource]; diff --git a/iOS/GBHubGameViewController.m b/iOS/GBHubGameViewController.m index d417928d5..5511650fe 100644 --- a/iOS/GBHubGameViewController.m +++ b/iOS/GBHubGameViewController.m @@ -218,7 +218,6 @@ - (void)rightButtonPressed 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 @@ - (void)rightButtonPressed 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 index c2e85e7c4..743df56f1 100644 --- a/iOS/GBLibraryViewController.h +++ b/iOS/GBLibraryViewController.h @@ -1,6 +1,6 @@ #import @interface GBLibraryViewController : UITabBarController - @end + diff --git a/iOS/GBLibraryViewController.m b/iOS/GBLibraryViewController.m index 988a4fe13..b360f3140 100644 --- 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 @@ + (UIViewController *)wrapViewController:(UIViewController *)controller - (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 @@ - (void)viewDidLoad } } 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 @@ - (void)viewDidLoad } } + @end diff --git a/iOS/GBLoadROMTableViewController.h b/iOS/GBLoadROMTableViewController.h deleted file mode 100644 index 8ac2602a7..000000000 --- a/iOS/GBLoadROMTableViewController.h +++ /dev/null @@ -1,5 +0,0 @@ -#import - -@interface GBLoadROMTableViewController : UITableViewController - -@end diff --git a/iOS/GBROMManager.h b/iOS/GBROMManager.h index 3e95436f1..998bb3121 100644 --- 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 index 496395683..dcd9d4a86 100644 --- a/iOS/GBROMManager.m +++ b/iOS/GBROMManager.m @@ -4,6 +4,8 @@ @implementation GBROMManager { NSString *_romFile; + NSMutableDictionary *_cloudNameToFile; + bool _doneInitializing; } + (instancetype)sharedManager @@ -21,6 +23,7 @@ - (instancetype)init self = [super init]; if (!self) return nil; self.currentROM = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBLastROM"]; + _doneInitializing = true; return self; } @@ -28,11 +31,16 @@ - (void)setCurrentROM:(NSString *)currentROM { _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 @@ - (NSString *)romFileForDirectory:(NSString *)romDirectory 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 @@ - (NSString *)romFileForROM:(NSString *)rom 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 @@ - (NSString *)cheatsFile - (NSArray *)allROMs { NSMutableArray *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 *)cheatsFile - (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 *)importROM:(NSString *)romFile keepOriginal:(bool)keep 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 @@ - (NSString *)renameROM:(NSString *)rom toName:(NSString *)newName { 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 @@ - (NSString *)duplicateROM:(NSString *)rom - (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 new file mode 100644 index 000000000..77a710c8d --- /dev/null +++ b/iOS/GBROMViewController.h @@ -0,0 +1,14 @@ +#import + +@interface GBROMViewController : UITableViewController + +/* 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/GBLoadROMTableViewController.m b/iOS/GBROMViewController.m similarity index 80% rename from iOS/GBLoadROMTableViewController.m rename to iOS/GBROMViewController.m index 8ecd08ce9..2c2c90ab6 100644 --- a/iOS/GBLoadROMTableViewController.m +++ b/iOS/GBROMViewController.m @@ -1,13 +1,11 @@ -#import "GBLoadROMTableViewController.h" +#import "GBROMViewController.h" #import "GBROMManager.h" #import "GBViewController.h" +#import "GBLibraryViewController.h" #import #import -@interface GBLoadROMTableViewController() -@end - -@implementation GBLoadROMTableViewController +@implementation GBROMViewController { NSIndexPath *_renamingPath; } @@ -36,19 +34,10 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger return [GBROMManager sharedManager].allROMs.count; } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +- (UITableViewCell *)cellForROM:(NSString *)rom { - 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.textLabel.text = rom.lastPathComponent; cell.accessoryType = [rom isEqualToString:[GBROMManager sharedManager].currentROM]? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; NSString *pngPath = [[[GBROMManager sharedManager] autosaveStateFileForROM:rom] stringByAppendingPathExtension:@"png"]; @@ -68,6 +57,20 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N 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 @@ -89,6 +92,12 @@ - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInte 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) { @@ -110,9 +119,9 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath 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) { + [alert addAction:[UIAlertAction actionWithTitle:@"Close" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) { [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBShownUTIWarning"]; [self tableView:tableView didSelectRowAtIndexPath:indexPath]; }]]; @@ -141,22 +150,22 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath return; } case 1: { - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"shareddocuments://%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject]] + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"shareddocuments://%@", + [self.rootPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]]; + [[UIApplication sharedApplication] openURL:url 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]; - }]; + [self romSelectedAtIndex:indexPath.row]; } - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { - [(GBViewController *)[UIApplication sharedApplication] handleOpenURLs:urls openInPlace:false]; + [(GBViewController *)[UIApplication sharedApplication].delegate handleOpenURLs:urls + openInPlace:false]; } - (UIModalPresentationStyle)modalPresentationStyle @@ -164,28 +173,34 @@ - (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 = [GBROMManager sharedManager].allROMs[[indexPath indexAtPosition:1]]; - UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Delete ROM “%@”?", rom] + 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) { - [[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:@"Delete" + style:UIAlertActionStyleDestructive + handler:^(UIAlertAction *action) { + [self deleteROMAtIndex:indexPath.row]; }]]; - [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" - style:UIAlertActionStyleCancel - handler:nil]]; + [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:nil]]; [self presentViewController:alert animated:true completion:nil]; } @@ -197,7 +212,7 @@ - (void)renameRow:(NSIndexPath *)indexPath UITextField *field = [[UITextField alloc] initWithFrame:cell.textLabel.frame]; field.font = cell.textLabel.font; field.text = cell.textLabel.text; - cell.textLabel.text = @""; + cell.textLabel.textColor = [UIColor clearColor]; [[cell.textLabel superview] addSubview:field]; [field becomeFirstResponder]; [field selectAll:nil]; @@ -205,29 +220,36 @@ - (void)renameRow:(NSIndexPath *)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 = [GBROMManager sharedManager].allROMs[[_renamingPath indexAtPosition:1]]; + 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]]; + [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]; + [self renameROM:oldName toName:newName]; _renamingPath = nil; } @@ -236,6 +258,13 @@ - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *) 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 @@ -266,8 +295,7 @@ - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView 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]; + [self duplicateROMAtIndex:indexPath.row]; }], deleteAction, ]]; @@ -286,4 +314,9 @@ - (void)viewWillAppear:(BOOL)animated [self.tableView reloadData]; } +- (NSString *)rootPath +{ + return [GBROMManager sharedManager].localRoot; +} + @end diff --git a/iOS/GBSettingsViewController.m b/iOS/GBSettingsViewController.m index bc8b4b60a..2b7fab67b 100644 --- a/iOS/GBSettingsViewController.m +++ b/iOS/GBSettingsViewController.m @@ -696,9 +696,9 @@ - (bool)configureGameControllers 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 index 15762513b..e8eb74172 100644 --- a/iOS/GBStatesViewController.m +++ b/iOS/GBStatesViewController.m @@ -35,6 +35,7 @@ - (void)updateSlotView:(GBSlotButton *)view } } + - (void)viewDidLoad { [super viewDidLoad]; diff --git a/iOS/GBThemePreviewController.m b/iOS/GBThemePreviewController.m index d1d99f447..19674c1e8 100644 --- a/iOS/GBThemePreviewController.m +++ b/iOS/GBThemePreviewController.m @@ -57,16 +57,16 @@ - (void)showPopup // 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 index caef8987b..e27714c67 100644 --- 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 *)urls openInPlace:(bool)inPlace; +- (bool)handleOpenURLs:(NSArray *)urls + openInPlace:(bool)inPlace; - (void)dismissViewController; @property (nonatomic) GBRunMode runMode; @end diff --git a/iOS/GBViewController.m b/iOS/GBViewController.m index 4708e7ae0..1b7a82a7a 100644 --- a/iOS/GBViewController.m +++ b/iOS/GBViewController.m @@ -32,6 +32,7 @@ @implementation GBViewController bool _rewindOver; bool _romLoaded; bool _swappingROM; + bool _loadingState; UIInterfaceOrientation _orientation; GBHorizontalLayout *_horizontalLayout; @@ -265,12 +266,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( _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 @@ - (void)verifyEntitlements 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 @@ - (void)saveStateToFile:(NSString *)file - (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 @@ - (bool)loadStateFromFile:(NSString *)file } 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 @@ - (void)changeModel 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 @@ - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))comp if (completion) { completion(); } - if (!self.presentedViewController) { + dispatch_async(dispatch_get_main_queue(), ^{ [self start]; - } + }); }]; } @@ -846,9 +850,9 @@ - (void)setNeedsUpdateOfSupportedInterfaceOrientations { /* 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 @@ - (void)preRun - (void)run { + [self loadROM]; + if (!_romLoaded) { + _running = false; + _stopping = false; + return; + } [self preRun]; while (_running) { if (_rewind) { @@ -1168,8 +1178,8 @@ - (void)postRun - (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 @@ - (void)updatePalette GB_set_palette(&_gb, &_palette); } -- (bool)handleOpenURLs:(NSArray *)urls openInPlace:(bool)inPlace +- (bool)handleOpenURLs:(NSArray *)urls + openInPlace:(bool)inPlace { NSMutableArray *validURLs = [NSMutableArray array]; NSMutableArray *skippedBasenames = [NSMutableArray array]; @@ -1335,10 +1346,9 @@ - (bool)handleOpenURLs:(NSArray *)urls openInPlace:(bool)inPlace 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 @@ - (bool)handleOpenURLs:(NSArray *)urls openInPlace:(bool)inPlace 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 @@ - (bool)handleOpenURLs:(NSArray *)urls openInPlace:(bool)inPlace 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 @@ - (bool)handleOpenURLs:(NSArray *)urls openInPlace:(bool)inPlace keepOriginal:![url.path hasPrefix:tempDir] && !inPlace]; [url stopAccessingSecurityScopedResource]; } - [self stop]; [self openLibrary]; return validURLs.count; @@ -1391,15 +1399,18 @@ - (bool)handleOpenURLs:(NSArray *)urls openInPlace:(bool)inPlace - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)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