From a9b5f7211c7d8f77d2947bcea8b017a2d6bcf0d1 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Tue, 12 Jul 2022 02:27:03 +0200 Subject: [PATCH] Some changes. Read full commit message. * title: implemented titleGetNcmStorageIdName(). * nxdt_log: implemented log verbosity levels (debug, info, warning, error, none) and helper macros for each level. The rest of the codebase still needs to be updated to take advantange of this change. * nxdt_log: implemented auxiliary logging via nxlink, if available. * nxdt_utils: system CPU/MEM overclocking is now only applied through utilsSetLongRunningProcessState(), as it should have been from the beginning. * nxdt_utils: nxlink initialization is now carried out without redirecting stdout and/or stderr, entirely removing the need for utilsRestoreConsoleOutput(). utilsGetNxLinkFileDescriptor() is used to send data to the nxlink host via dprintf() in log functions. --- Makefile | 23 +++++++- code_templates/nsp_dumper.c | 8 +-- include/core/nxdt_includes.h | 3 + include/core/nxdt_log.h | 98 ++++++++++++++++++++++++++++++--- include/core/nxdt_utils.h | 9 +-- include/core/title.h | 3 + source/core/nxdt_log.c | 89 +++++++++++++++++++++++------- source/core/nxdt_utils.c | 104 ++++++++++++++--------------------- source/core/title.c | 17 +++++- source/options_tab.cpp | 6 +- source/tasks.cpp | 5 +- todo.txt | 5 -- 12 files changed, 255 insertions(+), 115 deletions(-) diff --git a/Makefile b/Makefile index 9216949..912cc0d 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,9 @@ ifeq ($(strip $(DEVKITPRO)),) $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") endif +# Uncomment to enable verbose output. +#V:=1 + TOPDIR ?= $(CURDIR) include $(DEVKITPRO)/libnx/switch_rules @@ -38,11 +41,13 @@ include $(DEVKITPRO)/libnx/switch_rules # NACP building is skipped as well. #--------------------------------------------------------------------------------- +ROOTDIR ?= $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) GIT_COMMIT := $(shell git rev-parse --short HEAD) GIT_REV := ${GIT_BRANCH}-${GIT_COMMIT} -ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) +ifneq (,$(strip $(shell git status --porcelain 2>/dev/null))) GIT_REV := $(GIT_REV)-dirty endif @@ -72,7 +77,7 @@ ROMFS := romfs BOREALIS_PATH := libs/borealis BOREALIS_RESOURCES := romfs:/ -USBHSFS_PATH := $(TOPDIR)/libs/libusbhsfs +USBHSFS_PATH := $(ROOTDIR)/libs/libusbhsfs #--------------------------------------------------------------------------------- # options for code generation @@ -98,7 +103,7 @@ LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -ljson-c -lz -lusbhsfs #--------------------------------------------------------------------------------- LIBDIRS := $(PORTLIBS) $(LIBNX) $(USBHSFS_PATH) -include $(TOPDIR)/$(BOREALIS_PATH)/library/borealis.mk +include $(ROOTDIR)/$(BOREALIS_PATH)/library/borealis.mk #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -244,6 +249,18 @@ $(OUTPUT).elf : $(OFILES) $(OFILES_SRC) : $(HFILES_BIN) +#--------------------------------------------------------------------------------- +# Overrides for devkitA64/base_rules targets. +#--------------------------------------------------------------------------------- + +%.o: %.cpp + $(SILENTMSG) $(notdir $<) + $(SILENTCMD)$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -D__FILENAME__="\"$(subst $(ROOTDIR),,$(realpath $<))\"" -c $< -o $@ $(ERROR_FILTER) + +%.o: %.c + $(SILENTMSG) $(notdir $<) + $(SILENTCMD)$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -D__FILENAME__="\"$(subst $(ROOTDIR),,$(realpath $<))\"" -c $< -o $@ $(ERROR_FILTER) + #--------------------------------------------------------------------------------- # you need a rule like this for each extension you use as binary data #--------------------------------------------------------------------------------- diff --git a/code_templates/nsp_dumper.c b/code_templates/nsp_dumper.c index 5df7153..70ce115 100644 --- a/code_templates/nsp_dumper.c +++ b/code_templates/nsp_dumper.c @@ -956,7 +956,7 @@ static void nspDump(TitleInfo *title_info) consolePrint("publisher: %s\n", app_metadata->lang_entry.author); } - consolePrint("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); + consolePrint("source storage: %s\n", titleGetNcmStorageIdName(title_info->storage_id)); consolePrint("title id: %016lX\n", title_info->meta_key.id); consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, title_info->version.system_version.micro, title_info->version.system_version.major_relstep, \ title_info->version.system_version.minor_relstep); @@ -1168,10 +1168,10 @@ int main(int argc, char *argv[]) consolePrint("selected %s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); - consolePrint("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); + consolePrint("source storage: %s\n", titleGetNcmStorageIdName(title_info->storage_id)); if (title_info->meta_key.type != NcmContentMetaType_Application) consolePrint("title id: %016lX\n", title_info->meta_key.id); - consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, title_info->version.system_version.micro, title_info->version.system_version.major_relstep, \ - title_info->version.system_version.minor_relstep); + consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, \ + title_info->version.system_version.micro, title_info->version.system_version.major_relstep, title_info->version.system_version.minor_relstep); consolePrint("content count: %u\n", title_info->content_count); consolePrint("size: %s\n", title_info->size_str); } diff --git a/include/core/nxdt_includes.h b/include/core/nxdt_includes.h index b78ab8f..02f32e3 100644 --- a/include/core/nxdt_includes.h +++ b/include/core/nxdt_includes.h @@ -50,6 +50,9 @@ /* libnx header. */ #include +/* Internet operations. */ +#include + /* Global defines. */ #include "../defines.h" diff --git a/include/core/nxdt_log.h b/include/core/nxdt_log.h index a245abc..7854184 100644 --- a/include/core/nxdt_log.h +++ b/include/core/nxdt_log.h @@ -28,28 +28,85 @@ extern "C" { #endif +/// Used to control logfile verbosity. +#define LOG_LEVEL_DEBUG 0 +#define LOG_LEVEL_INFO 1 +#define LOG_LEVEL_WARNING 2 +#define LOG_LEVEL_ERROR 3 +#define LOG_LEVEL_NONE 4 + +/// Defines the log level used throughout the application. +/// Log messages with a log value lower than this one won't be compiled into the binary. +/// If a value lower than LOG_LEVEL_DEBUG or equal to/greater than LOG_LEVEL_NONE is used, logfile output will be entirely disabled. +#define LOG_LEVEL LOG_LEVEL_DEBUG /* TODO: change before release. */ + +#if (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) + /// Helper macros. -#define LOG_MSG(fmt, ...) logWriteFormattedStringToLogFile(__func__, fmt, ##__VA_ARGS__) -#define LOG_MSG_BUF(dst, dst_size, fmt, ...) logWriteFormattedStringToBuffer(dst, dst_size, __func__, fmt, ##__VA_ARGS__) -#define LOG_DATA(data, data_size, fmt, ...) logWriteBinaryDataToLogFile(data, data_size, __func__, fmt, ##__VA_ARGS__) + +#define LOG_MSG_GENERIC(level, fmt, ...) logWriteFormattedStringToLogFile(level, __FILENAME__, __LINE__, __func__, fmt, ##__VA_ARGS__) +#define LOG_MSG_BUF_GENERIC(dst, dst_size, level, fmt, ...) logWriteFormattedStringToBuffer(dst, dst_size, level, __FILENAME__, __LINE__, __func__, fmt, ##__VA_ARGS__) +#define LOG_DATA_GENERIC(data, data_size, level, fmt, ...) logWriteBinaryDataToLogFile(data, data_size, level, __FILENAME__, __LINE__, __func__, fmt, ##__VA_ARGS__) + +#define LOG_MSG_DEBUG(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__) +#define LOG_MSG_BUF_DEBUG(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__) +#define LOG_DATA_DEBUG(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__) + +/* TODO: delete these macros after migrating all log calls to the new verbosity-level-based system. */ +#define LOG_MSG(fmt, ...) LOG_MSG_DEBUG(fmt, ##__VA_ARGS__) +#define LOG_MSG_BUF(dst, dst_size, fmt, ...) LOG_MSG_BUF_DEBUG(dst, dst_size, fmt, ##__VA_ARGS__) +#define LOG_DATA(data, data_size, fmt, ...) LOG_DATA_DEBUG(data, data_size, fmt, ##__VA_ARGS__) + +#if LOG_LEVEL >= LOG_LEVEL_INFO +#define LOG_MSG_INFO(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_MSG_BUF_INFO(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_DATA_INFO(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__) +#else +#define LOG_MSG_INFO(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_INFO(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_INFO(data, data_size, fmt, ...) do {} while(0) +#endif /* LOG_LEVEL >= LOG_LEVEL_INFO */ + +#if LOG_LEVEL >= LOG_LEVEL_WARNING +#define LOG_MSG_WARNING(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__) +#define LOG_MSG_BUF_WARNING(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__) +#define LOG_DATA_WARNING(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__) +#else +#define LOG_MSG_WARNING(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_WARNING(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_WARNING(data, data_size, fmt, ...) do {} while(0) +#endif /* LOG_LEVEL >= LOG_LEVEL_WARNING */ + +#if LOG_LEVEL >= LOG_LEVEL_ERROR +#define LOG_MSG_ERROR(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) +#define LOG_MSG_BUF_ERROR(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) +#define LOG_DATA_ERROR(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) +#else +#define LOG_MSG_ERROR(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_ERROR(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_ERROR(data, data_size, fmt, ...) do {} while(0) +#endif /* LOG_LEVEL >= LOG_LEVEL_ERROR */ /// Writes the provided string to the logfile. +/// If the logfile hasn't been created and/or opened, this function takes care of it. void logWriteStringToLogFile(const char *src); /// Writes a formatted log string to the logfile. -__attribute__((format(printf, 2, 3))) void logWriteFormattedStringToLogFile(const char *func_name, const char *fmt, ...); +/// If the logfile hasn't been created and/or opened, this function takes care of it. +__attribute__((format(printf, 5, 6))) void logWriteFormattedStringToLogFile(u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...); /// Writes a formatted log string to the provided buffer. /// If the buffer isn't big enough to hold both its current contents and the new formatted string, it will be resized. -__attribute__((format(printf, 4, 5))) void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, const char *func_name, const char *fmt, ...); +__attribute__((format(printf, 7, 8))) void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...); /// Writes a formatted log string + a hex string representation of the provided binary data to the logfile. -__attribute__((format(printf, 4, 5))) void logWriteBinaryDataToLogFile(const void *data, size_t data_size, const char *func_name, const char *fmt, ...); +/// If the logfile hasn't been created and/or opened, this function takes care of it. +__attribute__((format(printf, 7, 8))) void logWriteBinaryDataToLogFile(const void *data, size_t data_size, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...); /// Forces a flush operation on the logfile. void logFlushLogFile(void); -/// Closes the logfile. +/// Write any pending data to the logfile, flushes it and then closes it. void logCloseLogFile(void); /// Stores the last log message in the provided buffer. @@ -59,6 +116,33 @@ void logGetLastMessage(char *dst, size_t dst_size); /// Use with caution. void logControlMutex(bool lock); +#else /* (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) */ + +/// Helper macros. + +#define LOG_STR(src) do {} while(0) +#define LOG_MSG_GENERIC(level, fmt, ...) do {} while(0) +#define LOG_MSG_BUF_GENERIC(dst, dst_size, level, fmt, ...) do {} while(0) +#define LOG_DATA_GENERIC(data, data_size, level, fmt, ...) do {} while(0) + +#define LOG_MSG_DEBUG(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_DEBUG(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_DEBUG(data, data_size, fmt, ...) do {} while(0) + +#define LOG_MSG_INFO(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_INFO(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_INFO(data, data_size, fmt, ...) do {} while(0) + +#define LOG_MSG_WARNING(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_WARNING(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_WARNING(data, data_size, fmt, ...) do {} while(0) + +#define LOG_MSG_ERROR(fmt, ...) do {} while(0) +#define LOG_MSG_BUF_ERROR(dst, dst_size, fmt, ...) do {} while(0) +#define LOG_DATA_ERROR(data, data_size, fmt, ...) do {} while(0) + +#endif /* (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) */ + #ifdef __cplusplus } #endif diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index de23ba6..37ff75a 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -74,6 +74,9 @@ void utilsCloseResources(void); /// Returns a pointer to the application launch path. const char *utilsGetLaunchPath(void); +/// Returns the nxlink socket descriptor, or -1 if an nxlink connection couldn't be established. +int utilsGetNxLinkFileDescriptor(void); + /// Returns a pointer to the FsFileSystem object for the SD card. FsFileSystem *utilsGetSdCardFileSystemObject(void); @@ -93,11 +96,9 @@ bool utilsAppletModeCheck(void); /// Returns a pointer to the FsStorage object for the eMMC BIS System partition. FsStorage *utilsGetEmmcBisSystemPartitionStorage(void); -/// Enables/disables CPU/MEM overclocking. -void utilsOverclockSystem(bool overclock); - -/// (Un)blocks HOME button presses and (un)sets screen dimming and auto sleep. +/// Blocks HOME button presses, disables screen dimming and auto sleep and overclocks system CPU/MEM. /// Must be called before starting long-running processes. +/// If state is set to false, regular system behavior is restored. void utilsSetLongRunningProcessState(bool state); /// Thread management functions. diff --git a/include/core/title.h b/include/core/title.h index 7b25f08..25b8d8a 100644 --- a/include/core/title.h +++ b/include/core/title.h @@ -137,6 +137,9 @@ char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 ille /// A valid gamecard must be inserted, and title info must have been loaded from it accordingly. char *titleGenerateGameCardFileName(u8 naming_convention, u8 illegal_char_replace_type); +/// Returns a pointer to a string holding a user-friendly name for the provided NcmStorageId value. Returns NULL if the provided value is invalid. +const char *titleGetNcmStorageIdName(u8 storage_id); + /// Returns a pointer to a string holding the name of the provided NcmContentType value. Returns NULL if the provided value is invalid. const char *titleGetNcmContentTypeName(u8 content_type); diff --git a/source/core/nxdt_log.c b/source/core/nxdt_log.c index faa25ce..915e3fa 100644 --- a/source/core/nxdt_log.c +++ b/source/core/nxdt_log.c @@ -21,6 +21,8 @@ #include "nxdt_utils.h" +#if (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) + /* Global variables. */ static Mutex g_logMutex = 0; @@ -33,34 +35,44 @@ static s64 g_logFileOffset = 0; static char *g_logBuffer = NULL; static size_t g_logBufferLength = 0; -static const char *g_logStrFormat = "[%d-%02d-%02d %02d:%02d:%02d.%09lu] %s -> "; +static const char *g_logStrFormat = "[%d-%02d-%02d %02d:%02d:%02d.%09lu] [%s] %s:%d:%s -> "; +static const char *g_logSessionSeparator = "________________________________________________________________\r\n"; + +static const char *g_logLevelNames[] = { + [LOG_LEVEL_DEBUG] = "DEBUG", + [LOG_LEVEL_INFO] = "INFO", + [LOG_LEVEL_WARNING] = "WARNING", + [LOG_LEVEL_ERROR] = "ERROR" +}; /* Function prototypes. */ static void _logWriteStringToLogFile(const char *src); -static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, const char *fmt, va_list args); +static void _logWriteFormattedStringToLogFile(bool save, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, va_list args); static void _logFlushLogFile(void); static bool logAllocateLogBuffer(void); static bool logOpenLogFile(void); +static void logWriteStringToNxLink(const char *str); + void logWriteStringToLogFile(const char *src) { SCOPED_LOCK(&g_logMutex) _logWriteStringToLogFile(src); } -__attribute__((format(printf, 2, 3))) void logWriteFormattedStringToLogFile(const char *func_name, const char *fmt, ...) +__attribute__((format(printf, 5, 6))) void logWriteFormattedStringToLogFile(u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...) { va_list args; va_start(args, fmt); - SCOPED_LOCK(&g_logMutex) _logWriteFormattedStringToLogFile(true, func_name, fmt, args); + SCOPED_LOCK(&g_logMutex) _logWriteFormattedStringToLogFile(true, level, file_name, line, func_name, fmt, args); va_end(args); } -__attribute__((format(printf, 4, 5))) void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, const char *func_name, const char *fmt, ...) +__attribute__((format(printf, 7, 8))) void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...) { - if (!dst || !dst_size || (!*dst && *dst_size) || (*dst && !*dst_size) || !func_name || !*func_name || !fmt || !*fmt) return; + if (!dst || !dst_size || (!*dst && *dst_size) || (*dst && !*dst_size) || level > LOG_LEVEL || !file_name || !*file_name || !func_name || !*func_name || !fmt || !*fmt) return; va_list args; @@ -86,7 +98,7 @@ __attribute__((format(printf, 4, 5))) void logWriteFormattedStringToBuffer(char ts.tm_mon++; /* Get formatted string length. */ - str1_len = snprintf(NULL, 0, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, func_name); + str1_len = snprintf(NULL, 0, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, g_logLevelNames[level], file_name, line, func_name); if (str1_len <= 0) goto end; str2_len = vsnprintf(NULL, 0, fmt, args); @@ -115,7 +127,7 @@ __attribute__((format(printf, 4, 5))) void logWriteFormattedStringToBuffer(char } /* Generate formatted string. */ - sprintf(dst_ptr + dst_str_len, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, func_name); + sprintf(dst_ptr + dst_str_len, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, g_logLevelNames[level], file_name, line, func_name); vsprintf(dst_ptr + dst_str_len + (size_t)str1_len, fmt, args); strcat(dst_ptr, CRLF); @@ -123,9 +135,9 @@ end: va_end(args); } -__attribute__((format(printf, 4, 5))) void logWriteBinaryDataToLogFile(const void *data, size_t data_size, const char *func_name, const char *fmt, ...) +__attribute__((format(printf, 7, 8))) void logWriteBinaryDataToLogFile(const void *data, size_t data_size, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...) { - if (!data || !data_size || !func_name || !*func_name || !fmt || !*fmt) return; + if (!data || !data_size || level > LOG_LEVEL || !file_name || !*file_name || !func_name || !*func_name || !fmt || !*fmt) return; va_list args; size_t data_str_size = ((data_size * 2) + 3); @@ -143,7 +155,7 @@ __attribute__((format(printf, 4, 5))) void logWriteBinaryDataToLogFile(const voi { /* Write formatted string. */ va_start(args, fmt); - _logWriteFormattedStringToLogFile(false, func_name, fmt, args); + _logWriteFormattedStringToLogFile(false, level, file_name, line, func_name, fmt, args); va_end(args); /* Write hex string representation. */ @@ -255,16 +267,19 @@ static void _logWriteStringToLogFile(const char *src) } } + /* Write data to nxlink. */ + logWriteStringToNxLink(src); + #if LOG_FORCE_FLUSH == 1 /* Flush log buffer. */ _logFlushLogFile(); #endif } -static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, const char *fmt, va_list args) +static void _logWriteFormattedStringToLogFile(bool save, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, va_list args) { /* Make sure we have allocated memory for the log buffer and opened the logfile. */ - if (!func_name || !*func_name || !fmt || !*fmt || !logAllocateLogBuffer() || !logOpenLogFile()) return; + if (level > LOG_LEVEL || !file_name || !*file_name || !func_name || !*func_name || !fmt || !*fmt || !logAllocateLogBuffer() || !logOpenLogFile()) return; Result rc = 0; @@ -286,7 +301,7 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, ts.tm_mon++; /* Get formatted string length. */ - str1_len = snprintf(NULL, 0, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, func_name); + str1_len = snprintf(NULL, 0, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, g_logLevelNames[level], file_name, line, func_name); if (str1_len <= 0) return; str2_len = vsnprintf(NULL, 0, fmt, args); @@ -318,9 +333,14 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, } /* Nice and easy string formatting using the log buffer. */ - sprintf(g_logBuffer + g_logBufferLength, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, func_name); + sprintf(g_logBuffer + g_logBufferLength, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, g_logLevelNames[level], file_name, line, func_name); vsprintf(g_logBuffer + g_logBufferLength + (size_t)str1_len, fmt, args); strcat(g_logBuffer, CRLF); + + /* Write data to nxlink. */ + logWriteStringToNxLink(g_logBuffer + g_logBufferLength); + + /* Update log buffer length. */ g_logBufferLength += log_str_len; } else { /* Flush log buffer. */ @@ -332,10 +352,13 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, if (!tmp_str) return; /* Generate formatted string. */ - sprintf(tmp_str, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, func_name); + sprintf(tmp_str, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, g_logLevelNames[level], file_name, line, func_name); vsprintf(tmp_str + (size_t)str1_len, fmt, args); strcat(tmp_str, CRLF); + /* Write data to nxlink. */ + logWriteStringToNxLink(tmp_str); + /* Write formatted string data until it no longer exceeds the log buffer size. */ while(log_str_len >= LOG_BUF_SIZE) { @@ -427,17 +450,41 @@ static bool logOpenLogFile(void) rc = fsFileGetSize(&g_logFile, &g_logFileOffset); if (R_SUCCEEDED(rc)) { + size_t len = 0; + /* Write UTF-8 BOM right away (if needed). */ if (!g_logFileOffset) { - size_t utf8_bom_len = strlen(UTF8_BOM); - fsFileWrite(&g_logFile, g_logFileOffset, UTF8_BOM, utf8_bom_len, FsWriteOption_Flush); - g_logFileOffset += (s64)utf8_bom_len; + len = strlen(UTF8_BOM); + fsFileWrite(&g_logFile, g_logFileOffset, UTF8_BOM, len, FsWriteOption_Flush); + g_logFileOffset += (s64)len; } - } else { - fsFileClose(&g_logFile); + + /* Write session separator right away. */ + len = strlen(g_logSessionSeparator); + rc = fsFileWrite(&g_logFile, g_logFileOffset, g_logSessionSeparator, len, FsWriteOption_Flush); + if (R_SUCCEEDED(rc)) g_logFileOffset += (s64)len; } } + /* Close file if we successfully opened it, but an error occurred afterwards. */ + if (R_FAILED(rc) && serviceIsActive(&(g_logFile.s))) + { + fsFileClose(&g_logFile); + memset(&g_logFile, 0, sizeof(FsFile)); + } + return R_SUCCEEDED(rc); } + +static void logWriteStringToNxLink(const char *str) +{ + int fd = utilsGetNxLinkFileDescriptor(); + if (fd >= 0) + { + dprintf(fd, "%s", str); + fsync(fd); + } +} + +#endif /* (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) */ diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index 3cc32e4..13a78f5 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -48,9 +48,11 @@ typedef struct { static bool g_resourcesInit = false; static Mutex g_resourcesMutex = 0; +static const char *g_appLaunchPath = NULL; + static FsFileSystem *g_sdCardFileSystem = NULL; -static const char *g_appLaunchPath = NULL; +static int g_nxLinkSocketFd = -1; static u8 g_customFirmwareType = UtilsCustomFirmwareType_Unknown; @@ -65,8 +67,6 @@ static AppletHookCookie g_systemOverclockCookie = {0}; static bool g_longRunningProcess = false; -static int g_stdoutFd = -1, g_stderrFd = -1, g_nxLinkSocketFd = -1; - static const char *g_sizeSuffixes[] = { "B", "KiB", "MiB", "GiB" }; static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes); @@ -102,13 +102,11 @@ static bool _utilsAppletModeCheck(void); static bool utilsMountEmmcBisSystemPartitionStorage(void); static void utilsUnmountEmmcBisSystemPartitionStorage(void); +static void utilsOverclockSystem(bool overclock); static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param); static void utilsChangeHomeButtonBlockStatus(bool block); -NX_INLINE void utilsCloseFileDescriptor(int *fd); -static void utilsRestoreConsoleOutput(void); - static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos); bool utilsInitializeResources(const int program_argc, const char **program_argv) @@ -134,17 +132,19 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv) break; } - /* Create logfile. */ - logWriteStringToLogFile("________________________________________________________________\r\n"); - LOG_MSG(APP_TITLE " v" APP_VERSION " starting (" GIT_REV "). Built on " BUILD_TIMESTAMP "."); - - /* Log Horizon OS version. */ - u32 hos_version = hosversionGet(); - LOG_MSG("Horizon OS version: %u.%u.%u.", HOSVER_MAJOR(hos_version), HOSVER_MINOR(hos_version), HOSVER_MICRO(hos_version)); - /* Initialize needed services. */ if (!servicesInitialize()) break; + /* Check if a valid nxlink host IP address was set by libnx. */ + /* If so, initialize nxlink connection without redirecting stdout and/or stderr. */ + if (__nxlink_host.s_addr != 0 && __nxlink_host.s_addr != INADDR_NONE) g_nxLinkSocketFd = nxlinkConnectToHost(false, false); + + /* Log info messages. */ + u32 hos_version = hosversionGet(); + LOG_MSG(APP_TITLE " v" APP_VERSION " starting (" GIT_REV "). Built on " BUILD_TIMESTAMP "."); + if (g_nxLinkSocketFd >= 0) LOG_MSG("nxlink enabled! Host IP address: %s.", inet_ntoa(__nxlink_host)); + LOG_MSG("Horizon OS version: %u.%u.%u.", HOSVER_MAJOR(hos_version), HOSVER_MINOR(hos_version), HOSVER_MICRO(hos_version)); + /* Retrieve custom firmware type. */ _utilsGetCustomFirmwareType(); if (g_customFirmwareType != UtilsCustomFirmwareType_Unknown) LOG_MSG("Detected %s CFW.", (g_customFirmwareType == UtilsCustomFirmwareType_Atmosphere ? "Atmosphère" : \ @@ -228,9 +228,6 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv) /* Initialize configuration interface. */ if (!configInitialize()) break; - /* Overclock system. */ - utilsOverclockSystem(configGetBoolean("overclock")); - /* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked). */ appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL); @@ -242,11 +239,6 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv) if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording(); } - /* Duplicate the stdout and stderr file handles, then redirect stdout and stderr over network to nxlink. */ - g_stdoutFd = dup(STDOUT_FILENO); - g_stderrFd = dup(STDERR_FILENO); - g_nxLinkSocketFd = nxlinkConnectToHost(true, true); - /* Update flags. */ ret = g_resourcesInit = true; } @@ -277,18 +269,12 @@ void utilsCloseResources(void) { SCOPED_LOCK(&g_resourcesMutex) { - /* Restore console output. */ - utilsRestoreConsoleOutput(); - /* Unset long running process state. */ utilsSetLongRunningProcessState(false); /* Unset our overclock applet hook. */ appletUnhook(&g_systemOverclockCookie); - /* Restore hardware clocks. */ - utilsOverclockSystem(false); - /* Close configuration interface. */ configExit(); @@ -322,6 +308,13 @@ void utilsCloseResources(void) /* Close HTTP interface. */ httpExit(); + /* Close nxlink socket. */ + if (g_nxLinkSocketFd >= 0) + { + close(g_nxLinkSocketFd); + g_nxLinkSocketFd = -1; + } + /* Close initialized services. */ servicesClose(); @@ -348,6 +341,11 @@ const char *utilsGetLaunchPath(void) return g_appLaunchPath; } +int utilsGetNxLinkFileDescriptor(void) +{ + return g_nxLinkSocketFd; +} + FsFileSystem *utilsGetSdCardFileSystemObject(void) { return g_sdCardFileSystem; @@ -378,26 +376,22 @@ FsStorage *utilsGetEmmcBisSystemPartitionStorage(void) return &g_emmcBisSystemPartitionStorage; } -void utilsOverclockSystem(bool overclock) -{ - u32 cpu_rate = ((overclock ? CPU_CLKRT_OVERCLOCKED : CPU_CLKRT_NORMAL) * 1000000); - u32 mem_rate = ((overclock ? MEM_CLKRT_OVERCLOCKED : MEM_CLKRT_NORMAL) * 1000000); - servicesChangeHardwareClockRates(cpu_rate, mem_rate); -} - void utilsSetLongRunningProcessState(bool state) { SCOPED_LOCK(&g_resourcesMutex) { - /* Don't proceed if the requested state matches the current one. */ - if (state == g_longRunningProcess) break; + /* Don't proceed if resources haven't been initialized, or if the requested state matches the current one. */ + if (!g_resourcesInit || state == g_longRunningProcess) break; /* Change HOME button block status. */ utilsChangeHomeButtonBlockStatus(state); - /* (Un)set screen dimming and auto sleep. */ + /* Enable/disable screen dimming and auto sleep. */ appletSetMediaPlaybackState(state); + /* Enable/disable system overclock. */ + utilsOverclockSystem(configGetBoolean("overclock") & state); + /* Update flag. */ g_longRunningProcess = state; } @@ -870,9 +864,6 @@ void utilsPrintConsoleError(const char *msg) padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl); padInitializeWithMask(&pad, 0x1000000FFUL); - /* Restore console output. */ - utilsRestoreConsoleOutput(); - /* Initialize console output. */ consoleInit(NULL); @@ -1118,13 +1109,22 @@ static void utilsUnmountEmmcBisSystemPartitionStorage(void) } } +static void utilsOverclockSystem(bool overclock) +{ + u32 cpu_rate = ((overclock ? CPU_CLKRT_OVERCLOCKED : CPU_CLKRT_NORMAL) * 1000000); + u32 mem_rate = ((overclock ? MEM_CLKRT_OVERCLOCKED : MEM_CLKRT_NORMAL) * 1000000); + servicesChangeHardwareClockRates(cpu_rate, mem_rate); +} + static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param) { (void)param; + /* Don't proceed if we're not dealing with a desired hook type. */ if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return; - utilsOverclockSystem(configGetBoolean("overclock")); + /* Overclock the system based on the overclock setting and the current long running state value. */ + SCOPED_LOCK(&g_resourcesMutex) utilsOverclockSystem(configGetBoolean("overclock") & g_longRunningProcess); } static void utilsChangeHomeButtonBlockStatus(bool block) @@ -1147,26 +1147,6 @@ NX_INLINE void utilsCloseFileDescriptor(int *fd) *fd = -1; } -static void utilsRestoreConsoleOutput(void) -{ - SCOPED_LOCK(&g_resourcesMutex) - { - if (g_nxLinkSocketFd >= 0) utilsCloseFileDescriptor(&g_nxLinkSocketFd); - - if (g_stdoutFd >= 0) - { - dup2(g_stdoutFd, STDOUT_FILENO); - utilsCloseFileDescriptor(&g_stdoutFd); - } - - if (g_stderrFd >= 0) - { - dup2(g_stderrFd, STDERR_FILENO); - utilsCloseFileDescriptor(&g_stderrFd); - } - } -} - static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_t cp_limit, size_t *last_cp_pos) { if (!str || !*str || !str_size || (!cp_limit && last_cp_pos) || (cp_limit && !last_cp_pos)) return 0; diff --git a/source/core/title.c b/source/core/title.c index c01be93..fc1be3c 100644 --- a/source/core/title.c +++ b/source/core/title.c @@ -62,6 +62,16 @@ static TitleStorage g_titleStorage[TITLE_STORAGE_COUNT] = {0}; static TitleInfo **g_orphanTitleInfo = NULL; static u32 g_orphanTitleInfoCount = 0; +static const char *g_titleNcmStorageIdNames[] = { + [NcmStorageId_None] = "None", + [NcmStorageId_Host] = "Host", + [NcmStorageId_GameCard] = "Gamecard", + [NcmStorageId_BuiltInSystem] = "eMMC (system)", + [NcmStorageId_BuiltInUser] = "eMMC (user)", + [NcmStorageId_SdCard] = "SD card", + [NcmStorageId_Any] = "Any" +}; + static const char *g_titleNcmContentTypeNames[] = { [NcmContentType_Meta] = "Meta", [NcmContentType_Program] = "Program", @@ -1090,6 +1100,11 @@ fallback: return filename; } +const char *titleGetNcmStorageIdName(u8 storage_id) +{ + return (storage_id <= NcmStorageId_Any ? g_titleNcmStorageIdNames[storage_id] : NULL); +} + const char *titleGetNcmContentTypeName(u8 content_type) { return (content_type <= NcmContentType_DeltaFragment ? g_titleNcmContentTypeNames[content_type] : NULL); @@ -1264,7 +1279,7 @@ static bool titleInitializeTitleStorage(u8 storage_id) goto end; } - LOG_MSG("Loaded %u title info %s from storage ID %u.", title_storage->title_count, (title_storage->title_count == 1 ? "entry" : "entries"), storage_id); + LOG_MSG("Loaded %u title info %s from %s.", title_storage->title_count, (title_storage->title_count == 1 ? "entry" : "entries"), titleGetNcmStorageIdName(storage_id)); /* Update flag. */ success = true; diff --git a/source/options_tab.cpp b/source/options_tab.cpp index 8e8ed43..2b7fb74 100644 --- a/source/options_tab.cpp +++ b/source/options_tab.cpp @@ -414,14 +414,10 @@ namespace nxdt::views "options_tab/overclock/value_disabled"_i18n); overclock->getClickEvent()->subscribe([](brls::View* view) { - brls::ToggleListItem *item = static_cast(view); - /* Get current value. */ + brls::ToggleListItem *item = static_cast(view); bool value = item->getToggleState(); - /* Change hardware clocks based on the current value. */ - utilsOverclockSystem(value); - /* Update configuration. */ configSetBoolean("overclock", value); diff --git a/source/tasks.cpp b/source/tasks.cpp index d05b48c..adc8db5 100644 --- a/source/tasks.cpp +++ b/source/tasks.cpp @@ -21,7 +21,6 @@ #include #include -#include #define NXDT_TASK_INTERVAL 250 /* 250 ms. */ @@ -70,9 +69,9 @@ namespace nxdt::tasks { if (status_info_data->connection_type && connection_status == NifmInternetConnectionStatus_Connected) { - struct in_addr addr = { .s_addr = 0 }; + struct in_addr addr = { .s_addr = INADDR_NONE }; nifmGetCurrentIpAddress(&(addr.s_addr)); - status_info_data->ip_addr = inet_ntoa(addr); + status_info_data->ip_addr = (addr.s_addr != INADDR_NONE ? inet_ntoa(addr) : NULL); } else { status_info_data->ip_addr = NULL; } diff --git a/todo.txt b/todo.txt index 86c7730..9aa47c5 100644 --- a/todo.txt +++ b/todo.txt @@ -1,10 +1,5 @@ todo: - nca: add function to retrieve a pointer to a nca fs ctx based on section type? (e.g. like titleGetContentInfoByTypeAndIdOffset) - - log: verbosity levels - log: nxlink output for advanced users - title: always retrieve names from unpacked nacps? (e.g. if an update changes the name of a title, like deltarune) title: use dlc index as part of the output dump filename? title: more functions for title lookup? (filters, patches / aoc, etc.)