From 0f897c2c2255e565dfc1c050d6bbe6c37000d291 Mon Sep 17 00:00:00 2001
From: Al Azif <33132478+Al-Azif@users.noreply.github.com>
Date: Sun, 31 Oct 2021 15:47:18 -0700
Subject: [PATCH] Initial commit
---
.clang-format | 114 ++++++++
.gitignore | 19 ++
LICENSE | 165 +++++++++++
Makefile | 90 ++++++
Makefile.pc | 58 ++++
README.md | 38 +++
include/libLog.h | 71 +++++
include/libLog_stub.h | 174 ++++++++++++
src/libLog.c | 622 ++++++++++++++++++++++++++++++++++++++++++
src/pc-example.c | 52 ++++
src/syscall.s | 10 +
11 files changed, 1413 insertions(+)
create mode 100644 .clang-format
create mode 100644 .gitignore
create mode 100644 LICENSE
create mode 100644 Makefile
create mode 100644 Makefile.pc
create mode 100644 README.md
create mode 100644 include/libLog.h
create mode 100644 include/libLog_stub.h
create mode 100644 src/libLog.c
create mode 100644 src/pc-example.c
create mode 100644 src/syscall.s
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..97726db
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,114 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeInheritanceComma: false
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 0
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 1
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+RawStringFormats:
+ - Delimiter: pb
+ Language: TextProto
+ BasedOnStyle: google
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 2
+UseTab: Never
+...
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6a9faa5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# IDE config
+.vscode
+
+# Testing Data
+flawfinder.log
+cppcheck.log
+pvs-studio.log
+strace_out
+project.log
+valgrind.log
+test.log
+test.bin
+
+# Build artifacts
+main
+build/
+libLog.prx
+libLog_stub.so
+*.zip
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..fc948aa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,90 @@
+# PRX metadata
+PROJECTNAME := libLog
+
+# Libraries linked into the ELF.
+LIBS := -lSceLibcInternal -lkernel
+
+# Directorys to include
+INCLUDES := -Iinclude
+
+# Additional compile flags
+ERRORFLAGS := -Wall -Wextra -Werror
+OTHERFLAGS :=
+
+# Compiler options. You likely won't need to touch these.
+TOOLCHAIN := $(OO_PS4_TOOLCHAIN)
+ODIR := build
+SDIR := src
+EXTRAFLAGS := $(INCLUDES) $(ERRORFLAGS) $(OTHERFLAGS)
+CFLAGS := -cc1 -triple x86_64-pc-freebsd-elf -munwind-tables -fuse-init-array -debug-info-kind=limited -debugger-tuning=gdb -emit-obj -isysroot $(TOOLCHAIN) -isystem $(TOOLCHAIN)/include $(EXTRAFLAGS)
+CXXFLAGS := $(CFLAGS) -isystem $(TOOLCHAIN)/include/c++/v1
+LFLAGS := -m elf_x86_64 -pie --script $(TOOLCHAIN)/link.x --eh-frame-hdr -L$(TOOLCHAIN)/lib $(LIBS) $(TOOLCHAIN)/lib/crtlib.o
+
+CFILES := $(wildcard $(SDIR)/*.c)
+CPPFILES := $(wildcard $(SDIR)/*.cpp)
+ASMFILES := $(wildcard $(SDIR)/*.s)
+OBJS := $(patsubst $(SDIR)/%.s, $(ODIR)/%.o, $(ASMFILES)) $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) $(patsubst $(SDIR)/%.cpp, $(ODIR)/%.o, $(CPPFILES))
+STUBOBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o.stub, $(CFILES)) $(patsubst $(SDIR)/%.cpp, $(ODIR)/%.o.stub, $(CPPFILES))
+
+TARGET = $(PROJECTNAME).prx
+TARGETSTUB = $(PROJECTNAME)_stub.so
+
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Linux)
+ AS := llvm-mc
+ CC := clang
+ CXX := clang++
+ LD := ld.lld
+ CDIR := linux
+endif
+ifeq ($(UNAME_S),Darwin)
+ AS := /usr/local/opt/llvm/bin/llvm-mc
+ CC := /usr/local/opt/llvm/bin/clang
+ CXX := /usr/local/opt/llvm/bin/clang++
+ LD := /usr/local/opt/llvm/bin/ld.lld
+ CDIR := macos
+endif
+
+# Make rules
+$(TARGET): $(ODIR) $(OBJS)
+ $(LD) $(ODIR)/*.o -o $(ODIR)/$(PROJECTNAME).elf $(LFLAGS)
+ $(TOOLCHAIN)/bin/$(CDIR)/create-lib -in=$(ODIR)/$(PROJECTNAME).elf -out=$(ODIR)/$(PROJECTNAME).oelf --paid 0x3800000000000011
+ @mv $(ODIR)/$(PROJECTNAME).prx $(TARGET)
+ @echo Built PRX successfully!
+
+$(TARGETSTUB): $(ODIR) $(STUBOBJS)
+ $(CXX) $(ODIR)/*.o.stub -o $(TARGETSTUB) -target x86_64-pc-linux-gnu -shared -fuse-ld=lld -ffreestanding -nostdlib -fno-builtin -L$(TOOLCHAIN)/lib $(LIBS)
+ @echo Built stub successfully!
+
+$(ODIR)/%.o: $(SDIR)/%.cpp
+ $(CXX) $(CXXFLAGS) -o $@ $<
+
+$(ODIR)/%.o: $(SDIR)/%.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+$(ODIR)/%.o: $(SDIR)/%.s
+ $(AS) -triple=x86_64-pc-freebsd-elf --filetype=obj -o $@ $<
+
+$(ODIR)/%.o.stub: $(SDIR)/%.cpp
+ $(CXX) -target x86_64-pc-linux-gnu -ffreestanding -nostdlib -fno-builtin -fPIC -c -isysroot $(TOOLCHAIN) -isystem $(TOOLCHAIN)/include -isystem $(TOOLCHAIN)/include/c++/v1 $(EXTRAFLAGS) -o $@ $<
+
+$(ODIR)/%.o.stub: $(SDIR)/%.c
+ $(CC) -target x86_64-pc-linux-gnu -ffreestanding -nostdlib -fno-builtin -fPIC -c -isysroot $(TOOLCHAIN) -isystem $(TOOLCHAIN)/include $(EXTRAFLAGS) -o $@ $<
+
+$(ODIR):
+ @echo Starting build...
+ @echo Creating build directory...
+ @mkdir $@
+
+.PHONY: all clean install
+.DEFAULT_GOAL := all
+
+all: $(TARGET) $(TARGETSTUB)
+
+clean:
+ @echo Cleaning up...
+ @rm -rf $(TARGET) $(TARGETSTUB) $(ODIR)
+
+install:
+ @echo Installing...
+ @yes | cp -f $(TARGETSTUB) $(OO_PS4_TOOLCHAIN)/lib/$(TARGETSTUB) 2>/dev/null && echo Installed!|| echo Failed to install, is it built?
diff --git a/Makefile.pc b/Makefile.pc
new file mode 100644
index 0000000..8fbc9b5
--- /dev/null
+++ b/Makefile.pc
@@ -0,0 +1,58 @@
+CC = clang
+
+# Paths
+SRC_PATH = src
+BUILD_PATH = build
+BIN_PATH = $(BUILD_PATH)/bin
+
+# Executable
+BIN_NAME = main
+
+# Code Lists #
+# Find all source files in the source directory, sorted by most recently modified
+SOURCES = $(shell find $(SRC_PATH) -name '*.c' | sort -k 1nr | cut -f2-)
+# Set the object file names, with the source directory stripped from the path, and the build path prepended in its place
+OBJECTS = $(SOURCES:$(SRC_PATH)/%.c=$(BUILD_PATH)/%.o)
+
+# Flags #
+CFLAGS = -O0 -g -std=c99 -D_DEFAULT_SOURCE -D__LIBLOG_PC__ -Wall -Wextra -Wpedantic -Werror
+INCLUDES = -Iinclude -I/usr/local/include
+
+# Check for valgrind flag
+ifeq ($(__VALGRIND__),true)
+CFLAGS += -D__VALGRIND__
+endif
+
+.PHONY: all clean
+.DEFAULT_GOAL := all
+
+# Checks the executable and symlinks to the output
+.PHONY: all
+all: $(BIN_PATH) $(BIN_PATH)/$(BIN_NAME)
+ @echo "Making symlink: $(BIN_NAME) -> $<"
+ @$(RM) $(BIN_NAME)
+ @ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
+
+# Creation of the executable
+$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
+ @echo "Linking: $@"
+ $(CC) $(OBJECTS) -o $@
+
+# Source file rules #
+# After the first compilation they will be joined with the rules from the dependency files to provide header dependencies
+$(BUILD_PATH)/%.o: $(SRC_PATH)/%.c
+ @echo "Compiling: $< -> $@"
+ $(CC) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@
+
+$(BIN_PATH):
+ @echo "Creating directories"
+ @mkdir -p $(dir $(OBJECTS))
+ @mkdir -p $(BIN_PATH)
+
+.PHONY: clean
+clean:
+ @echo "Deleting $(BIN_NAME) symlink"
+ @$(RM) $(BIN_NAME)
+ @echo "Deleting directories"
+ @$(RM) -r $(BUILD_PATH)
+ @$(RM) -r $(BIN_PATH)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7d219f3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# libLog
+
+## About
+
+A simple "do it all" logging library (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.
+
+Features include:
+
+- C99 standard, so it should be compatible with your code
+- Automatically formats output to display file and line where the log function was called from (Optional)
+- Various log levels
+ - Set log level to display on-the-fly
+- Colorized output based on log level (Optional/Where available)
+- Logs to
+ - `Print` (Simple printf)
+ - `Kernel` (Kernel log, no hooks required)
+ - `Socket` (Sent over UDP, does not wait for response or confirmation so it should not hang, but packets can be lost)
+ - `File`
+- Include a `Hexdump` for dumping arbitrary memory to a human readable format
+- Include a `Bindump` for dumping arbitrary memory (Only via `Socket` and `File`)
+ - This uses a separate socket/file setup so you won't pollute your logs with binary data
+
+## Notes
+
+- This can be debugged/tested PC side with the include `Makefile.pc` file. Just run `make -f Makefile.pc` to build with it.
+
+## Road to 1.0.0
+
+- [ ] Recreate C++ stream bindings
+- [ ] Document everything w/ examples
+- [ ] Release
+
+## 1.0.0+ Plans
+
+- [ ] Automatic log rotation
+
+[//]: #
+ [OpenOrbis Toolchain]:
diff --git a/include/libLog.h b/include/libLog.h
new file mode 100644
index 0000000..328f89a
--- /dev/null
+++ b/include/libLog.h
@@ -0,0 +1,71 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
+
+// License: LGPL-3.0
+
+#ifndef LIBLOG_H
+#define LIBLOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+enum LogLevels {
+ LL_None,
+ LL_Fatal,
+ LL_Error,
+ LL_Warn,
+ LL_Info,
+ LL_Debug,
+ LL_Trace,
+ LL_All
+};
+
+enum PrintTypes {
+ PT_Print,
+ PT_Kernel,
+ PT_Socket,
+ PT_File,
+};
+
+void _logPrint(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, ...);
+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);
+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);
+
+bool logSocketOpen(const char *p_IpAddress, uint16_t p_Port);
+bool logSocketClose();
+bool logSocketIsOpen();
+
+const char *logSocketGetIpAddress();
+uint16_t logSocketGetPort();
+
+bool logFileOpen(const char *p_Path);
+bool logFileClose();
+const char *logFileGetFilename();
+
+void logSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logGetLogLevel();
+
+void logPrintSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logPrintGetLogLevel();
+
+void logKernelSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logKernelGetLogLevel();
+
+void logFileSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logFileGetLogLevel();
+
+void logSocketSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logSocketGetLogLevel();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/libLog_stub.h b/include/libLog_stub.h
new file mode 100644
index 0000000..fa22a98
--- /dev/null
+++ b/include/libLog_stub.h
@@ -0,0 +1,174 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
+
+// License: LGPL-3.0
+
+#ifndef LIBLOG_H
+#define LIBLOG_H
+
+#ifdef __cplusplus
+// TODO: C++ stream bindings includes here
+
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef __LIBLOG_PC__
+#include
+#endif
+
+enum LogLevels {
+ LL_None,
+ LL_Fatal,
+ LL_Error,
+ LL_Warn,
+ LL_Info,
+ LL_Debug,
+ LL_Trace,
+ LL_All
+};
+
+enum PrintTypes {
+ PT_Print,
+ PT_Kernel,
+ PT_Socket,
+ PT_File,
+};
+
+#ifndef __LIBLOG_PC__
+void (*_logPrint)(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, ...) = NULL;
+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) = NULL;
+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) = NULL;
+
+bool (*logSocketOpen)(const char *p_IpAddress, uint16_t p_Port) = NULL;
+bool (*logSocketClose)() = NULL;
+bool (*logSocketIsOpen)() = NULL;
+
+const char *(*logSocketGetIpAddress)() = NULL;
+uint16_t (*logSocketGetPort)() = NULL;
+
+bool (*logFileOpen)(const char *p_Path) = NULL;
+bool (*logFileClose)() = NULL;
+const char *(*logFileGetFilename)() = NULL;
+
+void (*logSetLogLevel)(enum LogLevels p_LogLevel) = NULL;
+enum LogLevels (*logGetLogLevel)() = NULL;
+
+void (*logPrintSetLogLevel)(enum LogLevels p_LogLevel) = NULL;
+enum LogLevels (*logPrintGetLogLevel)() = NULL;
+
+void (*logKernelSetLogLevel)(enum LogLevels p_LogLevel) = NULL;
+enum LogLevels (*logKernelGetLogLevel)() = NULL;
+
+void (*logFileSetLogLevel)(enum LogLevels p_LogLevel) = NULL;
+enum LogLevels (*logFileGetLogLevel)() = NULL;
+
+void (*logSocketSetLogLevel)(enum LogLevels p_LogLevel) = NULL;
+enum LogLevels (*logSocketGetLogLevel)() = NULL;
+
+int32_t logInitalize(int handle) {
+ if (sceKernelDlsym(handle, "_logPrint", (void **)&_logPrint) != 0 ||
+ sceKernelDlsym(handle, "_logPrintHex", (void **)&_logPrintHex) != 0 ||
+ sceKernelDlsym(handle, "_logPrintBin", (void **)&_logPrintBin) != 0 ||
+
+ sceKernelDlsym(handle, "logSocketOpen", (void **)&logSocketOpen) != 0 ||
+ sceKernelDlsym(handle, "logSocketClose", (void **)&logSocketClose) != 0 ||
+ sceKernelDlsym(handle, "logSocketIsOpen", (void **)&logSocketIsOpen) != 0 ||
+
+ sceKernelDlsym(handle, "logSocketGetIpAddress", (void **)&logSocketGetIpAddress) != 0 ||
+ sceKernelDlsym(handle, "logSocketGetPort", (void **)&logSocketGetPort) != 0 ||
+
+ sceKernelDlsym(handle, "logFileOpen", (void **)&logFileOpen) != 0 ||
+ sceKernelDlsym(handle, "logFileClose", (void **)&logFileClose) != 0 ||
+ sceKernelDlsym(handle, "logFileGetFilename", (void **)&logFileGetFilename) != 0 ||
+
+ sceKernelDlsym(handle, "logSetLogLevel", (void **)&logSetLogLevel) != 0 ||
+ sceKernelDlsym(handle, "logGetLogLevel", (void **)&logGetLogLevel) != 0 ||
+
+ sceKernelDlsym(handle, "logPrintSetLogLevel", (void **)&logPrintSetLogLevel) != 0 ||
+ sceKernelDlsym(handle, "logPrintGetLogLevel", (void **)&logPrintGetLogLevel) != 0 ||
+
+ sceKernelDlsym(handle, "logKernelSetLogLevel", (void **)&logKernelSetLogLevel) != 0 ||
+ sceKernelDlsym(handle, "logKernelGetLogLevel", (void **)&logKernelGetLogLevel) != 0 ||
+
+ sceKernelDlsym(handle, "logFileSetLogLevel", (void **)&logFileSetLogLevel) != 0 ||
+ sceKernelDlsym(handle, "logFileGetLogLevel", (void **)&logFileGetLogLevel) != 0 ||
+
+ sceKernelDlsym(handle, "logSocketSetLogLevel", (void **)&logSocketSetLogLevel) != 0 ||
+ sceKernelDlsym(handle, "logSocketGetLogLevel", (void **)&logSocketGetLogLevel) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#else
+
+void _logPrint(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, ...);
+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);
+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);
+
+bool logSocketOpen(const char *p_IpAddress, uint16_t p_Port);
+bool logSocketClose();
+bool logSocketIsOpen();
+
+const char *logSocketGetIpAddress();
+uint16_t logSocketGetPort();
+
+bool logFileOpen(const char *p_Path);
+bool logFileClose();
+const char *logFileGetFilename();
+
+void logSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logGetLogLevel();
+
+void logPrintSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logPrintGetLogLevel();
+
+void logKernelSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logKernelGetLogLevel();
+
+void logFileSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logFileGetLogLevel();
+
+void logSocketSetLogLevel(enum LogLevels p_LogLevel);
+enum LogLevels logSocketGetLogLevel();
+
+#endif
+
+#define logPrint(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Print, true, __FILE__, __LINE__, __VA_ARGS__)
+#define logPrintUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Print, false, __FILE__, __LINE__, __VA_ARGS__)
+#define logPrintHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Print, true, __FILE__, __LINE__, p_Pointer, p_Length)
+#define logPrintHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Print, false, __FILE__, __LINE__, p_Pointer, p_Length)
+
+#define logKernel(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Kernel, true, __FILE__, __LINE__, __VA_ARGS__)
+#define logKernelUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Kernel, false, __FILE__, __LINE__, __VA_ARGS__)
+#define logKernelHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Kernel, true, __FILE__, __LINE__, p_Pointer, p_Length)
+#define logKernelHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Kernel, false, __FILE__, __LINE__, p_Pointer, p_Length)
+
+#define logSocket(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Socket, true, __FILE__, __LINE__, __VA_ARGS__)
+#define logSocketUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_Socket, false, __FILE__, __LINE__, __VA_ARGS__)
+#define logSocketHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Socket, true, __FILE__, __LINE__, p_Pointer, p_Length)
+#define logSocketHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_Socket, false, __FILE__, __LINE__, p_Pointer, p_Length)
+
+#define logFile(p_LogLevel, ...) _logPrint(p_LogLevel, PT_File, true, __FILE__, __LINE__, __VA_ARGS__)
+#define logFileUnformatted(p_LogLevel, ...) _logPrint(p_LogLevel, PT_File, false, __FILE__, __LINE__, __VA_ARGS__)
+#define logFileHexdump(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_File, true, __FILE__, __LINE__, p_Pointer, p_Length)
+#define logFileHexdumpUnformatted(p_LogLevel, p_Pointer, p_Length) _logPrintHex(p_LogLevel, PT_File, false, __FILE__, __LINE__, p_Pointer, p_Length)
+
+#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)
+#define logFileBindump(p_LogLevel, p_Path, p_Pointer, p_Length) _logPrintBin(p_LogLevel, PT_File, p_Path, 0, p_Pointer, p_Length)
+
+#ifdef __cplusplus
+}
+
+// TODO: C++ stream bindings here
+
+#endif
+
+#endif
diff --git a/src/libLog.c b/src/libLog.c
new file mode 100644
index 0000000..5870a2d
--- /dev/null
+++ b/src/libLog.c
@@ -0,0 +1,622 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
+
+// License: LGPL-3.0
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifndef __LIBLOG_PC__
+#include
+#endif
+
+#include "libLog.h"
+
+#define LOGGER_MAX_BUFFER 0x500
+
+#define KNRM "\x1B[0m"
+#define KRED "\x1B[31m"
+#define KGRN "\x1B[32m"
+#define KYEL "\x1B[33m"
+#define KBLU "\x1B[34m"
+#define KLBLU "\x1B[94m"
+#define KMAG "\x1B[35m"
+#define KCYN "\x1B[36m"
+#define KWHT "\x1B[37m"
+#define KGRY "\x1b[90m"
+
+// Globals
+enum LogLevels g_LogLevel = LL_All;
+enum LogLevels g_PrintLogLevel = LL_Trace;
+enum LogLevels g_KernelLogLevel = LL_Trace;
+enum LogLevels g_SocketLogLevel = LL_Trace;
+enum LogLevels g_FileLogLevel = LL_Trace;
+
+int g_Socket = -1;
+const char *g_SocketLogIpAddress = "000.000.000.000";
+uint16_t g_SocketLogPort = 0;
+
+const char *g_LogFileName = "";
+FILE *g_LogFilePointer;
+
+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) {
+ if (p_File == NULL || p_Format == NULL) {
+ return NULL;
+ }
+
+ char *s_Message = calloc(LOGGER_MAX_BUFFER + 1, sizeof(char));
+ if (s_Message == NULL) {
+ return NULL;
+ }
+
+ vsnprintf(s_Message, LOGGER_MAX_BUFFER, p_Format, p_Args);
+
+ if (!p_Meta) {
+ return s_Message;
+ }
+
+ const char *s_LevelString = "None ";
+ const char *s_LevelColor = KNRM;
+ const char *s_LevelResetColor = KNRM;
+
+ switch (p_LogLevel) {
+ case LL_None:
+ break;
+ case LL_Fatal:
+ s_LevelString = "Fatal";
+ s_LevelColor = KMAG;
+ break;
+ case LL_Error:
+ s_LevelString = "Error";
+ s_LevelColor = KRED;
+ break;
+ case LL_Warn:
+ s_LevelString = "Warn";
+ s_LevelColor = KYEL;
+ break;
+ case LL_Info:
+ s_LevelString = "Info";
+ s_LevelColor = KGRN;
+ break;
+ case LL_Debug:
+ s_LevelString = "Debug";
+ s_LevelColor = KCYN;
+ break;
+ case LL_Trace:
+ s_LevelString = "Trace";
+ s_LevelColor = KLBLU;
+ break;
+ case LL_All:
+ default:
+ break;
+ }
+
+ if (p_PrintType == PT_File) {
+ s_LevelColor = "\0";
+ s_LevelResetColor = "\0";
+ }
+
+ char *s_Output = calloc(LOGGER_MAX_BUFFER + 1, sizeof(char));
+ if (s_Output == NULL) {
+ free((void *)s_Message);
+ return NULL;
+ }
+
+ snprintf(s_Output, LOGGER_MAX_BUFFER, "%s[%-5s] %s:%d: %s%s\n", s_LevelColor, s_LevelString, p_File, p_Line, s_Message, s_LevelResetColor);
+ free((void *)s_Message);
+
+ return s_Output;
+}
+
+static size_t _hexdumpSize(int p_Input) {
+ int s_Lines = p_Input / 0x10;
+ if (s_Lines == 0) {
+ s_Lines++;
+ }
+
+ int s_Remain = p_Input % 0x10;
+ if (s_Remain > 0) {
+ s_Lines++;
+ }
+
+ int s_Total = s_Lines * 77 + 1;
+ if (s_Remain != 0) {
+ s_Total -= 0x10 - s_Remain;
+ }
+
+ return s_Total + 1; // + 1 for new line in formatted hexdumps
+}
+
+// Hexdump based on: https://stackoverflow.com/a/29865
+static const char *_hexdump(bool p_Meta, const void *p_Pointer, int p_Length) {
+ if (p_Pointer == NULL) {
+ return NULL;
+ }
+
+ unsigned char *s_Buffer = (unsigned char *)p_Pointer;
+
+ size_t s_HexdumpStringSize = _hexdumpSize(p_Length);
+ char *s_Ret = (char *)calloc(s_HexdumpStringSize, sizeof(char));
+ if (s_Ret == NULL) {
+ return NULL;
+ }
+
+ size_t s_Offset = 0;
+
+ if (p_Meta) {
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ }
+
+ for (int i = 0; i < p_Length; i += 0x10) {
+ if (s_Offset + 10 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ for (int j = 0; j < 0x10; j++) {
+ if (i + j < p_Length) {
+ if (s_Offset + 3 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ } else {
+ if (s_Offset + 3 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ }
+ if (j == 7) {
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ }
+ }
+
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+
+ for (int j = 0; j < 0x10; j++) {
+ if (i + j < p_Length) {
+ if (isprint(s_Buffer[i + j])) {
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ } else {
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ }
+ }
+ }
+
+ if (i + 0x10 < p_Length) {
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ }
+ }
+
+ if (!p_Meta) {
+ if (s_Offset + 1 + 1 > s_HexdumpStringSize) {
+ free((void *)s_Ret);
+ return NULL;
+ }
+ 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
+ }
+
+ return s_Ret;
+}
+
+static const char *_bindump(const void *p_Pointer, int p_Length) {
+ if (p_Pointer == NULL) {
+ return NULL;
+ }
+
+ unsigned char *s_Buffer = (unsigned char *)p_Pointer;
+
+ char *s_Output = (char *)calloc(p_Length + 1, sizeof(char));
+ if (s_Output == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < p_Length; i++) {
+ s_Output[i] = s_Buffer[i];
+ }
+
+ return s_Output;
+}
+
+// https://www.tutorialspoint.com/c-program-to-validate-an-ip-address
+static bool _validNumber(char *p_Str) {
+ while (*p_Str) {
+ if (!isdigit(*p_Str)) {
+ return false;
+ }
+ p_Str++;
+ }
+
+ return true;
+}
+
+// https://www.tutorialspoint.com/c-program-to-validate-an-ip-address
+static bool _validIPAddress(const char *p_IpAddress) {
+ if (!p_IpAddress) {
+ return false;
+ }
+
+ char *s_TempIp = strdup(p_IpAddress);
+
+ int s_Dots = 0;
+
+ char *s_Ptr;
+ s_Ptr = strtok(s_TempIp, ".");
+ if (!s_Ptr) {
+ free((void *)s_TempIp);
+ return false;
+ }
+
+ while (s_Ptr) {
+ if (!_validNumber(s_Ptr)) {
+ free((void *)s_TempIp);
+ return false;
+ }
+ int s_Num = atoi(s_Ptr); // Flawfinder: ignore. Value is checked below
+ if (s_Num >= 0 && s_Num <= 255) {
+ s_Ptr = strtok(NULL, ".");
+ if (s_Ptr) {
+ s_Dots++;
+ }
+ } else {
+ free((void *)s_TempIp);
+ return false;
+ }
+ }
+ if (s_Dots != 3) {
+ free((void *)s_TempIp);
+ return false;
+ }
+
+ free((void *)s_TempIp);
+
+ return true;
+}
+
+static void _send_socket(const char *s_Buffer) {
+ if (s_Buffer == NULL) {
+ return;
+ }
+
+ if (g_Socket < 0) {
+ return;
+ }
+
+ if (!_validIPAddress(g_SocketLogIpAddress) || g_SocketLogPort == 0) {
+ return;
+ }
+
+ struct sockaddr_in s_Servaddr = {
+ .sin_family = AF_INET,
+ .sin_addr = {
+ .s_addr = inet_addr(g_SocketLogIpAddress),
+ },
+ .sin_port = htons(g_SocketLogPort),
+ };
+
+ sendto(g_Socket, s_Buffer, strnlen(s_Buffer, LOGGER_MAX_BUFFER), 0, (struct sockaddr *)&s_Servaddr, sizeof(s_Servaddr));
+}
+
+static void _write_file(const char *s_Buffer) {
+ if (s_Buffer == NULL || g_LogFilePointer == NULL) {
+ return;
+ }
+
+ fprintf(g_LogFilePointer, "%s", s_Buffer);
+}
+
+static bool _checkLogLevelByType(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType) {
+ enum LogLevels s_TypeLogLevel;
+ switch (p_PrintType) {
+ case PT_Print:
+ s_TypeLogLevel = g_PrintLogLevel;
+ break;
+ case PT_Kernel:
+ s_TypeLogLevel = g_KernelLogLevel;
+ break;
+ case PT_Socket:
+ s_TypeLogLevel = g_SocketLogLevel;
+ break;
+ case PT_File:
+ s_TypeLogLevel = g_FileLogLevel;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ if (g_LogLevel >= g_PrintLogLevel && s_TypeLogLevel >= p_LogLevel) {
+ return true;
+ }
+
+ return false;
+}
+
+static void _printByType(enum PrintTypes p_PrintType, const char *s_Buffer) {
+ if (p_PrintType == PT_Print) {
+ printf("%s", s_Buffer);
+ } else if (p_PrintType == PT_Kernel) {
+#ifndef __LIBLOG_PC__
+ sceKernelDebugOutText(0, s_Buffer);
+#else
+ // 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
+ printf("sceKernelDebugOutText(0, %s);\n", s_Buffer);
+#endif
+ } else if (p_PrintType == PT_Socket) {
+ _send_socket(s_Buffer);
+ } else if (p_PrintType == PT_File) {
+ _write_file(s_Buffer);
+ }
+}
+
+void _logPrint(enum LogLevels p_LogLevel, enum PrintTypes p_PrintType, bool p_Meta, const char *p_File, int p_Line, const char *p_Format, ...) {
+ if (p_File == NULL || p_Format == NULL) {
+ return;
+ }
+
+ if (!_checkLogLevelByType(p_LogLevel, p_PrintType)) {
+ return;
+ }
+
+ va_list p_Args;
+ va_start(p_Args, p_Format);
+ const char *s_Output = _formatOutput(p_LogLevel, p_PrintType, p_Meta, p_File, p_Line, p_Format, p_Args);
+ va_end(p_Args);
+ if (s_Output == NULL) {
+ return;
+ }
+
+ _printByType(p_PrintType, s_Output);
+
+ free((void *)s_Output);
+}
+
+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) {
+ if (p_Pointer == NULL) {
+ return;
+ }
+
+ if (p_Length == 0) {
+ return;
+ }
+
+ if (!_checkLogLevelByType(p_LogLevel, p_PrintType)) {
+ return;
+ }
+
+ const char *s_Output = _hexdump(p_Meta, p_Pointer, p_Length);
+ if (s_Output == NULL) {
+ return;
+ }
+
+ _logPrint(p_LogLevel, p_PrintType, p_Meta, p_File, p_Line, "%s", s_Output);
+
+ free((void *)s_Output);
+}
+
+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) {
+ if (p_Input == NULL || p_Pointer == NULL) {
+ return;
+ }
+
+ if (p_Length == 0) {
+ return;
+ }
+
+ if (p_PrintType != PT_Socket && p_PrintType != PT_File) {
+ return;
+ }
+
+ if (!_checkLogLevelByType(p_LogLevel, p_PrintType)) {
+ return;
+ }
+
+ const char *s_Output = _bindump(p_Pointer, p_Length);
+ if (s_Output == NULL) {
+ return;
+ }
+
+ if (p_PrintType == PT_Socket) {
+ if (!_validIPAddress(p_Input) || p_Port == 0) {
+ free((void *)s_Output);
+ return;
+ }
+
+ int s_Socket = -1;
+ if ((s_Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ free((void *)s_Output);
+ return;
+ }
+
+ struct sockaddr_in s_Servaddr = {
+ .sin_family = AF_INET,
+ .sin_addr = {
+ .s_addr = inet_addr(p_Input),
+ },
+ .sin_port = htons(p_Port),
+ };
+
+ sendto(s_Socket, s_Output, p_Length, 0, (struct sockaddr *)&s_Servaddr, sizeof(s_Servaddr));
+
+ close(s_Socket);
+ } else if (p_PrintType == PT_File) {
+ FILE *s_FilePointer;
+
+ s_FilePointer = fopen(p_Input, "wb");
+
+ if (s_FilePointer == NULL) {
+ free((void *)s_Output);
+ return;
+ }
+
+ fwrite(s_Output, sizeof(char), p_Length, s_FilePointer);
+
+ fclose(s_FilePointer);
+ }
+
+ free((void *)s_Output);
+}
+
+bool logSocketOpen(const char *p_IpAddress, uint16_t p_Port) {
+ if (p_IpAddress == NULL) {
+ return false;
+ }
+
+ if (!_validIPAddress(p_IpAddress)) {
+ return false;
+ }
+
+ if (g_Socket < 0) {
+ if ((g_Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ return false;
+ }
+ }
+
+ g_SocketLogIpAddress = p_IpAddress;
+ g_SocketLogPort = p_Port;
+
+ return true;
+}
+
+bool logSocketClose() {
+ if (g_Socket < 0) {
+ return true;
+ }
+
+ if (close(g_Socket) < 0) {
+ return false;
+ }
+
+ g_Socket = -1;
+
+ return true;
+}
+
+bool logSocketIsOpen() {
+ if (g_Socket < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+const char *logSocketGetIpAddress() {
+ return g_SocketLogIpAddress;
+}
+
+uint16_t logSocketGetPort() {
+ return g_SocketLogPort;
+}
+
+bool logFileOpen(const char *p_Path) {
+ if (p_Path == NULL) {
+ return false;
+ }
+
+ if (g_LogFilePointer != NULL) {
+ // We should either close the currently open file and open the new file, or return false to "fail"
+ return false; // or logFileClose();
+ }
+
+ g_LogFilePointer = fopen(p_Path, "a");
+
+ if (g_LogFilePointer == NULL) {
+ return false;
+ }
+
+ g_LogFileName = p_Path;
+
+ return true;
+}
+
+bool logFileClose() {
+ if (g_LogFilePointer == NULL) {
+ g_LogFileName = "";
+ return true;
+ }
+
+ fclose(g_LogFilePointer);
+ g_LogFilePointer = NULL;
+ g_LogFileName = "";
+
+ return true;
+}
+
+const char *logFileGetFilename() {
+ return g_LogFileName;
+}
+
+void logSetLogLevel(enum LogLevels p_LogLevel) {
+ g_LogLevel = p_LogLevel;
+}
+
+enum LogLevels logGetLogLevel() {
+ return g_LogLevel;
+}
+
+void logPrintSetLogLevel(enum LogLevels p_LogLevel) {
+ g_PrintLogLevel = p_LogLevel;
+}
+
+enum LogLevels logPrintGetLogLevel() {
+ return g_PrintLogLevel;
+}
+
+void logKernelSetLogLevel(enum LogLevels p_LogLevel) {
+ g_KernelLogLevel = p_LogLevel;
+}
+
+enum LogLevels logKernelGetLogLevel() {
+ return g_KernelLogLevel;
+}
+
+void logFileSetLogLevel(enum LogLevels p_LogLevel) {
+ g_FileLogLevel = p_LogLevel;
+}
+
+enum LogLevels logFileGetLogLevel() {
+ return g_FileLogLevel;
+}
+
+void logSocketSetLogLevel(enum LogLevels p_LogLevel) {
+ g_SocketLogLevel = p_LogLevel;
+}
+
+enum LogLevels logSocketGetLogLevel() {
+ return g_SocketLogLevel;
+}
diff --git a/src/pc-example.c b/src/pc-example.c
new file mode 100644
index 0000000..33981a9
--- /dev/null
+++ b/src/pc-example.c
@@ -0,0 +1,52 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check it.
+// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
+
+// License: LGPL-3.0
+
+#ifdef __LIBLOG_PC__
+// This example code itself should work on the PS4 as well, as long as the library is initialized
+
+#include
+
+#include "libLog_stub.h"
+
+int main() {
+ char test[0x20];
+ memset(test, 0xCC, sizeof(test));
+
+#ifdef __VALGRIND__
+ for (int i = 0; i < 1000; i++) {
+#endif
+ logPrint(LL_Error, "This is a logPrint call");
+ logPrint(LL_Error, "This is a logPrint call #%i", 2);
+ logPrintUnformatted(LL_Error, "This is a logPrintUnformatted call\n");
+ logPrintUnformatted(LL_Error, "This is a logPrintUnformatted call #%i\n", 2);
+ logPrintHexdump(LL_Error, test, sizeof(test));
+ logPrintHexdumpUnformatted(LL_Error, test, sizeof(test));
+
+ logSocketOpen("192.168.1.4", 9023);
+ logSocket(LL_Error, "This is a logSocket call");
+ logSocket(LL_Error, "This is a logSocket call #%i", 2);
+ logSocketUnformatted(LL_Error, "This is a logSocketUnformatted call\n");
+ logSocketUnformatted(LL_Error, "This is a logSocketUnformatted call #%i\n", 2);
+ logSocketHexdump(LL_Error, test, sizeof(test));
+ logSocketHexdumpUnformatted(LL_Error, test, sizeof(test));
+ logSocketClose();
+
+ logFileOpen("./test.log");
+ logFile(LL_Error, "This is a logFile call");
+ logFileUnformatted(LL_Error, "This is a logFileUnformatted call\n");
+ logFileHexdump(LL_Error, test, sizeof(test));
+ logFileHexdumpUnformatted(LL_Error, test, sizeof(test));
+ logFileClose();
+
+ logSocketBindump(LL_Error, "192.168.1.4", 9022, test, sizeof(test));
+ logFileBindump(LL_Error, "./test.bin", test, sizeof(test));
+#ifdef __VALGRIND__
+ }
+#endif
+
+ return 0;
+}
+
+#endif
diff --git a/src/syscall.s b/src/syscall.s
new file mode 100644
index 0000000..cc6ac5d
--- /dev/null
+++ b/src/syscall.s
@@ -0,0 +1,10 @@
+.intel_syntax noprefix
+.text
+
+.global syscall
+
+syscall:
+ mov rax,0
+ mov r10,rcx
+ syscall
+ ret