├── .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 <