1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-03 00:38:44 +00:00

Add persistent content runtime logging

This commit is contained in:
jdgleaver 2019-02-27 16:19:29 +00:00
parent ebae7ebc79
commit 38c54eaebe
7 changed files with 759 additions and 49 deletions

View File

@ -239,6 +239,7 @@ OBJ += frontend/frontend.o \
core_info.o \
$(LIBRETRO_COMM_DIR)/file/config_file.o \
$(LIBRETRO_COMM_DIR)/file/config_file_userdata.o \
$(LIBRETRO_COMM_DIR)/file/runtime_file.o \
tasks/task_screenshot.o \
tasks/task_powerstate.o \
$(LIBRETRO_COMM_DIR)/gfx/scaler/scaler.o \

View File

@ -94,7 +94,8 @@ enum file_path_enum
FILE_PATH_S3M_EXTENSION,
FILE_PATH_XM_EXTENSION,
FILE_PATH_CONFIG_EXTENSION,
FILE_PATH_CORE_INFO_EXTENSION
FILE_PATH_CORE_INFO_EXTENSION,
FILE_PATH_RUNTIME_EXTENSION
};
enum application_special_type

View File

@ -227,6 +227,9 @@ const char *file_path_str(enum file_path_enum enum_idx)
case FILE_PATH_TTF_FONT:
str = "font.ttf";
break;
case FILE_PATH_RUNTIME_EXTENSION:
str = ".lrtl";
break;
case FILE_PATH_UNKNOWN:
default:
break;

View File

@ -140,6 +140,11 @@ CONFIG FILE
#include "../managers/core_manager.c"
#include "../managers/core_option_manager.c"
/*============================================================
RUNTIME FILE
============================================================ */
#include "../libretro-common/file/runtime_file.c"
/*============================================================
ACHIEVEMENTS
============================================================ */

View File

