mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
1 parent
e63c209fa7
commit
e0a5a3e2bf
11 files changed
+170
-45
No files matched your search
@@ -2,6 +2,8 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
@protocol PPSSPPViewController;
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
@@ -11,5 +13,8 @@
|
||||
@property (nonatomic, strong) NSDictionary *launchOptions;
|
||||
|
||||
- (BOOL)launchPPSSPP:(int)argc argv:(char**)argv;
|
||||
- (void)processFilePath:(NSString *)path;
|
||||
|
||||
@end
|
||||
|
||||
void copyDeepLinkForPath(std::string_view filePath);
|
||||
+65
-2
@@ -5,6 +5,7 @@
|
||||
#import "Common/System/NativeApp.h"
|
||||
#import "Core/System.h"
|
||||
#import "Core/Config.h"
|
||||
#import "Core/Util/PathUtil.h"
|
||||
#import "Common/Log.h"
|
||||
#import "IAPManager.h"
|
||||
|
||||
@@ -107,9 +108,48 @@ __attribute__((used)) static Class _forceLinkSceneDelegate = [SceneDelegate clas
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
}
|
||||
|
||||
// In AppDelegate.mm
|
||||
- (void)processFilePath:(NSString *)path {
|
||||
// Convert NSString to std::string
|
||||
std::string cppPath([path UTF8String]);
|
||||
|
||||
// Try to fixup the path, the app directory changes on every bootup.
|
||||
Path gamePath(cppPath);
|
||||
TryUpdateSavedPath(&gamePath);
|
||||
|
||||
// Call the C++ function to handle the file
|
||||
System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, gamePath.ToString());
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
|
||||
// 1. Check the scheme
|
||||
if (![[url scheme] isEqualToString:@"ppsspp"]) {
|
||||
NSLog(@"PPSSPPUIApplication.openURL called with invalid URL: %@", url);
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSLog(@"PPSSPPUIApplication.openURL called with valid URL: %@", url);
|
||||
// 2. Parse the URL (using NSURLComponents is safer than manual string parsing)
|
||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
NSArray *queryItems = [components queryItems];
|
||||
NSLog(@"PPSSPPUIApplication.openURL query items: %@", queryItems);
|
||||
for (NSURLQueryItem *item in queryItems) {
|
||||
if ([item.name isEqualToString:@"path"]) {
|
||||
NSString *receivedPath = item.value;
|
||||
[self processFilePath:receivedPath];
|
||||
} else {
|
||||
NSLog(@"PPSSPPUIApplication.openURL: Unrecognized query item: %@=%@", item.name, item.value);
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
|
||||
switch (g_Config.iScreenRotation) {
|
||||
case ROTATION_LOCKED_HORIZONTAL:
|
||||
switch (g_Config.iScreenRotation) {
|
||||
case ROTATION_LOCKED_HORIZONTAL:
|
||||
return UIInterfaceOrientationMaskLandscapeRight;
|
||||
case ROTATION_LOCKED_VERTICAL:
|
||||
case ROTATION_LOCKED_VERTICAL180:
|
||||
@@ -132,3 +172,26 @@ __attribute__((used)) static Class _forceLinkSceneDelegate = [SceneDelegate clas
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void copyDeepLinkForPath(std::string_view filePath) {
|
||||
// 1. Convert std::string_view to NSString
|
||||
// We use the length and data to avoid issues with null terminators
|
||||
NSString *pathString = [[NSString alloc] initWithBytes:filePath.data()
|
||||
length:filePath.length()
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
// 2. Percent-encode the path
|
||||
// This handles spaces, slashes, and special characters for the URL query
|
||||
NSCharacterSet *allowedChars = [NSCharacterSet URLQueryAllowedCharacterSet];
|
||||
NSString *encodedPath = [pathString stringByAddingPercentEncodingWithAllowedCharacters:allowedChars];
|
||||
|
||||
// 3. Construct the full URL
|
||||
// Format: scheme://host?query
|
||||
NSString *deepLink = [NSString stringWithFormat:@"ppsspp://open?path=%@", encodedPath];
|
||||
|
||||
// 4. Copy to the iOS System Clipboard (UIPasteboard)
|
||||
[UIPasteboard generalPasteboard].string = deepLink;
|
||||
|
||||
// Optional: Log it for debugging
|
||||
NSLog(@"Deep link copied: %@", deepLink);
|
||||
}
|
||||
@@ -40,6 +40,17 @@
|
||||
<string>AppIcon</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>org.ppsspp.ppsspp-free</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>ppsspp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
|
||||
+31
-31
@@ -70,48 +70,48 @@
|
||||
#if !PPSSPP_PLATFORM(IOS_APP_STORE)
|
||||
|
||||
- (void)decodeKeyEvent:(NSInteger *)eventMem {
|
||||
NSInteger eventType = eventMem[GSEVENT_TYPE];
|
||||
NSInteger eventScanCode = eventMem[GSEVENTKEY_KEYCODE];
|
||||
NSInteger eventType = eventMem[GSEVENT_TYPE];
|
||||
NSInteger eventScanCode = eventMem[GSEVENTKEY_KEYCODE];
|
||||
|
||||
//NSLog(@"Got key: %d", (int)eventScanCode);
|
||||
//NSLog(@"Got key: %d", (int)eventScanCode);
|
||||
|
||||
if (eventType == GSEVENT_TYPE_KEYUP) {
|
||||
struct KeyInput key;
|
||||
key.flags = KeyInputFlags::UP;
|
||||
key.keyCode = getSmartKeyboardMap((int)eventScanCode);
|
||||
key.deviceId = DEVICE_ID_KEYBOARD;
|
||||
NativeKey(key);
|
||||
} else if (GSEVENT_TYPE_KEYDOWN) {
|
||||
struct KeyInput key;
|
||||
key.flags = KeyInputFlags::DOWN;
|
||||
key.keyCode = getSmartKeyboardMap((int)eventScanCode);
|
||||
key.deviceId = DEVICE_ID_KEYBOARD;
|
||||
NativeKey(key);
|
||||
}
|
||||
if (eventType == GSEVENT_TYPE_KEYUP) {
|
||||
struct KeyInput key;
|
||||
key.flags = KeyInputFlags::UP;
|
||||
key.keyCode = getSmartKeyboardMap((int)eventScanCode);
|
||||
key.deviceId = DEVICE_ID_KEYBOARD;
|
||||
NativeKey(key);
|
||||
} else if (GSEVENT_TYPE_KEYDOWN) {
|
||||
struct KeyInput key;
|
||||
key.flags = KeyInputFlags::DOWN;
|
||||
key.keyCode = getSmartKeyboardMap((int)eventScanCode);
|
||||
key.deviceId = DEVICE_ID_KEYBOARD;
|
||||
NativeKey(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)handleKeyUIEvent:(UIEvent *) event {
|
||||
if ([event respondsToSelector:@selector(_gsEvent)]) {
|
||||
NSInteger *eventMem;
|
||||
if ([event respondsToSelector:@selector(_gsEvent)]) {
|
||||
NSInteger *eventMem;
|
||||
|
||||
eventMem = (NSInteger *) (__bridge void*)[event performSelector:@selector(_gsEvent)];
|
||||
if (eventMem) {
|
||||
[self decodeKeyEvent:eventMem];
|
||||
}
|
||||
}
|
||||
eventMem = (NSInteger *) (__bridge void*)[event performSelector:@selector(_gsEvent)];
|
||||
if (eventMem) {
|
||||
[self decodeKeyEvent:eventMem];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendEvent:(UIEvent *)event {
|
||||
[super sendEvent:event];
|
||||
if ([event respondsToSelector:@selector(_gsEvent)]) {
|
||||
NSInteger *eventMem;
|
||||
[super sendEvent:event];
|
||||
if ([event respondsToSelector:@selector(_gsEvent)]) {
|
||||
NSInteger *eventMem;
|
||||
|
||||
eventMem = (NSInteger *) (__bridge void*)[event performSelector:@selector(_gsEvent)];
|
||||
if (eventMem) {
|
||||
[self decodeKeyEvent:eventMem];
|
||||
}
|
||||
}
|
||||
eventMem = (NSInteger *) (__bridge void*)[event performSelector:@selector(_gsEvent)];
|
||||
if (eventMem) {
|
||||
[self decodeKeyEvent:eventMem];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !IOS_APP_STORE
|
||||
|
||||
@@ -95,6 +95,24 @@
|
||||
[sharedViewController didBecomeActive];
|
||||
}
|
||||
|
||||
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
|
||||
for (UIOpenURLContext *context in URLContexts) {
|
||||
NSLog(@"SceneDelegate: openURLContexts called with URL: %@", context.URL);
|
||||
NSURL *url = context.URL;
|
||||
if (![[url scheme] isEqualToString:@"ppsspp"]) {
|
||||
NSLog(@"SceneDelegate: ignoring URL with unknown scheme: %@", url);
|
||||
continue;
|
||||
}
|
||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
for (NSURLQueryItem *item in components.queryItems) {
|
||||
if ([item.name isEqualToString:@"path"]) {
|
||||
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
|
||||
[appDelegate processFilePath:item.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sceneWillResignActive:(UIScene *)scene {
|
||||
INFO_LOG(Log::G3D, "sceneWillResignActive");
|
||||
|
||||
|
||||
+13
-11
@@ -83,7 +83,7 @@ static void *exception_handler(void *argument) {
|
||||
static NSDictionary *parse_entitlements(const void *entitlements, size_t length) {
|
||||
char *copy = (char *)malloc(length);
|
||||
memcpy(copy, entitlements, length);
|
||||
|
||||
|
||||
// strip out psychic paper entitlement hiding
|
||||
if (@available(iOS 13.5, *)) {
|
||||
} else {
|
||||
@@ -95,7 +95,7 @@ static NSDictionary *parse_entitlements(const void *entitlements, size_t length)
|
||||
}
|
||||
NSData *data = [NSData dataWithBytes:copy length:length];
|
||||
free(copy);
|
||||
|
||||
|
||||
return [NSPropertyListSerialization propertyListWithData:data
|
||||
options:NSPropertyListImmutable
|
||||
format:nil
|
||||
@@ -104,7 +104,7 @@ static NSDictionary *parse_entitlements(const void *entitlements, size_t length)
|
||||
|
||||
static NSDictionary *app_entitlements(void) {
|
||||
// Inspired by codesign.c in Darwin sources for Security.framework
|
||||
|
||||
|
||||
// Find our mach-o header
|
||||
Dl_info dl_info;
|
||||
if (dladdr((const void *)app_entitlements, &dl_info) == 0)
|
||||
@@ -115,7 +115,7 @@ static NSDictionary *app_entitlements(void) {
|
||||
struct mach_header_64 *header = (struct mach_header_64 *)dl_info.dli_fbase;
|
||||
if (header->magic != MH_MAGIC_64)
|
||||
return nil;
|
||||
|
||||
|
||||
// Simulator executables have fake entitlements in the code signature. The real entitlements can be found in an __entitlements section.
|
||||
size_t entitlements_size;
|
||||
uint8_t *entitlements_data = getsectiondata(header, "__TEXT", "__entitlements", &entitlements_size);
|
||||
@@ -128,7 +128,7 @@ static NSDictionary *app_entitlements(void) {
|
||||
format:nil
|
||||
error:nil];
|
||||
}
|
||||
|
||||
|
||||
// Find the LC_CODE_SIGNATURE
|
||||
struct load_command *lc = (struct load_command *) (base + sizeof(*header));
|
||||
struct linkedit_data_command *cs_lc = NULL;
|
||||
@@ -152,7 +152,7 @@ static NSDictionary *app_entitlements(void) {
|
||||
const struct cs_superblob *cs = (const struct cs_superblob *)csData.bytes;
|
||||
if (ntohl(cs->magic) != 0xfade0cc0)
|
||||
return nil;
|
||||
|
||||
|
||||
// Find the entitlements in the code signature
|
||||
for (uint32_t i = 0; i < ntohl(cs->count); i++) {
|
||||
struct cs_entitlements *ents = (struct cs_entitlements *) ((char *) cs + ntohl(cs->index[i].offset));
|
||||
@@ -180,7 +180,7 @@ static char *childArgv[] = {NULL, "debugme", NULL};
|
||||
|
||||
bool jb_spawn_ptrace_child(int argc, char **argv) {
|
||||
int ret; pid_t pid;
|
||||
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], childArgv[1]) == 0) {
|
||||
ret = ptrace(PT_TRACE_ME, 0, NULL, 0);
|
||||
NSLog(@"child: ptrace(PT_TRACE_ME) %d", ret);
|
||||
@@ -250,7 +250,7 @@ static bool jb_has_debugger_attached(void) {
|
||||
|
||||
bool jb_enable_ptrace_hack(void) {
|
||||
bool debugged = jb_has_debugger_attached();
|
||||
|
||||
|
||||
// Thanks to this comment: https://news.ycombinator.com/item?id=18431524
|
||||
// We use this hack to allow mmap with PROT_EXEC (which usually requires the
|
||||
// dynamic-codesigning entitlement) by tricking the process into thinking
|
||||
@@ -259,7 +259,7 @@ bool jb_enable_ptrace_hack(void) {
|
||||
if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ptracing ourselves confuses the kernel and will cause bad things to
|
||||
// happen to the system (hangs…) if an exception or signal occurs. Setup
|
||||
// some "safety nets" so we can cause the process to exit in a somewhat sane
|
||||
@@ -269,7 +269,7 @@ bool jb_enable_ptrace_hack(void) {
|
||||
if (!debugged) {
|
||||
// First, ensure that signals are delivered as Mach software exceptions…
|
||||
ptrace(PT_SIGEXC, 0, NULL, 0);
|
||||
|
||||
|
||||
// …then ensure that this exception goes through our exception handler.
|
||||
// I think it's OK to just watch for EXC_SOFTWARE because the other
|
||||
// exceptions (e.g. EXC_BAD_ACCESS, EXC_BAD_INSTRUCTION, and friends)
|
||||
@@ -282,7 +282,7 @@ bool jb_enable_ptrace_hack(void) {
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, exception_handler, (void *)&port);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -358,6 +358,8 @@ float System_GetPropertyFloat(SystemProperty prop) {
|
||||
|
||||
bool System_GetPropertyBool(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
case SYSPROP_HAS_DEEP_LINKS:
|
||||
return true;
|
||||
case SYSPROP_DISPLAY_HAS_CAMERA_CUTOUT:
|
||||
return true;
|
||||
case SYSPROP_HAS_FILE_BROWSER:
|
||||
|
||||
Reference in new issue
Block a user