mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-01 02:02:01 +02:00
Add ZIP import support to the iOS version
This commit is contained in:
2
Makefile
2
Makefile
@@ -273,7 +273,7 @@ LDFLAGS += -arch arm64
|
|||||||
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT)
|
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT)
|
||||||
LDFLAGS += -miphoneos-version-min=$(IOS_MIN) -isysroot $(SYSROOT)
|
LDFLAGS += -miphoneos-version-min=$(IOS_MIN) -isysroot $(SYSROOT)
|
||||||
IOS_INSTALLER_LDFLAGS := $(LDFLAGS) -lobjc -framework CoreServices -framework Foundation
|
IOS_INSTALLER_LDFLAGS := $(LDFLAGS) -lobjc -framework CoreServices -framework Foundation
|
||||||
LDFLAGS += -lobjc -framework UIKit -framework Foundation -framework CoreGraphics -framework Metal -framework MetalKit -framework AudioToolbox -framework AVFoundation -framework QuartzCore -framework CoreMotion -framework CoreVideo -framework CoreMedia -framework CoreImage -framework UserNotifications -framework GameController -weak_framework CoreHaptics -framework MobileCoreServices
|
LDFLAGS += -lobjc -framework UIKit -framework Foundation -framework CoreGraphics -framework Metal -framework MetalKit -framework AudioToolbox -framework AVFoundation -framework QuartzCore -framework CoreMotion -framework CoreVideo -framework CoreMedia -framework CoreImage -framework UserNotifications -framework GameController -weak_framework CoreHaptics -framework MobileCoreServices -lcompression
|
||||||
CODESIGN := codesign -fs -
|
CODESIGN := codesign -fs -
|
||||||
else
|
else
|
||||||
ifeq ($(PLATFORM),Darwin)
|
ifeq ($(PLATFORM),Darwin)
|
||||||
|
@@ -122,6 +122,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[extensions addObject:@"zip"];
|
||||||
|
|
||||||
[self.presentingViewController dismissViewControllerAnimated:true completion:^{
|
[self.presentingViewController dismissViewControllerAnimated:true completion:^{
|
||||||
UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.github.liji32.sameboy.gb",
|
UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"com.github.liji32.sameboy.gb",
|
||||||
@"com.github.liji32.sameboy.gbc",
|
@"com.github.liji32.sameboy.gbc",
|
||||||
@@ -161,61 +163,7 @@
|
|||||||
|
|
||||||
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls
|
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls
|
||||||
{
|
{
|
||||||
NSMutableArray<NSURL *> *validURLs = [NSMutableArray array];
|
[(GBViewController *)[UIApplication sharedApplication] handleOpenURLs:urls openInPlace:false];
|
||||||
NSMutableArray<NSString *> *skippedBasenames = [NSMutableArray array];
|
|
||||||
|
|
||||||
for (NSURL *url in urls) {
|
|
||||||
if ([@[@"gb", @"gbc", @"isx"] containsObject:url.pathExtension.lowercaseString]) {
|
|
||||||
[validURLs addObject:url];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[skippedBasenames addObject:url.lastPathComponent];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skippedBasenames.count) {
|
|
||||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Unsupported Files"
|
|
||||||
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) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:false forKey:@"GBShownUTIWarning"]; // Somebody might need a reminder
|
|
||||||
}]];
|
|
||||||
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:true completion:nil];
|
|
||||||
urls = validURLs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (urls.count == 1) {
|
|
||||||
NSURL *url = urls.firstObject;
|
|
||||||
NSString *potentialROM = [[url.path stringByDeletingLastPathComponent] lastPathComponent];
|
|
||||||
if ([[[GBROMManager sharedManager] romFileForROM:potentialROM].stringByStandardizingPath isEqualToString:url.path.stringByStandardizingPath]) {
|
|
||||||
[GBROMManager sharedManager].currentROM = potentialROM;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[url startAccessingSecurityScopedResource];
|
|
||||||
[GBROMManager sharedManager].currentROM =
|
|
||||||
[[GBROMManager sharedManager] importROM:url.path
|
|
||||||
keepOriginal:true];
|
|
||||||
[url stopAccessingSecurityScopedResource];
|
|
||||||
}
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (NSURL *url in urls) {
|
|
||||||
NSString *potentialROM = [[url.path stringByDeletingLastPathComponent] lastPathComponent];
|
|
||||||
if ([[[GBROMManager sharedManager] romFileForROM:potentialROM].stringByStandardizingPath isEqualToString:url.path.stringByStandardizingPath]) {
|
|
||||||
// That's an already imported ROM
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
[url startAccessingSecurityScopedResource];
|
|
||||||
[[GBROMManager sharedManager] importROM:url.path
|
|
||||||
keepOriginal:true];
|
|
||||||
[url stopAccessingSecurityScopedResource];
|
|
||||||
}
|
|
||||||
[(GBViewController *)[UIApplication sharedApplication].keyWindow.rootViewController openLibrary];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIModalPresentationStyle)modalPresentationStyle
|
- (UIModalPresentationStyle)modalPresentationStyle
|
||||||
|
@@ -24,5 +24,6 @@ typedef enum {
|
|||||||
- (void)showAbout;
|
- (void)showAbout;
|
||||||
- (void)saveStateToFile:(NSString *)file;
|
- (void)saveStateToFile:(NSString *)file;
|
||||||
- (bool)loadStateFromFile:(NSString *)file;
|
- (bool)loadStateFromFile:(NSString *)file;
|
||||||
|
- (bool)handleOpenURLs:(NSArray <NSURL *> *)urls openInPlace:(bool)inPlace;
|
||||||
@property (nonatomic) GBRunMode runMode;
|
@property (nonatomic) GBRunMode runMode;
|
||||||
@end
|
@end
|
||||||
|
@@ -13,6 +13,8 @@
|
|||||||
#import "GBSettingsViewController.h"
|
#import "GBSettingsViewController.h"
|
||||||
#import "GBStatesViewController.h"
|
#import "GBStatesViewController.h"
|
||||||
#import "GCExtendedGamepad+AllElements.h"
|
#import "GCExtendedGamepad+AllElements.h"
|
||||||
|
#import "GBZipReader.h"
|
||||||
|
#import <sys/stat.h>
|
||||||
#import <CoreMotion/CoreMotion.h>
|
#import <CoreMotion/CoreMotion.h>
|
||||||
#import <dlfcn.h>
|
#import <dlfcn.h>
|
||||||
|
|
||||||
@@ -1147,23 +1149,117 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||||||
GB_set_palette(&_gb, &_palette);
|
GB_set_palette(&_gb, &_palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
- (bool)handleOpenURLs:(NSArray <NSURL *> *)urls openInPlace:(bool)inPlace
|
||||||
{
|
{
|
||||||
[self stop];
|
NSMutableArray<NSURL *> *validURLs = [NSMutableArray array];
|
||||||
NSString *potentialROM = [[url.path stringByDeletingLastPathComponent] lastPathComponent];
|
NSMutableArray<NSString *> *skippedBasenames = [NSMutableArray array];
|
||||||
if ([[[GBROMManager sharedManager] romFileForROM:potentialROM].stringByStandardizingPath isEqualToString:url.path.stringByStandardizingPath]) {
|
NSMutableArray<NSString *> *unusedZips = [NSMutableArray array];
|
||||||
[GBROMManager sharedManager].currentROM = potentialROM;
|
NSString *tempDir = NSTemporaryDirectory();
|
||||||
|
|
||||||
|
__block unsigned tempIndex = 0;
|
||||||
|
for (NSURL *url in urls) {
|
||||||
|
if ([url.pathExtension.lowercaseString isEqualToString:@"zip"]) {
|
||||||
|
GBZipReader *reader = [[GBZipReader alloc] initWithPath:url.path];
|
||||||
|
if (!reader) {
|
||||||
|
[skippedBasenames addObject:url.lastPathComponent];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
__block bool used = false;
|
||||||
|
[reader iterateFiles:^bool(NSString *filename, size_t uncompressedSize, bool (^getData)(void *), bool (^writeToPath)(NSString *)) {
|
||||||
|
if ([@[@"gb", @"gbc", @"isx"] containsObject:filename.pathExtension.lowercaseString] && uncompressedSize <= 32 * 1024 * 1024) {
|
||||||
|
tempIndex++;
|
||||||
|
NSString *subDir = [tempDir stringByAppendingFormat:@"/%u", tempIndex];
|
||||||
|
mkdir(subDir.UTF8String, 0755);
|
||||||
|
NSString *dest = [subDir stringByAppendingPathComponent:filename.lastPathComponent];
|
||||||
|
if (writeToPath(dest)) {
|
||||||
|
[validURLs addObject:[NSURL fileURLWithPath:dest]];
|
||||||
|
used = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}];
|
||||||
|
if (!used) {
|
||||||
|
[unusedZips addObject:url.lastPathComponent];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ([@[@"gb", @"gbc", @"isx"] containsObject:url.pathExtension.lowercaseString]) {
|
||||||
|
[validURLs addObject:url];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[skippedBasenames addObject:url.lastPathComponent];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
[url startAccessingSecurityScopedResource];
|
if (unusedZips.count) {
|
||||||
[GBROMManager sharedManager].currentROM =
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"No ROMs in archive"
|
||||||
|
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];
|
||||||
|
[self presentViewController:alert animated:true completion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skippedBasenames.count) {
|
||||||
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Unsupported Files"
|
||||||
|
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) {
|
||||||
|
[[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];
|
||||||
|
if ([[[GBROMManager sharedManager] romFileForROM:potentialROM].stringByStandardizingPath isEqualToString:url.path.stringByStandardizingPath]) {
|
||||||
|
[GBROMManager sharedManager].currentROM = potentialROM;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[url startAccessingSecurityScopedResource];
|
||||||
|
[GBROMManager sharedManager].currentROM =
|
||||||
[[GBROMManager sharedManager] importROM:url.path
|
[[GBROMManager sharedManager] importROM:url.path
|
||||||
keepOriginal:[options[UIApplicationOpenURLOptionsOpenInPlaceKey] boolValue]];
|
keepOriginal:![url.path hasPrefix:tempDir] && !inPlace];
|
||||||
|
[url stopAccessingSecurityScopedResource];
|
||||||
|
}
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBROMChanged" object:nil];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (NSURL *url in validURLs) {
|
||||||
|
NSString *potentialROM = [[url.path stringByDeletingLastPathComponent] lastPathComponent];
|
||||||
|
if ([[[GBROMManager sharedManager] romFileForROM:potentialROM].stringByStandardizingPath isEqualToString:url.path.stringByStandardizingPath]) {
|
||||||
|
// That's an already imported ROM
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
[url startAccessingSecurityScopedResource];
|
||||||
|
[[GBROMManager sharedManager] importROM:url.path
|
||||||
|
keepOriginal:![url.path hasPrefix:tempDir] && !inPlace];
|
||||||
[url stopAccessingSecurityScopedResource];
|
[url stopAccessingSecurityScopedResource];
|
||||||
}
|
}
|
||||||
[self loadROM];
|
[self stop];
|
||||||
[self start];
|
[self openLibrary];
|
||||||
return [GBROMManager sharedManager].currentROM != nil;
|
|
||||||
|
return validURLs.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||||
|
{
|
||||||
|
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]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setRunMode:(GBRunMode)runMode ignoreDynamicSpeed:(bool)ignoreDynamicSpeed
|
- (void)setRunMode:(GBRunMode)runMode ignoreDynamicSpeed:(bool)ignoreDynamicSpeed
|
||||||
|
7
iOS/GBZipReader.h
Normal file
7
iOS/GBZipReader.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface GBZipReader : NSObject
|
||||||
|
- (instancetype)initWithBuffer:(const void *)buffer size:(size_t)size free:(void (^)(void))callback;
|
||||||
|
- (instancetype)initWithPath:(NSString *)path;
|
||||||
|
- (void)iterateFiles:(bool (^)(NSString *filename, size_t uncompressedSize, bool (^getData)(void *), bool (^writeToPath)(NSString *)))callback;
|
||||||
|
@end
|
179
iOS/GBZipReader.m
Normal file
179
iOS/GBZipReader.m
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#import "GBZipReader.h"
|
||||||
|
#import <compression.h>
|
||||||
|
#import <sys/mman.h>
|
||||||
|
#import <mach/vm_region.h>
|
||||||
|
#pragma clang diagnostic ignored "-Wimplicit-retain-self"
|
||||||
|
|
||||||
|
@implementation GBZipReader
|
||||||
|
{
|
||||||
|
void (^_freeCallback)(void);
|
||||||
|
const void *_buffer;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
const struct __attribute__((packed)) {
|
||||||
|
uint32_t magic;
|
||||||
|
uint8_t skip[6];
|
||||||
|
uint16_t fileCount;
|
||||||
|
uint32_t cdSize;
|
||||||
|
uint32_t cdOffset;
|
||||||
|
uint16_t commentSize;
|
||||||
|
} *_eocd;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithBuffer:(const void *)buffer size:(size_t)size free:(void (^)(void))callback
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (!self) return nil;
|
||||||
|
|
||||||
|
_buffer = buffer;
|
||||||
|
_size = size;
|
||||||
|
_freeCallback = callback;
|
||||||
|
|
||||||
|
if (_size < sizeof(*_eocd)) return nil;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 0x10000; i++) {
|
||||||
|
_eocd = (void *)((uint8_t *)buffer + size - sizeof(*_eocd) - i);
|
||||||
|
if ((void *)_eocd < buffer) return nil;
|
||||||
|
if (_eocd->magic == htonl('PK\05\06')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithPath:(NSString *)path
|
||||||
|
{
|
||||||
|
int fd = open(path.UTF8String, O_RDONLY);
|
||||||
|
if (fd < 0) return nil;
|
||||||
|
size_t size = lseek(fd, 0, SEEK_END);
|
||||||
|
size_t alignedSize = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||||
|
void *mapping = mmap(NULL, alignedSize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
|
||||||
|
close(fd);
|
||||||
|
if (!mapping) return nil;
|
||||||
|
|
||||||
|
return [self initWithBuffer:mapping size:size free:^{
|
||||||
|
munmap(mapping, alignedSize);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)iterateFiles:(bool (^)(NSString *filename, size_t uncompressedSize, bool (^getData)(void *), bool (^writeToPath)(NSString *)))callback {
|
||||||
|
const struct __attribute__((packed)) {
|
||||||
|
uint32_t magic;
|
||||||
|
uint8_t skip[6];
|
||||||
|
uint16_t compressionMethod;
|
||||||
|
uint8_t skip2[8];
|
||||||
|
uint32_t compressedSize;
|
||||||
|
uint32_t uncompressedSize;
|
||||||
|
uint16_t nameLength;
|
||||||
|
uint16_t extraLength;
|
||||||
|
uint16_t commentLength;
|
||||||
|
uint8_t skip3[8];
|
||||||
|
uint32_t localHeaderOffset;
|
||||||
|
char name[0];
|
||||||
|
} *entry = (void *)((uint8_t *)_buffer + _eocd->cdOffset);
|
||||||
|
for (unsigned i = _eocd->fileCount; i--;) {
|
||||||
|
if ((uint8_t *)entry + sizeof(*entry) - (uint8_t *)_buffer >= _size) return;
|
||||||
|
if (entry->magic != htonl('PK\01\02')) return;
|
||||||
|
|
||||||
|
typeof(entry) next = (void *)((uint8_t *)entry + sizeof(*entry) +
|
||||||
|
entry->nameLength + entry->extraLength + entry->commentLength);
|
||||||
|
if ((uint8_t *)next - (uint8_t *)_buffer >= _size) return;
|
||||||
|
|
||||||
|
|
||||||
|
bool (^getData)(void *) = ^bool(void *output) {
|
||||||
|
// Directory, no data
|
||||||
|
if (entry->name[entry->nameLength - 1] == '/') return false;
|
||||||
|
|
||||||
|
if (entry->uncompressedSize == 0xffffffff || entry->compressedSize == 0xffffffff) {
|
||||||
|
// ZIP64
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct __attribute__((packed)) {
|
||||||
|
uint32_t magic;
|
||||||
|
uint8_t skip[4];
|
||||||
|
uint16_t compressionMethod;
|
||||||
|
uint8_t skip2[8];
|
||||||
|
uint32_t compressedSize;
|
||||||
|
uint32_t uncompressedSize;
|
||||||
|
uint16_t nameLength;
|
||||||
|
uint16_t extraLength;
|
||||||
|
char name[0];
|
||||||
|
} *localEntry = (void *)((uint8_t *)_buffer + entry->localHeaderOffset);
|
||||||
|
|
||||||
|
if ((uint8_t *)localEntry + sizeof(*localEntry) - (uint8_t *)_buffer >= _size) return nil;
|
||||||
|
if ((uint8_t *)localEntry + sizeof(*localEntry) +
|
||||||
|
localEntry->nameLength + localEntry->extraLength +
|
||||||
|
entry->compressedSize - (uint8_t *)_buffer >= _size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localEntry->magic != htonl('PK\03\04')) return nil;
|
||||||
|
if (entry->uncompressedSize != localEntry->uncompressedSize) return nil;
|
||||||
|
|
||||||
|
const void *dataStart = &localEntry->name[localEntry->nameLength + localEntry->extraLength];
|
||||||
|
if (localEntry->compressionMethod == 0) {
|
||||||
|
if (localEntry->uncompressedSize != entry->compressedSize) return false;
|
||||||
|
memcpy(output, dataStart, localEntry->uncompressedSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (localEntry->compressionMethod != 8) {
|
||||||
|
// Unsupported compression
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (compression_decode_buffer(output, localEntry->uncompressedSize,
|
||||||
|
dataStart, entry->compressedSize,
|
||||||
|
NULL, COMPRESSION_ZLIB) != localEntry->uncompressedSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool (^writeToPath)(NSString *) = ^bool(NSString *path) {
|
||||||
|
int fd = open(path.UTF8String, O_CREAT | O_RDWR, 0644);
|
||||||
|
if (!fd) return false;
|
||||||
|
if (ftruncate(fd, entry->uncompressedSize) != 0) {
|
||||||
|
close(fd);
|
||||||
|
unlink(path.UTF8String);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t alignedSize = (entry->uncompressedSize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
||||||
|
void *mapping = mmap(NULL, alignedSize, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
|
||||||
|
close(fd);
|
||||||
|
if (!mapping) {
|
||||||
|
unlink(path.UTF8String);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = getData(mapping);
|
||||||
|
if (!ret) {
|
||||||
|
unlink(path.UTF8String);
|
||||||
|
}
|
||||||
|
munmap(mapping, alignedSize);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (!callback([[NSString alloc] initWithBytes:entry->name
|
||||||
|
length:entry->nameLength
|
||||||
|
encoding:NSUTF8StringEncoding],
|
||||||
|
entry->uncompressedSize,
|
||||||
|
getData,
|
||||||
|
writeToPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
if (_freeCallback) {
|
||||||
|
_freeCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@@ -114,6 +114,14 @@
|
|||||||
<key>LSHandlerRank</key>
|
<key>LSHandlerRank</key>
|
||||||
<string>Owner</string>
|
<string>Owner</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>zip</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.pkware.zip-archive</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
<array>
|
<array>
|
||||||
@@ -174,24 +182,24 @@
|
|||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
<integer>2</integer>
|
<integer>2</integer>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array>
|
<array>
|
||||||
<string>arm64</string>
|
<string>arm64</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIRequiresFullScreen</key>
|
<key>UIRequiresFullScreen</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>GCSupportedGameControllers</key>
|
<key>GCSupportedGameControllers</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>ProfileName</key>
|
<key>ProfileName</key>
|
||||||
<string>ExtendedGamepad</string>
|
<string>ExtendedGamepad</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>GCSupportsControllerUserInteraction</key>
|
<key>GCSupportsControllerUserInteraction</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
Reference in New Issue
Block a user