Merge pull request #8259 from bparker06/playtime_log

Add option to track how long content has been running over time
This commit is contained in:
Twinaphex 2019-02-12 15:06:57 +01:00 committed by GitHub
commit 2ccc6534ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 574 additions and 9 deletions

View File

@ -1369,6 +1369,7 @@ static bool command_event_init_core(enum rarch_core_type *data)
return false;
rarch_ctl(RARCH_CTL_SET_FRAME_LIMIT, NULL);
rarch_ctl(RARCH_CTL_CONTENT_RUNTIME_LOG_INIT, NULL);
return true;
}
@ -2215,6 +2216,13 @@ TODO: Add a setting for these tweaks */
}
g_defaults.music_history = NULL;
if (g_defaults.content_runtime)
{
playlist_write_runtime_file(g_defaults.content_runtime);
playlist_free(g_defaults.content_runtime);
}
g_defaults.content_runtime = NULL;
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
if (g_defaults.video_history)
{
@ -2265,6 +2273,13 @@ TODO: Add a setting for these tweaks */
settings->paths.path_content_music_history,
content_history_size);
RARCH_LOG("%s: [%s].\n",
msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
settings->paths.path_content_runtime);
g_defaults.content_runtime = playlist_init(
settings->paths.path_content_runtime,
content_history_size);
#if defined(HAVE_FFMPEG) || defined(HAVE_MPV)
RARCH_LOG("%s: [%s].\n",
msg_hash_to_str(MSG_LOADING_HISTORY_FILE),
@ -2311,6 +2326,7 @@ TODO: Add a setting for these tweaks */
case CMD_EVENT_CORE_DEINIT:
{
struct retro_hw_render_callback *hwr = NULL;
rarch_ctl(RARCH_CTL_CONTENT_RUNTIME_LOG_DEINIT, NULL);
content_reset_savestate_backups();
hwr = video_driver_get_hw_context();
command_event_deinit_core(true);

View File

@ -772,6 +772,9 @@ static const bool ui_companion_toggle = false;
/* Only init the WIMP UI for this session if this is enabled */
static const bool desktop_menu_enable = true;
/* Keep track of how long each core+content has been running for over time */
static const bool content_runtime_log = false;
#if defined(__QNX__) || defined(_XBOX1) || defined(_XBOX360) || defined(__CELLOS_LV2__) || (defined(__MACH__) && defined(IOS)) || defined(ANDROID) || defined(WIIU) || defined(HAVE_NEON) || defined(GEKKO) || defined(__ARM_NEON__)
static enum resampler_quality audio_resampler_quality_level = RESAMPLER_QUALITY_LOWER;
#elif defined(PSP) || defined(_3DS) || defined(VITA) || defined(PS2)

View File

@ -1228,6 +1228,8 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
settings->paths.path_content_video_history, false, NULL, true);
SETTING_PATH("content_image_history_path",
settings->paths.path_content_image_history, false, NULL, true);
SETTING_PATH("content_runtime_path",
settings->paths.path_content_runtime, false, NULL, true);
#ifdef HAVE_OVERLAY
SETTING_PATH("input_overlay",
settings->paths.path_overlay, false, NULL, true);
@ -1571,6 +1573,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
#endif
SETTING_BOOL("playlist_use_old_format", &settings->bools.playlist_use_old_format, true, playlist_use_old_format, false);
SETTING_BOOL("content_runtime_log", &settings->bools.content_runtime_log, true, content_runtime_log, false);
*size = count;
@ -2066,6 +2069,7 @@ void config_set_defaults(void)
*settings->paths.path_content_music_history = '\0';
*settings->paths.path_content_image_history = '\0';
*settings->paths.path_content_video_history = '\0';
*settings->paths.path_content_runtime = '\0';
*settings->paths.path_cheat_settings = '\0';
*settings->paths.path_shader = '\0';
#ifndef IOS
@ -3049,6 +3053,25 @@ static bool config_load_file(const char *path, bool set_defaults,
}
}
if (string_is_empty(settings->paths.path_content_runtime))
{
if (string_is_empty(settings->paths.directory_content_history))
{
fill_pathname_resolve_relative(
settings->paths.path_content_runtime,
path_config,
file_path_str(FILE_PATH_CONTENT_RUNTIME),
sizeof(settings->paths.path_content_runtime));
}
else
{
fill_pathname_join(settings->paths.path_content_runtime,
settings->paths.directory_content_history,
file_path_str(FILE_PATH_CONTENT_RUNTIME),
sizeof(settings->paths.path_content_runtime));
}
}
if (!string_is_empty(settings->paths.directory_screenshot))
{
if (string_is_equal(settings->paths.directory_screenshot, "default"))

View File

@ -307,6 +307,7 @@ typedef struct settings
bool sustained_performance_mode;
bool playlist_use_old_format;
bool content_runtime_log;
} bools;
struct
@ -540,6 +541,7 @@ typedef struct settings
char path_content_music_history[PATH_MAX_LENGTH];
char path_content_image_history[PATH_MAX_LENGTH];
char path_content_video_history[PATH_MAX_LENGTH];
char path_content_runtime[PATH_MAX_LENGTH];
char path_libretro_info[PATH_MAX_LENGTH];
char path_cheat_settings[PATH_MAX_LENGTH];
char path_shader[PATH_MAX_LENGTH];

