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