-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d3bd48e
commit 6c3a06f
Showing
3 changed files
with
361 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# Library metadata. | ||
|
||
DEBUG_FLAGS = -D__FINAL__=1 | ||
LOG_TYPE = -D__USE_PRINTF__ | ||
BUILD_TYPE = _final | ||
|
||
ifeq ($(DEBUG),1) | ||
DEBUG_FLAGS = -D__FINAL__=0 | ||
BUILD_TYPE = _debug | ||
endif | ||
|
||
TYPE := $(BUILD_TYPE) | ||
FINAL := $(DEBUG_FLAGS) | ||
BUILD_FOLDER := $(shell pwd)/../../bin/plugins | ||
OUTPUT_PRX := $(shell basename $(CURDIR)) | ||
TARGET := $(BUILD_FOLDER)/prx$(TYPE)/$(OUTPUT_PRX) | ||
TARGET_ELF := $(BUILD_FOLDER)/elf$(TYPE)/$(OUTPUT_PRX) | ||
TARGETSTUB := $(OUTPUT_PRX).so | ||
|
||
# Libraries linked into the ELF. | ||
LIBS := -lSceLibcInternal -lSceGnmDriver -lScePad -lGoldHEN_Hook -lkernel -lSceSysmodule -lSceSystemService -lSceUserService | ||
|
||
EXTRAFLAGS := $(DEBUG_FLAGS) $(LOG_TYPE) -fcolor-diagnostics -Wall | ||
|
||
# You likely won't need to touch anything below this point. | ||
# Root vars | ||
TOOLCHAIN := $(OO_PS4_TOOLCHAIN) | ||
GH_SDK := $(GOLDHEN_SDK) | ||
PROJDIR := ../$(shell basename $(CURDIR))/source | ||
INTDIR := ../$(shell basename $(CURDIR))/build | ||
INCLUDEDIR := ../$(shell basename $(CURDIR))/include | ||
COMMON_DIR := ../../common | ||
|
||
# Define objects to build | ||
CFILES := $(wildcard $(PROJDIR)/*.c) | ||
CPPFILES := $(wildcard $(PROJDIR)/*.cpp) | ||
COMMONFILES := $(wildcard $(COMMONDIR)/*.cpp) | ||
OBJS := $(patsubst $(PROJDIR)/%.c, $(INTDIR)/%.o, $(CFILES)) $(patsubst $(PROJDIR)/%.cpp, $(INTDIR)/%.o, $(CPPFILES)) $(patsubst $(COMMONDIR)/%.cpp, $(INTDIR)/%.o, $(COMMONFILES)) | ||
STUBOBJS := $(patsubst $(PROJDIR)/%.c, $(INTDIR)/%.o, $(CFILES)) $(patsubst $(PROJDIR)/%.cpp, $(INTDIR)/%.o.stub, $(CPPFILES)) $(patsubst $(COMMONDIR)/%.cpp, $(INTDIR)/%.o.stub, $(COMMONFILES)) | ||
|
||
# Define final C/C++ flags | ||
CFLAGS := $(FINAL) --target=x86_64-pc-freebsd12-elf -fPIC -funwind-tables -c $(EXTRAFLAGS) -isysroot $(TOOLCHAIN) -isystem $(TOOLCHAIN)/include -I$(GH_SDK)/include -I$(INCLUDEDIR) -I$(COMMON_DIR) $(O_FLAG) | ||
CXXFLAGS := $(CFLAGS) -isystem $(TOOLCHAIN)/$(INCLUDEDIR)/c++/v1 | ||
LDFLAGS := -m elf_x86_64 -pie --script $(TOOLCHAIN)/link.x -e _init --eh-frame-hdr -L$(TOOLCHAIN)/lib -L$(GH_SDK) $(LIBS) | ||
|
||
# Create the intermediate directory incase it doesn't already exist. | ||
_unused := $(shell mkdir -p $(INTDIR)) | ||
|
||
# Check for linux vs macOS and account for clang/ld path | ||
UNAME_S := $(shell uname -s) | ||
|
||
ifeq ($(UNAME_S),Linux) | ||
CC := clang | ||
CCX := clang++ | ||
LD := ld.lld | ||
CDIR := linux | ||
endif | ||
ifeq ($(UNAME_S),Darwin) | ||
CC := /usr/local/opt/llvm/bin/clang | ||
CCX := /usr/local/opt/llvm/bin/clang++ | ||
LD := /usr/local/opt/llvm/bin/ld.lld | ||
CDIR := macos | ||
endif | ||
|
||
$(TARGET): $(INTDIR) $(OBJS) | ||
$(LD) $(GH_SDK)/build/crtprx.o $(INTDIR)/*.o -o $(TARGET_ELF).elf $(LDFLAGS) | ||
$(TOOLCHAIN)/bin/$(CDIR)/create-fself -in=$(TARGET_ELF).elf -out=$(TARGET_ELF).oelf --lib=$(TARGET).prx --paid 0x3800000000000011 | ||
|
||
$(TARGETSTUB): $(INTDIR) $(STUBOBJS) | ||
$(CC) $(INTDIR)/*.o.stub -o $(TARGETSTUB) -target x86_64-pc-linux-gnu -shared -fuse-ld=lld -ffreestanding -nostdlib -fno-builtin -L$(TOOLCHAIN)/lib $(LIBS) | ||
|
||
$(INTDIR)/%.o: $(PROJDIR)/%.c | ||
$(CC) $(CFLAGS) -o $@ $< | ||
|
||
$(INTDIR)/%.o: $(PROJDIR)/%.cpp | ||
$(CCX) $(CXXFLAGS) -o $@ $< | ||
|
||
$(INTDIR)/%.o.stub: $(PROJDIR)/%.c | ||
$(CC) -target x86_64-pc-linux-gnu -ffreestanding -nostdlib -fno-builtin -fPIC $(O_FLAG) -s -c -o $@ $< | ||
|
||
$(INTDIR)/%.o.stub: $(PROJDIR)/%.cpp | ||
$(CCX) -target x86_64-pc-linux-gnu -ffreestanding -nostdlib -fno-builtin -fPIC $(O_FLAG) -s -c -o $@ $< | ||
|
||
build-info: | ||
$(shell echo "#define GIT_COMMIT \"$(shell git rev-parse HEAD)\"" > $(COMMON_DIR)/git_ver.h) | ||
$(shell echo "#define GIT_VER \"$(shell git branch --show-current)\"" >> $(COMMON_DIR)/git_ver.h) | ||
$(shell echo "#define GIT_NUM $(shell git rev-list HEAD --count)" >> $(COMMON_DIR)/git_ver.h) | ||
$(shell echo "#define BUILD_DATE \"$(shell date '+%b %d %Y @ %T')\"" >> $(COMMON_DIR)/git_ver.h) | ||
|
||
plugin_common: | ||
$(CC) $(CFLAGS) -o $(INTDIR)/plugin_common.o $(COMMON_DIR)/plugin_common.c | ||
|
||
.PHONY: clean | ||
.DEFAULT_GOAL := all | ||
|
||
all: build-info plugin_common $(TARGET) | ||
|
||
clean: | ||
rm -rf $(TARGET) $(TARGETSTUB) $(INTDIR) $(OBJS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
// FrameTime Logger: Log frametime statistics. | ||
// Author: illusion0001 @ https://github.com/illusion0001 | ||
// Repository: https://github.com/GoldHEN/GoldHEN_Plugins_Repository | ||
|
||
#include <Common.h> | ||
#include <stdbool.h> | ||
#include <orbis/Pad.h> | ||
#include <time.h> | ||
#include <orbis/UserService.h> | ||
#include <orbis/SystemService.h> | ||
#include <orbis/Sysmodule.h> | ||
#include "plugin_common.h" | ||
|
||
#define PLUGIN_NAME "frame_logger" | ||
#define LOG_FOLDER "/data/" PLUGIN_NAME | ||
|
||
attr_public const char *g_pluginName = PLUGIN_NAME; | ||
attr_public const char *g_pluginDesc = "Log frametime statistics."; | ||
attr_public const char *g_pluginAuth = "illusion"; | ||
attr_public u32 g_pluginVersion = 0x00000100; // 1.00 | ||
|
||
int32_t sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void *dcbGpuAddrs[], uint32_t *dcbSizesInBytes, void *ccbGpuAddrs[], uint32_t *ccbSizesInBytes, uint32_t videoOutHandle, uint32_t displayBufferIndex, uint32_t flipMode, int64_t flipArg); | ||
|
||
HOOK_INIT(sceGnmSubmitAndFlipCommandBuffers); | ||
|
||
FILE *g_LogFILE = NULL; | ||
bool g_isRecording = false; | ||
uint64_t g_TimeStart = 0; | ||
uint64_t g_CurrentDelta = 0; | ||
double g_TscTick = 0; | ||
bool g_GnmHook = false; | ||
bool g_RunningThread = false; | ||
|
||
void doStats(void) | ||
{ | ||
if (!g_GnmHook) | ||
{ | ||
return; | ||
} | ||
uint64_t current_time = sceKernelGetProcessTimeCounter(); | ||
if (g_isRecording && g_LogFILE) | ||
{ | ||
fprintf(g_LogFILE, "%lf,%lf\n", ((current_time - g_TimeStart) / g_TscTick), ((current_time - g_CurrentDelta) / g_TscTick)); | ||
} | ||
g_CurrentDelta = current_time; | ||
} | ||
|
||
int32_t sceGnmSubmitAndFlipCommandBuffers_hook(uint32_t count, void *dcbGpuAddrs[], uint32_t *dcbSizesInBytes, void *ccbGpuAddrs[], uint32_t *ccbSizesInBytes, uint32_t videoOutHandle, uint32_t displayBufferIndex, uint32_t flipMode, int64_t flipArg) | ||
{ | ||
if (!g_GnmHook) | ||
{ | ||
g_GnmHook = true; | ||
} | ||
doStats(); | ||
return HOOK_CONTINUE(sceGnmSubmitAndFlipCommandBuffers, | ||
int32_t(*)(uint32_t, void **, uint32_t *, void **, uint32_t *, uint32_t, uint32_t, uint32_t, int64_t), | ||
count, dcbGpuAddrs, dcbSizesInBytes, ccbGpuAddrs, ccbSizesInBytes, videoOutHandle, displayBufferIndex, flipMode, flipArg); | ||
} | ||
|
||
struct tm get_local_time(void) | ||
{ | ||
int32_t tz_offset = 0; | ||
int32_t tz_dst = 0; | ||
int32_t ret = 0; | ||
|
||
if ((ret = sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_ZONE, &tz_offset)) < 0) | ||
{ | ||
final_printf("Failed to obtain ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_ZONE! Setting timezone offset to 0\n"); | ||
final_printf("sceSystemServiceParamGetInt: 0x%08x\n", ret); | ||
tz_offset = 0; | ||
} | ||
|
||
if ((ret = sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_SUMMERTIME, &tz_dst)) < 0) | ||
{ | ||
final_printf("Failed to obtain ORBIS_SYSTEM_SERVICE_PARAM_ID_SUMMERTIME! Setting timezone daylight time savings to 0\n"); | ||
final_printf("sceSystemServiceParamGetInt: 0x%08x\n", ret); | ||
tz_dst = 0; | ||
} | ||
|
||
time_t modifiedTime = time(NULL) + ((tz_offset + (tz_dst * 60)) * 60); | ||
return (*gmtime(&modifiedTime)); | ||
} | ||
|
||
void toggleRecording(void) | ||
{ | ||
g_isRecording = !g_isRecording; | ||
if (!g_GnmHook) | ||
{ | ||
NotifyStatic(TEX_ICON_SYSTEM, "g_GnmHook is false! cannot start data capture\nPlugin (" PLUGIN_NAME ") will now quit."); | ||
g_isRecording = false; | ||
g_RunningThread = false; | ||
return; | ||
} | ||
if (g_isRecording) | ||
{ | ||
sceKernelMkdir(LOG_FOLDER "/", 0777); | ||
char LogFilePath[MAX_PATH_] = {0}; | ||
g_LogFILE = NULL; | ||
struct tm t = get_local_time(); | ||
snprintf(LogFilePath, sizeof(LogFilePath), LOG_FOLDER "/" PLUGIN_NAME "-data-%d-%02d-%02d_%02d-%02d-%02d.csv", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); | ||
g_LogFILE = fopen(LogFilePath, "w"); | ||
if (g_LogFILE) | ||
{ | ||
g_TscTick = (double)sceKernelGetProcessTimeCounterFrequency(); | ||
g_TimeStart = sceKernelGetProcessTimeCounter(); | ||
g_CurrentDelta = g_TimeStart; | ||
Notify(TEX_ICON_SYSTEM, "Recording start:\n%s", LogFilePath); | ||
fputs("//Ignore=true\n" | ||
"TimeInSeconds,msBetweenPresents\n", | ||
g_LogFILE); | ||
} | ||
else | ||
{ | ||
Notify(TEX_ICON_SYSTEM, "Failed to create:\n%s", LogFilePath); | ||
g_isRecording = false; | ||
} | ||
} | ||
else | ||
{ | ||
fflush(g_LogFILE); | ||
fclose(g_LogFILE); | ||
NotifyStatic(TEX_ICON_SYSTEM, "Recording stop"); | ||
} | ||
} | ||
|
||
bool checkRecordingButton(OrbisPadData *pData) | ||
{ | ||
return (pData->buttons & ORBIS_PAD_BUTTON_L3) && | ||
(pData->buttons & ORBIS_PAD_BUTTON_L1) && | ||
(pData->buttons & ORBIS_PAD_BUTTON_TRIANGLE); | ||
} | ||
|
||
bool checkKillButton(OrbisPadData *pData) | ||
{ | ||
return (pData->buttons & ORBIS_PAD_BUTTON_L3) && | ||
(pData->buttons & ORBIS_PAD_BUTTON_R3) && | ||
(pData->buttons & ORBIS_PAD_BUTTON_L1) && | ||
(pData->buttons & ORBIS_PAD_BUTTON_R1) && | ||
(pData->buttons & ORBIS_PAD_BUTTON_SQUARE); | ||
} | ||
|
||
int32_t GetHandle(void) | ||
{ | ||
int32_t padHandle = 0; | ||
int32_t userID = 0; | ||
OrbisUserServiceInitializeParams param; | ||
param.priority = ORBIS_KERNEL_PRIO_FIFO_LOWEST; | ||
sceUserServiceInitialize(¶m); | ||
sceUserServiceGetInitialUser(&userID); | ||
if ((padHandle = scePadOpen(userID, 0, 0, 0)) == ORBIS_PAD_ERROR_ALREADY_OPENED) | ||
{ | ||
padHandle = scePadGetHandle(userID, 0, 0); | ||
} | ||
else if ((padHandle = scePadOpen(userID, 0, 0, 0)) < 0) | ||
{ | ||
final_printf("scePadOpen: return 0x%08x\n", padHandle); | ||
padHandle = 0; | ||
} | ||
return padHandle; | ||
} | ||
|
||
void *frame_logger_input_thread(void *args) | ||
{ | ||
final_printf("%s: Started\n", __func__); | ||
final_printf("%s: Waiting 10 seconds to start input lisener\n", __func__); | ||
sleep(10); | ||
int32_t ret = 0; | ||
bool prevTogglePressed = false; | ||
if ((ret = scePadInit() < 0)) | ||
{ | ||
NotifyStatic(TEX_ICON_SYSTEM, "Failed to init pad"); | ||
g_RunningThread = false; | ||
} | ||
OrbisPadData pData; | ||
final_printf("Start listening for controller inputs\n"); | ||
while (g_RunningThread) | ||
{ | ||
int32_t PadHandle = GetHandle(); | ||
int32_t ret = scePadReadState(PadHandle, &pData); | ||
debug_printf("scePadReadState: 0x%08x\n", ret); | ||
if (ret == 0 && PadHandle > 0 && pData.connected) | ||
{ | ||
bool currentTogglePressed = checkRecordingButton(&pData); | ||
if (currentTogglePressed && !prevTogglePressed) | ||
{ | ||
toggleRecording(); | ||
} | ||
prevTogglePressed = currentTogglePressed; | ||
if (checkKillButton(&pData)) | ||
{ | ||
NotifyStatic(TEX_ICON_SYSTEM, "User requested exit for Plugin (" PLUGIN_NAME ")"); | ||
g_RunningThread = false; | ||
} | ||
} | ||
else | ||
{ | ||
final_printf(STRINGIFY(PadHandle)": 0x%08x\n", PadHandle); | ||
final_printf(STRINGIFY(ret)": 0x%08x\n", ret); | ||
final_printf(STRINGIFY(pData.connected)": 0x%02x (%s)\n", pData.connected, pData.connected ? "true" : "false"); | ||
} | ||
// periodically flush the stream every second to avoid data loss | ||
if (g_LogFILE) | ||
{ | ||
#if (__FINAL__) == 0 | ||
int32_t flush_ret = fflush(g_LogFILE); | ||
debug_printf("fflush: 0x%08x\n", flush_ret); | ||
#else | ||
fflush(g_LogFILE); | ||
#endif | ||
} | ||
sleep(1); | ||
} | ||
final_printf("%s: Exit\n", __func__); | ||
scePthreadExit(NULL); | ||
return NULL; | ||
} | ||
|
||
s32 attr_public plugin_load(s32 argc, const char *argv[]) | ||
{ | ||
final_printf("[GoldHEN] <%s\\Ver.0x%08x> %s\n", g_pluginName, g_pluginVersion, __func__); | ||
final_printf("[GoldHEN] Plugin Author(s): %s\n", g_pluginAuth); | ||
boot_ver(); | ||
g_GnmHook = false; | ||
g_RunningThread = true; | ||
g_TimeStart = sceKernelGetProcessTimeCounter(); | ||
g_TscTick = (double)sceKernelGetProcessTimeCounterFrequency(); | ||
|
||
OrbisPthread thread; | ||
scePthreadCreate(&thread, NULL, frame_logger_input_thread, NULL, STRINGIFY(frame_logger_input_thread)); | ||
HOOK32(sceGnmSubmitAndFlipCommandBuffers); | ||
return 0; | ||
} | ||
|
||
s32 attr_public plugin_unload(s32 argc, const char *argv[]) | ||
{ | ||
final_printf("[GoldHEN] <%s\\Ver.0x%08x> %s\n", g_pluginName, g_pluginVersion, __func__); | ||
UNHOOK(sceGnmSubmitAndFlipCommandBuffers); | ||
return 0; | ||
} | ||
|
||
s32 attr_module_hidden module_start(s64 argc, const void *args) | ||
{ | ||
return 0; | ||
} | ||
|
||
s32 attr_module_hidden module_stop(s64 argc, const void *args) | ||
{ | ||
return 0; | ||
} |