View File

@ -103,6 +103,7 @@ struct defaults
#ifndef IS_SALAMANDER
playlist_t *content_history;
playlist_t *content_favorites;
playlist_t *content_runtime;
#ifdef HAVE_IMAGEVIEWER
playlist_t *image_history;
#endif

View File

@ -38,6 +38,7 @@ enum file_path_enum
FILE_PATH_LOG_ERROR,
FILE_PATH_LOG_INFO,
FILE_PATH_CONTENT_HISTORY,
FILE_PATH_CONTENT_RUNTIME,
FILE_PATH_CONTENT_FAVORITES,
FILE_PATH_CONTENT_MUSIC_HISTORY,
FILE_PATH_CONTENT_VIDEO_HISTORY,

View File

@ -200,6 +200,9 @@ const char *file_path_str(enum file_path_enum enum_idx)
case FILE_PATH_CONTENT_HISTORY:
str = "content_history.lpl";
break;
case FILE_PATH_CONTENT_RUNTIME:
str = "content_runtime.lpl";
break;
case FILE_PATH_CONTENT_FAVORITES:
str = "content_favorites.lpl";
break;

View File

@ -1775,3 +1775,5 @@ MSG_HASH(MENU_ENUM_LABEL_MENU_SOUND_NOTICE,
"menu_sound_notice")
MSG_HASH(MENU_ENUM_LABEL_MENU_SOUND_BGM,
"menu_sound_bgm")
MSG_HASH(MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG,
"content_runtime_log")

View File

@ -8194,3 +8194,11 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CONTENT_RUNTIME_LOG,
"Save content runtime log"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CONTENT_RUNTIME_LOG,
"Keeps track of how long your content has been running over time."
)

View File

@ -516,6 +516,7 @@ default_sublabel_macro(action_bind_sublabel_menu_rgui_lock_aspect,
default_sublabel_macro(action_bind_sublabel_rgui_menu_color_theme, MENU_ENUM_SUBLABEL_RGUI_MENU_COLOR_THEME)
default_sublabel_macro(action_bind_sublabel_rgui_menu_theme_preset, MENU_ENUM_SUBLABEL_RGUI_MENU_THEME_PRESET)
default_sublabel_macro(action_bind_sublabel_menu_rgui_thumbnail_downscaler, MENU_ENUM_SUBLABEL_MENU_RGUI_THUMBNAIL_DOWNSCALER)
default_sublabel_macro(action_bind_sublabel_content_runtime_log, MENU_ENUM_SUBLABEL_CONTENT_RUNTIME_LOG)
static int action_bind_sublabel_systeminfo_controller_entry(
file_list_t *list,
@ -2248,6 +2249,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_MENU_RGUI_THUMBNAIL_DOWNSCALER:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_thumbnail_downscaler);
break;
case MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_content_runtime_log);
break;
default:
case MSG_UNKNOWN:
return -1;

