Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- CONFIG: Fix saving main configuration after load configuration
- DOS: RetroArch for DOS can now start up and handle keyboard correctly
- CLOUDSYNC: Conflict resolution options
- CLOUDSYNC: Add --cloudsync command line option to run a sync and exit
- EMSCRIPTEN: Added dice to core selection dropdown
- EMSCRIPTEN: Numerous bugfixes
- FFMPEG: new features for save state, variable strength seek, audio/subtitle stream controls
Expand Down
4 changes: 4 additions & 0 deletions docs/retroarch.6
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ If no arguments are passed to retroarch, it is equivalent to calling retroarch w
\fB--features\fR
Prints available features compiled into RetroArch, then exits.

.TP
\fB--cloudsync\fR
Runs a cloud sync with the configured cloud sync driver, then exits.

.TP
\fB-L PATH, --libretro PATH\fR
Path to a libretro implementation which is to be used.
Expand Down
73 changes: 69 additions & 4 deletions retroarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ enum
RA_OPT_SET_SHADER,
RA_OPT_DATABASE_SCAN,
RA_OPT_ACCESSIBILITY,
RA_OPT_LOAD_MENU_ON_ERROR
RA_OPT_LOAD_MENU_ON_ERROR,
RA_OPT_CLOUDSYNC
};

/* DRIVERS */
Expand Down Expand Up @@ -4141,7 +4142,7 @@ bool command_event(enum event_command cmd, void *data)
#ifdef HAVE_CLOUDSYNC
/* Sync on core unload if in automatic mode */
if (settings->uints.cloud_sync_sync_mode == CLOUD_SYNC_MODE_AUTOMATIC)
task_push_cloud_sync();
task_push_cloud_sync(NULL, NULL);
#endif
}

Expand Down Expand Up @@ -5162,7 +5163,7 @@ bool command_event(enum event_command cmd, void *data)
break;
#ifdef HAVE_CLOUDSYNC
case CMD_EVENT_CLOUD_SYNC:
task_push_cloud_sync();
task_push_cloud_sync(NULL, NULL);
break;
case CMD_EVENT_CLOUD_SYNC_RESOLVE_KEEP_LOCAL:
task_push_cloud_sync_resolve_keep_local();
Expand Down Expand Up @@ -6721,7 +6722,7 @@ int rarch_main(int argc, char *argv[], void *data)
);
#ifdef HAVE_CLOUDSYNC
if (settings->uints.cloud_sync_sync_mode == CLOUD_SYNC_MODE_AUTOMATIC)
task_push_cloud_sync();
task_push_cloud_sync(NULL, NULL);
#endif
#ifdef HAVE_LAKKA
sd_notify(0, "READY=1");
Expand Down Expand Up @@ -7306,6 +7307,11 @@ static void retroarch_print_help(const char *arg0)
" --scan=PATH|FILE "
"Import content from path.\n");
#endif
#ifdef HAVE_CLOUDSYNC
strlcpy_append(buf, sizeof(buf), &_len,
" --cloudsync "
"Run a cloud sync, then exits.\n");
#endif

strlcpy_append(buf, sizeof(buf), &_len,
" -f, --fullscreen "
Expand Down Expand Up @@ -7625,6 +7631,15 @@ void handle_dbscan_finished(retro_task_t *task,
void *task_data, void *user_data, const char *err);
#endif

#ifdef HAVE_CLOUDSYNC
/* --cloudsync: records the verdict where the blocked caller can read it. */
static void handle_cloud_sync_cli_finished(retro_task_t *task,
void *task_data, void *user_data, const char *err)
{
*(int*)user_data = err ? EXIT_FAILURE : EXIT_SUCCESS;
}
#endif

/**
* retroarch_parse_input_and_config:
* @argc : Count of (commandline) arguments.
Expand Down Expand Up @@ -7718,6 +7733,9 @@ static bool retroarch_parse_input_and_config(
{ "entryslot", 1, NULL, 'e' },
#ifdef HAVE_LIBRETRODB
{ "scan", 1, NULL, RA_OPT_DATABASE_SCAN },
#endif
#ifdef HAVE_CLOUDSYNC
{ "cloudsync", 0, NULL, RA_OPT_CLOUDSYNC },
#endif
{ NULL, 0, NULL, 0 }
};
Expand Down Expand Up @@ -7901,6 +7919,13 @@ static bool retroarch_parse_input_and_config(
#ifdef HAVE_LIBRETRODB
verbosity_enable();
retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_DATABASE_SCAN, NULL);
#endif
break;
case RA_OPT_CLOUDSYNC:
#ifdef HAVE_CLOUDSYNC
/* Enable verbosity so that the sync's summary is printed. */
verbosity_enable();
retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_VERBOSITY, NULL);
#endif
break;

