[Plugin] Added screenshot plugin

This commit is contained in:
Maschell 2018-03-08 18:47:43 +01:00
parent 647c3a9246
commit 117d2ab7d6
11 changed files with 791 additions and 0 deletions

291
plugins/screenshot/Makefile Normal file
View 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
#---------------------------------------------------------------------------------

View 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

Binary file not shown.

View File

@ -0,0 +1,6 @@
#ifndef COMMON_H_
#define COMMON_H_
#define WIIU_SCREENSHOT_PATH "sd:/wiiu/screenshots/"
#endif // COMMON_H_

View 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);

View 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;
}

View 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;

View 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_

View 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;
}

View 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