@ -0,0 +1,534 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (runtime_file.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <retro_miscellaneous.h>
#include <file_path_special.h>
#include <dirs.h>
#include <core_info.h>
#include <configuration.h>
#include <file/file_path.h>
#include <streams/file_stream.h>
#include <string/stdstring.h>
#include <verbosity.h>
#include <file/runtime_file.h>
#define LOG_FILE_FORMAT_STR "%u:%02u:%02u\n%04u-%02u-%02u %02u:%02u:%02u\n"
/* Initialisation */
/* Parses log file referenced by runtime_log->path.
* Does nothing if log file does not exist. */
static void runtime_log_read_file(runtime_log_t *runtime_log)
{
unsigned runtime_hours = 0;
unsigned runtime_minutes = 0;
unsigned runtime_seconds = 0;
unsigned last_played_year = 0;
unsigned last_played_month = 0;
unsigned last_played_day = 0;
unsigned last_played_hour = 0;
unsigned last_played_minute = 0;
unsigned last_played_second = 0;
int ret = 0;
RFILE *file = NULL;
/* Check if log file exists */
if (!filestream_exists(runtime_log->path))
return;
/* Attempt to open log file */
file = filestream_open(runtime_log->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
{
RARCH_ERR("Failed to open runtime log file: %s\n", runtime_log->path);
return;
}
/* Parse log file */
ret = filestream_scanf(file, LOG_FILE_FORMAT_STR,
&runtime_hours, &runtime_minutes, &runtime_seconds,
&last_played_year, &last_played_month, &last_played_day,
&last_played_hour, &last_played_minute, &last_played_second);
if (ret == 9)
{
/* All is well - assign values to runtime_log object */
runtime_log->runtime.hours = runtime_hours;
runtime_log->runtime.minutes = runtime_minutes;
runtime_log->runtime.seconds = runtime_seconds;
runtime_log->last_played.year = last_played_year;
runtime_log->last_played.month = last_played_month;
runtime_log->last_played.day = last_played_day;
runtime_log->last_played.hour = last_played_hour;
runtime_log->last_played.minute = last_played_minute;
runtime_log->last_played.second = last_played_second;
}
else
RARCH_ERR("Invalid runtime log file: %s\n", runtime_log->path);
/* Close log file */
filestream_close(file);
}
/* Initialise runtime log, loading current parameters
* if log file exists. Returned object must be free()'d.
* Returns NULL if content_path and/or core_path are invalid */
runtime_log_t *runtime_log_init(const char *content_path, const char *core_path)
{
settings_t *settings = config_get_ptr();
core_info_list_t *core_info = NULL;
runtime_log_t *runtime_log = NULL;
const char *savefile_dir = dir_get(RARCH_DIR_SAVEFILE);
char content_name[PATH_MAX_LENGTH];
char core_name[PATH_MAX_LENGTH];
char log_file_dir[PATH_MAX_LENGTH];
char log_file_path[PATH_MAX_LENGTH];
unsigned i;
content_name[0] = '\0';
core_name[0] = '\0';
log_file_dir[0] = '\0';
log_file_path[0] = '\0';
/* Error checking */
if (!settings)
return NULL;
if (string_is_empty(content_path) || string_is_empty(core_path) || string_is_empty(savefile_dir))
return NULL;
if (string_is_equal(core_path, "builtin"))
return NULL;
/* Get core name */
core_info_get_list(&core_info);
if (!core_info)
return NULL;
for (i = 0; i < core_info->count; i++)
{
if (string_is_equal(core_info->list[i].path, core_path))
{
strlcpy(core_name, core_info->list[i].core_name, sizeof(core_name));
break;
}
}
if (string_is_empty(core_name))
return NULL;
/* Get runtime log directory */
if (settings->bools.sort_savefiles_enable)
{
fill_pathname_join(
log_file_dir,
savefile_dir,
core_name,
sizeof(log_file_dir));
}
else
{
strlcpy(log_file_dir, savefile_dir, sizeof(log_file_dir));
}
if (string_is_empty(log_file_dir))
return NULL;
/* Create directory, if required */
if (!path_is_directory(log_file_dir))
{
path_mkdir(log_file_dir);
if(!path_is_directory(log_file_dir))
{
RARCH_ERR("Failed to create directory for runtime log: %s.\n", log_file_dir);
return NULL;
}
}
/* Get content name
* Note: TyrQuake requires a specific hack, since all
* content has the same name... */
if (string_is_equal(core_name, "TyrQuake"))
{
const char *last_slash = find_last_slash(content_path);
if (last_slash)
{
size_t path_length = last_slash + 1 - content_path;
if (path_length < PATH_MAX_LENGTH)
{
char tmp[PATH_MAX_LENGTH];
memset(tmp, 0, sizeof(tmp));
strlcpy(tmp, content_path, path_length * sizeof(char));
strlcpy(content_name, path_basename(tmp), sizeof(content_name));
}
}
}
else
{
/* path_remove_extension() requires a char * (not const)
* so have to use a temporary buffer... */
char *tmp = strdup(path_basename(content_path));
strlcpy(
content_name,
path_remove_extension(tmp),
sizeof(content_name));
if (!string_is_empty(tmp))
free(tmp);
}
if (string_is_empty(content_name))
return NULL;
/* Build final log file path */
fill_pathname_join(log_file_path, log_file_dir, content_name, sizeof(log_file_path));
if (!settings->bools.sort_savefiles_enable)
{
strlcat(log_file_path, " - ", sizeof(log_file_path));
strlcat(log_file_path, core_name, sizeof(log_file_path));
}
strlcat(log_file_path, file_path_str(FILE_PATH_RUNTIME_EXTENSION), sizeof(log_file_path));
if (string_is_empty(log_file_path))
return NULL;
/* Phew... If we get this far then all is well.
* > Create 'runtime_log' object */
runtime_log = (runtime_log_t*)calloc(1, sizeof(*runtime_log));
if (!runtime_log)
return NULL;
/* > Populate default values */
runtime_log->runtime.hours = 0;
runtime_log->runtime.minutes = 0;
runtime_log->runtime.seconds = 0;
runtime_log->last_played.year = 0;
runtime_log->last_played.month = 0;
runtime_log->last_played.day = 0;
runtime_log->last_played.hour = 0;
runtime_log->last_played.minute = 0;
runtime_log->last_played.second = 0;
strlcpy(runtime_log->path, log_file_path, sizeof(runtime_log->path));
/* Load existing log file, if it exists */
runtime_log_read_file(runtime_log);
return runtime_log;
}
/* Setters */
/* Set runtime to specified hours, minutes, seconds value */
void runtime_log_set_runtime_hms(runtime_log_t *runtime_log, unsigned hours, unsigned minutes, unsigned seconds)
{
retro_time_t usec;
if (!runtime_log)
return;
/* Converting to usec and back again may be considered a
* waste of CPU cycles, but this allows us to handle any
* kind of broken input without issue - i.e. user can enter
* minutes and seconds values > 59, and everything still
* works correctly */
runtime_log_convert_hms2usec(hours, minutes, seconds, &usec);
runtime_log_convert_usec2hms(usec,
&runtime_log->runtime.hours, &runtime_log->runtime.minutes, &runtime_log->runtime.seconds);
}
/* Set runtime to specified microseconds value */
void runtime_log_set_runtime_usec(runtime_log_t *runtime_log, retro_time_t usec)
{
if (!runtime_log)
return;
runtime_log_convert_usec2hms(usec,
&runtime_log->runtime.hours, &runtime_log->runtime.minutes, &runtime_log->runtime.seconds);
}
/* Adds specified hours, minutes, seconds value to current runtime */
void runtime_log_add_runtime_hms(runtime_log_t *runtime_log, unsigned hours, unsigned minutes, unsigned seconds)
{
retro_time_t usec_old;
retro_time_t usec_new;
if (!runtime_log)
return;
runtime_log_convert_hms2usec(
runtime_log->runtime.hours, runtime_log->runtime.minutes, runtime_log->runtime.seconds,
&usec_old);
runtime_log_convert_hms2usec(hours, minutes, seconds, &usec_new);
runtime_log_convert_usec2hms(usec_old + usec_new,
&runtime_log->runtime.hours, &runtime_log->runtime.minutes, &runtime_log->runtime.seconds);
}
/* Adds specified microseconds value to current runtime */
void runtime_log_add_runtime_usec(runtime_log_t *runtime_log, retro_time_t usec)
{
retro_time_t usec_old;
if (!runtime_log)
return;
runtime_log_convert_hms2usec(
runtime_log->runtime.hours, runtime_log->runtime.minutes, runtime_log->runtime.seconds,
&usec_old);
runtime_log_convert_usec2hms(usec_old + usec,
&runtime_log->runtime.hours, &runtime_log->runtime.minutes, &runtime_log->runtime.seconds);
}
/* Sets last played entry to specified value */
void runtime_log_set_last_played(runtime_log_t *runtime_log,
unsigned year, unsigned month, unsigned day,
unsigned hour, unsigned minute, unsigned second)
{
if (!runtime_log)
return;
/* This function should never be needed, so just
* perform dumb value assignment (i.e. no validation
* using mktime()) */
runtime_log->last_played.year = year;
runtime_log->last_played.month = month;
runtime_log->last_played.day = day;
runtime_log->last_played.hour = hour;
runtime_log->last_played.minute = minute;
runtime_log->last_played.second = second;
}
/* Sets last played entry to current date/time */
void runtime_log_set_last_played_now(runtime_log_t *runtime_log)
{
time_t current_time;
struct tm * time_info;
if (!runtime_log)
return;
/* Get current time */
time(&current_time);
time_info = localtime(&current_time);
/* This can actually happen, but if does we probably
* have bigger problems to worry about... */
if(!time_info)
{
RARCH_ERR("Failed to get current time.\n");
return;
}
/* Extract values */
runtime_log->last_played.year = (unsigned)time_info->tm_year + 1900;
runtime_log->last_played.month = (unsigned)time_info->tm_mon + 1;
runtime_log->last_played.day = (unsigned)time_info->tm_mday;
runtime_log->last_played.hour = (unsigned)time_info->tm_hour;
runtime_log->last_played.minute = (unsigned)time_info->tm_min;
runtime_log->last_played.second = (unsigned)time_info->tm_sec;
}
/* Resets log to default (zero) values */
void runtime_log_reset(runtime_log_t *runtime_log)
{
if (!runtime_log)
return;
runtime_log->runtime.hours = 0;
runtime_log->runtime.minutes = 0;
runtime_log->runtime.seconds = 0;
runtime_log->last_played.year = 0;
runtime_log->last_played.month = 0;
runtime_log->last_played.day = 0;
runtime_log->last_played.hour = 0;
runtime_log->last_played.minute = 0;
runtime_log->last_played.second = 0;
}
/* Getters */
/* Gets runtime in hours, minutes, seconds */
void runtime_log_get_runtime_hms(runtime_log_t *runtime_log, unsigned *hours, unsigned *minutes, unsigned *seconds)
{
if (!runtime_log)
return;
*hours = runtime_log->runtime.hours;
*minutes = runtime_log->runtime.minutes;
*seconds = runtime_log->runtime.seconds;
}
/* Gets runtime in microseconds */
void runtime_log_get_runtime_usec(runtime_log_t *runtime_log, retro_time_t *usec)
{
if (!runtime_log)
return;
runtime_log_convert_hms2usec(
runtime_log->runtime.hours, runtime_log->runtime.minutes, runtime_log->runtime.seconds,
usec);
}
/* Gets last played entry values */
void runtime_log_get_last_played(runtime_log_t *runtime_log,
unsigned *year, unsigned *month, unsigned *day,
unsigned *hour, unsigned *minute, unsigned *second)
{
if (!runtime_log)
return;
*year = runtime_log->last_played.year;
*month = runtime_log->last_played.month;
*day = runtime_log->last_played.day;
*hour = runtime_log->last_played.hour;
*minute = runtime_log->last_played.minute;
*second = runtime_log->last_played.second;
}
/* Gets last played entry values as a time_t 'object'
* (e.g. for printing with strftime()) */
void runtime_log_get_last_played_time(runtime_log_t *runtime_log, time_t *time)
{
struct tm time_info;
if (!runtime_log)
return;
if (!time)
return;
/* Set tm values */
time_info.tm_year = (int)runtime_log->last_played.year - 1900;
time_info.tm_mon = (int)runtime_log->last_played.month - 1;
time_info.tm_mday = (int)runtime_log->last_played.day;
time_info.tm_hour = (int)runtime_log->last_played.hour;
time_info.tm_min = (int)runtime_log->last_played.minute;
time_info.tm_sec = (int)runtime_log->last_played.second;
time_info.tm_isdst = -1;
/* Get time */
*time = mktime(&time_info);
}
/* Status */
/* Returns true if log has a non-zero runtime entry */
bool runtime_log_has_runtime(runtime_log_t *runtime_log)
{
if (!runtime_log)
return false;
return !((runtime_log->runtime.hours == 0) &&
(runtime_log->runtime.minutes == 0) &&
(runtime_log->runtime.seconds == 0));
}
/* Returns true if log has a non-zero last played entry */
bool runtime_log_has_last_played(runtime_log_t *runtime_log)
{
if (!runtime_log)
return false;
return !((runtime_log->last_played.year == 0) &&
(runtime_log->last_played.month == 0) &&
(runtime_log->last_played.day == 0) &&
(runtime_log->last_played.hour == 0) &&
(runtime_log->last_played.minute == 0) &&
(runtime_log->last_played.second == 0));
}
/* Saving */
/* Saves specified runtime log to disk */
void runtime_log_save(runtime_log_t *runtime_log)
{
int ret = 0;
RFILE *file = NULL;
if (!runtime_log)
return;
/* Attempt to open log file */
file = filestream_open(runtime_log->path, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
{
RARCH_ERR("Failed to open runtime log file: %s\n", runtime_log->path);
return;
}
/* Write log file contents */
ret = filestream_printf(file, LOG_FILE_FORMAT_STR,
runtime_log->runtime.hours, runtime_log->runtime.minutes, runtime_log->runtime.seconds,
runtime_log->last_played.year, runtime_log->last_played.month, runtime_log->last_played.day,
runtime_log->last_played.hour, runtime_log->last_played.minute, runtime_log->last_played.second);
if (ret <= 0)
RARCH_ERR("Failed to write runtime log file: %s\n", runtime_log->path);
/* Close log file */
filestream_close(file);
}
/* Utility functions */
/* Convert from hours, minutes, seconds to microseconds */
void runtime_log_convert_hms2usec(unsigned hours, unsigned minutes, unsigned seconds, retro_time_t *usec)
{
*usec = ((retro_time_t)hours * 60 * 60 * 1000000) +
((retro_time_t)minutes * 60 * 1000000) +
((retro_time_t)seconds * 1000000);
}
/* Convert from microseconds to hours, minutes, seconds */
void runtime_log_convert_usec2hms(retro_time_t usec, unsigned *hours, unsigned *minutes, unsigned *seconds)
{
*seconds = usec / 1000000;
*minutes = *seconds / 60;
*hours = *minutes / 60;
*seconds -= *minutes * 60;
*minutes -= *hours * 60;
}

View File

@ -0,0 +1,133 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (runtime_file.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __RUNTIME_FILE_H
#define __RUNTIME_FILE_H
#include <retro_common_api.h>
#include <libretro.h>
#include <time.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
typedef struct
{
unsigned hours;
unsigned minutes;
unsigned seconds;
} rtl_runtime_t;
typedef struct
{
unsigned year;
unsigned month;
unsigned day;
unsigned hour;
unsigned minute;
unsigned second;
} rtl_last_played_t;
typedef struct
{
rtl_runtime_t runtime;
rtl_last_played_t last_played;
char path[PATH_MAX_LENGTH];
} runtime_log_t;
/* Initialisation */
/* Initialise runtime log, loading current parameters
* if log file exists. Returned object must be free()'d.
* Returns NULL if content_path and/or core_path are invalid */
runtime_log_t *runtime_log_init(const char *content_path, const char *core_path);
/* Setters */
/* Set runtime to specified hours, minutes, seconds value */
void runtime_log_set_runtime_hms(runtime_log_t *runtime_log, unsigned hours, unsigned minutes, unsigned seconds);
/* Set runtime to specified microseconds value */
void runtime_log_set_runtime_usec(runtime_log_t *runtime_log, retro_time_t usec);
/* Adds specified hours, minutes, seconds value to current runtime */
void runtime_log_add_runtime_hms(runtime_log_t *runtime_log, unsigned hours, unsigned minutes, unsigned seconds);
/* Adds specified microseconds value to current runtime */
void runtime_log_add_runtime_usec(runtime_log_t *runtime_log, retro_time_t usec);
/* Sets last played entry to specified value */
void runtime_log_set_last_played(runtime_log_t *runtime_log,
unsigned year, unsigned month, unsigned day,
unsigned hour, unsigned minute, unsigned second);
/* Sets last played entry to current date/time */
void runtime_log_set_last_played_now(runtime_log_t *runtime_log);
/* Resets log to default (zero) values */
void runtime_log_reset(runtime_log_t *runtime_log);
/* Getters */
/* (Not strictly required, since we can get everything
* from runtime_log directly - but perhaps it is logically
* cleaner to have a symmetrical set/get interface) */
/* Gets runtime in hours, minutes, seconds */
void runtime_log_get_runtime_hms(runtime_log_t *runtime_log, unsigned *hours, unsigned *minutes, unsigned *seconds);
/* Gets runtime in microseconds */
void runtime_log_get_runtime_usec(runtime_log_t *runtime_log, retro_time_t *usec);
/* Gets last played entry values */
void runtime_log_get_last_played(runtime_log_t *runtime_log,
unsigned *year, unsigned *month, unsigned *day,
unsigned *hour, unsigned *minute, unsigned *second);
/* Gets last played entry values as a time_t 'object'
* (e.g. for printing with strftime()) */
void runtime_log_get_last_played_time(runtime_log_t *runtime_log, time_t *time);
/* Status */
/* Returns true if log has a non-zero runtime entry */
bool runtime_log_has_runtime(runtime_log_t *runtime_log);
/* Returns true if log has a non-zero last played entry */
bool runtime_log_has_last_played(runtime_log_t *runtime_log);
/* Saving */
/* Saves specified runtime log to disk */
void runtime_log_save(runtime_log_t *runtime_log);
/* Utility functions */
/* Convert from hours, minutes, seconds to microseconds */
void runtime_log_convert_hms2usec(unsigned hours, unsigned minutes, unsigned seconds, retro_time_t *usec);
/* Convert from microseconds to hours, minutes, seconds */
void runtime_log_convert_usec2hms(retro_time_t usec, unsigned *hours, unsigned *minutes, unsigned *seconds);
RETRO_END_DECLS
#endif

View File

@ -53,6 +53,7 @@
#include <queues/message_queue.h>
#include <queues/task_queue.h>
#include <features/features_cpu.h>
#include <file/runtime_file.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -2387,73 +2388,105 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
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;
unsigned hours = 0;
unsigned minutes = 0;
unsigned seconds = 0;
char log[PATH_MAX_LENGTH] = {0};
size_t pos = 0;
int n = 0;
seconds -= minutes * 60;
minutes -= hours * 60;
pos = strlcpy(log, "Content ran for a total of", sizeof(log));
if (hours > 0)
pos += snprintf(log + pos, sizeof(log) - pos, ", %u hours", hours);
if (minutes > 0)
pos += snprintf(log + pos, sizeof(log) - pos, ", %u minutes", minutes);
pos += snprintf(log + pos, sizeof(log) - pos, ", %u seconds", seconds);
if (pos < sizeof(log) - 2)
{
log[pos++] = '.';
log[pos++] = '\n';
}
runtime_log_convert_usec2hms(libretro_core_runtime_usec, &hours, &minutes, &seconds);
n = snprintf(log, sizeof(log),
"Content ran for a total of: %02u hours, %02u minutes, %02u seconds.\n ",
hours, minutes, seconds);
if ((n < 0) || (n >= PATH_MAX_LENGTH))
n = 0; /* Just silence any potential gcc warnings... */
RARCH_LOG(log);
if (settings->bools.content_runtime_log && g_defaults.content_runtime)
if (settings->bools.content_runtime_log)
{
const char *path = path_get(RARCH_PATH_CONTENT);
const char *content_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) && !string_is_equal(core_path, "builtin"))
if (!string_is_empty(content_path) && !string_is_empty(core_path) && !string_is_equal(core_path, "builtin"))
{
playlist_push_runtime(g_defaults.content_runtime, path, core_path, 0, 0, 0);
unsigned playlist_hours = 0;
unsigned playlist_minutes = 0;
unsigned playlist_seconds = 0;
runtime_log_t *runtime_log = NULL;
bool playlist_file_is_valid = false;
bool runtime_log_file_is_valid = false;
/* if entry already existed, the runtime won't be updated, so manually update it again */
if (playlist_get_size(g_defaults.content_runtime) > 0)
/* Intialise content_runtime playlist entry and get
* existing values */
if (g_defaults.content_runtime)
{
unsigned runtime_hours = 0;
unsigned runtime_minutes = 0;
unsigned runtime_seconds = 0;
/* Push current entry to the top (does not update runtime
* values), or create new entry if it does not already exist */
playlist_push_runtime(g_defaults.content_runtime, content_path, core_path, 0, 0, 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)
/* Get current runtime */
if (playlist_get_size(g_defaults.content_runtime) > 0)
{
unsigned new_minutes = runtime_seconds / 60;
runtime_minutes += new_minutes;
runtime_seconds -= new_minutes * 60;
playlist_get_runtime_index(g_defaults.content_runtime, 0, NULL, NULL,
&playlist_hours, &playlist_minutes, &playlist_seconds);
playlist_file_is_valid = true;
}
}
/* Initialise runtime log file */
runtime_log = runtime_log_init(content_path, core_path);
if (runtime_log)
{
/* If runtime log file is empty, populate it with values
* from content_runtime playlist */
if (!runtime_log_has_runtime(runtime_log))
{
runtime_log_set_runtime_hms(runtime_log,
playlist_hours, playlist_minutes, playlist_seconds);
}
runtime_minutes += minutes;
/* Add additional runtime */
runtime_log_add_runtime_usec(runtime_log, libretro_core_runtime_usec);
if (runtime_minutes >= 60)
/* Read back current runtime, so we can copy it
* to content_runtime playlist */
runtime_log_get_runtime_hms(runtime_log,
&playlist_hours, &playlist_minutes, &playlist_seconds);
/* Update 'last played' entry */
runtime_log_set_last_played_now(runtime_log);
/* Save runtime log file */
runtime_log_save(runtime_log);
/* Clean up */
free(runtime_log);
runtime_log_file_is_valid = true;
}
/* Update content_runtime playlist */
if (playlist_file_is_valid)
{
/* If something went wrong with the runtime log
* file (can't happen...), then playlist_hours/minutes/seconds
* still contains original (old) values. Have to update them
* manually... */
if (!runtime_log_file_is_valid)
{
unsigned new_hours = runtime_minutes / 60;
runtime_hours += new_hours;
runtime_minutes -= new_hours * 60;
retro_time_t usec_old;
runtime_log_convert_hms2usec(
playlist_hours, playlist_minutes, playlist_seconds, &usec_old);
runtime_log_convert_usec2hms(usec_old + libretro_core_runtime_usec,
&playlist_hours, &playlist_minutes, &playlist_seconds);
}
runtime_hours += hours;
playlist_update_runtime(g_defaults.content_runtime, 0, path, core_path, runtime_hours, runtime_minutes, runtime_seconds);
playlist_update_runtime(g_defaults.content_runtime, 0, content_path, core_path,
playlist_hours, playlist_minutes, playlist_seconds);
}
}
}