mirror of
https://github.com/libretro/RetroArch
synced 2025-03-03 04:14:00 +00:00
iCloud cloud sync driver (#16794)
This commit is contained in:
parent
39a2cecdc5
commit
63799385fc
@ -1349,6 +1349,18 @@ const char *config_get_default_led(void)
|
||||
return "null";
|
||||
}
|
||||
|
||||
/**
|
||||
* config_get_default_cloudsync:
|
||||
*
|
||||
* Gets default cloud sync driver.
|
||||
*
|
||||
* Returns: Default cloud sync driver.
|
||||
**/
|
||||
const char *config_get_default_cloudsync(void)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
/**
|
||||
* config_get_default_location:
|
||||
*
|
||||
@ -2706,6 +2718,7 @@ void config_set_defaults(void *data)
|
||||
const char *def_bluetooth = config_get_default_bluetooth();
|
||||
const char *def_wifi = config_get_default_wifi();
|
||||
const char *def_led = config_get_default_led();
|
||||
const char *def_cloudsync = config_get_default_cloudsync();
|
||||
const char *def_location = config_get_default_location();
|
||||
const char *def_record = config_get_default_record();
|
||||
const char *def_midi = config_get_default_midi();
|
||||
@ -2788,6 +2801,10 @@ void config_set_defaults(void *data)
|
||||
configuration_set_string(settings,
|
||||
settings->arrays.led_driver,
|
||||
def_led);
|
||||
if (def_cloudsync)
|
||||
configuration_set_string(settings,
|
||||
settings->arrays.cloud_sync_driver,
|
||||
def_cloudsync);
|
||||
if (def_location)
|
||||
configuration_set_string(settings,
|
||||
settings->arrays.location_driver,
|
||||
|
@ -71,3 +71,7 @@
|
||||
#if defined(HAVE_NETWORKING) && defined(HAVE_NETPLAYDISCOVERY) && defined(HAVE_NETPLAYDISCOVERY_NSNET)
|
||||
#import "../network/netplay/netplay_nsnetservice.m"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CLOUDSYNC) && defined(HAVE_ICLOUD)
|
||||
#include "../network/cloud_sync/icloud.m"
|
||||
#endif
|
||||
|
@ -10704,20 +10704,25 @@ unsigned menu_displaylist_build_list(
|
||||
break;
|
||||
case DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST:
|
||||
{
|
||||
menu_displaylist_build_info_t build_list[] = {
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE, PARSE_ONLY_BOOL },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SAVES, PARSE_ONLY_BOOL },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_CONFIGS, PARSE_ONLY_BOOL },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER, PARSE_ONLY_STRING_OPTIONS },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_URL, PARSE_ONLY_STRING },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME, PARSE_ONLY_STRING },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, PARSE_ONLY_STRING },
|
||||
menu_displaylist_build_info_selective_t build_list[] = {
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SAVES, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_CONFIGS, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER, PARSE_ONLY_STRING_OPTIONS, true},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_URL, PARSE_ONLY_STRING, false},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME, PARSE_ONLY_STRING, false},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, PARSE_ONLY_STRING, false},
|
||||
};
|
||||
|
||||
if (string_is_equal(settings->arrays.cloud_sync_driver, "webdav"))
|
||||
for (i = 0; i < ARRAY_SIZE(build_list); i++)
|
||||
build_list[i].checked = true;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(build_list); i++)
|
||||
{
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
if (build_list[i].checked &&
|
||||
MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
build_list[i].enum_idx, build_list[i].parse_type,
|
||||
false) == 0)
|
||||
count++;
|
||||
|
@ -9059,6 +9059,16 @@ static void general_write_handler(rarch_setting_t *setting)
|
||||
video_driver_is_threaded());
|
||||
}
|
||||
break;
|
||||
#if HAVE_CLOUDSYNC
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER:
|
||||
{
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
menu_st->flags |= MENU_ST_FLAG_PREVENT_POPULATE
|
||||
| MENU_ST_FLAG_ENTRIES_NEED_REFRESH;
|
||||
task_push_cloud_sync_update_driver();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Special cases */
|
||||
|
||||
|
184
network/cloud_sync/icloud.m
Normal file
184
network/cloud_sync/icloud.m
Normal file
@ -0,0 +1,184 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import <CloudKit/CloudKit.h>
|
||||
|
||||
#include "../cloud_sync_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#define IC_RECORD_TYPE @"cloudsync"
|
||||
|
||||
static bool icloud_sync_begin(cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
[CKContainer.defaultContainer accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
|
||||
BOOL success = (error == nil) && (accountStatus == CKAccountStatusAvailable);
|
||||
cb(user_data, NULL, success, NULL);
|
||||
}];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool icloud_sync_end(cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
cb(user_data, NULL, true, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static CKRecord *icloud_remove_duplicates(NSArray<CKRecord *> *results)
|
||||
{
|
||||
if (!results || ![results count])
|
||||
return nil;
|
||||
if ([results count] == 1)
|
||||
return results[0];
|
||||
|
||||
CKRecord *newest = nil;
|
||||
for (CKRecord *rec in results)
|
||||
{
|
||||
CKRecord *toDelete = rec;
|
||||
if (newest == nil || [newest.modificationDate compare:rec.modificationDate] == NSOrderedAscending)
|
||||
{
|
||||
toDelete = newest;
|
||||
newest = rec;
|
||||
}
|
||||
if (toDelete)
|
||||
{
|
||||
[CKContainer.defaultContainer.privateCloudDatabase deleteRecordWithID:toDelete.recordID
|
||||
completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
|
||||
RARCH_DBG("[iCloud] delete callback for duplicate of %s %s\n", toDelete[@"path"], error == nil ? "succeeded" : "failed");
|
||||
}];
|
||||
}
|
||||
}
|
||||
return newest;
|
||||
}
|
||||
|
||||
static void icloud_query_path(const char *path, void(^cb)(CKRecord * results, NSError * error))
|
||||
{
|
||||
NSPredicate *pred = [NSComparisonPredicate
|
||||
predicateWithLeftExpression:[NSExpression expressionForKeyPath:@"path"]
|
||||
rightExpression:[NSExpression expressionForConstantValue:[NSString stringWithUTF8String:path]]
|
||||
modifier:NSDirectPredicateModifier
|
||||
type:NSEqualToPredicateOperatorType
|
||||
options:0];
|
||||
CKQuery *query = [[CKQuery alloc] initWithRecordType:IC_RECORD_TYPE predicate:pred];
|
||||
[CKContainer.defaultContainer.privateCloudDatabase performQuery:query
|
||||
inZoneWithID:nil
|
||||
completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
|
||||
if (error || ![results count])
|
||||
{
|
||||
RARCH_DBG("[iCloud] could not find %s (%s)\n", path, error == nil ? "successfully" : "failure");
|
||||
if (error)
|
||||
RARCH_DBG("[iCloud] error: %s\n", [[error debugDescription] UTF8String]);
|
||||
cb(nil, nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_DBG("[iCloud] found %d results looking for %s\n", [results count], path);
|
||||
cb(icloud_remove_duplicates(results), nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
static bool icloud_read(const char *path, const char *file, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
icloud_query_path(path, ^(CKRecord *result, NSError *error) {
|
||||
if (result)
|
||||
{
|
||||
[CKContainer.defaultContainer.privateCloudDatabase fetchRecordWithID:result.recordID
|
||||
completionHandler:^(CKRecord * _Nullable fetchedRecord, NSError * _Nullable error) {
|
||||
if (error)
|
||||
{
|
||||
RARCH_DBG("[iCloud] failed to fetch record for %s\n", path);
|
||||
cb(user_data, path, false, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
CKAsset *asset = fetchedRecord[@"data"];
|
||||
NSData *data = [NSFileManager.defaultManager contentsAtPath:asset.fileURL.path];
|
||||
RARCH_DBG("[iCloud] successfully fetched %s, size %d\n", path, [data length]);
|
||||
RFILE *rfile = filestream_open(file,
|
||||
RETRO_VFS_FILE_ACCESS_READ_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (rfile)
|
||||
{
|
||||
filestream_truncate(rfile, 0);
|
||||
filestream_write(rfile, [data bytes], [data length]);
|
||||
filestream_seek(rfile, 0, SEEK_SET);
|
||||
}
|
||||
cb(user_data, path, true, rfile);
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
cb(user_data, path, error == nil, NULL);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool icloud_update(const char *path, RFILE *rfile, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
icloud_query_path(path, ^(CKRecord *record, NSError *error) {
|
||||
bool update = true;
|
||||
if (error || !record)
|
||||
{
|
||||
record = [[CKRecord alloc] initWithRecordType:IC_RECORD_TYPE];
|
||||
record[@"path"] = [NSString stringWithUTF8String:path];
|
||||
update = false;
|
||||
}
|
||||
NSString *fileStr = [NSString stringWithUTF8String:filestream_get_path(rfile)];
|
||||
NSURL *fileURL = [NSURL fileURLWithPath:fileStr];
|
||||
record[@"data"] = [[CKAsset alloc] initWithFileURL:fileURL];
|
||||
[CKContainer.defaultContainer.privateCloudDatabase saveRecord:record completionHandler:^(CKRecord * _Nullable newrecord, NSError * _Nullable error) {
|
||||
RARCH_DBG("[iCloud] %s %s %s\n", error == nil ? "succeeded" : "failed", update ? "updating" : "creating", path);
|
||||
if (error)
|
||||
RARCH_DBG("[iCloud] error: %s\n", [[error debugDescription] UTF8String]);
|
||||
cb(user_data, path, error == nil, rfile);
|
||||
}];
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool icloud_delete(const char *path, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
NSPredicate *pred = [NSComparisonPredicate
|
||||
predicateWithLeftExpression:[NSExpression expressionForKeyPath:@"path"]
|
||||
rightExpression:[NSExpression expressionForConstantValue:[NSString stringWithUTF8String:path]]
|
||||
modifier:NSDirectPredicateModifier
|
||||
type:NSEqualToPredicateOperatorType
|
||||
options:0];
|
||||
CKQuery *query = [[CKQuery alloc] initWithRecordType:IC_RECORD_TYPE predicate:pred];
|
||||
[CKContainer.defaultContainer.privateCloudDatabase performQuery:query
|
||||
inZoneWithID:nil
|
||||
completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
|
||||
RARCH_DBG("[iCloud] deleting %d records for %s\n", [results count], path);
|
||||
for (CKRecord *record in results)
|
||||
{
|
||||
[CKContainer.defaultContainer.privateCloudDatabase deleteRecordWithID:record.recordID
|
||||
completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
|
||||
RARCH_DBG("[iCloud] delete callback for %s %s\n", path, error == nil ? "succeeded" : "failed");
|
||||
if (error)
|
||||
RARCH_DBG("[iCloud] error: %s\n", [[error debugDescription] UTF8String]);
|
||||
}];
|
||||
}
|
||||
cb(user_data, path, error == nil, NULL);
|
||||
}];
|
||||
return true;
|
||||
}
|
||||
|
||||
cloud_sync_driver_t cloud_sync_icloud = {
|
||||
icloud_sync_begin,
|
||||
icloud_sync_end,
|
||||
icloud_read,
|
||||
icloud_update,
|
||||
icloud_delete,
|
||||
"icloud" /* ident */
|
||||
};
|
@ -28,6 +28,9 @@ static cloud_sync_driver_t cloud_sync_null = {
|
||||
|
||||
const cloud_sync_driver_t *cloud_sync_drivers[] = {
|
||||
&cloud_sync_webdav,
|
||||
#ifdef HAVE_ICLOUD
|
||||
&cloud_sync_icloud,
|
||||
#endif
|
||||
&cloud_sync_null,
|
||||
NULL
|
||||
};
|
||||
|
@ -51,6 +51,9 @@ typedef struct
|
||||
cloud_sync_driver_state_t *cloud_sync_state_get_ptr(void);
|
||||
|
||||
extern cloud_sync_driver_t cloud_sync_webdav;
|
||||
#ifdef HAVE_ICLOUD
|
||||
extern cloud_sync_driver_t cloud_sync_icloud;
|
||||
#endif
|
||||
|
||||
extern const cloud_sync_driver_t *cloud_sync_drivers[];
|
||||
|
||||
|
@ -8,3 +8,5 @@
|
||||
INFOPLIST_FILE = $(SRCROOT)/OSX/Info_AppStore.plist
|
||||
DEVELOPMENT_TEAM=UK699V5ZS8
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_APPLE_STORE
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_ICLOUD
|
||||
CODE_SIGN_ENTITLEMENTS = RetroArchAppStore.entitlements
|
||||
|
@ -9,3 +9,5 @@
|
||||
|
||||
#include "Metal.xcconfig"
|
||||
DEVELOPMENT_TEAM=UK699V5ZS8
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_ICLOUD
|
||||
CODE_SIGN_ENTITLEMENTS = RetroArchCI.entitlements
|
||||
|
@ -1653,7 +1653,6 @@
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
|
@ -2,6 +2,16 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.com.libretro.dist.RetroArch</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
<string>CloudKit</string>
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
|
22
pkg/apple/RetroArchCI.entitlements
Normal file
22
pkg/apple/RetroArchCI.entitlements
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.com.libretro.dist.RetroArch</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
<string>CloudKit</string>
|
||||
</array>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@ -65,6 +65,7 @@
|
||||
0720995929B1258C001642BB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84DD5EB41A89E737007336C1 /* IOKit.framework */; };
|
||||
072976DD296284F600D6E00C /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 072976DC296284F600D6E00C /* OpenGL.framework */; };
|
||||
0746953A2997393000CCB7BD /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 074695362995C03900CCB7BD /* GameController.framework */; };
|
||||
075650252C488918004C5E7E /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 075650242C488918004C5E7E /* CloudKit.framework */; };
|
||||
076E640C2BF30A7A00681536 /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0795A8C6299A095300D5035D /* CoreHaptics.framework */; };
|
||||
076E640D2BF30A9200681536 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 074695362995C03900CCB7BD /* GameController.framework */; };
|
||||
076E640E2BF30AA200681536 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 070A883F2A4E7A1B003161C0 /* OpenAL.framework */; };
|
||||
@ -563,6 +564,8 @@
|
||||
0720996029B1258C001642BB /* RetroArch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
072976DC296284F600D6E00C /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
|
||||
074695362995C03900CCB7BD /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
|
||||
075650242C488918004C5E7E /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||
075650262C48B417004C5E7E /* RetroArchCI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RetroArchCI.entitlements; sourceTree = "<group>"; };
|
||||
0776EF3829A005D600AF0237 /* Steam.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Steam.xcconfig; sourceTree = "<group>"; };
|
||||
0790F6782BF282B400AA58C9 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = OSX/Media.xcassets; sourceTree = "<group>"; };
|
||||
0795A8C6299A095300D5035D /* CoreHaptics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreHaptics.framework; path = System/Library/Frameworks/CoreHaptics.framework; sourceTree = SDKROOT; };
|
||||
@ -691,6 +694,7 @@
|
||||
0795A8C7299A095300D5035D /* CoreHaptics.framework in Frameworks */,
|
||||
05A8E23820A63CB40084ABDA /* Metal.framework in Frameworks */,
|
||||
05269A6220ABF20500C29F1E /* MetalKit.framework in Frameworks */,
|
||||
075650252C488918004C5E7E /* CloudKit.framework in Frameworks */,
|
||||
5061C8A41AE47E510080AE14 /* libz.dylib in Frameworks */,
|
||||
070A88402A4E7A1B003161C0 /* OpenAL.framework in Frameworks */,
|
||||
07EF0FF92BEB117000EDCA9B /* MoltenVK.xcframework in Frameworks */,
|
||||
@ -1466,6 +1470,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
07F8037C2BEFE4BD000FD557 /* RetroArchAppStore.entitlements */,
|
||||
075650262C48B417004C5E7E /* RetroArchCI.entitlements */,
|
||||
686201AB24B823A800EBDD95 /* RetroArch.entitlements */,
|
||||
05D7753120A55D2700646447 /* BaseConfig.xcconfig */,
|
||||
05422E5C2140CFC500F09961 /* Metal.xcconfig */,
|
||||
@ -1501,6 +1506,7 @@
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
075650242C488918004C5E7E /* CloudKit.framework */,
|
||||
07EF0FF42BEB114000EDCA9B /* MoltenVK.xcframework */,
|
||||
070A883F2A4E7A1B003161C0 /* OpenAL.framework */,
|
||||
0795A8C6299A095300D5035D /* CoreHaptics.framework */,
|
||||
@ -2022,8 +2028,6 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = RetroArchAppStore.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 44;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.dist.RetroArch;
|
||||
@ -2037,8 +2041,6 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = RetroArchAppStore.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 44;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.dist.RetroArch;
|
||||
@ -2088,8 +2090,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = RetroArch.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEVELOPMENT_TEAM = UK699V5ZS8;
|
||||
@ -2136,7 +2137,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = libretro.RetroArch;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.dist.RetroArch;
|
||||
PRODUCT_NAME = RetroArch;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SDKROOT = macosx;
|
||||
@ -2169,8 +2170,7 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = RetroArch.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = UK699V5ZS8;
|
||||
@ -2218,7 +2218,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
MTL_IGNORE_WARNINGS = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = libretro.RetroArch;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.libretro.dist.RetroArch;
|
||||
PRODUCT_NAME = RetroArch;
|
||||
SDKROOT = macosx;
|
||||
STRIP_STYLE = debugging;
|
||||
|
@ -203,6 +203,7 @@
|
||||
0712A7772B807AE400C9765F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0718BC5F2ABBA807001F2CBE /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS17.0.sdk/System/Library/Frameworks/Network.framework; sourceTree = DEVELOPER_DIR; };
|
||||
073DB2892B8706490001BA32 /* RetroArchTV.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RetroArchTV.entitlements; sourceTree = "<group>"; };
|
||||
076A9EAF2C438E1D00504BDF /* RetroArchiOS.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = RetroArchiOS.entitlements; path = iOS/RetroArchiOS.entitlements; sourceTree = SOURCE_ROOT; };
|
||||
076CA50C2B695C2C00840EA5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS17.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
|
||||
077AB2C82BFB0E28002BBE2F /* AppStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = AppStore.xcconfig; path = iOS/AppStore.xcconfig; sourceTree = "<group>"; };
|
||||
0789FC2E2A07845300D042B7 /* AltKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = AltKit; path = Frameworks/AltKit; sourceTree = "<group>"; };
|
||||
@ -582,6 +583,7 @@
|
||||
9222F20A2315DD3D0097C0FD /* retroarch_logo.png */,
|
||||
83EB675F19EEAF050096F441 /* iOS/modules */,
|
||||
69D31DE31A547EC800EF4C92 /* iOS/Resources/Icons.xcassets */,
|
||||
076A9EAF2C438E1D00504BDF /* RetroArchiOS.entitlements */,
|
||||
);
|
||||
name = iOS;
|
||||
path = Resources;
|
||||
@ -1785,6 +1787,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Default;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "$(IOS_CODE_SIGN_ENTITLEMENTS)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/iOS/Frameworks",
|
||||
@ -1833,6 +1836,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Default;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "$(IOS_CODE_SIGN_ENTITLEMENTS)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/iOS/Frameworks",
|
||||
|
@ -14,11 +14,13 @@ MARKETING_VERSION = 1.18.3
|
||||
CURRENT_PROJECT_VERSION = 12
|
||||
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_APPLE_STORE
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_ICLOUD
|
||||
OTHER_CFLAGS = $(inherited) -DkRetroArchAppGroup=@\"group.com.libretro.dist.RetroArchGroup\"
|
||||
|
||||
IOS_BUNDLE_IDENTIFIER = com.libretro.dist.RetroArch
|
||||
TVOS_BUNDLE_IDENTIFIER = com.libretro.dist.RetroArch
|
||||
|
||||
IOS_CODE_SIGN_ENTITLEMENTS = iOS/RetroArchiOS.entitlements
|
||||
TVOS_CODE_SIGN_ENTITLEMENTS = tvOS/RetroArchTV.entitlements
|
||||
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.2
|
||||
|
16
pkg/apple/iOS/RetroArchiOS.entitlements
Normal file
16
pkg/apple/iOS/RetroArchiOS.entitlements
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.com.libretro.dist.RetroArch</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
<string>CloudKit</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@ -2,6 +2,16 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.com.libretro.dist.RetroArch</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
<string>CloudKit</string>
|
||||
</array>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.libretro.dist.RetroArchGroup</string>
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <lists/dir_list.h>
|
||||
#include <lists/file_list.h>
|
||||
#include <lrc_hash.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <time/rtime.h>
|
||||
@ -68,6 +69,8 @@ typedef struct
|
||||
bool conflicts;
|
||||
} task_cloud_sync_state_t;
|
||||
|
||||
static slock_t *tcs_running_lock = NULL;
|
||||
|
||||
static void task_cloud_sync_begin_handler(void *user_data, const char *path, bool success, RFILE *file)
|
||||
{
|
||||
retro_task_t *task = (retro_task_t *)user_data;
|
||||
@ -79,7 +82,6 @@ static void task_cloud_sync_begin_handler(void *user_data, const char *path, boo
|
||||
if (!(sync_state = (task_cloud_sync_state_t *)task->state))
|
||||
return;
|
||||
|
||||
sync_state->waiting = false;
|
||||
if (success)
|
||||
{
|
||||
RARCH_LOG(CSPFX "begin succeeded\n");
|
||||
@ -91,6 +93,9 @@ static void task_cloud_sync_begin_handler(void *user_data, const char *path, boo
|
||||
task_set_title(task, strdup("Cloud Sync failed"));
|
||||
task_set_finished(task, true);
|
||||
}
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
}
|
||||
|
||||
static bool tcs_object_member_handler(void *ctx, const char *s, size_t len)
|
||||
@ -184,12 +189,14 @@ static void task_cloud_sync_manifest_handler(void *user_data, const char *path,
|
||||
if (!sync_state)
|
||||
return;
|
||||
|
||||
sync_state->waiting = false;
|
||||
if (!success)
|
||||
{
|
||||
RARCH_WARN(CSPFX "server manifest fetch failed\n");
|
||||
sync_state->failures = true;
|
||||
sync_state->phase = CLOUD_SYNC_PHASE_END;
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -201,6 +208,9 @@ static void task_cloud_sync_manifest_handler(void *user_data, const char *path,
|
||||
filestream_close(file);
|
||||
}
|
||||
sync_state->phase = CLOUD_SYNC_PHASE_READ_LOCAL_MANIFEST;
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
}
|
||||
|
||||
static void task_cloud_sync_fetch_server_manifest(task_cloud_sync_state_t *sync_state)
|
||||
@ -479,8 +489,6 @@ static void task_cloud_sync_fetch_cb(void *user_data, const char *path, bool suc
|
||||
if (!sync_state)
|
||||
return;
|
||||
|
||||
sync_state->waiting = false;
|
||||
|
||||
if (success && file)
|
||||
{
|
||||
hash = task_cloud_sync_md5_rfile(file);
|
||||
@ -496,8 +504,11 @@ static void task_cloud_sync_fetch_cb(void *user_data, const char *path, bool suc
|
||||
else
|
||||
RARCH_WARN(CSPFX "failed to write file from server: %s\n", path);
|
||||
sync_state->failures = true;
|
||||
return;
|
||||
}
|
||||
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
}
|
||||
|
||||
static void task_cloud_sync_fetch_server_file(task_cloud_sync_state_t *sync_state)
|
||||
@ -584,8 +595,6 @@ static void task_cloud_sync_upload_cb(void *user_data, const char *path, bool su
|
||||
if (!sync_state)
|
||||
return;
|
||||
|
||||
sync_state->waiting = false;
|
||||
|
||||
if (success)
|
||||
{
|
||||
/* need to update server manifest as well */
|
||||
@ -609,6 +618,10 @@ static void task_cloud_sync_upload_cb(void *user_data, const char *path, bool su
|
||||
RARCH_WARN(CSPFX "uploading %s failed\n", path);
|
||||
sync_state->failures = true;
|
||||
}
|
||||
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
}
|
||||
|
||||
static void task_cloud_sync_upload_current_file(task_cloud_sync_state_t *sync_state)
|
||||
@ -722,8 +735,6 @@ static void task_cloud_sync_delete_cb(void *user_data, const char *path, bool su
|
||||
if (!sync_state)
|
||||
return;
|
||||
|
||||
sync_state->waiting = false;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
/* if the delete fails, resurrect the hash from the last sync */
|
||||
@ -735,6 +746,9 @@ static void task_cloud_sync_delete_cb(void *user_data, const char *path, bool su
|
||||
}
|
||||
RARCH_WARN(CSPFX "deleting %s failed\n", path);
|
||||
sync_state->failures = true;
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -745,6 +759,9 @@ static void task_cloud_sync_delete_cb(void *user_data, const char *path, bool su
|
||||
task_cloud_sync_add_to_updated_manifest(sync_state, path, NULL, true);
|
||||
task_cloud_sync_add_to_updated_manifest(sync_state, path, NULL, false);
|
||||
sync_state->need_manifest_uploaded = true;
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
}
|
||||
|
||||
static void task_cloud_sync_delete_server_file(task_cloud_sync_state_t *sync_state)
|
||||
@ -910,8 +927,10 @@ static void task_cloud_sync_update_manifest_cb(void *user_data, const char *path
|
||||
return;
|
||||
|
||||
RARCH_LOG(CSPFX "uploading updated manifest succeeded\n");
|
||||
sync_state->waiting = false;
|
||||
sync_state->phase = CLOUD_SYNC_PHASE_END;
|
||||
slock_lock(tcs_running_lock);
|
||||
sync_state->waiting = false;
|
||||
slock_unlock(tcs_running_lock);
|
||||
}
|
||||
|
||||
static RFILE *task_cloud_sync_write_updated_manifest(file_list_t *manifest, char *path)
|
||||
@ -1034,11 +1053,14 @@ static void task_cloud_sync_task_handler(retro_task_t *task)
|
||||
if (!(sync_state = (task_cloud_sync_state_t *)task->state))
|
||||
goto task_finished;
|
||||
|
||||
slock_lock(tcs_running_lock);
|
||||
if (sync_state->waiting)
|
||||
{
|
||||
task->when = cpu_features_get_time_usec() + 500 * 1000; /* 500ms */
|
||||
slock_unlock(tcs_running_lock);
|
||||
return;
|
||||
}
|
||||
slock_unlock(tcs_running_lock);
|
||||
|
||||
switch (sync_state->phase)
|
||||
{
|
||||
@ -1126,6 +1148,9 @@ void task_push_cloud_sync(void)
|
||||
if (!settings->bools.cloud_sync_enable)
|
||||
return;
|
||||
|
||||
if (!tcs_running_lock)
|
||||
tcs_running_lock = slock_new();
|
||||
|
||||
find_data.func = task_cloud_sync_task_finder;
|
||||
if (task_queue_find(&find_data))
|
||||
{
|
||||
@ -1154,3 +1179,18 @@ void task_push_cloud_sync(void)
|
||||
|
||||
task_queue_push(task);
|
||||
}
|
||||
|
||||
void task_push_cloud_sync_update_driver(void)
|
||||
{
|
||||
char manifest_path[PATH_MAX_LENGTH];
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
cloud_sync_find_driver(settings, "cloud sync driver", verbosity_is_enabled());
|
||||
|
||||
/* The sync does a three-way diff: current local <- last sync -> current server.
|
||||
* When the server changes it becomes a four way diff, which can lead to odd
|
||||
* conflicts or data loss. The easiest way to resolve it is to reset the last sync
|
||||
*/
|
||||
task_cloud_sync_manifest_filename(manifest_path, sizeof(manifest_path), false);
|
||||
filestream_delete(manifest_path);
|
||||
}
|
||||
|
@ -308,6 +308,7 @@ void menu_explore_wait_for_init_task(void);
|
||||
extern const char* const input_builtin_autoconfs[];
|
||||
|
||||
/* cloud sync tasks */
|
||||
void task_push_cloud_sync_update_driver(void);
|
||||
void task_push_cloud_sync(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
Loading…
x
Reference in New Issue
Block a user