mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-07-01 00:48:30 +02:00
So, the codebase was kind of undecided about who is responsible for initializing the parameters passed to FILESYSTEM_loadFileToMemory() - is it the caller? Is it FILESYSTEM_loadFileToMemory()? Sometimes callers would initialize one variable but not the other, and it was always a toss-up whether or not FILESYSTEM_loadFileToMemory() would end up initializing everything in the end. All of this is to say that the game dereferences an uninitialized pointer if it can't load a sound effect. Which is bad. Now, I could either fix that single case, or fix every case. Judging by the title of this commit, you can infer that I decided to fix every case - fixing every case means not just all cases that currently exist (which, as far as I know, is only the sound effect one), but all cases that could exist in the future. So, FILESYSTEM_loadFileToMemory() is now guaranteed to initialize its parameters even if the file fails to be loaded. This is better than passing the responsibility to the caller anyway, because if the caller initialized it, then that would be wasted work if the file succeeds anyway because FILESYSTEM_loadFileToMemory() will overwrite it, and if the file fails to load, well that's when the variables get initialized anyway.
402 lines
7.9 KiB
C++
402 lines
7.9 KiB
C++
#include "Screen.h"
|
|
|
|
#include <SDL.h>
|
|
#include <stdio.h>
|
|
|
|
#include "FileSystemUtils.h"
|
|
#include "GraphicsUtil.h"
|
|
|
|
// Used to create the window icon
|
|
extern "C"
|
|
{
|
|
extern unsigned lodepng_decode24(
|
|
unsigned char** out,
|
|
unsigned* w,
|
|
unsigned* h,
|
|
const unsigned char* in,
|
|
size_t insize
|
|
);
|
|
}
|
|
|
|
ScreenSettings::ScreenSettings(void)
|
|
{
|
|
windowWidth = 320;
|
|
windowHeight = 240;
|
|
fullscreen = false;
|
|
useVsync = false;
|
|
stretch = 0;
|
|
linearFilter = false;
|
|
badSignal = false;
|
|
}
|
|
|
|
void Screen::init(const ScreenSettings& settings)
|
|
{
|
|
m_window = NULL;
|
|
m_renderer = NULL;
|
|
m_screenTexture = NULL;
|
|
m_screen = NULL;
|
|
isWindowed = !settings.fullscreen;
|
|
stretchMode = settings.stretch;
|
|
isFiltered = settings.linearFilter;
|
|
vsync = settings.useVsync;
|
|
filterSubrect.x = 1;
|
|
filterSubrect.y = 1;
|
|
filterSubrect.w = 318;
|
|
filterSubrect.h = 238;
|
|
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_SCALE_QUALITY,
|
|
isFiltered ? "linear" : "nearest",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_VSYNC,
|
|
vsync ? "1" : "0",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
|
|
// Uncomment this next line when you need to debug -flibit
|
|
// SDL_SetHintWithPriority(SDL_HINT_RENDER_DRIVER, "software", SDL_HINT_OVERRIDE);
|
|
// FIXME: m_renderer is also created in resetRendererWorkaround()!
|
|
SDL_CreateWindowAndRenderer(
|
|
640,
|
|
480,
|
|
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI,
|
|
&m_window,
|
|
&m_renderer
|
|
);
|
|
SDL_SetWindowTitle(m_window, "VVVVVV");
|
|
|
|
LoadIcon();
|
|
|
|
// FIXME: This surface should be the actual backbuffer! -flibit
|
|
m_screen = SDL_CreateRGBSurface(
|
|
0,
|
|
320,
|
|
240,
|
|
32,
|
|
0x00FF0000,
|
|
0x0000FF00,
|
|
0x000000FF,
|
|
0xFF000000
|
|
);
|
|
// ALSO FIXME: This SDL_CreateTexture() is duplicated twice in this file!
|
|
m_screenTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
320,
|
|
240
|
|
);
|
|
|
|
badSignalEffect = settings.badSignal;
|
|
|
|
ResizeScreen(settings.windowWidth, settings.windowHeight);
|
|
}
|
|
|
|
void Screen::destroy(void)
|
|
{
|
|
#define X(CLEANUP, POINTER) \
|
|
CLEANUP(POINTER); \
|
|
POINTER = NULL;
|
|
|
|
/* Order matters! */
|
|
X(SDL_DestroyTexture, m_screenTexture);
|
|
X(SDL_FreeSurface, m_screen);
|
|
X(SDL_DestroyRenderer, m_renderer);
|
|
X(SDL_DestroyWindow, m_window);
|
|
|
|
#undef X
|
|
}
|
|
|
|
void Screen::GetSettings(ScreenSettings* settings)
|
|
{
|
|
int width, height;
|
|
GetWindowSize(&width, &height);
|
|
|
|
settings->windowWidth = width;
|
|
settings->windowHeight = height;
|
|
|
|
settings->fullscreen = !isWindowed;
|
|
settings->useVsync = vsync;
|
|
settings->stretch = stretchMode;
|
|
settings->linearFilter = isFiltered;
|
|
settings->badSignal = badSignalEffect;
|
|
}
|
|
|
|
void Screen::LoadIcon(void)
|
|
{
|
|
#ifndef __APPLE__
|
|
unsigned char *fileIn;
|
|
size_t length;
|
|
unsigned char *data;
|
|
unsigned int width, height;
|
|
FILESYSTEM_loadAssetToMemory("VVVVVV.png", &fileIn, &length, false);
|
|
lodepng_decode24(&data, &width, &height, fileIn, length);
|
|
FILESYSTEM_freeMemory(&fileIn);
|
|
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
|
|
data,
|
|
width,
|
|
height,
|
|
24,
|
|
width * 3,
|
|
0x000000FF,
|
|
0x0000FF00,
|
|
0x00FF0000,
|
|
0x00000000
|
|
);
|
|
SDL_SetWindowIcon(m_window, icon);
|
|
SDL_FreeSurface(icon);
|
|
SDL_free(data);
|
|
#endif /* __APPLE__ */
|
|
}
|
|
|
|
void Screen::ResizeScreen(int x, int y)
|
|
{
|
|
static int resX = 320;
|
|
static int resY = 240;
|
|
if (x != -1 && y != -1)
|
|
{
|
|
// This is a user resize!
|
|
resX = x;
|
|
resY = y;
|
|
}
|
|
|
|
if(!isWindowed)
|
|
{
|
|
int result = SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
if (result != 0)
|
|
{
|
|
printf("Error: could not set the game to fullscreen mode: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int result = SDL_SetWindowFullscreen(m_window, 0);
|
|
if (result != 0)
|
|
{
|
|
printf("Error: could not set the game to windowed mode: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
if (x != -1 && y != -1)
|
|
{
|
|
SDL_SetWindowSize(m_window, resX, resY);
|
|
SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
}
|
|
}
|
|
if (stretchMode == 1)
|
|
{
|
|
int winX, winY;
|
|
GetWindowSize(&winX, &winY);
|
|
int result = SDL_RenderSetLogicalSize(m_renderer, winX, winY);
|
|
if (result != 0)
|
|
{
|
|
printf("Error: could not set logical size: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
result = SDL_RenderSetIntegerScale(m_renderer, SDL_FALSE);
|
|
if (result != 0)
|
|
{
|
|
printf("Error: could not set scale: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SDL_RenderSetLogicalSize(m_renderer, 320, 240);
|
|
int result = SDL_RenderSetIntegerScale(m_renderer, (SDL_bool) (stretchMode == 2));
|
|
if (result != 0)
|
|
{
|
|
printf("Error: could not set scale: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
}
|
|
SDL_ShowWindow(m_window);
|
|
}
|
|
|
|
void Screen::ResizeToNearestMultiple(void)
|
|
{
|
|
int w, h;
|
|
GetWindowSize(&w, &h);
|
|
|
|
// Check aspect ratio first
|
|
bool using_width;
|
|
int usethisdimension, usethisratio;
|
|
|
|
if ((float) w / (float) h > 4.0 / 3.0)
|
|
{
|
|
// Width is bigger, so it's limited by height
|
|
usethisdimension = h;
|
|
usethisratio = 240;
|
|
using_width = false;
|
|
}
|
|
else
|
|
{
|
|
// Height is bigger, so it's limited by width. Or we're exactly 4:3 already
|
|
usethisdimension = w;
|
|
usethisratio = 320;
|
|
using_width = true;
|
|
}
|
|
|
|
int floor = (usethisdimension / usethisratio) * usethisratio;
|
|
int ceiling = floor + usethisratio;
|
|
|
|
int final_dimension;
|
|
|
|
if (usethisdimension - floor < ceiling - usethisdimension)
|
|
{
|
|
// Floor is nearest
|
|
final_dimension = floor;
|
|
}
|
|
else
|
|
{
|
|
// Ceiling is nearest. Or we're exactly on a multiple already
|
|
final_dimension = ceiling;
|
|
}
|
|
|
|
if (final_dimension == 0)
|
|
{
|
|
// We're way too small!
|
|
ResizeScreen(320, 240);
|
|
return;
|
|
}
|
|
|
|
if (using_width)
|
|
{
|
|
ResizeScreen(final_dimension, final_dimension / 4 * 3);
|
|
}
|
|
else
|
|
{
|
|
ResizeScreen(final_dimension * 4 / 3, final_dimension);
|
|
}
|
|
}
|
|
|
|
void Screen::GetWindowSize(int* x, int* y)
|
|
{
|
|
SDL_GetRendererOutputSize(m_renderer, x, y);
|
|
}
|
|
|
|
void Screen::UpdateScreen(SDL_Surface* buffer, SDL_Rect* rect )
|
|
{
|
|
if((buffer == NULL) && (m_screen == NULL) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(badSignalEffect)
|
|
{
|
|
buffer = ApplyFilter(buffer);
|
|
}
|
|
|
|
|
|
ClearSurface(m_screen);
|
|
BlitSurfaceStandard(buffer,NULL,m_screen,rect);
|
|
|
|
if(badSignalEffect)
|
|
{
|
|
SDL_FreeSurface(buffer);
|
|
}
|
|
|
|
}
|
|
|
|
const SDL_PixelFormat* Screen::GetFormat(void)
|
|
{
|
|
return m_screen->format;
|
|
}
|
|
|
|
void Screen::FlipScreen(void)
|
|
{
|
|
SDL_UpdateTexture(
|
|
m_screenTexture,
|
|
NULL,
|
|
m_screen->pixels,
|
|
m_screen->pitch
|
|
);
|
|
SDL_RenderCopy(
|
|
m_renderer,
|
|
m_screenTexture,
|
|
isFiltered ? &filterSubrect : NULL,
|
|
NULL
|
|
);
|
|
SDL_RenderPresent(m_renderer);
|
|
SDL_RenderClear(m_renderer);
|
|
ClearSurface(m_screen);
|
|
}
|
|
|
|
void Screen::toggleFullScreen(void)
|
|
{
|
|
isWindowed = !isWindowed;
|
|
ResizeScreen(-1, -1);
|
|
}
|
|
|
|
void Screen::toggleStretchMode(void)
|
|
{
|
|
stretchMode = (stretchMode + 1) % 3;
|
|
ResizeScreen(-1, -1);
|
|
}
|
|
|
|
void Screen::toggleLinearFilter(void)
|
|
{
|
|
isFiltered = !isFiltered;
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_SCALE_QUALITY,
|
|
isFiltered ? "linear" : "nearest",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
SDL_DestroyTexture(m_screenTexture);
|
|
m_screenTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
320,
|
|
240
|
|
);
|
|
}
|
|
|
|
void Screen::resetRendererWorkaround(void)
|
|
{
|
|
SDL_SetHintWithPriority(
|
|
SDL_HINT_RENDER_VSYNC,
|
|
vsync ? "1" : "0",
|
|
SDL_HINT_OVERRIDE
|
|
);
|
|
|
|
/* FIXME: This exists because SDL_HINT_RENDER_VSYNC is not dynamic!
|
|
* As a result, our only workaround is to tear down the renderer
|
|
* and recreate everything so that it can process the variable.
|
|
* -flibit
|
|
*/
|
|
|
|
if (m_renderer == NULL)
|
|
{
|
|
/* We haven't made it to init yet, don't worry about it */
|
|
return;
|
|
}
|
|
|
|
SDL_RendererInfo renderInfo;
|
|
SDL_GetRendererInfo(m_renderer, &renderInfo);
|
|
bool curVsync = (renderInfo.flags & SDL_RENDERER_PRESENTVSYNC) != 0;
|
|
if (vsync == curVsync)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SDL_DestroyTexture(m_screenTexture);
|
|
SDL_DestroyRenderer(m_renderer);
|
|
|
|
m_renderer = SDL_CreateRenderer(m_window, -1, 0);
|
|
m_screenTexture = SDL_CreateTexture(
|
|
m_renderer,
|
|
SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
320,
|
|
240
|
|
);
|
|
|
|
/* Ugh, have to make sure to re-apply graphics options after doing the
|
|
* above, otherwise letterbox/integer won't be applied...
|
|
*/
|
|
ResizeScreen(-1, -1);
|
|
}
|