Play-/Source/ui_ios/CoverViewController.mm
2023-10-25 10:47:10 -04:00

258 lines
7.9 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#import <SDWebImage/UIImageView+WebCache.h>
#import "CoverViewController.h"
#import "EmulatorViewController.h"
#import "SettingsViewController.h"
#import "../ui_shared/BootablesProcesses.h"
#import "../ui_shared/BootablesDbClient.h"
#import "PathUtils.h"
#import "BackgroundLayer.h"
#import "CoverViewCell.h"
#import "AltServerJitService.h"
static bool IsJitAvailable()
{
//If ppid != 1, it means we're being run in the debugger
if(getppid() != 1) return true;
if([[AltServerJitService sharedAltServerJitService] jitEnabled])
{
return true;
}
{
//Check if we can scan the mobile directory (only possible if jailbroken)
std::error_code errorCode;
fs::directory_iterator dirIterator("/private/var/mobile", errorCode);
if(!errorCode)
{
return true;
}
}
return false;
}
@interface CoverViewController ()
@end
@implementation CoverViewController
static NSString* const reuseIdentifier = @"coverCell";
- (void)buildCollectionWithForcedFullScan:(BOOL)forceFullDeviceScan
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Building collection" message:@"Please wait..." preferredStyle:UIAlertControllerStyleAlert];
CGRect aivRect = CGRectMake(0, 0, 40, 40);
UIActivityIndicatorView* aiv = [[UIActivityIndicatorView alloc] initWithFrame:aivRect];
[aiv startAnimating];
UIViewController* vc = [[UIViewController alloc] init];
vc.preferredContentSize = aivRect.size;
[vc.view addSubview:aiv];
[alert setValue:vc forKey:@"contentViewController"];
[self presentViewController:alert animated:YES completion:nil];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
auto activeDirs = GetActiveBootableDirectories();
if(forceFullDeviceScan || activeDirs.empty())
{
dispatch_async(dispatch_get_main_queue(), ^{
alert.message = @"Scanning games on filesystem...";
});
ScanBootables("/private/var/mobile");
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
alert.message = @"Scanning games in active directories...";
});
for(const auto& activeDir : activeDirs)
{
ScanBootables(activeDir, false);
}
}
//Always scan games in app storage. The app's path change when it's reinstalled,
//thus, games from the previous installation won't be found (will be deleted in PurgeInexistingFiles).
dispatch_async(dispatch_get_main_queue(), ^{
alert.message = @"Scanning games in app storage...";
});
ScanBootables(Framework::PathUtils::GetPersonalDataPath());
dispatch_async(dispatch_get_main_queue(), ^{
alert.message = @"Purging inexisting files...";
});
PurgeInexistingFiles();
dispatch_async(dispatch_get_main_queue(), ^{
alert.message = @"Fetching game titles...";
});
FetchGameTitles();
if(_bootables)
{
delete _bootables;
_bootables = nullptr;
}
_bootables = new BootableArray(BootablesDb::CClient::GetInstance().GetBootables());
//Done
dispatch_async(dispatch_get_main_queue(), ^{
[alert dismissViewControllerAnimated:YES completion:nil];
[self.collectionView reloadData];
});
});
}
- (void)viewDidLoad
{
[super viewDidLoad];
CAGradientLayer* bgLayer = [BackgroundLayer blueGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
self.collectionView.allowsMultipleSelection = NO;
if(@available(iOS 11.0, *))
{
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
}
[[AltServerJitService sharedAltServerJitService] startProcess];
[self buildCollectionWithForcedFullScan:NO];
}
- (void)viewDidUnload
{
assert(_bootables != nullptr);
delete _bootables;
[super viewDidUnload];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
// resize your layers based on the views new bounds
[[[self.view.layer sublayers] objectAtIndex:0] setFrame:self.view.bounds];
}
- (BOOL)shouldAutorotate
{
if([self isViewLoaded] && self.view.window)
{
return YES;
}
else
{
return NO;
}
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView
{
return 1;
}
- (NSString*)collectionView:(UICollectionView*)collectionView titleForHeaderInSection:(NSInteger)section
{
return @"";
}
- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section
{
return _bootables ? _bootables->size() : 0;
}
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath
{
CoverViewCell* cell = (CoverViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
auto bootable = (*_bootables)[indexPath.row];
UIImage* placeholder = [UIImage imageNamed:@"boxart.png"];
cell.nameLabel.text = [NSString stringWithUTF8String:bootable.title.c_str()];
cell.backgroundView = [[UIImageView alloc] initWithImage:placeholder];
if(!bootable.coverUrl.empty())
{
NSString* coverUrl = [NSString stringWithUTF8String:bootable.coverUrl.c_str()];
[(UIImageView*)cell.backgroundView sd_setImageWithURL:[NSURL URLWithString:coverUrl] placeholderImage:placeholder];
}
return cell;
}
#pragma mark <UICollectionViewDelegate>
- (BOOL)shouldPerformSegueWithIdentifier:(NSString*)identifier sender:(id)sender
{
if([identifier isEqualToString:@"showEmulator"] && !IsJitAvailable())
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"JIT unavailable" message:@"JIT doesn't seem to be available at the moment. If JIT is not available, the emulator will crash. Do you wish to continue?" preferredStyle:UIAlertControllerStyleAlert];
{
UIAlertAction* continueAction = [UIAlertAction
actionWithTitle:@"Continue"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction*) {
[self performSegueWithIdentifier:@"showEmulator" sender:sender];
}];
[alert addAction:continueAction];
}
{
UIAlertAction* cancelAction = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction*){}];
[alert addAction:cancelAction];
}
{
UIAlertAction* helpAction = [UIAlertAction
actionWithTitle:@"Help"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction*) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://github.com/jpd002/Play-#running-on-ios"]];
}];
[alert addAction:helpAction];
}
[self presentViewController:alert animated:YES completion:nil];
return NO;
}
return YES;
}
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if([segue.identifier isEqualToString:@"showEmulator"])
{
NSIndexPath* indexPath = [[self.collectionView indexPathsForSelectedItems] objectAtIndex:0];
auto bootable = (*_bootables)[indexPath.row];
BootablesDb::CClient::GetInstance().SetLastBootedTime(bootable.path, time(nullptr));
EmulatorViewController* emulatorViewController = segue.destinationViewController;
emulatorViewController.bootablePath = [NSString stringWithUTF8String:bootable.path.native().c_str()];
[self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
}
else if([segue.identifier isEqualToString:@"showSettings"])
{
UINavigationController* navViewController = segue.destinationViewController;
SettingsViewController* settingsViewController = (SettingsViewController*)navViewController.visibleViewController;
settingsViewController.allowFullDeviceScan = true;
settingsViewController.allowGsHandlerSelection = true;
settingsViewController.completionHandler = ^(bool fullScanRequested) {
[[AltServerJitService sharedAltServerJitService] startProcess];
if(fullScanRequested)
{
[self buildCollectionWithForcedFullScan:YES];
}
};
}
}
- (IBAction)onExit:(id)sender
{
exit(0);
}
@end