From 0ec23e74b52cda57a9066a06d205072d3e010168 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 28 Jul 2021 17:38:31 -0700 Subject: [PATCH] cs: implement TakeScreenShot command --- .../capsrv/capsrv_screen_shot_control_api.hpp | 4 ++ .../stratosphere/cs/cs_command_processor.hpp | 3 + .../capsrv/capsrv_screen_shot_control_api.cpp | 23 ++++++++ .../source/cs/cs_command_impl.cpp | 56 ++++++++++++++++++ .../source/cs/cs_command_impl.hpp | 10 ++++ .../source/cs/cs_command_processor.cpp | 57 ++++++++++++++++++- 6 files changed, 152 insertions(+), 1 deletion(-) diff --git a/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_control_api.hpp b/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_control_api.hpp index f3fdc23ae..e80cb7ee9 100644 --- a/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_control_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_control_api.hpp @@ -25,6 +25,10 @@ namespace ams::capsrv { Result InitializeScreenShotControl(); void FinalizeScreenShotControl(); + Result OpenRawScreenShotReadStreamForDevelop(size_t *out_data_size, s32 *out_width, s32 *out_height, vi::LayerStack layer_stack, TimeSpan timeout); + Result ReadRawScreenShotReadStreamForDevelop(size_t *out_read_size, void *dst, size_t dst_size, std::ptrdiff_t offset); + void CloseRawScreenShotReadStreamForDevelop(); + Result CaptureJpegScreenshot(u64 *out_size, void *dst, size_t dst_size, vi::LayerStack layer_stack, TimeSpan timeout); } diff --git a/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp b/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp index 0a250ce49..74e44e054 100644 --- a/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp +++ b/libraries/libstratosphere/include/stratosphere/cs/cs_command_processor.hpp @@ -16,6 +16,7 @@ #pragma once #include #include +#include namespace ams::cs { @@ -25,6 +26,8 @@ namespace ams::cs { class CommandProcessor : public scs::CommandProcessor { public: virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) override; + private: + void TakeScreenShot(const CommandHeader &header, s32 socket, vi::LayerStack layer_stack); private: static void SendFirmwareVersion(s32 socket, const CommandHeader &header); }; diff --git a/libraries/libstratosphere/source/capsrv/capsrv_screen_shot_control_api.cpp b/libraries/libstratosphere/source/capsrv/capsrv_screen_shot_control_api.cpp index a262f47de..8362ddbbc 100644 --- a/libraries/libstratosphere/source/capsrv/capsrv_screen_shot_control_api.cpp +++ b/libraries/libstratosphere/source/capsrv/capsrv_screen_shot_control_api.cpp @@ -29,4 +29,27 @@ namespace ams::capsrv { return ::capsscCaptureJpegScreenShot(out_size, dst, dst_size, static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds()); } + Result OpenRawScreenShotReadStreamForDevelop(size_t *out_data_size, s32 *out_width, s32 *out_height, vi::LayerStack layer_stack, TimeSpan timeout) { + u64 data_size, width, height; + R_TRY(::capsscOpenRawScreenShotReadStream(std::addressof(data_size), std::addressof(width), std::addressof(height), static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds())); + + *out_data_size = static_cast(data_size); + *out_width = static_cast(width); + *out_height = static_cast(height); + + return ResultSuccess(); + } + + Result ReadRawScreenShotReadStreamForDevelop(size_t *out_read_size, void *dst, size_t dst_size, std::ptrdiff_t offset) { + u64 read_size; + R_TRY(::capsscReadRawScreenShotReadStream(std::addressof(read_size), dst, dst_size, static_cast(offset))); + + *out_read_size = static_cast(read_size); + return ResultSuccess(); + } + + void CloseRawScreenShotReadStreamForDevelop() { + ::capsscCloseRawScreenShotReadStream(); + } + } diff --git a/libraries/libstratosphere/source/cs/cs_command_impl.cpp b/libraries/libstratosphere/source/cs/cs_command_impl.cpp index c2615eead..d730acb27 100644 --- a/libraries/libstratosphere/source/cs/cs_command_impl.cpp +++ b/libraries/libstratosphere/source/cs/cs_command_impl.cpp @@ -18,9 +18,65 @@ namespace ams::cs { + namespace { + + void SendEmptyData(const CommandDataTakeScreenShot ¶ms, size_t remaining_size) { + /* Clear the data buffer. */ + std::memset(params.buffer, 0, params.buffer_size); + + /* Send data until the end. */ + while (remaining_size > 0) { + /* Send as much as we can. */ + const auto cur_size = std::min(remaining_size, params.buffer_size); + params.send_data(params.buffer, cur_size); + + /* Advance. */ + remaining_size -= cur_size; + } + } + + } + Result DoGetFirmwareVersionCommand(settings::system::FirmwareVersion *out) { settings::system::GetFirmwareVersion(out); return ResultSuccess(); } + Result DoTakeScreenShotCommand(const CommandDataTakeScreenShot ¶ms) { + /* Initialize screenshot control. */ + R_TRY(capsrv::InitializeScreenShotControl()); + + /* Finalize screenshot control when we're done. */ + ON_SCOPE_EXIT { capsrv::FinalizeScreenShotControl(); }; + + /* Open screenshot read stream. */ + size_t data_size; + s32 width, height; + R_TRY(capsrv::OpenRawScreenShotReadStreamForDevelop(std::addressof(data_size), std::addressof(width), std::addressof(height), params.layer_stack, TimeSpan::FromSeconds(10))); + + /* Close the screenshot stream when we're done. */ + ON_SCOPE_EXIT { capsrv::CloseRawScreenShotReadStreamForDevelop(); }; + + /* Send the header. */ + params.send_header(static_cast(data_size), width, height); + + /* Read and send data. */ + size_t total_read_size = 0; + auto data_guard = SCOPE_GUARD { SendEmptyData(params, data_size - total_read_size); }; + while (total_read_size < data_size) { + /* Read data from the stream. */ + size_t read_size; + R_TRY(capsrv::ReadRawScreenShotReadStreamForDevelop(std::addressof(read_size), params.buffer, params.buffer_size, total_read_size)); + + /* Send the data that was read. */ + params.send_data(params.buffer, read_size); + + /* Advance. */ + total_read_size += read_size; + } + data_guard.Cancel(); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/cs/cs_command_impl.hpp b/libraries/libstratosphere/source/cs/cs_command_impl.hpp index 7ad92ac2b..02151864d 100644 --- a/libraries/libstratosphere/source/cs/cs_command_impl.hpp +++ b/libraries/libstratosphere/source/cs/cs_command_impl.hpp @@ -18,6 +18,16 @@ namespace ams::cs { + struct CommandDataTakeScreenShot { + vi::LayerStack layer_stack; + std::function send_header; + std::function send_data; + u8 *buffer; + size_t buffer_size; + }; + Result DoGetFirmwareVersionCommand(settings::system::FirmwareVersion *out); + Result DoTakeScreenShotCommand(const CommandDataTakeScreenShot ¶ms); + } diff --git a/libraries/libstratosphere/source/cs/cs_command_processor.cpp b/libraries/libstratosphere/source/cs/cs_command_processor.cpp index 434433c9e..4de8cd29d 100644 --- a/libraries/libstratosphere/source/cs/cs_command_processor.cpp +++ b/libraries/libstratosphere/source/cs/cs_command_processor.cpp @@ -25,6 +25,15 @@ namespace ams::cs { settings::system::FirmwareVersion firmware_version; }; + struct ResponseTakeScreenShot { + ResponseHeader header; + s32 data_size; + s32 width; + s32 height; + }; + + constinit u8 g_data[0x1000]; + } bool CommandProcessor::ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) { @@ -32,7 +41,12 @@ namespace ams::cs { case Command_GetFirmwareVersion: SendFirmwareVersion(socket, header); break; - /* TODO: Command support. */ + case Command_TakeScreenShot: + this->TakeScreenShot(header, socket, vi::LayerStack_ApplicationForDebug); + break; + case Command_TakeForegroundScreenShot: + this->TakeScreenShot(header, socket, vi::LayerStack_LastFrame); + break; /* TODO: Command support. */ default: scs::CommandProcessor::ProcessCommand(header, body, socket); @@ -64,4 +78,45 @@ namespace ams::cs { } } + void CommandProcessor::TakeScreenShot(const CommandHeader &header, s32 socket, vi::LayerStack layer_stack) { + /* Create the command data. */ + const CommandDataTakeScreenShot params = { + .layer_stack = layer_stack, + .send_header = [&](s32 data_size, s32 width, s32 height) { + /* Use global buffer for response. */ + ResponseTakeScreenShot *response = reinterpret_cast(g_data); + + /* Set response header. */ + *response = { + .header = { + .id = header.id, + .response = Response_ScreenShot, + .body_size = static_cast(sizeof(data_size) + sizeof(width) + sizeof(height) + data_size), + }, + .data_size = data_size, + .width = width, + .height = height, + }; + + /* Send data. */ + Send(socket, response, sizeof(*response)); + }, + .send_data = [&](u8 *data, size_t data_size) { + /* Send data. */ + Send(socket, data, data_size); + }, + .buffer = g_data, + .buffer_size = sizeof(g_data), + }; + + /* Acquire the send lock. */ + auto lk = MakeSendGuardBlock(); + + /* Take the screenshot. */ + const Result result = DoTakeScreenShotCommand(params); + if (R_FAILED(result)) { + SendErrorResult(socket, header, result); + } + } + }