mirror of
https://github.com/wiiu-env/WiiUPluginSystem.git
synced 2024-11-16 15:49:23 +01:00
[Plugin] Added screenshot plugin
This commit is contained in:
parent
647c3a9246
commit
117d2ab7d6
291
plugins/screenshot/Makefile
Normal file
291
plugins/screenshot/Makefile
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
DO_LOGGING := 1
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# Clear the implicit built in rules
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(DEVKITPPC)),)
|
||||||
|
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
|
||||||
|
endif
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPRO")
|
||||||
|
endif
|
||||||
|
export PATH := $(DEVKITPPC)/bin:$(PORTLIBS)/bin:$(PATH)
|
||||||
|
export PORTLIBS := $(DEVKITPRO)/portlibs/ppc
|
||||||
|
export WUPSDIR := $(DEVKITPRO)/wups
|
||||||
|
export GCC_VER := $(shell $(DEVKITPPC)/bin/powerpc-eabi-gcc -dumpversion)
|
||||||
|
|
||||||
|
PREFIX := powerpc-eabi-
|
||||||
|
|
||||||
|
export AS := $(PREFIX)as
|
||||||
|
export CC := $(PREFIX)gcc
|
||||||
|
export CXX := $(PREFIX)g++
|
||||||
|
export LD := $(PREFIX)ld
|
||||||
|
export AR := $(PREFIX)ar
|
||||||
|
export OBJCOPY := $(PREFIX)objcopy
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# INCLUDES is a list of directories containing extra header files
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR)).mod
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := src
|
||||||
|
|
||||||
|
DATA :=
|
||||||
|
|
||||||
|
INCLUDES := src
|
||||||
|
|
||||||
|
MAP ?= $(TARGET:.mod=.map)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# -Os: optimise size
|
||||||
|
# -Wall: generate lots of warnings
|
||||||
|
# -DGEKKO_U: define the symbol GEKKO (used in some headers)
|
||||||
|
# -D__wiiu__: define the symbol __wii__ (used in some headers)
|
||||||
|
# -mrvl: enable wii/gamecube compilation
|
||||||
|
# -mcpu=750: enable processor specific compilation
|
||||||
|
# -meabi: enable eabi specific compilation
|
||||||
|
# -mhard-float: enable hardware floating point instructions
|
||||||
|
# -fshort-wchar: use 16 bit whcar_t type in keeping with Wii executables
|
||||||
|
# -fno-common: stop common variables which the loader can't understand
|
||||||
|
# -msdata-none: do not use r2 or r13 as small data areas
|
||||||
|
# -memb: enable embedded application specific compilation
|
||||||
|
# -ffunction-sections: split up functions so linker can garbage collect
|
||||||
|
# -fdata-sections: split up data so linker can garbage collect
|
||||||
|
COMMON_CFLAGS += -Os -Wall -DGEKKO_U -D__wiiu__ -mrvl -mcpu=750 -meabi -mhard-float -fshort-wchar -fno-common -msdata=none -memb -ffunction-sections -fdata-sections
|
||||||
|
|
||||||
|
|
||||||
|
# -x c: compile as c code
|
||||||
|
# -std=c11: use the c11 standard
|
||||||
|
CFLAGS += $(COMMON_CFLAGS) -x c -std=c11
|
||||||
|
|
||||||
|
# -x c: compile as c++ code
|
||||||
|
# -std=gnu++11: use the c++11 standard
|
||||||
|
CXXFLAGS += $(COMMON_CFLAGS) -x c++ -std=gnu++11
|
||||||
|
|
||||||
|
ifeq ($(DO_LOGGING), 1)
|
||||||
|
CFLAGS += -D__LOGGING__
|
||||||
|
CXXFLAGS += -D__LOGGING__
|
||||||
|
endif
|
||||||
|
|
||||||
|
ASFLAGS := -mregnames
|
||||||
|
# --relocatable: make sure ld doesn't remove relocations wups will need
|
||||||
|
# -s: strip local symbols to speed linking
|
||||||
|
# -u: keep certain sections
|
||||||
|
# -wrap: wrap function
|
||||||
|
# --gc-sections: remove unneeded symbols
|
||||||
|
# -T: use the linker script specified (to force certain wups sections together)
|
||||||
|
# -Map: generate a map file
|
||||||
|
|
||||||
|
LDFLAG_COMMON += -u wups_load -u wups_meta -u wups_hooks -T $(WUPSDIR)/wups.ld \
|
||||||
|
-Wl,-wrap,open,-wrap,close,-wrap,write,-wrap,read,-wrap,lseek,-wrap,stat,-wrap,fstat,-wrap,opendir,-wrap,closedir,-wrap,readdir,-wrap,mkdir \
|
||||||
|
-Wl,-Map,$(notdir $@).map,-wrap,malloc,-wrap,free,-wrap,memalign,-wrap,calloc,-wrap,realloc,-wrap,malloc_usable_size,-wrap,_malloc_r,-wrap,_free_r,-wrap,_realloc_r,-wrap,_calloc_r,-wrap,_memalign_r,-wrap,_malloc_usable_size_r,--gc-sections
|
||||||
|
|
||||||
|
LDFLAGS_MOD += $(LDFLAG_COMMON),--relocatable
|
||||||
|
LDFLAGS_ELF += --relocatable -s -T $(WUPSDIR)/wups_elf.ld
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
Q := @
|
||||||
|
MAKEFLAGS += --no-print-directory
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# any extra libraries we wish to link with the project
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBS := -lwups -lutils -ldynamiclibs -lgd -ljpeg
|
||||||
|
#
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(DEVKITPPC)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export PROJECTDIR := $(CURDIR)
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGETDIR)/$(TARGET)
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# automatically build a list of object files for our project
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
TTFFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.ttf)))
|
||||||
|
PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
export LD_MOD := $(CC)
|
||||||
|
else
|
||||||
|
export LD_MOD := $(CXX)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
|
||||||
|
$(sFILES:.s=.o) $(SFILES:.S=.o) \
|
||||||
|
$(PNGFILES:.png=.png.o) $(addsuffix .o,$(BINFILES))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# build a list of include paths
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD) -I$(PORTLIBS)/include \
|
||||||
|
-I$(PORTLIBS)/include/libutils -I$(WUPSDIR)/include
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# build a list of library paths
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
|
||||||
|
-L$(PORTLIBS)/lib \
|
||||||
|
-L$(WUPSDIR)/lib
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
.PHONY: $(BUILD) clean install
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).bin $(BUILD_DBG).elf $(OUTPUT)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Rule to make everything.
|
||||||
|
PHONY += all
|
||||||
|
|
||||||
|
all : $(OUTPUT)
|
||||||
|
###############################################################################
|
||||||
|
# Special build rules
|
||||||
|
|
||||||
|
# Rule to make the module file.
|
||||||
|
$(OUTPUT) : output.elf
|
||||||
|
@echo "checking for missing symbols ..."
|
||||||
|
@$(LD_MOD) ../$(BUILD)/output.elf $(LDFLAG_COMMON) $(LIBS) $(LIBPATHS) -o check_linking.elf
|
||||||
|
@echo "linking ..." $@
|
||||||
|
@$(LD_MOD) ../$(BUILD)/output.elf $(LDFLAGS_MOD) $(LIBS) $(LIBPATHS) -o $@
|
||||||
|
|
||||||
|
# Rule to make the module file.
|
||||||
|
output.elf : $(OFILES)
|
||||||
|
@echo "linking ... output.elf"
|
||||||
|
|
||||||
|
|
||||||
|
@$(LD) $(OFILES) $(LDFLAGS_ELF) $(LIBS) $(LIBPATHS) -o $@
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Standard build rules
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.a:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $@)
|
||||||
|
@rm -f $@
|
||||||
|
@$(AR) -rc $@ $^
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.o: %.cpp
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(INCLUDE) -c $< -o $@ $(ERROR_FILTER)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.o: %.c
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(INCLUDE) -c $< -o $@ $(ERROR_FILTER)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.o: %.S
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.png.o : %.png
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.jpg.o : %.jpg
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.ttf.o : %.ttf
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o : %.bin
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.wav.o : %.wav
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.mp3.o : %.mp3
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.ogg.o : %.ogg
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@bin2s -a 32 $< | $(AS) -o $(@)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Assembly listing rules
|
||||||
|
|
||||||
|
# Rule to make assembly listing.
|
||||||
|
PHONY += list
|
||||||
|
list : $(LIST)
|
||||||
|
|
||||||
|
# Rule to make the listing file.
|
||||||
|
%.list : $(TARGET)
|
||||||
|
$(LOG)
|
||||||
|
-$Qmkdir -p $(dir $@)
|
||||||
|
$Q$(OBJDUMP) -d $< > $@
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Clean rule
|
||||||
|
|
||||||
|
# Rule to clean files.
|
||||||
|
PHONY += clean
|
||||||
|
clean :
|
||||||
|
$Qrm -rf $(wildcard $(BUILD) $(BIN))
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Phony targets
|
||||||
|
|
||||||
|
.PHONY : $(PHONY)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
20
plugins/screenshot/README.md
Normal file
20
plugins/screenshot/README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Screenshot tool
|
||||||
|
|
||||||
|
This is just a simple plugin that takes screenshot of the TV and DRC screen. The screenshot will saved on the sd card.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
For building you need:
|
||||||
|
- [wups](https://github.com/Maschell/WiiUPluginSystem)
|
||||||
|
- [dynamic_libs](https://github.com/Maschell/dynamic_libs/tree/lib) for access to the functions.
|
||||||
|
- [libutils](https://github.com/Maschell/libutils) for common functions.
|
||||||
|
|
||||||
|
Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself.
|
||||||
|
|
||||||
|
You also need some other, external libraries.
|
||||||
|
|
||||||
|
All needed dependencies are in the "libs" folder of this plugin. Extract the "portlibs.zip" archive into your devkitPro directory.
|
||||||
|
The archive includes:
|
||||||
|
|
||||||
|
- gd
|
||||||
|
- libjpeg
|
BIN
plugins/screenshot/libs/portlibs.zip
Normal file
BIN
plugins/screenshot/libs/portlibs.zip
Normal file
Binary file not shown.
6
plugins/screenshot/src/common.h
Normal file
6
plugins/screenshot/src/common.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef COMMON_H_
|
||||||
|
#define COMMON_H_
|
||||||
|
|
||||||
|
#define WIIU_SCREENSHOT_PATH "sd:/wiiu/screenshots/"
|
||||||
|
|
||||||
|
#endif // COMMON_H_
|
63
plugins/screenshot/src/function_patcher.cpp
Normal file
63
plugins/screenshot/src/function_patcher.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include <wups.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
#include <dynamic_libs/os_functions.h>
|
||||||
|
#include <dynamic_libs/os_types.h>
|
||||||
|
#include <dynamic_libs/vpad_functions.h>
|
||||||
|
#include <dynamic_libs/gx2_functions.h>
|
||||||
|
#include <utils/StringTools.h>
|
||||||
|
#include <fs/FSUtils.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "function_patcher.h"
|
||||||
|
#include "screenshot_utils.h"
|
||||||
|
#include "retain_vars.hpp"
|
||||||
|
|
||||||
|
static bool takeScreenshotTV = false;
|
||||||
|
static bool takeScreenshotDRC = false;
|
||||||
|
static u8 screenshotCooldown = 0;
|
||||||
|
static u32 counter = 0;
|
||||||
|
|
||||||
|
DECL_FUNCTION(int, VPADRead, int chan, VPADData *buffer, u32 buffer_size, s32 *error) {
|
||||||
|
int result = real_VPADRead(chan, buffer, buffer_size, error);
|
||||||
|
u32 btns = gButtonCombo;
|
||||||
|
if(result > 0 && (buffer[0].btns_h == btns) && screenshotCooldown == 0 && OSIsHomeButtonMenuEnabled()) {
|
||||||
|
counter++;
|
||||||
|
takeScreenshotTV = true;
|
||||||
|
takeScreenshotDRC = true;
|
||||||
|
|
||||||
|
screenshotCooldown = 0x3C;
|
||||||
|
}
|
||||||
|
if(screenshotCooldown > 0) {
|
||||||
|
screenshotCooldown--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, s32 scan_target) {
|
||||||
|
if(takeScreenshotTV || takeScreenshotDRC) {
|
||||||
|
OSCalendarTime output;
|
||||||
|
OSTicksToCalendarTime(OSGetTime(), &output);
|
||||||
|
char buffer[255] = {0};
|
||||||
|
snprintf(buffer,254,"%s%04i-%02i-%02i/",WIIU_SCREENSHOT_PATH,output.year,output.mon,output.mday);
|
||||||
|
|
||||||
|
FSUtils::CreateSubfolder(buffer);
|
||||||
|
|
||||||
|
snprintf(buffer,254,"%s%04i-%02i-%02i/%04i-%02i.%02i_%02i.%02i.%02i_",
|
||||||
|
WIIU_SCREENSHOT_PATH,output.year,output.mon,output.mday,output.year,output.mon,output.mday,output.hour,output.min,output.sec);
|
||||||
|
|
||||||
|
if(scan_target == 1 && colorBuffer != NULL && takeScreenshotTV && gAppStatus == WUPS_APP_STATUS_FOREGROUND) {
|
||||||
|
DEBUG_FUNCTION_LINE("Lets take a screenshot from TV. Source format: %d \n",colorBuffer->surface.format);
|
||||||
|
takeScreenshot((GX2ColorBuffer *)colorBuffer, StringTools::strfmt("%sTV.jpg",buffer).c_str());
|
||||||
|
takeScreenshotTV = false;
|
||||||
|
}
|
||||||
|
if(scan_target == 4 && colorBuffer != NULL && takeScreenshotDRC && gAppStatus == WUPS_APP_STATUS_FOREGROUND) {
|
||||||
|
DEBUG_FUNCTION_LINE("Lets take a screenshot from DRC. Source format: %d \n",colorBuffer->surface.format);
|
||||||
|
takeScreenshot((GX2ColorBuffer *)colorBuffer, StringTools::strfmt("%sDRC.jpg",buffer).c_str());
|
||||||
|
takeScreenshotDRC = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
real_GX2CopyColorBufferToScanBuffer(colorBuffer,scan_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPS_MUST_REPLACE(VPADRead, WUPS_LOADER_LIBRARY_VPAD, VPADRead);
|
||||||
|
WUPS_MUST_REPLACE(GX2CopyColorBufferToScanBuffer, WUPS_LOADER_LIBRARY_GX2, GX2CopyColorBufferToScanBuffer);
|
0
plugins/screenshot/src/function_patcher.h
Normal file
0
plugins/screenshot/src/function_patcher.h
Normal file
156
plugins/screenshot/src/main.cpp
Normal file
156
plugins/screenshot/src/main.cpp
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#include <wups.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <dynamic_libs/os_functions.h>
|
||||||
|
#include <dynamic_libs/socket_functions.h>
|
||||||
|
#include <dynamic_libs/fs_functions.h>
|
||||||
|
#include <dynamic_libs/vpad_functions.h>
|
||||||
|
#include <dynamic_libs/gx2_functions.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
#include "retain_vars.hpp"
|
||||||
|
|
||||||
|
// Mandatory plugin information.
|
||||||
|
WUPS_PLUGIN_NAME("Screenshot tool");
|
||||||
|
WUPS_PLUGIN_DESCRIPTION("This plugin allows you to make screenshots that will be saved to the sd card");
|
||||||
|
WUPS_PLUGIN_VERSION("v0.1");
|
||||||
|
WUPS_PLUGIN_AUTHOR("Maschell");
|
||||||
|
WUPS_PLUGIN_LICENSE("GPL");
|
||||||
|
|
||||||
|
// FS Access
|
||||||
|
WUPS_FS_ACCESS()
|
||||||
|
|
||||||
|
u32 SplashScreen(s32 time,s32 combotime);
|
||||||
|
|
||||||
|
// Gets called once the loader exists.
|
||||||
|
INITIALIZE_PLUGIN() {
|
||||||
|
InitOSFunctionPointers();
|
||||||
|
InitSocketFunctionPointers();
|
||||||
|
InitVPadFunctionPointers();
|
||||||
|
InitGX2FunctionPointers();
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
|
||||||
|
InitOSFunctionPointers();
|
||||||
|
InitVPadFunctionPointers();
|
||||||
|
u32 res = SplashScreen(10,2);
|
||||||
|
gButtonCombo = res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called whenever an application was started.
|
||||||
|
ON_APPLICATION_START(my_args) {
|
||||||
|
InitOSFunctionPointers();
|
||||||
|
InitSocketFunctionPointers();
|
||||||
|
InitFSFunctionPointers();
|
||||||
|
|
||||||
|
log_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_APP_STATUS_CHANGED(status) {
|
||||||
|
gAppStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define FPS 60
|
||||||
|
u32 SplashScreen(s32 time,s32 combotime) {
|
||||||
|
u32 result = VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_ZR | VPAD_BUTTON_ZL;
|
||||||
|
|
||||||
|
// Init screen
|
||||||
|
OSScreenInit();
|
||||||
|
|
||||||
|
u32 screen_buf0_size = OSScreenGetBufferSizeEx(0);
|
||||||
|
u32 screen_buf1_size = OSScreenGetBufferSizeEx(1);
|
||||||
|
|
||||||
|
u32 * screenbuffer0 = (u32*)memalign(0x100, screen_buf0_size);
|
||||||
|
u32 * screenbuffer1 = (u32*)memalign(0x100, screen_buf1_size);
|
||||||
|
|
||||||
|
if(screenbuffer0 == NULL || screenbuffer1 == NULL) {
|
||||||
|
if(screenbuffer0 != NULL) {
|
||||||
|
free(screenbuffer0);
|
||||||
|
}
|
||||||
|
if(screenbuffer1 != NULL) {
|
||||||
|
free(screenbuffer1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSScreenSetBufferEx(0, (void *)screenbuffer0);
|
||||||
|
OSScreenSetBufferEx(1, (void *)screenbuffer1);
|
||||||
|
|
||||||
|
OSScreenEnableEx(0, 1);
|
||||||
|
OSScreenEnableEx(1, 1);
|
||||||
|
|
||||||
|
// Clear screens
|
||||||
|
OSScreenClearBufferEx(0, 0);
|
||||||
|
OSScreenClearBufferEx(1, 0);
|
||||||
|
|
||||||
|
// Flip buffers
|
||||||
|
OSScreenFlipBuffersEx(0);
|
||||||
|
OSScreenFlipBuffersEx(1);
|
||||||
|
|
||||||
|
OSScreenClearBufferEx(0, 0);
|
||||||
|
OSScreenClearBufferEx(1, 0);
|
||||||
|
|
||||||
|
std::vector<std::string> strings;
|
||||||
|
strings.push_back("Screenshot tool 0.1 - by Maschell.");
|
||||||
|
strings.push_back("");
|
||||||
|
strings.push_back("");
|
||||||
|
strings.push_back("Press the combo you want to use for making screenshots now");
|
||||||
|
strings.push_back("for 2 seconds.");
|
||||||
|
strings.push_back(" ");
|
||||||
|
strings.push_back("Otherwise the default combo (L+R+ZR+ZL button) will be used");
|
||||||
|
strings.push_back("in 10 seconds.");
|
||||||
|
strings.push_back(" ");
|
||||||
|
strings.push_back("Press the TV buttons to exit with the default combo.");
|
||||||
|
|
||||||
|
u8 pos = 0;
|
||||||
|
for (std::vector<std::string>::iterator it = strings.begin() ; it != strings.end(); ++it) {
|
||||||
|
OSScreenPutFontEx(0, 0, pos, (*it).c_str());
|
||||||
|
OSScreenPutFontEx(1, 0, pos, (*it).c_str());
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSScreenFlipBuffersEx(0);
|
||||||
|
OSScreenFlipBuffersEx(1);
|
||||||
|
|
||||||
|
s32 tickswait = time * FPS * 16;
|
||||||
|
|
||||||
|
s32 sleepingtime = 16;
|
||||||
|
s32 times = tickswait/16;
|
||||||
|
s32 i=0;
|
||||||
|
|
||||||
|
VPADData vpad_data;
|
||||||
|
s32 error;
|
||||||
|
u32 last = 0xFFFFFFFF;
|
||||||
|
s32 timer = 0;
|
||||||
|
while(i<times) {
|
||||||
|
VPADRead(0, &vpad_data, 1, &error);
|
||||||
|
if(vpad_data.btns_h == VPAD_BUTTON_TV) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(last == vpad_data.btns_h && last != 0) {
|
||||||
|
timer++;
|
||||||
|
} else {
|
||||||
|
last = vpad_data.btns_h;
|
||||||
|
timer = 0;
|
||||||
|
}
|
||||||
|
if(timer >= combotime*FPS) {
|
||||||
|
result = vpad_data.btns_h;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
os_usleep(sleepingtime*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(screenbuffer0 != NULL) {
|
||||||
|
free(screenbuffer0);
|
||||||
|
}
|
||||||
|
if(screenbuffer1 != NULL) {
|
||||||
|
free(screenbuffer1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
3
plugins/screenshot/src/retain_vars.cpp
Normal file
3
plugins/screenshot/src/retain_vars.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#include "retain_vars.hpp"
|
||||||
|
wups_loader_app_status_t gAppStatus __attribute__((section(".data"))) = WUPS_APP_STATUS_UNKNOWN;
|
||||||
|
u32 gButtonCombo __attribute__((section(".data"))) = 0;
|
10
plugins/screenshot/src/retain_vars.hpp
Normal file
10
plugins/screenshot/src/retain_vars.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _RETAINS_VARS_H_
|
||||||
|
#define _RETAINS_VARS_H_
|
||||||
|
|
||||||
|
#include <wups.h>
|
||||||
|
#include <dynamic_libs/os_types.h>
|
||||||
|
|
||||||
|
extern wups_loader_app_status_t gAppStatus;
|
||||||
|
extern u32 gButtonCombo;
|
||||||
|
|
||||||
|
#endif // _RETAINS_VARS_H_
|
222
plugins/screenshot/src/screenshot_utils.cpp
Normal file
222
plugins/screenshot/src/screenshot_utils.cpp
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
#include "screenshot_utils.h"
|
||||||
|
#include <fs/FSUtils.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
bool UnormR8G8B8A8TogdImage(gdImagePtr *gdImgTmp, void *image_data, u32 width, u32 rows, u32 pitch) {
|
||||||
|
*gdImgTmp = gdImageCreateTrueColor(width , rows );
|
||||||
|
if(*gdImgTmp != NULL){
|
||||||
|
R8G8B8A8_COLOR *buffer = (R8G8B8A8_COLOR *) image_data;
|
||||||
|
R8G8B8A8_COLOR val;
|
||||||
|
for (u32 row = 0; row < rows; ++row) {
|
||||||
|
for (u32 x = 0; x < width; ++x) {
|
||||||
|
val = buffer[row * pitch + x];
|
||||||
|
gdImageSetPixel(*gdImgTmp, x , row , gdTrueColor(val.R, val.G, val.B));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool saveAsJPEG(const char * path, u8 * sourceBuffer, u32 width, u32 height, u32 pitch, u32 format) {
|
||||||
|
if(path == NULL || sourceBuffer == NULL) {
|
||||||
|
DEBUG_FUNCTION_LINE("path or buffer NULL\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(( format != GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB &&
|
||||||
|
format != GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM)) {
|
||||||
|
DEBUG_FUNCTION_LINE("Format not supported\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdImagePtr gdImagePtr = 0;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
if(!UnormR8G8B8A8TogdImage(&gdImagePtr, sourceBuffer, width, height, pitch)){
|
||||||
|
DEBUG_FUNCTION_LINE("Setting up the GD buffer failed\n");
|
||||||
|
if(gdImagePtr != NULL){
|
||||||
|
gdImageDestroy(gdImagePtr);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imd_size = 0;
|
||||||
|
void *data = gdImageJpegPtr(gdImagePtr, &imd_size, 95);
|
||||||
|
if (data) {
|
||||||
|
DEBUG_FUNCTION_LINE("Encoded file as JPEG. size = %d.\n",imd_size);
|
||||||
|
//FSUtils::CreateSubfolder("sd:/screenshots);
|
||||||
|
result = FSUtils::saveBufferToFile(path,data,imd_size);
|
||||||
|
if(!result){
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to save buffer to %s \n",path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gdFree(data);
|
||||||
|
gdImageDestroy(gdImagePtr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool copyBuffer(GX2ColorBuffer * sourceBuffer, GX2ColorBuffer * targetBuffer, u32 targetWidth, u32 targetHeight) {
|
||||||
|
// Making sure the buffers are not NULL
|
||||||
|
if (sourceBuffer != NULL && targetBuffer != NULL) {
|
||||||
|
GX2InitColorBuffer(targetBuffer, GX2_SURFACE_DIM_2D, targetWidth, targetHeight, 1, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_SRGB, GX2_AA_MODE_1X);
|
||||||
|
|
||||||
|
// We need to override some values.
|
||||||
|
targetBuffer->surface.tile = GX2_TILE_MODE_LINEAR_ALIGNED;
|
||||||
|
targetBuffer->surface.use = GX2_SURFACE_USE_COLOR_BUFFER_TEXTURE;
|
||||||
|
targetBuffer->view_mip = 0;
|
||||||
|
targetBuffer->view_first_slice = 0;
|
||||||
|
targetBuffer->view_slices_count = 1;
|
||||||
|
GX2CalcSurfaceSizeAndAlignment(&targetBuffer->surface);
|
||||||
|
GX2InitColorBufferRegs(targetBuffer);
|
||||||
|
|
||||||
|
// Let's allocate the memory.
|
||||||
|
targetBuffer->surface.image_data = memalign(targetBuffer->surface.align,targetBuffer->surface.image_size);
|
||||||
|
if(targetBuffer->surface.image_data == NULL) {
|
||||||
|
DEBUG_FUNCTION_LINE("failed to allocate memory.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DEBUG_FUNCTION_LINE("Allocated image data buffer. data %08X size %08X \n",targetBuffer->surface.image_data,targetBuffer->surface.image_size);
|
||||||
|
|
||||||
|
GX2Invalidate(GX2_INVALIDATE_CPU, targetBuffer->surface.image_data, targetBuffer->surface.image_size);
|
||||||
|
if (sourceBuffer->surface.aa == GX2_AA_MODE_1X) {
|
||||||
|
// If AA is disabled, we can simply use GX2CopySurface.
|
||||||
|
GX2CopySurface(&sourceBuffer->surface,
|
||||||
|
sourceBuffer->view_mip,
|
||||||
|
sourceBuffer->view_first_slice,
|
||||||
|
&targetBuffer->surface, 0, 0);
|
||||||
|
} else {
|
||||||
|
// If AA is enabled, we need to resolve the AA buffer.
|
||||||
|
GX2Surface tempSurface;
|
||||||
|
tempSurface = sourceBuffer->surface;
|
||||||
|
tempSurface.aa = GX2_AA_MODE_1X;
|
||||||
|
GX2CalcSurfaceSizeAndAlignment(&tempSurface);
|
||||||
|
|
||||||
|
tempSurface.image_data = memalign(tempSurface.align,tempSurface.image_size);
|
||||||
|
if(tempSurface.image_data == NULL) {
|
||||||
|
DEBUG_FUNCTION_LINE("failed to allocate data AA.\n");
|
||||||
|
if(targetBuffer->surface.image_data != NULL) {
|
||||||
|
free(targetBuffer->surface.image_data);
|
||||||
|
targetBuffer->surface.image_data = NULL;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GX2ResolveAAColorBuffer(sourceBuffer,&tempSurface, 0, 0);
|
||||||
|
GX2CopySurface(&tempSurface, 0, 0,&targetBuffer->surface, 0, 0);
|
||||||
|
|
||||||
|
// Sync CPU and GPU
|
||||||
|
GX2DrawDone();
|
||||||
|
|
||||||
|
if(tempSurface.image_data != NULL) {
|
||||||
|
free(tempSurface.image_data);
|
||||||
|
tempSurface.image_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("Couldn't copy buffer, pointer was NULL\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool takeScreenshot(GX2ColorBuffer *srcBuffer,const char * path) {
|
||||||
|
if(srcBuffer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DEBUG_FUNCTION_LINE("Taking screenshot. %s\n",path);
|
||||||
|
|
||||||
|
GX2ColorBuffer colorBuffer;
|
||||||
|
GX2ColorBuffer * saveBuffer = NULL;
|
||||||
|
|
||||||
|
// keep dimensions
|
||||||
|
u32 width = srcBuffer->surface.width;
|
||||||
|
u32 height = srcBuffer->surface.height;
|
||||||
|
|
||||||
|
bool valid = false;
|
||||||
|
bool cancel = false;
|
||||||
|
bool low_memory = false;
|
||||||
|
do {
|
||||||
|
// At first we need to copy the buffer to fit our resolution.
|
||||||
|
if(saveBuffer == NULL) {
|
||||||
|
do {
|
||||||
|
valid = copyBuffer(srcBuffer,&colorBuffer,width,height);
|
||||||
|
// If the copying failed, we don't have enough memory. Let's decrease the resolution.
|
||||||
|
if(!valid) {
|
||||||
|
low_memory = true;
|
||||||
|
|
||||||
|
if(height >= 1080) {
|
||||||
|
width = 1280;
|
||||||
|
height = 720;
|
||||||
|
DEBUG_FUNCTION_LINE("Switching to 720p.\n");
|
||||||
|
} else if(height >= 720) {
|
||||||
|
width = 854;
|
||||||
|
height = 480;
|
||||||
|
DEBUG_FUNCTION_LINE("Switching to 480p.\n");
|
||||||
|
} else if(height >= 480) {
|
||||||
|
width = 640;
|
||||||
|
height = 360;
|
||||||
|
DEBUG_FUNCTION_LINE("Switching to 360p.\n");
|
||||||
|
} else {
|
||||||
|
// Cancel the screenshot if the resolution would be too low.
|
||||||
|
cancel = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// On success save the pointer.
|
||||||
|
saveBuffer = &colorBuffer;
|
||||||
|
}
|
||||||
|
} while(!valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should proceed
|
||||||
|
if(cancel) {
|
||||||
|
// Free the memory on error.
|
||||||
|
if(colorBuffer.surface.image_data != NULL) {
|
||||||
|
free(colorBuffer.surface.image_data);
|
||||||
|
colorBuffer.surface.image_data = NULL;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush out destinations caches
|
||||||
|
GX2Invalidate(GX2_INVALIDATE_COLOR_BUFFER, colorBuffer.surface.image_data,colorBuffer.surface.image_size);
|
||||||
|
|
||||||
|
// Wait for GPU to finish
|
||||||
|
GX2DrawDone();
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE("Trying to save.\n");
|
||||||
|
|
||||||
|
// Trying to save as JPEG.
|
||||||
|
valid = saveAsJPEG(path,(u8*) saveBuffer->surface.image_data,width,height,saveBuffer->surface.pitch,saveBuffer->surface.format);
|
||||||
|
|
||||||
|
// Free the colorbuffer copy.
|
||||||
|
if(colorBuffer.surface.image_data != NULL) {
|
||||||
|
free(colorBuffer.surface.image_data);
|
||||||
|
colorBuffer.surface.image_data = NULL;
|
||||||
|
saveBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When taking the screenshot failed, decrease the resolution again ~.
|
||||||
|
if(!valid) {
|
||||||
|
low_memory = true;
|
||||||
|
if(height >= 1080) {
|
||||||
|
width = 1280;
|
||||||
|
height = 720;
|
||||||
|
DEBUG_FUNCTION_LINE("Switching to 720p.\n");
|
||||||
|
} else if(height >= 720) {
|
||||||
|
width = 854;
|
||||||
|
height = 480;
|
||||||
|
DEBUG_FUNCTION_LINE("Switching to 480p.\n");
|
||||||
|
} else if(height >= 480) {
|
||||||
|
width = 640;
|
||||||
|
height = 360;
|
||||||
|
DEBUG_FUNCTION_LINE("Switching to 360p.\n");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(!valid);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
20
plugins/screenshot/src/screenshot_utils.h
Normal file
20
plugins/screenshot/src/screenshot_utils.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _SCREENSHOT_UTILS_H_
|
||||||
|
#define _SCREENSHOT_UTILS_H_
|
||||||
|
|
||||||
|
#include <dynamic_libs/gx2_functions.h>
|
||||||
|
#include <gd.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
typedef struct _R8G8B8A8_COLOR {
|
||||||
|
u8 R, G, B, A;
|
||||||
|
} R8G8B8A8_COLOR;
|
||||||
|
|
||||||
|
bool UnormR8G8B8A8TogdImage(gdImagePtr *gdImgTmp, void *image_data, u32 width, u32 rows, u32 pitch);
|
||||||
|
|
||||||
|
bool saveAsJPEG(const char * path, u8 * sourceBuffer, u32 width, u32 height, u32 pitch, u32 format);
|
||||||
|
|
||||||
|
bool copyBuffer(GX2ColorBuffer * sourceBuffer, GX2ColorBuffer * targetBuffer, u32 targetWidth, u32 targetHeight);
|
||||||
|
|
||||||
|
bool takeScreenshot(GX2ColorBuffer *srcBuffer,const char * path);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user