View File

@ -5397,6 +5397,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, menu_displaylist
menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_SCREENSHOTS_IN_CONTENT_DIR_ENABLE,
PARSE_ONLY_BOOL, false);
menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG,
PARSE_ONLY_BOOL, false);
info->need_refresh = true;
info->need_push = true;

View File

@ -531,6 +531,7 @@ bool menu_display_libretro(bool is_idle,
input_driver_set_libretro_input_blocked();
core_run();
rarch_core_runtime_tick();
input_driver_unset_libretro_input_blocked();
return true;

View File

@ -4831,9 +4831,25 @@ static bool setting_append_list(
&setting_get_string_representation_uint_autosave_interval;
#endif
CONFIG_BOOL(
list, list_info,
&settings->bools.content_runtime_log,
MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG,
MENU_ENUM_LABEL_VALUE_CONTENT_RUNTIME_LOG,
content_runtime_log,
MENU_ENUM_LABEL_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_ON,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler,
SD_FLAG_NONE);
END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group);
}
break;
case SETTINGS_LIST_REWIND:
START_GROUP(list, list_info, &group_info, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_REWIND_SETTINGS), parent_group);

View File

@ -2253,6 +2253,7 @@ enum msg_hash_enums
MENU_LABEL(MENU_SOUND_NOTICE),
MENU_LABEL(MENU_SOUND_BGM),
MENU_ENUM_LABEL_VALUE_VIDEO_DRIVER_FALLBACK,
MENU_LABEL(CONTENT_RUNTIME_LOG),
MSG_LAST
};

View File

