naomi: save printer output as a screenshot

Use hostfs::saveScreenshot()
This commit is contained in:
Flyinghead 2024-06-23 16:00:31 +02:00
parent e270621a03
commit 49ad2b6864
5 changed files with 75 additions and 26 deletions

View File

@ -341,6 +341,11 @@ public:
{
if (page.empty())
return false;
const auto& appendData = [](void *context, void *data, int size) {
std::vector<u8>& v = *(std::vector<u8> *)context;
v.insert(v.end(), (u8 *)data, (u8 *)data + size);
};
stbi_flip_vertically_on_write(0);
if (settings.content.gameId.substr(0, 4) == "F355")
{
u8 *data = nullptr;
@ -383,15 +388,29 @@ public:
*p = 0xff000000;
p++;
}
stbi_write_png(filename.c_str(), printerWidth, lines, 4, data, printerWidth * 4);
std::vector<u8> pngData;
stbi_write_png_to_func(appendData, &pngData, printerWidth, lines, 4, data, printerWidth * 4);
stbi_image_free(data);
try {
hostfs::saveScreenshot(filename, pngData);
} catch (const FlycastException& e) {
ERROR_LOG(NAOMI, "Error saving print out: %s", e.what());
return false;
}
return true;
}
}
for (u8& b : page)
b = 0xff - b;
stbi_write_png(filename.c_str(), printerWidth, lines, 1, &page[0], printerWidth);
std::vector<u8> pngData;
stbi_write_png_to_func(appendData, &pngData, printerWidth, lines, 1, &page[0], printerWidth);
try {
hostfs::saveScreenshot(filename, pngData);
} catch (const FlycastException& e) {
ERROR_LOG(NAOMI, "Error saving print out: %s", e.what());
return false;
}
return true;
}
@ -827,12 +846,16 @@ private:
state = Default;
if (bitmapWriter && bitmapWriter->isDirty())
{
// TODO save to ~/Pictures instead
std::string s = get_writable_data_path(settings.content.gameId + "-results.png");
bitmapWriter->save(s);
std::string date = timeToISO8601(time(nullptr));
std::replace(date.begin(), date.end(), '/', '-');
std::replace(date.begin(), date.end(), ':', '-');
std::string s = settings.content.gameId + " - " + date + ".png";
const bool success = bitmapWriter->save(s);
bitmapWriter.reset();
os_notify("Print out saved", 5000, s.c_str());
NOTICE_LOG(NAOMI, "%s", s.c_str());
if (success) {
os_notify("Print out saved", 5000, s.c_str());
NOTICE_LOG(NAOMI, "%s", s.c_str());
}
}
break;
case 'K': // Set Kanji mode

View File

@ -221,3 +221,20 @@ u64 getTimeMs()
return std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
}
#ifdef _WIN32
static struct tm *localtime_r(const time_t *_clock, struct tm *_result)
{
return localtime_s(_result, _clock) ? nullptr : _result;
}
#endif
std::string timeToISO8601(time_t time)
{
tm t;
if (localtime_r(&time, &t) == nullptr)
return {};
std::string s(32, '\0');
s.resize(snprintf(s.data(), 32, "%04d/%02d/%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec));
return s;
}

View File

@ -11,6 +11,7 @@
#include <vector>
#include <functional>
#include <cassert>
#include <time.h>
#ifdef __ANDROID__
#include <sys/mman.h>
@ -201,6 +202,7 @@ public:
};
u64 getTimeMs();
std::string timeToISO8601(time_t time);
class ThreadRunner
{

View File

@ -604,23 +604,6 @@ static void getScreenshot(std::vector<u8>& data, int width = 0)
stbi_write_png_to_func(appendVectorData, &data, width, height, 3, &rawData[0], 0);
}
#ifdef _WIN32
static struct tm *localtime_r(const time_t *_clock, struct tm *_result)
{
return localtime_s(_result, _clock) ? nullptr : _result;
}
#endif
static std::string timeToString(time_t time)
{
tm t;
if (localtime_r(&time, &t) == nullptr)
return {};
std::string s(32, '\0');
s.resize(snprintf(s.data(), 32, "%04d/%02d/%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec));
return s;
}
static void savestate()
{
// TODO save state async: png compression, savestate file compression/write
@ -795,7 +778,7 @@ static void gui_display_commands()
if (savestateDate == 0)
ImGui::TextColored(gray, "Empty");
else
ImGui::TextColored(gray, "%s", timeToString(savestateDate).c_str());
ImGui::TextColored(gray, "%s", timeToISO8601(savestateDate).c_str());
}
savestatePic.draw(ScaledVec2(buttonWidth, 0.f));
}
@ -3764,7 +3747,7 @@ void gui_takeScreenshot()
if (!game_started)
return;
uiThreadRunner.runOnThread([]() {
std::string date = timeToString(time(nullptr));
std::string date = timeToISO8601(time(nullptr));
std::replace(date.begin(), date.end(), '/', '-');
std::replace(date.begin(), date.end(), ':', '-');
std::string name = "Flycast-" + date + ".png";

View File

@ -19,6 +19,9 @@
#include "oslib/oslib.h"
#include "stdclass.h"
#include "file/file_path.h"
#ifndef _WIN32
#include <unistd.h>
#endif
const char *retro_get_system_directory();
@ -128,6 +131,27 @@ std::string getTextureDumpPath()
+ "texdump" + std::string(path_default_slash());
}
std::string getScreenshotsPath()
{
// Unfortunately retroarch doesn't expose its "screenshots" path
return std::string(retro_get_system_directory()) + "/dc";
}
void saveScreenshot(const std::string& name, const std::vector<u8>& data)
{
std::string path = getScreenshotsPath();
path += "/" + name;
FILE *f = nowide::fopen(path.c_str(), "wb");
if (f == nullptr)
throw FlycastException(path);
if (std::fwrite(&data[0], data.size(), 1, f) != 1) {
std::fclose(f);
unlink(path.c_str());
throw FlycastException(path);
}
std::fclose(f);
}
}
#if defined(_WIN32) || defined(__APPLE__)