Expand Down Expand Up @@ -8357,6 +8382,46 @@ static bool retroarch_parse_input_and_config(
exit(0);
}
}
#endif
break;
case RA_OPT_CLOUDSYNC:
#ifdef HAVE_CLOUDSYNC
{
int result = -1;
settings_t *settings = config_get_ptr();
int reinit_flags = DRIVERS_CMD_ALL &
~(DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK | DRIVER_MICROPHONE_MASK | DRIVER_INPUT_MASK | DRIVER_MIDI_MASK);

if (!settings->bools.cloud_sync_enable)
{
RARCH_WARN("[CloudSync] Cloud Sync is disabled, nothing to sync.\n");
exit(0);
}

/* This is normally done in retroarch_main_init(), which isn't run here. */
drivers_init(settings, reinit_flags, (enum driver_lifetime_flags)0, false);
retroarch_init_task_queue();
cloud_sync_find_driver(settings->arrays.cloud_sync_driver,
"cloud sync driver", verbosity_is_enabled());

/* Create the task queue and repeatedly check it until it's complete. */
if (task_push_cloud_sync(handle_cloud_sync_cli_finished, &result))
{
while (result < 0)
{
task_queue_check();
retro_sleep(10);
}
}
else
result = EXIT_FAILURE;
driver_uninit(DRIVERS_CMD_ALL, (enum driver_lifetime_flags)0);
task_queue_deinit();
#ifdef HAVE_NETWORKING
net_http_deinit();
#endif
exit(result);
}
#endif
break;
default:
Expand Down
58 changes: 45 additions & 13 deletions tasks/task_cloudsync.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ typedef struct
* than a process-wide cache: a fresh list per sync is a handful of
* strdups, and it cannot go stale against the sync toggles. */
struct string_list *dirlist;
/* Optional completion callback supplied by whoever pushed the sync. */
retro_task_callback_t cb;
void *user_data;
} task_cloud_sync_state_t;