@ -45,6 +45,9 @@ struct playlist_entry
char *core_name;
char *db_name;
char *crc32;
unsigned runtime_hours;
unsigned runtime_minutes;
unsigned runtime_seconds;
};
struct content_playlist
@ -67,6 +70,8 @@ typedef struct
unsigned array_depth;
unsigned object_depth;
char **current_entry_val;
int *current_entry_int_val;
unsigned *current_entry_uint_val;
char *current_meta_string;
bool in_items;
} JSONContext;
@ -125,6 +130,27 @@ void playlist_get_index(playlist_t *playlist,
*crc32 = playlist->entries[idx].crc32;
}
void playlist_get_runtime_index(playlist_t *playlist,
size_t idx,
const char **path, const char **core_path,
unsigned *runtime_hours, unsigned *runtime_minutes,
unsigned *runtime_seconds)
{
if (!playlist)
return;
if (path)
*path = playlist->entries[idx].path;
if (core_path)
*core_path = playlist->entries[idx].core_path;
if (runtime_hours)
*runtime_hours = playlist->entries[idx].runtime_hours;
if (runtime_minutes)
*runtime_minutes = playlist->entries[idx].runtime_minutes;
if (runtime_seconds)
*runtime_seconds = playlist->entries[idx].runtime_seconds;
}
/**
* playlist_delete_index:
* @playlist : Playlist handle.
@ -223,6 +249,9 @@ static void playlist_free_entry(struct playlist_entry *entry)
entry->core_name = NULL;
entry->db_name = NULL;
entry->crc32 = NULL;
entry->runtime_hours = 0;
entry->runtime_minutes = 0;
entry->runtime_seconds = 0;
}
void playlist_update(playlist_t *playlist, size_t idx,
@ -288,6 +317,146 @@ void playlist_update(playlist_t *playlist, size_t idx,
}
}
void playlist_update_runtime(playlist_t *playlist, size_t idx,
const char *path, const char *core_path,
unsigned runtime_hours, unsigned runtime_minutes,
unsigned runtime_seconds)
{
struct playlist_entry *entry = NULL;
if (!playlist || idx > playlist->size)
return;
entry = &playlist->entries[idx];
if (path && (path != entry->path))
{
if (entry->path != NULL)
free(entry->path);
entry->path = strdup(path);
playlist->modified = true;
}
if (core_path && (core_path != entry->core_path))
{
if (entry->core_path != NULL)
free(entry->core_path);
entry->core_path = NULL;
entry->core_path = strdup(core_path);
playlist->modified = true;
}
if (runtime_hours != entry->runtime_hours)
{
entry->runtime_hours = runtime_hours;
playlist->modified = true;
}
if (runtime_minutes != entry->runtime_minutes)
{
entry->runtime_minutes = runtime_minutes;
playlist->modified = true;
}
if (runtime_seconds != entry->runtime_seconds)
{
entry->runtime_seconds = runtime_seconds;
playlist->modified = true;
}
}
bool playlist_push_runtime(playlist_t *playlist,
const char *path, const char *core_path,
unsigned runtime_hours, unsigned runtime_minutes,
unsigned runtime_seconds)
{
size_t i;
bool core_path_empty = string_is_empty(core_path);
if (core_path_empty)
{
RARCH_ERR("cannot push NULL or empty core name into the playlist.\n");
return false;
}
if (string_is_empty(path))
path = NULL;
if (!playlist)
return false;
for (i = 0; i < playlist->size; i++)
{
struct playlist_entry tmp;
bool equal_path;
equal_path = (!path && !playlist->entries[i].path) ||
(path && playlist->entries[i].path &&
#ifdef _WIN32
/*prevent duplicates on case-insensitive operating systems*/
string_is_equal_noncase(path,playlist->entries[i].path)
#else
string_is_equal(path,playlist->entries[i].path)
#endif
);
/* Core name can have changed while still being the same core.
* Differentiate based on the core path only. */
if (!equal_path)
continue;
if (!string_is_equal(playlist->entries[i].core_path, core_path))
continue;
/* If top entry, we don't want to push a new entry since
* the top and the entry to be pushed are the same. */
if (i == 0)
return false;
/* Seen it before, bump to top. */
tmp = playlist->entries[i];
memmove(playlist->entries + 1, playlist->entries,
i * sizeof(struct playlist_entry));
playlist->entries[0] = tmp;
goto success;
}
if (playlist->size == playlist->cap)
{
struct playlist_entry *entry = &playlist->entries[playlist->cap - 1];
if (entry)
playlist_free_entry(entry);
playlist->size--;
}
if (playlist->entries)
{
memmove(playlist->entries + 1, playlist->entries,
(playlist->cap - 1) * sizeof(struct playlist_entry));
playlist->entries[0].path = NULL;
playlist->entries[0].core_path = NULL;
if (!string_is_empty(path))
playlist->entries[0].path = strdup(path);
if (!string_is_empty(core_path))
playlist->entries[0].core_path = strdup(core_path);
playlist->entries[0].runtime_hours = runtime_hours;
playlist->entries[0].runtime_minutes = runtime_minutes;
playlist->entries[0].runtime_seconds = runtime_seconds;
}
playlist->size++;
success:
playlist->modified = true;
return true;
}
/**
* playlist_push:
* @playlist : Playlist handle.
@ -380,12 +549,15 @@ bool playlist_push(playlist_t *playlist,
memmove(playlist->entries + 1, playlist->entries,
(playlist->cap - 1) * sizeof(struct playlist_entry));
playlist->entries[0].path = NULL;
playlist->entries[0].label = NULL;
playlist->entries[0].core_path = NULL;
playlist->entries[0].core_name = NULL;
playlist->entries[0].db_name = NULL;
playlist->entries[0].crc32 = NULL;
playlist->entries[0].path = NULL;
playlist->entries[0].label = NULL;
playlist->entries[0].core_path = NULL;
playlist->entries[0].core_name = NULL;
playlist->entries[0].db_name = NULL;
playlist->entries[0].crc32 = NULL;
playlist->entries[0].runtime_hours = 0;
playlist->entries[0].runtime_minutes = 0;
playlist->entries[0].runtime_seconds = 0;
if (!string_is_empty(path))
playlist->entries[0].path = strdup(path);
if (!string_is_empty(label))
@ -435,6 +607,136 @@ static void JSONLogError(JSONContext *pCtx)
}
}
void playlist_write_runtime_file(playlist_t *playlist)
{
size_t i;
RFILE *file = NULL;
settings_t *settings = config_get_ptr();
JSONContext context = {0};
if (!playlist || !playlist->modified)
return;
file = filestream_open(playlist->conf_path,
RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
{
RARCH_ERR("Failed to write to playlist file: %s\n", playlist->conf_path);
return;
}
context.writer = JSON_Writer_Create(NULL);
context.file = file;
if (!context.writer)
{
RARCH_ERR("Failed to create JSON writer\n");
goto end;
}
JSON_Writer_SetOutputEncoding(context.writer, JSON_UTF8);
JSON_Writer_SetOutputHandler(context.writer, &JSONOutputHandler);
JSON_Writer_SetUserData(context.writer, &context);
JSON_Writer_WriteStartObject(context.writer);
JSON_Writer_WriteNewLine(context.writer);
JSON_Writer_WriteSpace(context.writer, 2);
JSON_Writer_WriteString(context.writer, "version", strlen("version"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteString(context.writer, "1.0", strlen("1.0"), JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
JSON_Writer_WriteSpace(context.writer, 2);
JSON_Writer_WriteString(context.writer, "items", strlen("items"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteStartArray(context.writer);
JSON_Writer_WriteNewLine(context.writer);
for (i = 0; i < playlist->size; i++)
{
JSON_Writer_WriteSpace(context.writer, 4);
JSON_Writer_WriteStartObject(context.writer);
JSON_Writer_WriteNewLine(context.writer);
JSON_Writer_WriteSpace(context.writer, 6);
JSON_Writer_WriteString(context.writer, "path", strlen("path"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteString(context.writer, playlist->entries[i].path ? playlist->entries[i].path : "", playlist->entries[i].path ? strlen(playlist->entries[i].path) : 0, JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
JSON_Writer_WriteSpace(context.writer, 6);
JSON_Writer_WriteString(context.writer, "core_path", strlen("core_path"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteString(context.writer, playlist->entries[i].core_path, strlen(playlist->entries[i].core_path), JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
{
char tmp[32] = {0};
snprintf(tmp, sizeof(tmp), "%u", playlist->entries[i].runtime_hours);
JSON_Writer_WriteSpace(context.writer, 6);
JSON_Writer_WriteString(context.writer, "runtime_hours", strlen("runtime_hours"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteNumber(context.writer, tmp, strlen(tmp), JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
memset(tmp, 0, sizeof(tmp));
snprintf(tmp, sizeof(tmp), "%u", playlist->entries[i].runtime_minutes);
JSON_Writer_WriteSpace(context.writer, 6);
JSON_Writer_WriteString(context.writer, "runtime_minutes", strlen("runtime_minutes"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteNumber(context.writer, tmp, strlen(tmp), JSON_UTF8);
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
memset(tmp, 0, sizeof(tmp));
snprintf(tmp, sizeof(tmp), "%u", playlist->entries[i].runtime_seconds);
JSON_Writer_WriteSpace(context.writer, 6);
JSON_Writer_WriteString(context.writer, "runtime_seconds", strlen("runtime_seconds"), JSON_UTF8);
JSON_Writer_WriteColon(context.writer);
JSON_Writer_WriteSpace(context.writer, 1);
JSON_Writer_WriteNumber(context.writer, tmp, strlen(tmp), JSON_UTF8);
JSON_Writer_WriteNewLine(context.writer);
}
JSON_Writer_WriteSpace(context.writer, 4);
JSON_Writer_WriteEndObject(context.writer);
if (i < playlist->size - 1)
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
}
JSON_Writer_WriteSpace(context.writer, 2);
JSON_Writer_WriteEndArray(context.writer);
JSON_Writer_WriteNewLine(context.writer);
JSON_Writer_WriteEndObject(context.writer);
JSON_Writer_WriteNewLine(context.writer);
JSON_Writer_Free(context.writer);
playlist->modified = false;
RARCH_LOG("Written to playlist file: %s\n", playlist->conf_path);
end:
filestream_close(file);
}
void playlist_write_file(playlist_t *playlist)
{
size_t i;
@ -735,6 +1037,9 @@ static JSON_Parser_HandlerResult JSONStringHandler(JSON_Parser parser, char *pVa
{
if (pCtx->current_entry_val && length && !string_is_empty(pValue))
{
if (*pCtx->current_entry_val)
free(*pCtx->current_entry_val);
*pCtx->current_entry_val = strdup(pValue);
}
else
@ -753,6 +1058,7 @@ static JSON_Parser_HandlerResult JSONStringHandler(JSON_Parser parser, char *pVa
/*RARCH_LOG("found meta: %s = %s\n", pCtx->current_meta_string, pValue);*/
free(pCtx->current_meta_string);
pCtx->current_meta_string = NULL;
}
}
}
@ -762,6 +1068,50 @@ static JSON_Parser_HandlerResult JSONStringHandler(JSON_Parser parser, char *pVa
return JSON_Parser_Continue;
}
static JSON_Parser_HandlerResult JSONNumberHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes)
{
JSONContext *pCtx = (JSONContext*)JSON_Parser_GetUserData(parser);
(void)attributes; /* unused */
if (pCtx->in_items && pCtx->object_depth == 2)
{
if (pCtx->array_depth == 1)
{
if (pCtx->current_entry_int_val && length && !string_is_empty(pValue))
{
*pCtx->current_entry_int_val = strtoul(pValue, NULL, 10);
}
else if (pCtx->current_entry_uint_val && length && !string_is_empty(pValue))
{
*pCtx->current_entry_uint_val = strtoul(pValue, NULL, 10);
}
else
{
/* must be a value for an unknown member we aren't tracking, skip it */
}
}
}
else if (pCtx->object_depth == 1)
{
if (pCtx->array_depth == 0)
{
if (pCtx->current_meta_string && length && !string_is_empty(pValue))
{
/* handle any top-level playlist metadata here */
/*RARCH_LOG("found meta: %s = %s\n", pCtx->current_meta_string, pValue);*/
free(pCtx->current_meta_string);
pCtx->current_meta_string = NULL;
}
}
}
pCtx->current_entry_int_val = NULL;
pCtx->current_entry_uint_val = NULL;
return JSON_Parser_Continue;
}
static JSON_Parser_HandlerResult JSONObjectMemberHandler(JSON_Parser parser, char *pValue, size_t length, JSON_StringAttributes attributes)
{
JSONContext *pCtx = (JSONContext*)JSON_Parser_GetUserData(parser);
@ -792,6 +1142,12 @@ static JSON_Parser_HandlerResult JSONObjectMemberHandler(JSON_Parser parser, cha
pCtx->current_entry_val = &pCtx->current_entry->crc32;
else if (string_is_equal(pValue, "db_name"))
pCtx->current_entry_val = &pCtx->current_entry->db_name;
else if (string_is_equal(pValue, "runtime_hours"))
pCtx->current_entry_uint_val = &pCtx->current_entry->runtime_hours;
else if (string_is_equal(pValue, "runtime_minutes"))
pCtx->current_entry_uint_val = &pCtx->current_entry->runtime_minutes;
else if (string_is_equal(pValue, "runtime_seconds"))
pCtx->current_entry_uint_val = &pCtx->current_entry->runtime_seconds;
else
{
/* ignore unknown members */
@ -805,6 +1161,9 @@ static JSON_Parser_HandlerResult JSONObjectMemberHandler(JSON_Parser parser, cha
{
if (length)
{
if (pCtx->current_meta_string)
free(pCtx->current_meta_string);
pCtx->current_meta_string = strdup(pValue);
}
}
@ -886,10 +1245,10 @@ static bool playlist_read_file(
/*JSON_Parser_SetNullHandler(context.parser, &JSONNullHandler);
JSON_Parser_SetBooleanHandler(context.parser, &JSONBooleanHandler);
JSON_Parser_SetNumberHandler(context.parser, &JSONNumberHandler);
JSON_Parser_SetSpecialNumberHandler(context.parser, &JSONSpecialNumberHandler);
JSON_Parser_SetArrayItemHandler(context.parser, &JSONArrayItemHandler);*/
JSON_Parser_SetNumberHandler(context.parser, &JSONNumberHandler);
JSON_Parser_SetStringHandler(context.parser, &JSONStringHandler);
JSON_Parser_SetStartObjectHandler(context.parser, &JSONStartObjectHandler);
JSON_Parser_SetEndObjectHandler(context.parser, &JSONEndObjectHandler);
@ -928,6 +1287,9 @@ static bool playlist_read_file(
}
JSON_Parser_Free(context.parser);
if (context.current_meta_string)
free(context.current_meta_string);
}
else
{

View File

@ -79,6 +79,12 @@ void playlist_get_index(playlist_t *playlist,
const char **db_name,
const char **crc32);
void playlist_get_runtime_index(playlist_t *playlist,
size_t idx,
const char **path, const char **core_path,
unsigned *runtime_hours, unsigned *runtime_minutes,
unsigned *runtime_seconds);
/**
* playlist_delete_index:
* @playlist : Playlist handle.
@ -104,12 +110,22 @@ bool playlist_push(playlist_t *playlist,
const char *crc32,
const char *db_name);
bool playlist_push_runtime(playlist_t *playlist,
const char *path, const char *core_path,
unsigned runtime_hours, unsigned runtime_minutes,
unsigned runtime_seconds);
void playlist_update(playlist_t *playlist, size_t idx,
const char *path, const char *label,
const char *core_path, const char *core_name,
const char *crc32,
const char *db_name);
void playlist_update_runtime(playlist_t *playlist, size_t idx,
const char *path, const char *core_path,
unsigned runtime_hours, unsigned runtime_minutes,
unsigned runtime_seconds);
void playlist_get_index_by_path(playlist_t *playlist,
const char *search_path,
char **path, char **label,
@ -127,6 +143,8 @@ uint32_t playlist_get_size(playlist_t *playlist);
void playlist_write_file(playlist_t *playlist);
void playlist_write_runtime_file(playlist_t *playlist);
void playlist_qsort(playlist_t *playlist);
void playlist_free_cached(void);

View File

@ -102,6 +102,8 @@
#include "file_path_special.h"
#include "ui/ui_companion_driver.h"
#include "verbosity.h"
#include "defaults.h"
#include "playlist.h"
#include "frontend/frontend_driver.h"
#include "audio/audio_driver.h"
@ -263,11 +265,20 @@ static unsigned fastforward_after_frames = 0;
static retro_usec_t runloop_frame_time_last = 0;
static retro_time_t frame_limit_minimum_time = 0.0;
static retro_time_t frame_limit_last_time = 0.0;
static retro_time_t libretro_core_runtime_usec = 0;
extern bool input_driver_flushing_input;
static char launch_arguments[4096];
void rarch_core_runtime_tick(void)
{
struct retro_system_av_info *av_info = video_viewport_get_system_av_info();
if (av_info && av_info->timing.fps)
libretro_core_runtime_usec += (1.0 / av_info->timing.fps) * 1000 * 1000;
}
#ifdef HAVE_THREADS
void runloop_msg_queue_lock(void)
{
@ -1813,6 +1824,82 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
/ (av_info->timing.fps * fastforward_ratio));
}
break;
case RARCH_CTL_CONTENT_RUNTIME_LOG_INIT:
libretro_core_runtime_usec = 0;
break;
case RARCH_CTL_CONTENT_RUNTIME_LOG_DEINIT:
{
settings_t *settings = config_get_ptr();
unsigned seconds = libretro_core_runtime_usec / 1000 / 1000;
unsigned minutes = seconds / 60;
unsigned hours = minutes / 60;
char log[PATH_MAX_LENGTH] = {0};
size_t pos = 0;
seconds -= minutes * 60;
seconds -= hours * 60 *60;
pos = strlcpy(log, "Content ran for a total of", sizeof(log));
if (hours > 0)
pos += snprintf(log + pos, sizeof(log) - pos, ", %d hours", hours);
if (minutes > 0)
pos += snprintf(log + pos, sizeof(log) - pos, ", %d minutes", minutes);
pos += snprintf(log + pos, sizeof(log) - pos, ", %d seconds", seconds);
if (pos < sizeof(log) - 2)
{
log[pos++] = '.';
log[pos++] = '\n';
}
RARCH_LOG(log);
if (settings->bools.content_runtime_log && g_defaults.content_runtime)
{
const char *path = path_get(RARCH_PATH_CONTENT);
const char *core_path = path_get(RARCH_PATH_CORE);
if (!string_is_empty(path) && !string_is_empty(core_path))
{
playlist_push_runtime(g_defaults.content_runtime, path_get(RARCH_PATH_CONTENT), path_get(RARCH_PATH_CORE), 0, 0, 0);
/* if entry already existed, the runtime won't be updated, so manually update it again */
if (playlist_get_size(g_defaults.content_runtime) > 0)
{
unsigned runtime_hours = 0;
unsigned runtime_minutes = 0;
unsigned runtime_seconds = 0;
playlist_get_runtime_index(g_defaults.content_runtime, 0, NULL, NULL, &runtime_hours, &runtime_minutes, &runtime_seconds);
runtime_seconds += seconds;
if (runtime_seconds >= 60)
{
runtime_minutes += runtime_seconds / 60;
runtime_seconds -= runtime_minutes * 60;
}
runtime_minutes += minutes;
if (runtime_minutes >= 60)
{
runtime_hours += runtime_minutes / 60;
runtime_minutes -= runtime_hours * 60;
}
runtime_hours += hours;
playlist_update_runtime(g_defaults.content_runtime, 0, path_get(RARCH_PATH_CONTENT), path_get(RARCH_PATH_CORE), runtime_hours, runtime_minutes, runtime_seconds);
}
}
}
break;
}
case RARCH_CTL_GET_PERFCNT:
{
bool **perfcnt = (bool**)data;
@ -3685,10 +3772,16 @@ int runloop_iterate(unsigned *sleep_ms)
)
run_ahead(run_ahead_num_frames, settings->bools.run_ahead_secondary_instance);
else
{
core_run();
rarch_core_runtime_tick();
}
}
#else
core_run();
{
core_run();
rarch_core_runtime_tick();
}
#endif
#ifdef HAVE_CHEEVOS

View File

@ -897,3 +897,6 @@
# File format to use when writing playlists to disk
# playlist_use_old_format = false
# Keep track of how long each core+content has been running for over time
# content_runtime_log = false

View File

@ -183,7 +183,10 @@ enum rarch_ctl_state
/* HTTP server */
RARCH_CTL_HTTPSERVER_INIT,
RARCH_CTL_HTTPSERVER_DESTROY
RARCH_CTL_HTTPSERVER_DESTROY,
RARCH_CTL_CONTENT_RUNTIME_LOG_INIT,
RARCH_CTL_CONTENT_RUNTIME_LOG_DEINIT
};
enum rarch_capabilities
@ -417,6 +420,8 @@ void runloop_msg_queue_unlock(void);
void rarch_force_video_driver_fallback(const char *driver);
void rarch_core_runtime_tick(void);
RETRO_END_DECLS
#endif