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.)