From c7bf80fb72d98ba8454d9d2868acc7a9b7cbf278 Mon Sep 17 00:00:00 2001 From: Russell Parks Date: Mon, 5 May 2025 17:32:27 -0500 Subject: [PATCH 01/12] S3 Implementation --- configuration.c | 7 + configuration.h | 9 + griffin/griffin.c | 3 + intl/msg_hash_lbl.h | 12 + intl/msg_hash_us.h | 24 + menu/cbs/menu_cbs_sublabel.c | 12 + menu/menu_displaylist.c | 58 +- menu/menu_setting.c | 53 + msg_hash.h | 5 +- network/cloud_sync/s3.c | 1328 +++++++++++++++++ network/cloud_sync_driver.c | 3 + network/cloud_sync_driver.h | 3 + .../RetroArch_Metal.xcodeproj/project.pbxproj | 2 + 13 files changed, 1503 insertions(+), 16 deletions(-) create mode 100644 network/cloud_sync/s3.c diff --git a/configuration.c b/configuration.c index 1a9917de6ebe..5d76f2481137 100644 --- a/configuration.c +++ b/configuration.c @@ -1619,10 +1619,17 @@ static struct config_array_setting *populate_settings_array( #ifdef HAVE_NETWORKING SETTING_ARRAY("netplay_mitm_server", settings->arrays.netplay_mitm_server, false, NULL, true); +#ifdef HAVE_CLOUDSYNC SETTING_ARRAY("webdav_url", settings->arrays.webdav_url, false, NULL, true); SETTING_ARRAY("webdav_username", settings->arrays.webdav_username, false, NULL, true); SETTING_ARRAY("webdav_password", settings->arrays.webdav_password, false, NULL, true); SETTING_ARRAY("google_drive_refresh_token", settings->arrays.google_drive_refresh_token, false, NULL, true); +#ifdef HAVE_S3 + SETTING_ARRAY("s3_url", settings->arrays.s3_url, false, NULL, true); + SETTING_ARRAY("access_key_id", settings->arrays.access_key_id, false, NULL, true); + SETTING_ARRAY("secret_access_key", settings->arrays.secret_access_key, false, NULL, true); +#endif +#endif SETTING_ARRAY("youtube_stream_key", settings->arrays.youtube_stream_key, true, NULL, true); SETTING_ARRAY("twitch_stream_key", settings->arrays.twitch_stream_key, true, NULL, true); SETTING_ARRAY("facebook_stream_key", settings->arrays.facebook_stream_key, true, NULL, true); diff --git a/configuration.h b/configuration.h index 3225a2aae2ef..82fbc33d826c 100644 --- a/configuration.h +++ b/configuration.h @@ -541,10 +541,19 @@ typedef struct settings char audio_device[NAME_MAX_LENGTH]; char camera_device[NAME_MAX_LENGTH]; char netplay_mitm_server[NAME_MAX_LENGTH]; +#ifdef HAVE_NETWORKING +#ifdef HAVE_CLOUDSYNC char webdav_url[NAME_MAX_LENGTH]; char webdav_username[NAME_MAX_LENGTH]; char webdav_password[NAME_MAX_LENGTH]; char google_drive_refresh_token[2048]; +#ifdef HAVE_S3 + char s3_url[NAME_MAX_LENGTH]; + char access_key_id[128]; + char secret_access_key[186]; /* TODO/RESEARCH - check size, ex https://github.com/winscp/winscp/pull/15/files */ +#endif +#endif +#endif char crt_switch_timings[NAME_MAX_LENGTH]; char input_reserved_devices[MAX_USERS][NAME_MAX_LENGTH]; diff --git a/griffin/griffin.c b/griffin/griffin.c index 8b84bc0ad3d6..2a3479835e2b 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1721,6 +1721,9 @@ CLOUD SYNC #ifdef HAVE_SMBCLIENT #include "../network/cloud_sync/smb.c" #endif +#ifdef HAVE_S3 +#include "../network/cloud_sync/s3.c" +#endif #endif /*============================================================ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 383fcb3370f3..b3b0b32db9c2 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -3550,6 +3550,18 @@ MSG_HASH( MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, "cloud_sync_password" ) +MSG_HASH( + MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID, + "cloud_sync_access_key_id" + ) +MSG_HASH( + MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, + "cloud_sync_secret_access_key" + ) +MSG_HASH( + MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL, + "cloud_sync_s3_url" + ) MSG_HASH( MENU_ENUM_LABEL_SCAN_DIRECTORY, "scan_directory" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 81cde5d6cec4..ad20e4843531 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1158,6 +1158,30 @@ MSG_HASH( MENU_ENUM_SUBLABEL_CLOUD_SYNC_PASSWORD, "Your password for your cloud storage account." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_ACCESS_KEY_ID, + "Access Key ID" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CLOUD_SYNC_ACCESS_KEY_ID, + "Your access key ID for your cloud storage account." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SECRET_ACCESS_KEY, + "Secret Access Key" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, + "Your secret access key for your cloud storage account." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_S3_URL, + "S3 URL" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CLOUD_SYNC_S3_URL, + "Your S3 endpoint URL for cloud storage." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_LOGGING_SETTINGS, "Logging" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 4904e423d9a1..de2d9d2cdd08 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -270,8 +270,11 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_sync_thumbs, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_sync_system, MENU_ENUM_SUBLABEL_CLOUD_SYNC_SYNC_SYSTEM) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_driver, MENU_ENUM_SUBLABEL_CLOUD_SYNC_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_url, MENU_ENUM_SUBLABEL_CLOUD_SYNC_URL) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_s3_url, MENU_ENUM_SUBLABEL_CLOUD_SYNC_S3_URL) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_username, MENU_ENUM_SUBLABEL_CLOUD_SYNC_USERNAME) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_password, MENU_ENUM_SUBLABEL_CLOUD_SYNC_PASSWORD) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_access_key_id, MENU_ENUM_SUBLABEL_CLOUD_SYNC_ACCESS_KEY_ID) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_secret_access_key, MENU_ENUM_SUBLABEL_CLOUD_SYNC_SECRET_ACCESS_KEY) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_logging_settings_list, MENU_ENUM_SUBLABEL_LOGGING_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_user_interface_settings_list, MENU_ENUM_SUBLABEL_USER_INTERFACE_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_ai_service_settings_list, MENU_ENUM_SUBLABEL_AI_SERVICE_SETTINGS) @@ -5254,12 +5257,21 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CLOUD_SYNC_URL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_url); break; + case MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_s3_url); + break; case MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_username); break; case MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_password); break; + case MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_access_key_id); + break; + case MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_secret_access_key); + break; case MENU_ENUM_LABEL_LOGGING_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_logging_settings_list); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 09730a024165..351c1c6c93f1 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -10364,7 +10364,7 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_VIDEO_HDR_SCANLINES, PARSE_ONLY_BOOL, false) == 0) count++; - + if(settings->bools.video_hdr_scanlines) { if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, @@ -11345,22 +11345,50 @@ unsigned menu_displaylist_build_list( case DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST: { menu_displaylist_build_info_selective_t build_list[] = { - {MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_MODE, PARSE_ONLY_UINT, 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_SYNC_THUMBS, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SYSTEM, 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}, + {MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_MODE, PARSE_ONLY_UINT, 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_SYNC_THUMBS, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SYSTEM, 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}, + {MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, 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++) + switch(build_list[i].enum_idx) + { + case MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_MODE: + case MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SAVES: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_CONFIGS: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_THUMBS: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SYSTEM: + case MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER: + if (string_is_equal(settings->arrays.cloud_sync_driver, "webdav") + || string_is_equal(settings->arrays.cloud_sync_driver, "s3")) + build_list[i].checked = true; + break; + case MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME: + case MENU_ENUM_LABEL_CLOUD_SYNC_URL: + case MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD: + if (string_is_equal(settings->arrays.cloud_sync_driver, "webdav")) + build_list[i].checked = true; + break; + case MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL: + case MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID: + case MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY: + if (string_is_equal(settings->arrays.cloud_sync_driver, "s3")) + build_list[i].checked = true; + break; + } for (i = 0; i < ARRAY_SIZE(build_list); i++) { diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 22efacec5d84..2681090c8af6 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -12282,6 +12282,59 @@ static bool setting_append_list( (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_PASSWORD_LINE_EDIT; (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; +#ifdef HAVE_S3 + /* AWS */ + CONFIG_STRING( + list, list_info, + settings->arrays.s3_url, + sizeof(settings->arrays.s3_url), + MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL, + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_S3_URL, + "", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT; + (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; + + CONFIG_STRING( + list, list_info, + settings->arrays.access_key_id, + sizeof(settings->arrays.access_key_id), + MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID, + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_ACCESS_KEY_ID, + "", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT; + (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; + + CONFIG_STRING( + list, list_info, + settings->arrays.secret_access_key, + sizeof(settings->arrays.secret_access_key), + MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SECRET_ACCESS_KEY, + "", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_password; + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_PASSWORD_LINE_EDIT; + (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; +#endif + END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); #endif diff --git a/msg_hash.h b/msg_hash.h index e9db968da693..05206a60cb70 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1536,7 +1536,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT_RGB, MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT_RBG, MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT_BGR, - + MENU_LBL_H(PARENT_DIRECTORY), MENU_LBL_H(FILE_BROWSER_OPEN_UWP_PERMISSIONS), MENU_LABEL(FILE_BROWSER_OPEN_PICKER), @@ -3212,6 +3212,9 @@ enum msg_hash_enums MENU_LABEL(CLOUD_SYNC_SYNC_NOW), MENU_LABEL(CLOUD_SYNC_RESOLVE_KEEP_LOCAL), MENU_LABEL(CLOUD_SYNC_RESOLVE_KEEP_SERVER), + MENU_LABEL(CLOUD_SYNC_S3_URL), + MENU_LABEL(CLOUD_SYNC_ACCESS_KEY_ID), + MENU_LABEL(CLOUD_SYNC_SECRET_ACCESS_KEY), MENU_LABEL(RECORDING_SETTINGS), MENU_LABEL(OVERLAY_SETTINGS), MENU_LABEL(REWIND_SETTINGS), diff --git a/network/cloud_sync/s3.c b/network/cloud_sync/s3.c new file mode 100644 index 000000000000..6f2d1dd0e646 --- /dev/null +++ b/network/cloud_sync/s3.c @@ -0,0 +1,1328 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include