/* Serialises appends to updated_server_manifest /
Expand Down Expand Up @@ -188,6 +191,7 @@ static void task_cloud_sync_begin_handler(void *user_data, const char *path, boo
else
{
RARCH_WARN(CSPFX "Begin failed.\n");
sync_state->failures = true;
task_free_title(task);
task_set_title(task, strdup("Cloud Sync failed"));
task_set_flags(task, RETRO_TASK_FLG_FINISHED, true);
Expand Down Expand Up @@ -618,7 +622,9 @@ static void task_cloud_sync_add_to_updated_manifest(task_cloud_sync_state_t *syn
idx = list->size;
file_list_append(list, NULL, NULL, 0, 0, 0);
file_list_set_alt_at_offset(list, idx, key);
list->list[idx].userdata = hash;
/* This is copied with strdup() so that the manifest owns its hash
* and can be freed without worrying about other callers. */
list->list[idx].userdata = hash ? strdup(hash) : NULL;
#ifdef HAVE_THREADS
slock_unlock(tcs_manifest_lock);
#endif
Expand Down Expand Up @@ -847,6 +853,7 @@ static void task_cloud_sync_fetch_cb(void *user_data, const char *path, bool suc
if (!string_is_equal(hash, CS_FILE_HASH(server_file)))
sync_state->need_manifest_uploaded = true;
sync_state->downloads++;
free(hash);
}
else
{
Expand Down Expand Up @@ -1048,7 +1055,10 @@ static void task_cloud_sync_upload_current_file(task_cloud_sync_state_t *sync_st

RARCH_LOG(CSPFX "Uploading \"%s\".\n", path);

item->userdata = task_cloud_sync_md5_rfile(file);
/* The three-way compare may already have hashed this item;
* reassigning here leaked that hash. */
if (!item->userdata)
item->userdata = task_cloud_sync_md5_rfile(file);

filestream_seek(file, 0, SEEK_SET);
task_cloud_sync_waiting_inc(sync_state);
Expand Down Expand Up @@ -1637,6 +1647,7 @@ static void task_cloud_sync_task_handler(retro_task_t *task)
if (!cloud_sync_begin(task_cloud_sync_begin_handler, task))
{
RARCH_WARN(CSPFX "Could not begin.\n");
sync_state->failures = true;
task_free_title(task);
task_set_title(task, strdup("Cloud Sync failed"));
goto task_finished;
Expand Down Expand Up @@ -1678,7 +1689,22 @@ static void task_cloud_sync_task_handler(retro_task_t *task)
static void task_cloud_sync_cb(retro_task_t *task, void *task_data,
void *user_data, const char *error)
{
task_cloud_sync_state_t *sync_state = (task_cloud_sync_state_t *)task_data;
task_cloud_sync_state_t *sync_state = (task_cloud_sync_state_t *)task->state;

/* The title is the sync's own summary ("Cloud Sync failed",
* "Cloud Sync finished with failures and conflicts"), so it
* doubles as the error string. */
if (sync_state && sync_state->cb)
sync_state->cb(task, NULL, sync_state->user_data,
(sync_state->failures || sync_state->conflicts)
? task->title : NULL);
}

/* The state used to be released from the callback above, but as
* task_data - which this task never sets - so it leaked every sync. */
static void task_cloud_sync_cleanup(retro_task_t *task)
{
task_cloud_sync_state_t *sync_state = (task_cloud_sync_state_t *)task->state;

if (!sync_state)
return;
Expand All @@ -1699,6 +1725,7 @@ static void task_cloud_sync_cb(retro_task_t *task, void *task_data,
string_list_free(sync_state->dirlist);

free(sync_state);
task->state = NULL;
}

static bool task_cloud_sync_task_finder(retro_task_t *task, void *user_data)
Expand All @@ -1710,7 +1737,8 @@ static bool task_cloud_sync_task_finder(retro_task_t *task, void *user_data)
return task->handler == task_cloud_sync_task_handler;
}

static void task_push_cloud_sync_with_mode(int conflict_resolution)
static bool task_push_cloud_sync_with_mode(int conflict_resolution,
retro_task_callback_t cb, void *user_data)
{
char task_title[128];
task_finder_data_t find_data;
Expand All @@ -1719,7 +1747,7 @@ static void task_push_cloud_sync_with_mode(int conflict_resolution)
bool cloud_sync_enable = config_get_ptr()->bools.cloud_sync_enable;

if (!cloud_sync_enable)
return;
return false;

#ifdef HAVE_THREADS
if (!tcs_manifest_lock)
Expand All @@ -1730,12 +1758,12 @@ static void task_push_cloud_sync_with_mode(int conflict_resolution)
if (task_queue_find(&find_data))
{
RARCH_LOG(CSPFX "Already in progress.\n");
return;
return false;
}

sync_state = (task_cloud_sync_state_t *)calloc(1, sizeof(task_cloud_sync_state_t));
if (!sync_state)
return;
return false;

/* Captured here, on the main thread, for the worker the threaded
* task queue runs the handler on. */
Expand All @@ -1750,14 +1778,14 @@ static void task_push_cloud_sync_with_mode(int conflict_resolution)
if (!sync_state->dirlist)
{
free(sync_state);
return;
return false;
}

if (!(task = task_init()))
{
string_list_free(sync_state->dirlist);
free(sync_state);
return;
return false;
}

/* calloc zero-fill is not a portable initializer for an atomic
Expand All @@ -1769,21 +1797,25 @@ static void task_push_cloud_sync_with_mode(int conflict_resolution)
task_cloud_sync_phase_set(sync_state, CLOUD_SYNC_PHASE_BEGIN);
sync_state->start_time = cpu_features_get_time_usec();
sync_state->conflict_resolution = conflict_resolution;
sync_state->cb = cb;
sync_state->user_data = user_data;

strlcpy_lit(task_title, "Cloud Sync in progress", sizeof(task_title));

task->state = sync_state;
task->title = strdup(task_title);
task->handler = task_cloud_sync_task_handler;
task->callback = task_cloud_sync_cb;
task->cleanup = task_cloud_sync_cleanup;
task->progress_cb = task_window_progress_cb;

task_queue_push(task);
return true;
}

void task_push_cloud_sync(void)
bool task_push_cloud_sync(retro_task_callback_t cb, void *user_data)
{
task_push_cloud_sync_with_mode(0);
return task_push_cloud_sync_with_mode(0, cb, user_data);
}

void task_push_cloud_sync_update_driver(void)
Expand All @@ -1806,11 +1838,11 @@ void task_push_cloud_sync_update_driver(void)
void task_push_cloud_sync_resolve_keep_local(void)
{
RARCH_LOG(CSPFX "Starting sync with conflict resolution: keep local.\n");
task_push_cloud_sync_with_mode(1);
task_push_cloud_sync_with_mode(1, NULL, NULL);
}

void task_push_cloud_sync_resolve_keep_server(void)
{
RARCH_LOG(CSPFX "Starting sync with conflict resolution: keep server.\n");
task_push_cloud_sync_with_mode(2);
task_push_cloud_sync_with_mode(2, NULL, NULL);
}
5 changes: 4 additions & 1 deletion tasks/tasks_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,10 @@ extern const char* const input_builtin_autoconfs[];

/* cloud sync tasks */
void task_push_cloud_sync_update_driver(void);
void task_push_cloud_sync(void);
/* The cb parameter is the callback that's called when the sync is done.
* Its error argument is non-NULL when the sync failed or finished with
* failures or conflicts. Returns false if no sync was queued. */
bool task_push_cloud_sync(retro_task_callback_t cb, void *user_data);
void task_push_cloud_sync_resolve_keep_local(void);
void task_push_cloud_sync_resolve_keep_server(void);

Expand Down
2 changes: 1 addition & 1 deletion ui/drivers/ui_cocoatouch.m
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ - (void)applicationDidBecomeActive:(UIApplication *)application
if ( [[NSDate date] timeIntervalSinceDate:self.bgDate] > 60.0f
&& ( !(runloop_get_flags() & RUNLOOP_FLAG_CORE_RUNNING)
|| retroarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)))
task_push_cloud_sync();
task_push_cloud_sync(NULL, NULL);
self.bgDate = nil;
}
#endif
Expand Down
Loading