mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-25 23:25:40 +00:00
Added Screenshot feature (ALT+S).
This commit is contained in:
parent
ca57659fcb
commit
981cdc33f5
|
@ -58,6 +58,7 @@ CInputs::CInputs(CInputSystem *system)
|
|||
uiToggleFrLimit = AddSwitchInput("UIToggleFrameLimit", "Toggle Frame Limiting", Game::INPUT_UI, "KEY_ALT+KEY_T");
|
||||
uiDumpInpState = AddSwitchInput("UIDumpInputState", "Dump Input State", Game::INPUT_UI, "KEY_ALT+KEY_U");
|
||||
uiDumpTimings = AddSwitchInput("UIDumpTimings", "Dump Frame Timings", Game::INPUT_UI, "KEY_ALT+KEY_O");
|
||||
uiScreenshot = AddSwitchInput("UIScreenShot", "Screenshot", Game::INPUT_UI, "KEY_ALT+KEY_S");
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
uiEnterDebugger = AddSwitchInput("UIEnterDebugger", "Enter Debugger", Game::INPUT_UI, "KEY_ALT+KEY_B");
|
||||
#endif
|
||||
|
|
|
@ -105,6 +105,7 @@ public:
|
|||
CSwitchInput *uiToggleFrLimit;
|
||||
CSwitchInput *uiDumpInpState;
|
||||
CSwitchInput *uiDumpTimings;
|
||||
CSwitchInput *uiScreenshot;
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
CSwitchInput *uiEnterDebugger;
|
||||
#endif
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#include "SDLIncludes.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "Util/BMPFile.h"
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -195,7 +196,7 @@ static bool CreateGLScreen(const std::string &caption, bool focusWindow, unsigne
|
|||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
||||
|
||||
// Set video mode
|
||||
|
@ -413,6 +414,27 @@ static void DumpPPCRegisters(IBus *bus)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void SaveFrameBuffer(const std::string& file)
|
||||
{
|
||||
std::shared_ptr<uint8_t> pixels(new uint8_t[totalXRes * totalYRes * 4], std::default_delete<uint8_t[]>());
|
||||
glReadPixels(0, 0, totalXRes, totalYRes, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
|
||||
Util::WriteSurfaceToBMP<Util::RGBA8>(file, pixels.get(), totalXRes, totalYRes, true);
|
||||
}
|
||||
|
||||
void Screenshot()
|
||||
{
|
||||
// Make a screenshot
|
||||
char file[128];
|
||||
string info = "Screenshot created: ";
|
||||
time_t now = time(0);
|
||||
tm* ltm = localtime(&now);
|
||||
|
||||
sprintf(file, "Screenshot %.4d-%.2d-%.2d (%.2d-%.2d-%.2d).bmp", 1900 + ltm->tm_year, 1 + ltm->tm_mon, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
|
||||
|
||||
info += file;
|
||||
puts(info.c_str());
|
||||
SaveFrameBuffer(file);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Render State Analysis
|
||||
|
@ -421,17 +443,9 @@ static void DumpPPCRegisters(IBus *bus)
|
|||
#ifdef DEBUG
|
||||
|
||||
#include "Model3/Model3GraphicsState.h"
|
||||
#include "Util/BMPFile.h"
|
||||
#include "OSD/SDL/PolyAnalysis.h"
|
||||
#include <fstream>
|
||||
|
||||
static void SaveFrameBuffer(const std::string &file)
|
||||
{
|
||||
std::shared_ptr<uint8_t> pixels(new uint8_t[totalXRes*totalYRes*4], std::default_delete<uint8_t[]>());
|
||||
glReadPixels(0, 0, totalXRes, totalYRes, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
|
||||
Util::WriteSurfaceToBMP<Util::RGBA8>(file, pixels.get(), totalXRes, totalYRes, true);
|
||||
}
|
||||
|
||||
static std::string s_gfxStatePath;
|
||||
|
||||
static std::string GetFileBaseName(const std::string &file)
|
||||
|
@ -715,91 +729,91 @@ static void PrintGLError(GLenum error)
|
|||
case GL_NO_ERROR: break;
|
||||
default: printf("unknown error\n"); break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned crosshairs)
|
||||
|
||||
{
|
||||
bool offscreenTrigger[2];
|
||||
float x[2], y[2];
|
||||
|
||||
crosshairs &= 3;
|
||||
if (!crosshairs)
|
||||
return;
|
||||
|
||||
// Set up the viewport and orthogonal projection
|
||||
glUseProgram(0); // no shaders
|
||||
glViewport(xOffset, yOffset, xRes, yRes);
|
||||
}
|
||||
*/
|
||||
|
||||
static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned crosshairs)
|
||||
|
||||
{
|
||||
bool offscreenTrigger[2];
|
||||
float x[2], y[2];
|
||||
|
||||
crosshairs &= 3;
|
||||
if (!crosshairs)
|
||||
return;
|
||||
|
||||
// Set up the viewport and orthogonal projection
|
||||
glUseProgram(0); // no shaders
|
||||
glViewport(xOffset, yOffset, xRes, yRes);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0.0, 1.0, 1.0, 0.0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glDisable(GL_TEXTURE_2D); // no texture mapping
|
||||
glDisable(GL_BLEND); // no blending
|
||||
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// Convert gun coordinates to viewspace coordinates
|
||||
if (currentInputs & Game::INPUT_ANALOG_GUN1)
|
||||
{
|
||||
x[0] = ((float)Inputs->analogGunX[0]->value / 255.0f);
|
||||
y[0] = ((255.0f - (float)Inputs->analogGunY[0]->value) / 255.0f);
|
||||
offscreenTrigger[0] = Inputs->analogTriggerLeft[0]->value || Inputs->analogTriggerRight[0]->value;
|
||||
}
|
||||
else if (currentInputs & Game::INPUT_GUN1)
|
||||
{
|
||||
x[0] = (float)Inputs->gunX[0]->value;
|
||||
y[0] = (float)Inputs->gunY[0]->value;
|
||||
GunToViewCoords(&x[0], &y[0]);
|
||||
offscreenTrigger[0] = (Inputs->trigger[0]->offscreenValue) > 0;
|
||||
}
|
||||
if (currentInputs & Game::INPUT_ANALOG_GUN2)
|
||||
{
|
||||
x[1] = ((float)Inputs->analogGunX[1]->value / 255.0f);
|
||||
y[1] = ((255.0f - (float)Inputs->analogGunY[1]->value) / 255.0f);
|
||||
offscreenTrigger[1] = Inputs->analogTriggerLeft[1]->value || Inputs->analogTriggerRight[1]->value;
|
||||
}
|
||||
else if (currentInputs & Game::INPUT_GUN2)
|
||||
{
|
||||
x[1] = (float)Inputs->gunX[1]->value;
|
||||
y[1] = (float)Inputs->gunY[1]->value;
|
||||
GunToViewCoords(&x[1], &y[1]);
|
||||
offscreenTrigger[1] = (Inputs->trigger[1]->offscreenValue) > 0;
|
||||
}
|
||||
// Draw visible crosshairs
|
||||
glBegin(GL_TRIANGLES);
|
||||
if ((crosshairs & 1) && !offscreenTrigger[0]) // Player 1
|
||||
DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f);
|
||||
if ((crosshairs & 2) && !offscreenTrigger[1]) // Player 2
|
||||
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
|
||||
glEnd();
|
||||
|
||||
//PrintGLError(glGetError());
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND); // no blending
|
||||
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// Convert gun coordinates to viewspace coordinates
|
||||
if (currentInputs & Game::INPUT_ANALOG_GUN1)
|
||||
{
|
||||
x[0] = ((float)Inputs->analogGunX[0]->value / 255.0f);
|
||||
y[0] = ((255.0f - (float)Inputs->analogGunY[0]->value) / 255.0f);
|
||||
offscreenTrigger[0] = Inputs->analogTriggerLeft[0]->value || Inputs->analogTriggerRight[0]->value;
|
||||
}
|
||||
else if (currentInputs & Game::INPUT_GUN1)
|
||||
{
|
||||
x[0] = (float)Inputs->gunX[0]->value;
|
||||
y[0] = (float)Inputs->gunY[0]->value;
|
||||
GunToViewCoords(&x[0], &y[0]);
|
||||
offscreenTrigger[0] = (Inputs->trigger[0]->offscreenValue) > 0;
|
||||
}
|
||||
if (currentInputs & Game::INPUT_ANALOG_GUN2)
|
||||
{
|
||||
x[1] = ((float)Inputs->analogGunX[1]->value / 255.0f);
|
||||
y[1] = ((255.0f - (float)Inputs->analogGunY[1]->value) / 255.0f);
|
||||
offscreenTrigger[1] = Inputs->analogTriggerLeft[1]->value || Inputs->analogTriggerRight[1]->value;
|
||||
}
|
||||
else if (currentInputs & Game::INPUT_GUN2)
|
||||
{
|
||||
x[1] = (float)Inputs->gunX[1]->value;
|
||||
y[1] = (float)Inputs->gunY[1]->value;
|
||||
GunToViewCoords(&x[1], &y[1]);
|
||||
offscreenTrigger[1] = (Inputs->trigger[1]->offscreenValue) > 0;
|
||||
}
|
||||
// Draw visible crosshairs
|
||||
glBegin(GL_TRIANGLES);
|
||||
if ((crosshairs & 1) && !offscreenTrigger[0]) // Player 1
|
||||
DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f);
|
||||
if ((crosshairs & 2) && !offscreenTrigger[1]) // Player 2
|
||||
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
|
||||
glEnd();
|
||||
|
||||
//PrintGLError(glGetError());
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Video Callbacks
|
||||
******************************************************************************/
|
||||
|
||||
static CInputs *videoInputs = NULL;
|
||||
static uint32_t currentInputs = 0;
|
||||
|
||||
bool BeginFrameVideo()
|
||||
{
|
||||
******************************************************************************/
|
||||
|
||||
static CInputs *videoInputs = NULL;
|
||||
static uint32_t currentInputs = 0;
|
||||
|
||||
bool BeginFrameVideo()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void EndFrameVideo()
|
||||
{
|
||||
// Show crosshairs for light gun games
|
||||
if (videoInputs)
|
||||
UpdateCrosshairs(currentInputs, videoInputs, s_runtime_config["Crosshairs"].ValueAs<unsigned>());
|
||||
|
||||
// Swap the buffers
|
||||
SDL_GL_SwapWindow(s_window);
|
||||
{
|
||||
// Show crosshairs for light gun games
|
||||
if (videoInputs)
|
||||
UpdateCrosshairs(currentInputs, videoInputs, s_runtime_config["Crosshairs"].ValueAs<unsigned>());
|
||||
|
||||
// Swap the buffers
|
||||
SDL_GL_SwapWindow(s_window);
|
||||
}
|
||||
|
||||
static void SuperSleep(UINT32 time)
|
||||
|
@ -863,14 +877,14 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
|
|||
if (OKAY != OpenAudio())
|
||||
return 1;
|
||||
|
||||
// Hide mouse if fullscreen, enable crosshairs for gun games
|
||||
Inputs->GetInputSystem()->SetMouseVisibility(!s_runtime_config["FullScreen"].ValueAs<bool>());
|
||||
gameHasLightguns = !!(game.inputs & (Game::INPUT_GUN1|Game::INPUT_GUN2));
|
||||
gameHasLightguns |= game.name == "lostwsga";
|
||||
currentInputs = game.inputs;
|
||||
if (gameHasLightguns)
|
||||
videoInputs = Inputs;
|
||||
else
|
||||
// Hide mouse if fullscreen, enable crosshairs for gun games
|
||||
Inputs->GetInputSystem()->SetMouseVisibility(!s_runtime_config["FullScreen"].ValueAs<bool>());
|
||||
gameHasLightguns = !!(game.inputs & (Game::INPUT_GUN1|Game::INPUT_GUN2));
|
||||
gameHasLightguns |= game.name == "lostwsga";
|
||||
currentInputs = game.inputs;
|
||||
if (gameHasLightguns)
|
||||
videoInputs = Inputs;
|
||||
else
|
||||
videoInputs = NULL;
|
||||
|
||||
// Attach the inputs to the emulator
|
||||
|
@ -1175,6 +1189,11 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
|
|||
s_runtime_config.Get("Throttle").SetValue(!s_runtime_config["Throttle"].ValueAs<bool>());
|
||||
printf("Frame limiting: %s\n", s_runtime_config["Throttle"].ValueAs<bool>() ? "On" : "Off");
|
||||
}
|
||||
else if (Inputs->uiScreenshot->Pressed())
|
||||
{
|
||||
// Make a screenshot
|
||||
Screenshot();
|
||||
}
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
else if (Debugger != NULL && Inputs->uiEnterDebugger->Pressed())
|
||||
{
|
||||
|
@ -1320,16 +1339,16 @@ static void PrintGameList(const std::string &xml_file, const std::map<std::strin
|
|||
for (auto &v: games)
|
||||
{
|
||||
const Game &game = v.second;
|
||||
printf(" %s", game.name.c_str());
|
||||
for (int i = game.name.length(); i < 9; i++) // pad for alignment (no game ID should be more than 9 letters)
|
||||
printf(" ");
|
||||
if (!game.version.empty())
|
||||
printf(" %s (%s)\n", game.title.c_str(), game.version.c_str());
|
||||
else
|
||||
printf(" %s\n", game.title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
printf(" %s", game.name.c_str());
|
||||
for (int i = game.name.length(); i < 9; i++) // pad for alignment (no game ID should be more than 9 letters)
|
||||
printf(" ");
|
||||
if (!game.version.empty())
|
||||
printf(" %s (%s)\n", game.title.c_str(), game.version.c_str());
|
||||
else
|
||||
printf(" %s\n", game.title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void LogConfig(const Util::Config::Node &config)
|
||||
{
|
||||
InfoLog("Runtime configuration:");
|
||||
|
|
Loading…
Reference in a new issue