├── .clang-format ├── .gitignore ├── LICENSE ├── Makefile ├── Makefile.pc ├── README.md ├── include └── libLog.h └── src ├── libLog.c ├── pc-example.c └── syscall.s /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 0 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBlockIndentWidth: 2 83 | ObjCSpaceAfterProperty: false 84 | ObjCSpaceBeforeProtocolList: true 85 | PenaltyBreakAssignment: 2 86 | PenaltyBreakBeforeFirstCallParameter: 19 87 | PenaltyBreakComment: 300 88 | PenaltyBreakFirstLessLess: 120 89 | PenaltyBreakString: 1000 90 | PenaltyExcessCharacter: 1000000 91 | PenaltyReturnTypeOnItsOwnLine: 60 92 | PointerAlignment: Right 93 | RawStringFormats: 94 | - Delimiter: pb 95 | Language: TextProto 96 | BasedOnStyle: google 97 | ReflowComments: true 98 | SortIncludes: true 99 | SortUsingDeclarations: true 100 | SpaceAfterCStyleCast: false 101 | SpaceAfterTemplateKeyword: true 102 | SpaceBeforeAssignmentOperators: true 103 | SpaceBeforeParens: ControlStatements 104 | SpaceInEmptyParentheses: false 105 | SpacesBeforeTrailingComments: 1 106 | SpacesInAngles: false 107 | SpacesInContainerLiterals: true 108 | SpacesInCStyleCastParentheses: false 109 | SpacesInParentheses: false 110 | SpacesInSquareBrackets: false 111 | Standard: Cpp11 112 | TabWidth: 2 113 | UseTab: Never 114 | ... 115 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE config 2 | .vscode 3 | 4 | # Testing Data 5 | flawfinder.log 6 | cppcheck.log 7 | pvs-studio.log 8 | strace_out 9 | project.log 10 | valgrind.log 11 | test.log 12 | test-stream.log 13 | test.bin 14 | 15 | # Build artifacts 16 | libLog 17 | build/ 18 | libLog.prx 19 | libLog_stub.so 20 | libLog.a 21 | *.zip 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # PRX metadata 2 | PROJECTNAME := libLog 3 | 4 | # Libraries linked into the ELF 5 | LIBS := -lSceLibcInternal -lkernel 6 | 7 | # Directorys to include 8 | INCLUDES := -Iinclude 9 | 10 | # Additional compile flags 11 | ERRORFLAGS := -Wall -Wextra -Wpedantic -Werror 12 | OTHERFLAGS := -O3 -std=c99 -D_DEFAULT_SOURCE 13 | 14 | # ----------------------------------------------------------------------------- 15 | # Do not edit below this line unless you know what you are doing 16 | # ----------------------------------------------------------------------------- 17 | 18 | TOOLCHAIN := $(OO_PS4_TOOLCHAIN) 19 | ODIR := build 20 | SDIR := src 21 | EXTRAFLAGS := $(INCLUDES) $(ERRORFLAGS) $(OTHERFLAGS) 22 | CFLAGS := --target=x86_64-pc-freebsd12-elf -fPIC -funwind-tables -c -isysroot $(TOOLCHAIN) -isystem $(TOOLCHAIN)/include $(EXTRAFLAGS) 23 | LFLAGS := -m elf_x86_64 -pie --script $(TOOLCHAIN)/link.x --eh-frame-hdr -L$(TOOLCHAIN)/lib $(LIBS) $(TOOLCHAIN)/lib/crtlib.o 24 | 25 | CFILES := $(wildcard $(SDIR)/*.c) 26 | ASMFILES := $(wildcard $(SDIR)/*.s) 27 | OBJS := $(patsubst $(SDIR)/%.s, $(ODIR)/%.o, $(ASMFILES)) $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) 28 | STUBOBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o.stub, $(CFILES)) 29 | 30 | TARGET = $(PROJECTNAME).prx 31 | TARGETSTUB = $(PROJECTNAME)_stub.so 32 | TARGETSTATIC = $(PROJECTNAME).a 33 | 34 | UNAME_S := $(shell uname -s) 35 | ifeq ($(UNAME_S),Linux) 36 | AR := llvm-ar 37 | AS := llvm-mc 38 | CC := clang 39 | LD := ld.lld 40 | CDIR := linux 41 | endif 42 | ifeq ($(UNAME_S),Darwin) 43 | AR := /usr/local/opt/llvm/bin/llvm-ar 44 | AS := /usr/local/opt/llvm/bin/llvm-mc 45 | CC := /usr/local/opt/llvm/bin/clang 46 | LD := /usr/local/opt/llvm/bin/ld.lld 47 | CDIR := macos 48 | endif 49 | 50 | # Make rules 51 | $(TARGET): $(ODIR) $(OBJS) 52 | $(LD) $(ODIR)/*.o -o $(ODIR)/$(PROJECTNAME).elf $(LFLAGS) 53 | $(TOOLCHAIN)/bin/$(CDIR)/create-fself -in=$(ODIR)/$(PROJECTNAME).elf -out=$(ODIR)/$(PROJECTNAME).oelf --lib=$(TARGET) --paid 0x3800000000000011 54 | @echo Built PRX successfully! 55 | 56 | $(TARGETSTATIC): $(ODIR) $(OBJS) 57 | $(AR) rcs $(TARGETSTATIC) $(ODIR)/*.o 58 | @echo Built static library successfully! 59 | 60 | $(TARGETSTUB): $(ODIR) $(STUBOBJS) 61 | $(CC) $(ODIR)/*.o.stub -o $(TARGETSTUB) -target x86_64-pc-linux-gnu -shared -fuse-ld=lld -ffreestanding -nostdlib -fno-builtin -L$(TOOLCHAIN)/lib $(LIBS) 62 | @echo Built stub successfully! 63 | 64 | $(ODIR)/%.o: $(SDIR)/%.c 65 | $(CC) $(CFLAGS) -o $@ $< 66 | 67 | $(ODIR)/%.o: $(SDIR)/%.s 68 | $(AS) -triple=x86_64-pc-freebsd-elf --filetype=obj -o $@ $< 69 | 70 | $(ODIR)/%.o.stub: $(SDIR)/%.c 71 | $(CC) -target x86_64-pc-linux-gnu -ffreestanding -nostdlib -fno-builtin -fPIC -c -isysroot $(TOOLCHAIN) -isystem $(TOOLCHAIN)/include $(EXTRAFLAGS) -o $@ $< 72 | 73 | $(ODIR): 74 | @echo Creating build directory... 75 | @mkdir $@ 76 | 77 | .PHONY: all clean install 78 | .DEFAULT_GOAL := all 79 | 80 | all: $(TARGET) $(TARGETSTATIC) $(TARGETSTUB) 81 | 82 | clean: 83 | @echo Cleaning up... 84 | @rm -rf $(TARGET) $(TARGETSTATIC) $(TARGETSTUB) $(ODIR) 85 | 86 | install: 87 | @echo Installing... 88 | @yes | cp -f $(TARGETSTATIC) $(OO_PS4_TOOLCHAIN)/lib/$(TARGETSTATIC) 2>/dev/null && echo Installed!|| echo Failed to install, is it built? 89 | -------------------------------------------------------------------------------- /Makefile.pc: -------------------------------------------------------------------------------- 1 | # Binary name 2 | PROJECTNAME := libLog 3 | 4 | # Libraries linked into the binary 5 | LIBS := # Not currently used 6 | 7 | # Directorys to include 8 | INCLUDES := -Iinclude 9 | 10 | # Additional compile flags 11 | ERRORFLAGS := -Wall -Wextra -Wpedantic -Werror 12 | OTHERFLAGS := -O0 -g -std=c99 -c -D_DEFAULT_SOURCE -D__LIBLOG_PC__ 13 | 14 | # ----------------------------------------------------------------------------- 15 | # Do not edit below this line unless you know what you are doing 16 | # ----------------------------------------------------------------------------- 17 | 18 | ODIR := build 19 | SDIR := src 20 | CFLAGS := $(INCLUDES) $(ERRORFLAGS) $(OTHERFLAGS) 21 | 22 | # Check for valgrind flag 23 | ifeq ($(__VALGRIND__),true) 24 | CFLAGS += -D__VALGRIND__ 25 | endif 26 | 27 | LFLAGS := $(LIBS) # Not currently used 28 | 29 | CFILES := $(wildcard $(SDIR)/*.c) 30 | OBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) 31 | 32 | UNAME_S := $(shell uname -s) 33 | ifeq ($(UNAME_S),Linux) 34 | CC := clang 35 | LD := ld.lld # Not currently used 36 | endif 37 | ifeq ($(UNAME_S),Darwin) 38 | CC := /usr/local/opt/llvm/bin/clang 39 | LD := /usr/local/opt/llvm/bin/ld.lld # Not currently used 40 | endif 41 | 42 | # Make rules 43 | $(PROJECTNAME): $(ODIR) $(OBJS) 44 | $(CC) $(ODIR)/*.o -o $(PROJECTNAME) 45 | @echo Built binary successfully! 46 | 47 | $(ODIR)/%.o: $(SDIR)/%.c 48 | $(CC) $(CFLAGS) -o $@ $< 49 | 50 | $(ODIR): 51 | @echo Creating build directory... 52 | @mkdir $@ 53 | 54 | .PHONY: all clean 55 | .DEFAULT_GOAL := all 56 | 57 | all: $(PROJECTNAME) 58 | 59 | clean: 60 | @echo Cleaning up... 61 | @rm -rf $(PROJECTNAME) $(ODIR) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libLog 2 | 3 | ## About 4 | 5 | A simple "do it all" logging library (Static or PRX) designed for use on the PS4 using the [OpenOrbis Toolchain]. It should be easy enough to follow along with for beginners as it doesn't do anything "fancy," most of it is just conditionals/data formatting, and it's less than 1,000 lines. 6 | 7 | Features include: 8 | 9 | - C99 standard, so it should be compatible with your code 10 | - Automatically formats output to display file and line where the log function was called from (Optional) 11 | - Various log levels 12 | - Set log level to display on-the-fly 13 | - Colorized output based on log level (Optional/Where available) 14 | - Logs to 15 | - `Print` (Simple printf) 16 | - `Kernel` (Kernel log, no hooks required) 17 | - `Socket` (Sent over UDP, does not wait for response or confirmation so it should not hang, but packets can be lost) 18 | - `File` 19 | - Include a `Hexdump` for dumping arbitrary memory to a human readable format 20 | - Include a `Bindump` for dumping arbitrary memory (Only via `Socket` and `File`) 21 | - This uses a separate socket/file setup so you won't pollute your logs with binary data 22 | 23 | ## Notes 24 | 25 | - This can be debugged/tested PC side with the include `Makefile.pc` file. Just run `make -f Makefile.pc` to build with it. 26 | 27 | ## Road to 1.0.0 28 | 29 | - [ ] Test everything better 30 | - [ ] Document everything w/ examples 31 | - [ ] Release 32 | 33 | ## 1.0.0+ Plans 34 | 35 | - [ ] System for callbacks to log multiple predefined logs with one log function call 36 | - [ ] Automatic log rotation for logging to files 37 | 38 | [//]: # 39 | [OpenOrbis Toolchain]: 40 | -------------------------------------------------------------------------------- /include/libLog.h: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | 4 | // License: LGPL-3.0 5 | 6 | #ifndef LIBLOG_H 7 | #define LIBLOG_H 8 | 9 | #ifdef __cplusplus 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __LIBLOG_PC__ 15 | #include 16 | #endif 17 | 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifndef __LIBLOG_PC__ 28 | #include 29 | #endif 30 | 31 | #define KNRM "\x1B[0m" 32 | #define KRED "\x1B[31m" 33 | #define KGRN "\x1B[32m" 34 | #define KYEL "\x1B[33m" 35 | #define KBLU "\x1B[34m" 36 | #define KLBLU "\x1B[94m" 37 | #define KMAG "\x1B[35m" 38 | #define KCYN "\x1B[36m" 39 | #define KWHT "\x1B[37m" 40 | #define KGRY "\x1b[90m" 41 | 42 | enum LogLevels { 43 | LL_None, 44 | LL_Fatal, 45 | LL_Error, 46 | LL_Warn, 47 | LL_Info, 48 | LL_Debug, 49 | LL_Trace, 50 | LL_All 51 | }; 52 | 53 | enum PrintTypes { 54 | PT_Print, 55 | PT_Kernel, 56 | PT_Socket, 57 | PT_File, 58 | }; 59 | 60 | void _logPrint(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, ...); 61 | void _logPrintHex(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const void *p_Pointer, int p_Length); 62 | void _logPrintBin(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, const char *p_Input, uint16_t p_Port, const void *p_Pointer, int p_Length); 63 | 64 | bool logSocketOpen(const char *p_IpAddress, uint16_t p_Port); 65 | bool logSocketClose(); 66 | bool logSocketIsOpen(); 67 | 68 | const char *logSocketGetIpAddress(); 69 | uint16_t logSocketGetPort(); 70 | 71 | bool logFileOpen(const char *p_Path); 72 | bool logFileClose(); 73 | const char *logFileGetFilename(); 74 | 75 | void logSetLogLevel(enum LogLevels p_LogLevel); 76 | enum LogLevels logGetLogLevel(); 77 | 78 | void logPrintSetLogLevel(enum LogLevels p_LogLevel); 79 | enum LogLevels logPrintGetLogLevel(); 80 | 81 | void logKernelSetLogLevel(enum LogLevels p_LogLevel); 82 | enum LogLevels logKernelGetLogLevel(); 83 | 84 | void logFileSetLogLevel(enum LogLevels p_LogLevel); 85 | enum LogLevels logFileGetLogLevel(); 86 | 87 | void logSocketSetLogLevel(enum LogLevels p_LogLevel); 88 | enum LogLevels logSocketGetLogLevel(); 89 | 90 | #define logPrint(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Print, true, __FILE__, __LINE__, __VA_ARGS__) 91 | #define logPrintUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Print, false, __FILE__, __LINE__, __VA_ARGS__) 92 | #define logPrintHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Print, true, __FILE__, __LINE__, p_Pointer, p_Length) 93 | #define logPrintHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Print, false, __FILE__, __LINE__, p_Pointer, p_Length) 94 | 95 | #define logKernel(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Kernel, true, __FILE__, __LINE__, __VA_ARGS__) 96 | #define logKernelUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Kernel, false, __FILE__, __LINE__, __VA_ARGS__) 97 | #define logKernelHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Kernel, true, __FILE__, __LINE__, p_Pointer, p_Length) 98 | #define logKernelHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Kernel, false, __FILE__, __LINE__, p_Pointer, p_Length) 99 | 100 | #define logSocket(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Socket, true, __FILE__, __LINE__, __VA_ARGS__) 101 | #define logSocketUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Socket, false, __FILE__, __LINE__, __VA_ARGS__) 102 | #define logSocketHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Socket, true, __FILE__, __LINE__, p_Pointer, p_Length) 103 | #define logSocketHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Socket, false, __FILE__, __LINE__, p_Pointer, p_Length) 104 | 105 | #define logFile(p_LogLevel, ...) _logPrint(p_LogLevel, PT_File, true, __FILE__, __LINE__, __VA_ARGS__) 106 | #define logFileUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_File, false, __FILE__, __LINE__, __VA_ARGS__) 107 | #define logFileHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_File, true, __FILE__, __LINE__, p_Pointer, p_Length) 108 | #define logFileHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_File, false, __FILE__, __LINE__, p_Pointer, p_Length) 109 | 110 | #define logSocketBindump(p_LogLevel, p_IpAddress, p_Port, p_Pointer, p_Length) _logPrintBin(p_LogLevel, PT_Socket, p_IpAddress, p_Port, p_Pointer, p_Length) 111 | #define logFileBindump(p_LogLevel, p_Path, p_Pointer, p_Length) _logPrintBin(p_LogLevel, PT_File, p_Path, 0, p_Pointer, p_Length) 112 | 113 | #ifdef __cplusplus 114 | } 115 | 116 | extern FILE *g_LogFilePointer; 117 | 118 | void _sendSocket(const char *s_Buffer); 119 | 120 | // C++ Bindings 121 | 122 | class PrintLog { 123 | public: 124 | PrintLog(const char *p_File, int32_t p_Line, bool p_Format) { 125 | m_Format = p_Format; 126 | if (m_Format) { 127 | if (logGetLogLevel() <= LL_None) { 128 | m_Skip = true; 129 | return; 130 | } 131 | 132 | if (logPrintGetLogLevel() > logGetLogLevel()) { 133 | m_Skip = true; 134 | return; 135 | } 136 | 137 | const char *s_LevelString = "None"; 138 | const char *s_LevelColor = KNRM; 139 | 140 | switch (logPrintGetLogLevel()) { 141 | case LL_Info: 142 | s_LevelString = "Info"; 143 | s_LevelColor = KGRN; 144 | break; 145 | case LL_Warn: 146 | s_LevelString = "Warn"; 147 | s_LevelColor = KYEL; 148 | break; 149 | case LL_Error: 150 | s_LevelString = "Error"; 151 | s_LevelColor = KRED; 152 | break; 153 | case LL_Debug: 154 | s_LevelString = "Debug"; 155 | s_LevelColor = KGRY; 156 | break; 157 | case LL_None: 158 | default: 159 | s_LevelString = "None"; 160 | s_LevelColor = KNRM; 161 | break; 162 | } 163 | m_LogStream << s_LevelColor << "[" << s_LevelString << "] " << p_File << ":" << p_Line << ": "; 164 | } 165 | } 166 | 167 | template 168 | PrintLog &operator<<(const T &v) { 169 | m_LogStream << v; 170 | return *this; 171 | } 172 | 173 | PrintLog &operator<<(std::ostream&(*f)(std::ostream&)) { 174 | m_LogStream << f; 175 | return *this; 176 | } 177 | 178 | ~PrintLog() { 179 | if (m_Format) { 180 | if (m_Skip) { 181 | return; 182 | } 183 | m_LogStream << KNRM << std::endl; 184 | } 185 | 186 | std::cout << m_LogStream.str(); 187 | m_LogStream.str(""); 188 | } 189 | 190 | private: 191 | bool m_Skip = false; 192 | bool m_Format = false; 193 | std::stringstream m_LogStream; 194 | }; 195 | 196 | class KernelLog { 197 | public: 198 | KernelLog(const char *p_File, int32_t p_Line, bool p_Format) { 199 | m_Format = p_Format; 200 | if (m_Format) { 201 | if (logGetLogLevel() <= LL_None) { 202 | m_Skip = true; 203 | return; 204 | } 205 | 206 | if (logKernelGetLogLevel() > logGetLogLevel()) { 207 | m_Skip = true; 208 | return; 209 | } 210 | 211 | const char *s_LevelString = "None"; 212 | const char *s_LevelColor = KNRM; 213 | 214 | switch (logKernelGetLogLevel()) { 215 | case LL_Info: 216 | s_LevelString = "Info"; 217 | s_LevelColor = KGRN; 218 | break; 219 | case LL_Warn: 220 | s_LevelString = "Warn"; 221 | s_LevelColor = KYEL; 222 | break; 223 | case LL_Error: 224 | s_LevelString = "Error"; 225 | s_LevelColor = KRED; 226 | break; 227 | case LL_Debug: 228 | s_LevelString = "Debug"; 229 | s_LevelColor = KGRY; 230 | break; 231 | case LL_None: 232 | default: 233 | s_LevelString = "None"; 234 | s_LevelColor = KNRM; 235 | break; 236 | } 237 | m_LogStream << s_LevelColor << "[" << s_LevelString << "] " << p_File << ":" << p_Line << ": "; 238 | } 239 | } 240 | 241 | template 242 | KernelLog &operator<<(const T &v) { 243 | m_LogStream << v; 244 | return *this; 245 | } 246 | 247 | KernelLog &operator<<(std::ostream&(*f)(std::ostream&)) { 248 | m_LogStream << f; 249 | return *this; 250 | } 251 | 252 | ~KernelLog() { 253 | if (m_Format) { 254 | if (m_Skip) { 255 | return; 256 | } 257 | m_LogStream << KNRM << std::endl; 258 | } 259 | 260 | #ifndef __LIBLOG_PC__ 261 | sceKernelDebugOutText(0, m_LogStream.str().c_str()); 262 | #else 263 | // Not the same as `sceKernelDebugOutText` but it workings as a way to tell if it's even getting here and the data input into it 264 | std::cout << m_LogStream.str(); 265 | #endif 266 | m_LogStream.str(""); 267 | } 268 | 269 | private: 270 | bool m_Skip = false; 271 | bool m_Format = false; 272 | std::stringstream m_LogStream; 273 | }; 274 | 275 | class SocketLog { 276 | public: 277 | SocketLog(const char *p_File, int32_t p_Line, bool p_Format) { 278 | m_Format = p_Format; 279 | if (m_Format) { 280 | if (logGetLogLevel() <= LL_None) { 281 | m_Skip = true; 282 | return; 283 | } 284 | 285 | if (logSocketGetLogLevel() > logGetLogLevel()) { 286 | m_Skip = true; 287 | return; 288 | } 289 | 290 | const char *s_LevelString = "None"; 291 | const char *s_LevelColor = KNRM; 292 | 293 | switch (logSocketGetLogLevel()) { 294 | case LL_Info: 295 | s_LevelString = "Info"; 296 | s_LevelColor = KGRN; 297 | break; 298 | case LL_Warn: 299 | s_LevelString = "Warn"; 300 | s_LevelColor = KYEL; 301 | break; 302 | case LL_Error: 303 | s_LevelString = "Error"; 304 | s_LevelColor = KRED; 305 | break; 306 | case LL_Debug: 307 | s_LevelString = "Debug"; 308 | s_LevelColor = KGRY; 309 | break; 310 | case LL_None: 311 | default: 312 | s_LevelString = "None"; 313 | s_LevelColor = KNRM; 314 | break; 315 | } 316 | m_LogStream << s_LevelColor << "[" << s_LevelString << "] " << p_File << ":" << p_Line << ": "; 317 | } 318 | } 319 | 320 | template 321 | SocketLog &operator<<(const T &v) { 322 | m_LogStream << v; 323 | return *this; 324 | } 325 | 326 | SocketLog &operator<<(std::ostream&(*f)(std::ostream&)) { 327 | m_LogStream << f; 328 | return *this; 329 | } 330 | 331 | ~SocketLog() { 332 | if (m_Format) { 333 | if (m_Skip) { 334 | return; 335 | } 336 | m_LogStream << KNRM << std::endl; 337 | } 338 | 339 | //TODO: _sendSocket(m_LogStream.str().c_str()); 340 | m_LogStream.str(""); 341 | } 342 | 343 | private: 344 | bool m_Skip = false; 345 | bool m_Format = false; 346 | std::stringstream m_LogStream; 347 | }; 348 | 349 | class FileLog { 350 | public: 351 | FileLog(const char *p_File, int32_t p_Line, bool p_Format) { 352 | m_Format = p_Format; 353 | if (m_Format) { 354 | if (logGetLogLevel() <= LL_None) { 355 | m_Skip = true; 356 | return; 357 | } 358 | 359 | if (logFileGetLogLevel() > logGetLogLevel()) { 360 | m_Skip = true; 361 | return; 362 | } 363 | 364 | const char *s_LevelString = "None"; 365 | 366 | switch (logFileGetLogLevel()) { 367 | case LL_Info: 368 | s_LevelString = "Info"; 369 | break; 370 | case LL_Warn: 371 | s_LevelString = "Warn"; 372 | break; 373 | case LL_Error: 374 | s_LevelString = "Error"; 375 | break; 376 | case LL_Debug: 377 | s_LevelString = "Debug"; 378 | break; 379 | case LL_None: 380 | default: 381 | s_LevelString = "None"; 382 | break; 383 | } 384 | m_LogStream << "[" << s_LevelString << "] " << p_File << ":" << p_Line << ": "; 385 | } 386 | } 387 | 388 | template 389 | FileLog &operator<<(const T &v) { 390 | m_LogStream << v; 391 | return *this; 392 | } 393 | 394 | FileLog &operator<<(std::ostream&(*f)(std::ostream&)) { 395 | m_LogStream << f; 396 | return *this; 397 | } 398 | 399 | ~FileLog() { 400 | if (m_Format) { 401 | if (m_Skip) { 402 | return; 403 | } 404 | m_LogStream << std::endl; 405 | } 406 | 407 | fprintf(g_LogFilePointer, "%s", m_LogStream.str().c_str()); 408 | m_LogStream.str(""); 409 | } 410 | 411 | private: 412 | bool m_Skip = false; 413 | bool m_Format = false; 414 | std::stringstream m_LogStream; 415 | }; 416 | 417 | #define PRINTLOG PrintLog(__FILE__, __LINE__, true) 418 | #define KERNELLOG KernelLog(__FILE__, __LINE__, true) 419 | #define SOCKETLOG SocketLog(__FILE__, __LINE__, true) 420 | #define FILELOG FileLog(__FILE__, __LINE__, true) 421 | 422 | #define PRINTLOG_UNFORMATTED PrintLog(__FILE__, __LINE__, false) 423 | #define KERNELLOG_UNFORMATTED KernelLog(__FILE__, __LINE__, false) 424 | #define SOCKETLOG_UNFORMATTED SocketLog(__FILE__, __LINE__, false) 425 | #define FILELOG_UNFORMATTED FileLog(__FILE__, __LINE__, false) 426 | 427 | #endif 428 | 429 | #endif 430 | -------------------------------------------------------------------------------- /src/libLog.c: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | 4 | // License: LGPL-3.0 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef __LIBLOG_PC__ 20 | #include 21 | #endif 22 | 23 | #include "libLog.h" 24 | 25 | // Globals 26 | enum LogLevels g_LogLevel = LL_All; 27 | enum LogLevels g_PrintLogLevel = LL_Trace; 28 | enum LogLevels g_KernelLogLevel = LL_Trace; 29 | enum LogLevels g_SocketLogLevel = LL_Trace; 30 | enum LogLevels g_FileLogLevel = LL_Trace; 31 | 32 | int g_Socket = -1; 33 | const char *g_SocketLogIpAddress = "000.000.000.000"; 34 | uint16_t g_SocketLogPort = 0; 35 | 36 | const char *g_LogFileName = ""; 37 | FILE *g_LogFilePointer; 38 | 39 | static const char *VERSION __attribute__((used)) = "libLog v0.9.3"; 40 | static const char *LICENSE __attribute__((used)) = "LGPL-3.0"; 41 | static const char *URL __attribute__((used)) = "https://github.com/Al-Azif/ps4-libLog"; 42 | 43 | static const char *_formatOutput(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, va_list p_Args) { 44 | if (p_File == NULL || p_Format == NULL) { 45 | return NULL; 46 | } 47 | 48 | va_list s_ArgSize; 49 | va_copy(s_ArgSize, p_Args); 50 | size_t s_MessageSize = vsnprintf(NULL, 0, p_Format, s_ArgSize); 51 | va_end(s_ArgSize); 52 | if (s_MessageSize <= 0) { 53 | return NULL; 54 | } 55 | s_MessageSize++; // Null terminator 56 | 57 | char *s_Message = calloc(s_MessageSize, sizeof(char)); 58 | if (s_Message == NULL) { 59 | return NULL; 60 | } 61 | 62 | vsnprintf(s_Message, s_MessageSize, p_Format, p_Args); 63 | 64 | if (!p_Meta) { 65 | return s_Message; 66 | } 67 | 68 | const char *s_LevelString = "None "; 69 | const char *s_LevelColor = KNRM; 70 | const char *s_LevelResetColor = KNRM; 71 | 72 | switch (p_LogLevel) { 73 | case LL_None: 74 | break; 75 | case LL_Fatal: 76 | s_LevelString = "Fatal"; 77 | s_LevelColor = KMAG; 78 | break; 79 | case LL_Error: 80 | s_LevelString = "Error"; 81 | s_LevelColor = KRED; 82 | break; 83 | case LL_Warn: 84 | s_LevelString = "Warn"; 85 | s_LevelColor = KYEL; 86 | break; 87 | case LL_Info: 88 | s_LevelString = "Info"; 89 | s_LevelColor = KGRN; 90 | break; 91 | case LL_Debug: 92 | s_LevelString = "Debug"; 93 | s_LevelColor = KCYN; 94 | break; 95 | case LL_Trace: 96 | s_LevelString = "Trace"; 97 | s_LevelColor = KLBLU; 98 | break; 99 | case LL_All: 100 | default: 101 | break; 102 | } 103 | 104 | if (p_PrintType == PT_File) { 105 | s_LevelColor = "\0"; 106 | s_LevelResetColor = "\0"; 107 | } 108 | 109 | size_t s_OutputSize = snprintf(NULL, 0, "%s[%-5s] %s:%d: %s%s\n", s_LevelColor, s_LevelString, p_File, p_Line, s_Message, s_LevelResetColor); 110 | if (s_OutputSize <= 0) { 111 | return NULL; 112 | } 113 | s_OutputSize++; // Null terminator 114 | 115 | char *s_Output = calloc(s_OutputSize, sizeof(char)); 116 | if (s_Output == NULL) { 117 | free((void *)s_Message); 118 | return NULL; 119 | } 120 | 121 | snprintf(s_Output, s_OutputSize, "%s[%-5s] %s:%d: %s%s\n", s_LevelColor, s_LevelString, p_File, p_Line, s_Message, s_LevelResetColor); 122 | free((void *)s_Message); 123 | 124 | return s_Output; 125 | } 126 | 127 | static size_t _hexdumpSize(int p_Input) { 128 | int s_Lines = p_Input / 0x10; 129 | if (s_Lines == 0) { 130 | s_Lines++; 131 | } 132 | 133 | int s_Remain = p_Input % 0x10; 134 | if (s_Remain > 0) { 135 | s_Lines++; 136 | } 137 | 138 | int s_Total = s_Lines * 77 + 1; 139 | if (s_Remain != 0) { 140 | s_Total -= 0x10 - s_Remain; 141 | } 142 | 143 | return s_Total + 1; // + 1 for new line in formatted hexdumps 144 | } 145 | 146 | // Hexdump based on: https://stackoverflow.com/a/29865 147 | static const char *_hexdump(bool p_Meta, const void *p_Pointer, int p_Length) { 148 | if (p_Pointer == NULL) { 149 | return NULL; 150 | } 151 | 152 | unsigned char *s_Buffer = (unsigned char *)p_Pointer; 153 | 154 | size_t s_HexdumpStringSize = _hexdumpSize(p_Length); 155 | char *s_Ret = (char *)calloc(s_HexdumpStringSize, sizeof(char)); 156 | if (s_Ret == NULL) { 157 | return NULL; 158 | } 159 | 160 | size_t s_Offset = 0; 161 | 162 | if (p_Meta) { 163 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 164 | free((void *)s_Ret); 165 | return NULL; 166 | } 167 | s_Offset += snprintf(&s_Ret[s_Offset], 2, "%s", "\n"); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 1 + null term 168 | } 169 | 170 | for (int i = 0; i < p_Length; i += 0x10) { 171 | if (s_Offset + 10 + 1 > s_HexdumpStringSize) { 172 | free((void *)s_Ret); 173 | return NULL; 174 | } 175 | s_Offset += snprintf(&s_Ret[s_Offset], 11, "%08X: ", i); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%08X: `. Copy size is 10 + null term 176 | for (int j = 0; j < 0x10; j++) { 177 | if (i + j < p_Length) { 178 | if (s_Offset + 3 + 1 > s_HexdumpStringSize) { 179 | free((void *)s_Ret); 180 | return NULL; 181 | } 182 | s_Offset += snprintf(&s_Ret[s_Offset], 4, "%02X ", s_Buffer[i + j]); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%02X `. Copy size is 3 + null term 183 | } else { 184 | if (s_Offset + 3 + 1 > s_HexdumpStringSize) { 185 | free((void *)s_Ret); 186 | return NULL; 187 | } 188 | s_Offset += snprintf(&s_Ret[s_Offset], 4, "%s", " "); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 3 + null term 189 | } 190 | if (j == 7) { 191 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 192 | free((void *)s_Ret); 193 | return NULL; 194 | } 195 | s_Offset += snprintf(&s_Ret[s_Offset], 2, "%s", " "); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 1 + null term 196 | } 197 | } 198 | 199 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 200 | free((void *)s_Ret); 201 | return NULL; 202 | } 203 | s_Offset += snprintf(&s_Ret[s_Offset], 2, "%s", " "); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 1 + null term 204 | 205 | for (int j = 0; j < 0x10; j++) { 206 | if (i + j < p_Length) { 207 | if (isprint(s_Buffer[i + j])) { 208 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 209 | free((void *)s_Ret); 210 | return NULL; 211 | } 212 | s_Offset += snprintf(&s_Ret[s_Offset], 2, "%c", s_Buffer[i + j]); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%c`. Copy size is 1 + null term 213 | } else { 214 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 215 | free((void *)s_Ret); 216 | return NULL; 217 | } 218 | s_Offset += snprintf(&s_Ret[s_Offset], 2, "%s", "."); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 1 + null term 219 | } 220 | } 221 | } 222 | 223 | if (i + 0x10 < p_Length) { 224 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 225 | free((void *)s_Ret); 226 | return NULL; 227 | } 228 | s_Offset += snprintf(&s_Ret[s_Offset], 2, "%s", "\n"); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 1 + null term 229 | } 230 | } 231 | 232 | if (!p_Meta) { 233 | if (s_Offset + 1 + 1 > s_HexdumpStringSize) { 234 | free((void *)s_Ret); 235 | return NULL; 236 | } 237 | snprintf(&s_Ret[s_Offset], 2, "%s", "\n"); // Flawfinder: Ignore. Format cannot be controlled externally here... it's `%s`. Copy size is 1 + null term 238 | } 239 | 240 | return s_Ret; 241 | } 242 | 243 | static const char *_bindump(const void *p_Pointer, int p_Length) { 244 | if (p_Pointer == NULL) { 245 | return NULL; 246 | } 247 | 248 | unsigned char *s_Buffer = (unsigned char *)p_Pointer; 249 | 250 | char *s_Output = (char *)calloc(p_Length + 1, sizeof(char)); 251 | if (s_Output == NULL) { 252 | return NULL; 253 | } 254 | 255 | for (int i = 0; i < p_Length; i++) { 256 | s_Output[i] = s_Buffer[i]; 257 | } 258 | 259 | return s_Output; 260 | } 261 | 262 | // https://www.tutorialspoint.com/c-program-to-validate-an-ip-address 263 | static bool _validNumber(char *p_Str) { 264 | while (*p_Str) { 265 | if (!isdigit(*p_Str)) { 266 | return false; 267 | } 268 | p_Str++; 269 | } 270 | 271 | return true; 272 | } 273 | 274 | // https://www.tutorialspoint.com/c-program-to-validate-an-ip-address 275 | static bool _validIPAddress(const char *p_IpAddress) { 276 | if (!p_IpAddress) { 277 | return false; 278 | } 279 | 280 | char *s_TempIp = strdup(p_IpAddress); 281 | 282 | int s_Dots = 0; 283 | 284 | char *s_Ptr; 285 | s_Ptr = strtok(s_TempIp, "."); 286 | if (!s_Ptr) { 287 | free((void *)s_TempIp); 288 | return false; 289 | } 290 | 291 | while (s_Ptr) { 292 | if (!_validNumber(s_Ptr)) { 293 | free((void *)s_TempIp); 294 | return false; 295 | } 296 | int s_Num = atoi(s_Ptr); // Flawfinder: ignore. Value is checked below 297 | if (s_Num >= 0 && s_Num <= 255) { 298 | s_Ptr = strtok(NULL, "."); 299 | if (s_Ptr) { 300 | s_Dots++; 301 | } 302 | } else { 303 | free((void *)s_TempIp); 304 | return false; 305 | } 306 | } 307 | if (s_Dots != 3) { 308 | free((void *)s_TempIp); 309 | return false; 310 | } 311 | 312 | free((void *)s_TempIp); 313 | 314 | return true; 315 | } 316 | 317 | static void _sendSocket(const char *s_Buffer) { 318 | if (s_Buffer == NULL) { 319 | return; 320 | } 321 | 322 | if (g_Socket < 0) { 323 | return; 324 | } 325 | 326 | if (!_validIPAddress(g_SocketLogIpAddress) || g_SocketLogPort == 0) { 327 | return; 328 | } 329 | 330 | struct sockaddr_in s_Servaddr = { 331 | .sin_family = AF_INET, 332 | .sin_addr = { 333 | .s_addr = inet_addr(g_SocketLogIpAddress), 334 | }, 335 | .sin_port = htons(g_SocketLogPort), 336 | }; 337 | 338 | sendto(g_Socket, s_Buffer, strlen(s_Buffer), 0, (struct sockaddr *)&s_Servaddr, sizeof(s_Servaddr)); 339 | } 340 | 341 | static void _writeFile(const char *s_Buffer) { 342 | if (s_Buffer == NULL || g_LogFilePointer == NULL) { 343 | return; 344 | } 345 | 346 | fprintf(g_LogFilePointer, "%s", s_Buffer); 347 | } 348 | 349 | static bool _checkLogLevelByType(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType) { 350 | enum LogLevels s_TypeLogLevel; 351 | switch (p_PrintType) { 352 | case PT_Print: 353 | s_TypeLogLevel = g_PrintLogLevel; 354 | break; 355 | case PT_Kernel: 356 | s_TypeLogLevel = g_KernelLogLevel; 357 | break; 358 | case PT_Socket: 359 | s_TypeLogLevel = g_SocketLogLevel; 360 | break; 361 | case PT_File: 362 | s_TypeLogLevel = g_FileLogLevel; 363 | break; 364 | default: 365 | return false; 366 | break; 367 | } 368 | 369 | if (g_LogLevel >= g_PrintLogLevel && s_TypeLogLevel >= p_LogLevel) { 370 | return true; 371 | } 372 | 373 | return false; 374 | } 375 | 376 | static void _printByType(enum PrintTypes p_PrintType, const char *s_Buffer) { 377 | if (p_PrintType == PT_Print) { 378 | printf("%s", s_Buffer); 379 | } else if (p_PrintType == PT_Kernel) { 380 | #ifndef __LIBLOG_PC__ 381 | sceKernelDebugOutText(0, s_Buffer); 382 | #else 383 | // Not the same as `sceKernelDebugOutText` but it workings as a way to tell if it's even getting here and the data input into it 384 | printf("%s", s_Buffer); 385 | #endif 386 | } else if (p_PrintType == PT_Socket) { 387 | _sendSocket(s_Buffer); 388 | } else if (p_PrintType == PT_File) { 389 | _writeFile(s_Buffer); 390 | } 391 | } 392 | 393 | void _logPrint(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, ...) { 394 | if (p_File == NULL || p_Format == NULL) { 395 | return; 396 | } 397 | 398 | if (!_checkLogLevelByType(p_LogLevel, p_PrintType)) { 399 | return; 400 | } 401 | 402 | va_list s_Args; 403 | va_start(s_Args, p_Format); 404 | const char *s_Output = _formatOutput(p_LogLevel, p_PrintType, p_Meta, p_File, p_Line, p_Format, s_Args); 405 | va_end(s_Args); 406 | if (s_Output == NULL) { 407 | return; 408 | } 409 | 410 | _printByType(p_PrintType, s_Output); 411 | 412 | free((void *)s_Output); 413 | } 414 | 415 | void _logPrintHex(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const void *p_Pointer, int p_Length) { 416 | if (p_Pointer == NULL) { 417 | return; 418 | } 419 | 420 | if (p_Length == 0) { 421 | return; 422 | } 423 | 424 | if (!_checkLogLevelByType(p_LogLevel, p_PrintType)) { 425 | return; 426 | } 427 | 428 | const char *s_Output = _hexdump(p_Meta, p_Pointer, p_Length); 429 | if (s_Output == NULL) { 430 | return; 431 | } 432 | 433 | _logPrint(p_LogLevel, p_PrintType, p_Meta, p_File, p_Line, "%s", s_Output); 434 | 435 | free((void *)s_Output); 436 | } 437 | 438 | void _logPrintBin(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, const char *p_Input, uint16_t p_Port, const void *p_Pointer, int p_Length) { 439 | if (p_Input == NULL || p_Pointer == NULL) { 440 | return; 441 | } 442 | 443 | if (p_Length == 0) { 444 | return; 445 | } 446 | 447 | if (p_PrintType != PT_Socket && p_PrintType != PT_File) { 448 | return; 449 | } 450 | 451 | if (!_checkLogLevelByType(p_LogLevel, p_PrintType)) { 452 | return; 453 | } 454 | 455 | const char *s_Output = _bindump(p_Pointer, p_Length); 456 | if (s_Output == NULL) { 457 | return; 458 | } 459 | 460 | if (p_PrintType == PT_Socket) { 461 | if (!_validIPAddress(p_Input) || p_Port == 0) { 462 | free((void *)s_Output); 463 | return; 464 | } 465 | 466 | int s_Socket = -1; 467 | if ((s_Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 468 | free((void *)s_Output); 469 | return; 470 | } 471 | 472 | struct sockaddr_in s_Servaddr = { 473 | .sin_family = AF_INET, 474 | .sin_addr = { 475 | .s_addr = inet_addr(p_Input), 476 | }, 477 | .sin_port = htons(p_Port), 478 | }; 479 | 480 | sendto(s_Socket, s_Output, p_Length, 0, (struct sockaddr *)&s_Servaddr, sizeof(s_Servaddr)); 481 | 482 | close(s_Socket); 483 | } else if (p_PrintType == PT_File) { 484 | FILE *s_FilePointer; 485 | 486 | s_FilePointer = fopen(p_Input, "wb"); 487 | 488 | if (s_FilePointer == NULL) { 489 | free((void *)s_Output); 490 | return; 491 | } 492 | 493 | fwrite(s_Output, sizeof(char), p_Length, s_FilePointer); 494 | 495 | fclose(s_FilePointer); 496 | } 497 | 498 | free((void *)s_Output); 499 | } 500 | 501 | bool logSocketOpen(const char *p_IpAddress, uint16_t p_Port) { 502 | if (p_IpAddress == NULL) { 503 | return false; 504 | } 505 | 506 | if (!_validIPAddress(p_IpAddress)) { 507 | return false; 508 | } 509 | 510 | if (g_Socket < 0) { 511 | if ((g_Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 512 | return false; 513 | } 514 | } 515 | 516 | g_SocketLogIpAddress = p_IpAddress; 517 | g_SocketLogPort = p_Port; 518 | 519 | return true; 520 | } 521 | 522 | bool logSocketClose() { 523 | if (g_Socket < 0) { 524 | return true; 525 | } 526 | 527 | if (close(g_Socket) < 0) { 528 | return false; 529 | } 530 | 531 | g_Socket = -1; 532 | 533 | return true; 534 | } 535 | 536 | bool logSocketIsOpen() { 537 | if (g_Socket < 0) { 538 | return false; 539 | } 540 | 541 | return true; 542 | } 543 | 544 | const char *logSocketGetIpAddress() { 545 | return g_SocketLogIpAddress; 546 | } 547 | 548 | uint16_t logSocketGetPort() { 549 | return g_SocketLogPort; 550 | } 551 | 552 | bool logFileOpen(const char *p_Path) { 553 | if (p_Path == NULL) { 554 | return false; 555 | } 556 | 557 | if (g_LogFilePointer != NULL) { 558 | // We should either close the currently open file and open the new file, or return false to "fail" 559 | return false; // or logFileClose(); 560 | } 561 | 562 | g_LogFilePointer = fopen(p_Path, "a"); 563 | 564 | if (g_LogFilePointer == NULL) { 565 | return false; 566 | } 567 | 568 | g_LogFileName = p_Path; 569 | 570 | return true; 571 | } 572 | 573 | bool logFileClose() { 574 | if (g_LogFilePointer == NULL) { 575 | g_LogFileName = ""; 576 | return true; 577 | } 578 | 579 | fclose(g_LogFilePointer); 580 | g_LogFilePointer = NULL; 581 | g_LogFileName = ""; 582 | 583 | return true; 584 | } 585 | 586 | const char *logFileGetFilename() { 587 | return g_LogFileName; 588 | } 589 | 590 | void logSetLogLevel(enum LogLevels p_LogLevel) { 591 | g_LogLevel = p_LogLevel; 592 | } 593 | 594 | enum LogLevels logGetLogLevel() { 595 | return g_LogLevel; 596 | } 597 | 598 | void logPrintSetLogLevel(enum LogLevels p_LogLevel) { 599 | g_PrintLogLevel = p_LogLevel; 600 | } 601 | 602 | enum LogLevels logPrintGetLogLevel() { 603 | return g_PrintLogLevel; 604 | } 605 | 606 | void logKernelSetLogLevel(enum LogLevels p_LogLevel) { 607 | g_KernelLogLevel = p_LogLevel; 608 | } 609 | 610 | enum LogLevels logKernelGetLogLevel() { 611 | return g_KernelLogLevel; 612 | } 613 | 614 | void logFileSetLogLevel(enum LogLevels p_LogLevel) { 615 | g_FileLogLevel = p_LogLevel; 616 | } 617 | 618 | enum LogLevels logFileGetLogLevel() { 619 | return g_FileLogLevel; 620 | } 621 | 622 | void logSocketSetLogLevel(enum LogLevels p_LogLevel) { 623 | g_SocketLogLevel = p_LogLevel; 624 | } 625 | 626 | enum LogLevels logSocketGetLogLevel() { 627 | return g_SocketLogLevel; 628 | } 629 | -------------------------------------------------------------------------------- /src/pc-example.c: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 3 | 4 | // License: LGPL-3.0 5 | 6 | #ifdef __LIBLOG_PC__ 7 | // This example code itself should work on the PS4 as well, as long as the library is initialized 8 | 9 | #include 10 | 11 | #include "libLog.h" 12 | 13 | int main() { 14 | char test[0x20]; 15 | memset(test, 0xCC, sizeof(test)); 16 | 17 | #ifdef __VALGRIND__ 18 | for (int i = 0; i < 1000; i++) { 19 | #endif 20 | logPrint(LL_Error, "This is a logPrint call"); 21 | logPrint(LL_Error, "This is a logPrint call #%i", 2); 22 | logPrintUnformatted(LL_Error, "This is a logPrintUnformatted call\n"); 23 | logPrintUnformatted(LL_Error, "This is a logPrintUnformatted call #%i\n", 2); 24 | logPrintHexdump(LL_Error, test, sizeof(test)); 25 | logPrintHexdumpUnformatted(LL_Error, test, sizeof(test)); 26 | 27 | logKernel(LL_Error, "This is a logKernel call"); 28 | logKernel(LL_Error, "This is a logKernel call #%i", 2); 29 | logKernelUnformatted(LL_Error, "This is a logKernelUnformatted call\n"); 30 | logKernelUnformatted(LL_Error, "This is a logKernelUnformatted call #%i\n", 2); 31 | logKernelHexdump(LL_Error, test, sizeof(test)); 32 | logKernelHexdumpUnformatted(LL_Error, test, sizeof(test)); 33 | 34 | logSocketOpen("192.0.2.2", 9023); 35 | logSocket(LL_Error, "This is a logSocket call"); 36 | logSocket(LL_Error, "This is a logSocket call #%i", 2); 37 | logSocketUnformatted(LL_Error, "This is a logSocketUnformatted call\n"); 38 | logSocketUnformatted(LL_Error, "This is a logSocketUnformatted call #%i\n", 2); 39 | logSocketHexdump(LL_Error, test, sizeof(test)); 40 | logSocketHexdumpUnformatted(LL_Error, test, sizeof(test)); 41 | logSocketClose(); 42 | 43 | logFileOpen("./test.log"); 44 | logFile(LL_Error, "This is a logFile call"); 45 | logFile(LL_Error, "This is a logFile call #%i", 2); 46 | logFileUnformatted(LL_Error, "This is a logFileUnformatted call\n"); 47 | logFileUnformatted(LL_Error, "This is a logFileUnformatted call #%i\n", 2); 48 | logFileHexdump(LL_Error, test, sizeof(test)); 49 | logFileHexdumpUnformatted(LL_Error, test, sizeof(test)); 50 | logFileClose(); 51 | 52 | logSocketBindump(LL_Error, "192.0.2.2", 9024, test, sizeof(test)); 53 | logFileBindump(LL_Error, "./test.bin", test, sizeof(test)); 54 | #ifdef __cplusplus 55 | PRINTLOG << "This is a C++ PRINTLOG stream"; 56 | PRINTLOG << "This is a C++ PRINTLOG stream #" << 2; 57 | PRINTLOG_UNFORMATTED << "This is a C++ PRINTLOG_UNFORMATTED stream" << std::endl; 58 | PRINTLOG_UNFORMATTED << "This is a c++ PRINTLOG_UNFORMATTED stream #" << 2 << std::endl; 59 | 60 | KERNELLOG << "This is a C++ KERNELLOG stream"; 61 | KERNELLOG << "This is a C++ KERNELLOG stream #" << 2; 62 | KERNELLOG_UNFORMATTED << "This is a C++ KERNELLOG_UNFORMATTED stream" << std::endl; 63 | KERNELLOG_UNFORMATTED << "This is a c++ KERNELLOG_UNFORMATTED stream #" << 2 <