mirror of https://github.com/LIJI32/SameBoy.git
iOS cheats support
This commit is contained in:
parent
50a56a4b68
commit
92f425655d
|
@ -1166,7 +1166,7 @@ static bool is_path_writeable(const char *path)
|
|||
}
|
||||
}
|
||||
GB_load_battery(&_gb, self.savPath.UTF8String);
|
||||
GB_load_cheats(&_gb, self.chtPath.UTF8String);
|
||||
GB_load_cheats(&_gb, self.chtPath.UTF8String, true);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.cheatWindowController cheatsUpdated];
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ static id nop(id self, SEL _cmd)
|
|||
return nil;
|
||||
}
|
||||
|
||||
static double blah(id self, SEL _cmd)
|
||||
static double minSize(id self, SEL _cmd)
|
||||
{
|
||||
return 80.0;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ static double blah(id self, SEL _cmd)
|
|||
// Prevent collapsing toolbar items into menu items, they don't work in that form
|
||||
method_setImplementation(class_getInstanceMethod(self, @selector(menuFormRepresentation)), (IMP)nop);
|
||||
// Prevent over-agressive collapsing of the Printer Feed menu
|
||||
method_setImplementation(class_getInstanceMethod(NSClassFromString(@"NSToolbarTitleView"), @selector(minSize)), (IMP)blah);
|
||||
method_setImplementation(class_getInstanceMethod(NSClassFromString(@"NSToolbarTitleView"), @selector(minSize)), (IMP)minSize);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -141,6 +141,13 @@ void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat)
|
|||
free((void *)cheat);
|
||||
}
|
||||
|
||||
void GB_remove_all_cheats(GB_gameboy_t *gb)
|
||||
{
|
||||
while (gb->cheats) {
|
||||
GB_remove_cheat(gb, gb->cheats[0]);
|
||||
}
|
||||
}
|
||||
|
||||
const GB_cheat_t *GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const char *description, bool enabled)
|
||||
{
|
||||
GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
|
||||
|
@ -261,13 +268,13 @@ void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *_cheat, const char *des
|
|||
|
||||
#define CHEAT_MAGIC 'SBCh'
|
||||
|
||||
void GB_load_cheats(GB_gameboy_t *gb, const char *path)
|
||||
int GB_load_cheats(GB_gameboy_t *gb, const char *path, bool replace_existing)
|
||||
{
|
||||
GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
return;
|
||||
return errno;
|
||||
}
|
||||
|
||||
uint32_t magic = 0;
|
||||
|
@ -276,17 +283,17 @@ void GB_load_cheats(GB_gameboy_t *gb, const char *path)
|
|||
fread(&struct_size, sizeof(struct_size), 1, f);
|
||||
if (magic != LE32(CHEAT_MAGIC) && magic != BE32(CHEAT_MAGIC)) {
|
||||
GB_log(gb, "The file is not a SameBoy cheat database");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (struct_size != sizeof(GB_cheat_t)) {
|
||||
GB_log(gb, "This cheat database is not compatible with this version of SameBoy");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Remove all cheats first
|
||||
while (gb->cheats) {
|
||||
GB_remove_cheat(gb, gb->cheats[0]);
|
||||
if (replace_existing) {
|
||||
GB_remove_all_cheats(gb);
|
||||
}
|
||||
|
||||
GB_cheat_t cheat;
|
||||
|
@ -299,7 +306,7 @@ void GB_load_cheats(GB_gameboy_t *gb, const char *path)
|
|||
GB_add_cheat(gb, cheat.description, cheat.address, cheat.bank, cheat.value, cheat.old_value, cheat.use_old_value, cheat.enabled);
|
||||
}
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GB_save_cheats(GB_gameboy_t *gb, const char *path)
|
||||
|
|
|
@ -11,9 +11,10 @@ void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat, const char *desc
|
|||
const GB_cheat_t *GB_import_cheat(GB_gameboy_t *gb, const char *cheat, const char *description, bool enabled);
|
||||
const GB_cheat_t *const *GB_get_cheats(GB_gameboy_t *gb, size_t *size);
|
||||
void GB_remove_cheat(GB_gameboy_t *gb, const GB_cheat_t *cheat);
|
||||
void GB_remove_all_cheats(GB_gameboy_t *gb);
|
||||
bool GB_cheats_enabled(GB_gameboy_t *gb);
|
||||
void GB_set_cheats_enabled(GB_gameboy_t *gb, bool enabled);
|
||||
void GB_load_cheats(GB_gameboy_t *gb, const char *path);
|
||||
int GB_load_cheats(GB_gameboy_t *gb, const char *path, bool replace_existing);
|
||||
int GB_save_cheats(GB_gameboy_t *gb, const char *path);
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
|
|
|
@ -226,9 +226,7 @@ void GB_free(GB_gameboy_t *gb)
|
|||
#endif
|
||||
GB_rewind_reset(gb);
|
||||
#ifndef GB_DISABLE_CHEATS
|
||||
while (gb->cheats) {
|
||||
GB_remove_cheat(gb, gb->cheats[0]);
|
||||
}
|
||||
GB_remove_all_cheats(gb);
|
||||
#endif
|
||||
#ifndef GB_DISABLE_CHEAT_SEARCH
|
||||
GB_cheat_search_reset(gb);
|
||||
|
|
|
@ -923,7 +923,9 @@ restart:;
|
|||
|
||||
char cheat_path[path_length + 5];
|
||||
replace_extension(filename, path_length, cheat_path, ".cht");
|
||||
GB_load_cheats(&gb, cheat_path);
|
||||
// Remove all cheats before loading, so they're cleared even if loading fails.
|
||||
GB_remove_all_cheats(&gb);
|
||||
GB_load_cheats(&gb, cheat_path, false);
|
||||
|
||||
end_capturing_logs(true, error, SDL_MESSAGEBOX_WARNING, "Warning", previous);
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,7 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import <Core/gb.h>
|
||||
|
||||
@interface GBCheatsController : UITableViewController
|
||||
- (instancetype)initWithGameBoy:(GB_gameboy_t *)gb;
|
||||
@end
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
#import "GBCheatsController.h"
|
||||
#import "GBROMManager.h"
|
||||
#import "UIToolbar+disableCompact.h"
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface GBCheatsController()<UIDocumentPickerDelegate>
|
||||
@end
|
||||
|
||||
@implementation GBCheatsController
|
||||
{
|
||||
GB_gameboy_t *_gb;
|
||||
NSIndexPath *_renamingPath;
|
||||
__weak UITextField *_editingField;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == 0) return 1;
|
||||
size_t count;
|
||||
GB_get_cheats(_gb, &count);
|
||||
self.toolbarItems.firstObject.enabled = count;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
|
||||
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
|
||||
|
||||
UISwitch *button = [[UISwitch alloc] init];
|
||||
cell.accessoryView = button;
|
||||
const GB_cheat_t *cheat = NULL;
|
||||
if (indexPath.section == 0) {
|
||||
button.on = GB_cheats_enabled(_gb);
|
||||
cell.textLabel.text = @"Enable Cheats";
|
||||
}
|
||||
else {
|
||||
cheat = GB_get_cheats(_gb, NULL)[indexPath.row];
|
||||
button.on = cheat->enabled;
|
||||
cell.textLabel.text = @(cheat->description) ?: @"Unnamed Cheat";
|
||||
button.enabled = GB_cheats_enabled(_gb);
|
||||
}
|
||||
|
||||
id block = ^(){
|
||||
if (!cheat) {
|
||||
GB_set_cheats_enabled(_gb, button.on);
|
||||
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
|
||||
}
|
||||
else {
|
||||
GB_update_cheat(_gb, cheat, cheat->description,
|
||||
cheat->address, cheat->bank,
|
||||
cheat->value, cheat->old_value, cheat->use_old_value,
|
||||
button.on);
|
||||
}
|
||||
};
|
||||
objc_setAssociatedObject(cell, "RetainedBlock", block, OBJC_ASSOCIATION_RETAIN);
|
||||
|
||||
[button addTarget:block action:@selector(invoke) forControlEvents:UIControlEventValueChanged];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)addCheat
|
||||
{
|
||||
[self setEditing:false animated:true];
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle: @"Add Cheat"
|
||||
message: @"Add a GameShark or Game Genie cheat code"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.placeholder = @"Description";
|
||||
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
|
||||
}];
|
||||
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.placeholder = @"Cheat Code";
|
||||
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
|
||||
textField.keyboardType = UIKeyboardTypeASCIICapable;
|
||||
}];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Add" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
size_t index = [self tableView:self.tableView numberOfRowsInSection:1];
|
||||
NSString *name = alertController.textFields[0].text;
|
||||
if (GB_import_cheat(_gb, alertController.textFields[1].text.UTF8String, name.length? name.UTF8String : "Unnamed Cheat", true)) {
|
||||
[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:1]] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
else {
|
||||
alertController.title = @"Invalid cheat code entered";
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
+ (UIBarButtonItem *)buttonWithLabel:(NSString *)label
|
||||
imageWithName:(NSString *)imageName
|
||||
target:(id)target
|
||||
action:(SEL)action
|
||||
{
|
||||
if (@available(iOS 13.0, *)) {
|
||||
UIImage *image = [UIImage systemImageNamed:imageName
|
||||
withConfiguration:[UIImageSymbolConfiguration configurationWithScale:UIImageSymbolScaleLarge]];
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[button setImage:image forState:UIControlStateNormal];
|
||||
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
|
||||
if (label) {
|
||||
[button setTitle:label forState:UIControlStateNormal];
|
||||
[button setTitleColor:button.tintColor forState:UIControlStateNormal];
|
||||
button.titleEdgeInsets = UIEdgeInsetsMake(0, 4, 0, 0);
|
||||
button.contentEdgeInsets = UIEdgeInsetsMake(0, 12, 0, 0);
|
||||
}
|
||||
[button sizeToFit];
|
||||
CGRect frame = button.frame;
|
||||
frame.size.width = ceil(frame.size.width + (label? 4 : 0));
|
||||
frame.size.height = 28;
|
||||
button.frame = frame;
|
||||
return [[UIBarButtonItem alloc] initWithCustomView:button];
|
||||
}
|
||||
return [[UIBarButtonItem alloc] initWithTitle:label style:UIBarButtonItemStylePlain target:target action:action];
|
||||
}
|
||||
|
||||
- (void)importCheats
|
||||
{
|
||||
[self setEditing:false animated:true];
|
||||
NSString *chtUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)@"cht", NULL);
|
||||
|
||||
|
||||
UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[chtUTI]
|
||||
inMode:UIDocumentPickerModeImport];
|
||||
if (@available(iOS 13.0, *)) {
|
||||
picker.shouldShowFileExtensions = true;
|
||||
}
|
||||
picker.delegate = self;
|
||||
[self presentViewController:picker animated:true completion:nil];
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
- (void)exportCheats
|
||||
{
|
||||
[self setEditing:false animated:true];
|
||||
NSString *cheatsFile = [[GBROMManager sharedManager] cheatsFile];
|
||||
GB_save_cheats(_gb, cheatsFile.UTF8String);
|
||||
NSURL *url = [NSURL fileURLWithPath:cheatsFile];
|
||||
UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:@[url]
|
||||
applicationActivities:nil];
|
||||
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
controller.popoverPresentationController.barButtonItem = self.toolbarItems.firstObject;
|
||||
}
|
||||
|
||||
[self presentViewController:controller
|
||||
animated:true
|
||||
completion:nil];
|
||||
|
||||
}
|
||||
|
||||
- (instancetype)initWithGameBoy:(GB_gameboy_t *)gb
|
||||
{
|
||||
UITableViewStyle style = UITableViewStyleGrouped;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
style = UITableViewStyleInsetGrouped;
|
||||
}
|
||||
self = [super initWithStyle:style];
|
||||
self.tableView.allowsSelectionDuringEditing = true;
|
||||
self.navigationItem.rightBarButtonItem = self.editButtonItem;
|
||||
|
||||
bool hasSFSymbols = false;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
hasSFSymbols = true;
|
||||
}
|
||||
|
||||
self.toolbarItems = @[
|
||||
hasSFSymbols?
|
||||
[self.class buttonWithLabel:nil
|
||||
imageWithName:@"square.and.arrow.up"
|
||||
target:self
|
||||
action:@selector(exportCheats)] :
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction
|
||||
target:self
|
||||
action:@selector(exportCheats)],
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil
|
||||
action:NULL],
|
||||
[self.class buttonWithLabel:@"Import"
|
||||
imageWithName:@"square.and.arrow.down"
|
||||
target:self
|
||||
action:@selector(importCheats)],
|
||||
[self.class buttonWithLabel:@"Add"
|
||||
imageWithName:@"plus"
|
||||
target:self
|
||||
action:@selector(addCheat)],
|
||||
|
||||
];
|
||||
|
||||
_gb = gb;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)title
|
||||
{
|
||||
return @"Cheats";
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return indexPath.section == 1;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section != 1) return;
|
||||
if (editingStyle != UITableViewCellEditingStyleDelete) return;
|
||||
|
||||
const GB_cheat_t *cheat = GB_get_cheats(_gb, NULL)[indexPath.row];
|
||||
GB_remove_cheat(_gb, cheat);
|
||||
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self.navigationController setToolbarHidden:false animated:false];
|
||||
self.navigationController.toolbar.disableCompactLayout = true;
|
||||
}
|
||||
|
||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
|
||||
{
|
||||
[url startAccessingSecurityScopedResource];
|
||||
NSString *tempDir = NSTemporaryDirectory();
|
||||
NSString *newPath = [tempDir stringByAppendingPathComponent:@"import.cht"];
|
||||
[[NSFileManager defaultManager] copyItemAtPath:url.path toPath:newPath error:nil];
|
||||
[url stopAccessingSecurityScopedResource];
|
||||
unsigned count = [self tableView:self.tableView numberOfRowsInSection:1];
|
||||
|
||||
void (^load)(bool) = ^(bool replace) {
|
||||
if (GB_load_cheats(_gb, newPath.UTF8String, replace)) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:newPath error:nil];
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Import Failed"
|
||||
message:@"The imported cheats file is invalid."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alertController animated:true completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSFileManager defaultManager] removeItemAtPath:newPath error:nil];
|
||||
unsigned newCount = [self tableView:self.tableView numberOfRowsInSection:1];
|
||||
if (!replace) {
|
||||
NSMutableArray *paths = [NSMutableArray arrayWithCapacity:newCount - count];
|
||||
for (unsigned i = count; i < newCount; i++) {
|
||||
[paths addObject:[NSIndexPath indexPathForRow:i inSection:1]];
|
||||
}
|
||||
if (paths.count) {
|
||||
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSMutableArray *paths = [NSMutableArray arrayWithCapacity:abs((signed)newCount - (signed)count)];
|
||||
for (unsigned i = MIN(newCount, count); i < count || i < newCount; i++) {
|
||||
[paths addObject:[NSIndexPath indexPathForRow:i inSection:1]];
|
||||
}
|
||||
if (newCount > count) {
|
||||
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
else {
|
||||
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
paths = [NSMutableArray arrayWithCapacity:MIN(newCount, count)];
|
||||
for (unsigned i = 0; i < count && i < newCount; i++) {
|
||||
[paths addObject:[NSIndexPath indexPathForRow:i inSection:1]];
|
||||
}
|
||||
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationRight];
|
||||
}
|
||||
};
|
||||
|
||||
if (count) {
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Replace Existing Cheats?"
|
||||
message:@"Append the newly imported cheats or replace the existing ones?"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Append" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
load(false);
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Replace" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
|
||||
load(true);
|
||||
}]];
|
||||
[self presentViewController:alertController animated:true completion:nil];
|
||||
}
|
||||
else {
|
||||
load(true);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == 0) return;
|
||||
if (!self.editing) return;
|
||||
|
||||
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
|
||||
CGRect frame = cell.textLabel.frame;
|
||||
frame.size.width = cell.textLabel.superview.frame.size.width - 8 - frame.origin.x;
|
||||
UITextField *field = [[UITextField alloc] initWithFrame: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];
|
||||
_editingField = field;
|
||||
}
|
||||
|
||||
- (void)doneRename:(UITextField *)sender
|
||||
{
|
||||
if (!_renamingPath) return;
|
||||
const GB_cheat_t *cheat = GB_get_cheats(_gb, NULL)[_renamingPath.row];
|
||||
GB_update_cheat(_gb, cheat, sender.text.length? sender.text.UTF8String : "Unnamed Cheat",
|
||||
cheat->address, cheat->bank,
|
||||
cheat->value, cheat->old_value, cheat->use_old_value,
|
||||
cheat->enabled);
|
||||
[self.tableView reloadRowsAtIndexPaths:@[_renamingPath] withRowAnimation:UITableViewRowAnimationNone];
|
||||
_renamingPath = nil;
|
||||
}
|
||||
|
||||
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
|
||||
{
|
||||
[super setEditing:editing animated:animated];
|
||||
if (!editing && _editingField) {
|
||||
[self doneRename:_editingField];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
NSString *cheatsFile = [[GBROMManager sharedManager] cheatsFile];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:cheatsFile error:nil];
|
||||
GB_save_cheats(_gb, cheatsFile.UTF8String);
|
||||
}
|
||||
|
||||
@end
|
|
@ -61,7 +61,7 @@ static NSString *const tips[] = {
|
|||
{@"Connect", @"LinkCableTemplate", SelectorString(openConnectMenu), true},
|
||||
{@"Model", @"ModelTemplate", SelectorString(changeModel)},
|
||||
{@"States", @"square.stack", SelectorString(openStates), true},
|
||||
{@"Cheats", @"wand.and.stars", nil}, // TODO
|
||||
{@"Cheats", @"CheatsTemplate", SelectorString(openCheats), true}, // TODO
|
||||
{@"Settings", @"gear", SelectorString(openSettings)},
|
||||
{@"About", @"info.circle", SelectorString(showAbout)},
|
||||
};
|
||||
|
@ -85,10 +85,6 @@ static NSString *const tips[] = {
|
|||
button.enabled = false;
|
||||
continue;
|
||||
}
|
||||
if (buttons[i].selector == nil) {
|
||||
button.enabled = false;
|
||||
continue;
|
||||
}
|
||||
SEL selector = NSSelectorFromString(buttons[i].selector);
|
||||
if (buttons[i].requireRunning && ![GBROMManager sharedManager].currentROM) {
|
||||
button.enabled = false;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
@property (readonly) NSString *romFile;
|
||||
@property (readonly) NSString *batterySaveFile;
|
||||
@property (readonly) NSString *autosaveStateFile;
|
||||
@property (readonly) NSString *cheatsFile;
|
||||
- (NSString *)stateFile:(unsigned)index;
|
||||
|
||||
- (NSString *)romFileForROM:(NSString *)rom;
|
||||
|
|
|
@ -103,6 +103,17 @@
|
|||
return [self stateFile:index forROM:_currentROM];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)cheatsFileForROM:(NSString *)rom
|
||||
{
|
||||
return [self auxilaryFileForROM:rom withExtension:@"cht"];
|
||||
}
|
||||
|
||||
- (NSString *)cheatsFile
|
||||
{
|
||||
return [self cheatsFileForROM:_currentROM];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)allROMs
|
||||
{
|
||||
NSMutableArray<NSString *> *ret = [NSMutableArray array];
|
||||
|
|
|
@ -23,6 +23,7 @@ typedef enum {
|
|||
- (void)openSettings;
|
||||
- (void)showAbout;
|
||||
- (void)openConnectMenu;
|
||||
- (void)openCheats;
|
||||
- (void)emptyPrinterFeed;
|
||||
- (void)saveStateToFile:(NSString *)file;
|
||||
- (bool)loadStateFromFile:(NSString *)file;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#import "GBStatesViewController.h"
|
||||
#import "GBCheckableAlertController.h"
|
||||
#import "GBPrinterFeedController.h"
|
||||
#import "GBCheatsController.h"
|
||||
#import "GCExtendedGamepad+AllElements.h"
|
||||
#import "GBZipReader.h"
|
||||
#import <sys/stat.h>
|
||||
|
@ -674,6 +675,8 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
|||
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);
|
||||
|
@ -820,6 +823,19 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
|||
[self presentViewController:[[GBAboutController alloc] init] animated:true completion:nil];
|
||||
}
|
||||
|
||||
- (void)openCheats
|
||||
{
|
||||
UINavigationController *controller = [[UINavigationController alloc] initWithRootViewController:[[GBCheatsController alloc] initWithGameBoy:&_gb]];
|
||||
UIBarButtonItem *close = [[UIBarButtonItem alloc] initWithTitle:@"Close"
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(dismissViewController)];
|
||||
[controller.visibleViewController.navigationItem setLeftBarButtonItem:close];
|
||||
[self presentViewController:controller
|
||||
animated:true
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
|
||||
{
|
||||
[super dismissViewControllerAnimated:flag completion:^() {
|
||||
|
|
|
@ -176,6 +176,23 @@
|
|||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>SameBoy Cheats Database</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.github.liji32.sameboy.cheats</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>cht</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 561 B After Width: | Height: | Size: 596 B |
Binary file not shown.
Before Width: | Height: | Size: 851 B After Width: | Height: | Size: 927 B |
|
@ -0,0 +1,5 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIToolbar (disableCompact)
|
||||
@property bool disableCompactLayout;
|
||||
@end
|
|
@ -0,0 +1,33 @@
|
|||
#import "UIToolbar+disableCompact.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIToolbar (disableCompact)
|
||||
|
||||
- (void)setDisableCompactLayout:(bool)disableCompactLayout
|
||||
{
|
||||
objc_setAssociatedObject(self, @selector(disableCompactLayout), @(disableCompactLayout), OBJC_ASSOCIATION_RETAIN);
|
||||
}
|
||||
|
||||
- (bool)disableCompactLayout
|
||||
{
|
||||
return [objc_getAssociatedObject(self, _cmd) boolValue];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFitsHook:(CGSize)size
|
||||
{
|
||||
CGSize ret = [self sizeThatFitsHook:size];
|
||||
if (!self.disableCompactLayout) return ret;
|
||||
if (ret.height < 44) {
|
||||
ret.height = 44;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
method_exchangeImplementations(class_getInstanceMethod(self, @selector(sizeThatFitsHook:)),
|
||||
class_getInstanceMethod(self, @selector(sizeThatFits:)));
|
||||
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue