Linux: Implemented HUD (ported SpriteFont to SDL)
This commit is contained in:
parent
baa8f275c8
commit
35979c6291
7 changed files with 822 additions and 28 deletions
2
GUI.NET/Forms/frmMain.Designer.cs
generated
2
GUI.NET/Forms/frmMain.Designer.cs
generated
|
@ -1301,7 +1301,7 @@ namespace Mesen.GUI.Forms
|
||||||
this.AllowDrop = true;
|
this.AllowDrop = true;
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.BackColor = System.Drawing.Color.Maroon;
|
this.BackColor = System.Drawing.Color.Black;
|
||||||
this.ClientSize = new System.Drawing.Size(360, 263);
|
this.ClientSize = new System.Drawing.Size(360, 263);
|
||||||
this.Controls.Add(this.panelRenderer);
|
this.Controls.Add(this.panelRenderer);
|
||||||
this.Controls.Add(this.menuStrip);
|
this.Controls.Add(this.menuStrip);
|
||||||
|
|
138
Linux/BaseRenderer.cpp
Normal file
138
Linux/BaseRenderer.cpp
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
#include "BaseRenderer.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include "../Core/EmulationSettings.h"
|
||||||
|
#include "../Core/VideoDecoder.h"
|
||||||
|
|
||||||
|
void BaseRenderer::DisplayMessage(string title, string message)
|
||||||
|
{
|
||||||
|
shared_ptr<ToastInfo> toast(new ToastInfo(title, message, 4000, ""));
|
||||||
|
_toasts.push_front(toast);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseRenderer::RemoveOldToasts()
|
||||||
|
{
|
||||||
|
_toasts.remove_if([](shared_ptr<ToastInfo> toast) { return toast->IsToastExpired(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseRenderer::DrawToasts()
|
||||||
|
{
|
||||||
|
RemoveOldToasts();
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
int lastHeight = 5;
|
||||||
|
for(shared_ptr<ToastInfo> toast : _toasts) {
|
||||||
|
if(counter < 6) {
|
||||||
|
DrawToast(toast, lastHeight);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount)
|
||||||
|
{
|
||||||
|
using std::wstring;
|
||||||
|
wstring text = utf8::utf8::decode(utf8Text);
|
||||||
|
wstring wrappedText;
|
||||||
|
list<wstring> words;
|
||||||
|
wstring currentWord;
|
||||||
|
for(size_t i = 0, len = text.length(); i < len; i++) {
|
||||||
|
if(text[i] == L' ' || text[i] == L'\n') {
|
||||||
|
if(currentWord.length() > 0) {
|
||||||
|
words.push_back(currentWord);
|
||||||
|
currentWord.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentWord += text[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(currentWord.length() > 0) {
|
||||||
|
words.push_back(currentWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
lineCount = 1;
|
||||||
|
float spaceWidth = MeasureString(L" ");
|
||||||
|
|
||||||
|
float lineWidth = 0.0f;
|
||||||
|
for(wstring word : words) {
|
||||||
|
for(unsigned int i = 0; i < word.size(); i++) {
|
||||||
|
if(!ContainsCharacter(word[i])) {
|
||||||
|
word[i] = L'?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float wordWidth = MeasureString(word.c_str());
|
||||||
|
|
||||||
|
if(lineWidth + wordWidth < maxLineWidth) {
|
||||||
|
wrappedText += word + L" ";
|
||||||
|
lineWidth += wordWidth + spaceWidth;
|
||||||
|
} else {
|
||||||
|
wrappedText += L"\n" + word + L" ";
|
||||||
|
lineWidth = wordWidth + spaceWidth;
|
||||||
|
lineCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrappedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseRenderer::DrawToast(shared_ptr<ToastInfo> toast, int &lastHeight)
|
||||||
|
{
|
||||||
|
//Get opacity for fade in/out effect
|
||||||
|
uint8_t opacity = (uint8_t)(toast->GetOpacity()*255);
|
||||||
|
int textLeftMargin = 4;
|
||||||
|
|
||||||
|
int lineHeight = 25;
|
||||||
|
string text = "[" + toast->GetToastTitle() + "] " + toast->GetToastMessage();
|
||||||
|
uint32_t lineCount = 0;
|
||||||
|
std::wstring wrappedText = WrapText(text, _screenWidth - textLeftMargin * 2 - 20, lineCount);
|
||||||
|
lastHeight += lineCount * lineHeight;
|
||||||
|
DrawString(wrappedText, textLeftMargin, (float)(_screenHeight - lastHeight), opacity, opacity, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseRenderer::DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
std::wstring textStr = utf8::utf8::decode(message);
|
||||||
|
DrawString(textStr, x, y, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseRenderer::ShowFpsCounter()
|
||||||
|
{
|
||||||
|
double elapsedSeconds = _fpsTimer.GetElapsedMS() / 1000;
|
||||||
|
if(elapsedSeconds > 1.0) {
|
||||||
|
//Update fps every sec
|
||||||
|
uint32_t frameCount = VideoDecoder::GetInstance()->GetFrameCount();
|
||||||
|
if(frameCount - _lastFrameCount < 0) {
|
||||||
|
_currentFPS = 0;
|
||||||
|
} else {
|
||||||
|
_currentFPS = (int)(std::round((double)(frameCount - _lastFrameCount) / elapsedSeconds));
|
||||||
|
_currentRenderedFPS = (int)(std::round((double)(_renderedFrameCount - _lastRenderedFrameCount) / elapsedSeconds));
|
||||||
|
}
|
||||||
|
_lastFrameCount = frameCount;
|
||||||
|
_lastRenderedFrameCount = _renderedFrameCount;
|
||||||
|
_fpsTimer.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_currentFPS > 5000) {
|
||||||
|
_currentFPS = 0;
|
||||||
|
}
|
||||||
|
if(_currentRenderedFPS > 5000) {
|
||||||
|
_currentRenderedFPS = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string fpsString = string("FPS: ") + std::to_string(_currentFPS) + " / " + std::to_string(_currentRenderedFPS);
|
||||||
|
DrawString(fpsString, (float)(_screenWidth - 120), 13, 250, 235, 215);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseRenderer::ShowLagCounter()
|
||||||
|
{
|
||||||
|
float yPos = EmulationSettings::CheckFlag(EmulationFlags::ShowFPS) ? 37.0f : 13.0f;
|
||||||
|
string lagCounter = MessageManager::Localize("Lag") + ": " + std::to_string(Console::GetLagCounter());
|
||||||
|
DrawString(lagCounter, (float)(_screenWidth - 120), yPos, 250, 235, 215);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseRenderer::IsMessageShown()
|
||||||
|
{
|
||||||
|
return !_toasts.empty();
|
||||||
|
}
|
37
Linux/BaseRenderer.h
Normal file
37
Linux/BaseRenderer.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../Core/IMessageManager.h"
|
||||||
|
#include "../Utilities/Timer.h"
|
||||||
|
|
||||||
|
class BaseRenderer : public IMessageManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
list<shared_ptr<ToastInfo>> _toasts;
|
||||||
|
|
||||||
|
Timer _fpsTimer;
|
||||||
|
uint32_t _lastFrameCount = 0;
|
||||||
|
uint32_t _lastRenderedFrameCount = 0;
|
||||||
|
uint32_t _currentFPS = 0;
|
||||||
|
uint32_t _currentRenderedFPS = 0;
|
||||||
|
|
||||||
|
void RemoveOldToasts();
|
||||||
|
std::wstring WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount);
|
||||||
|
virtual float MeasureString(std::wstring text) = 0;
|
||||||
|
virtual bool ContainsCharacter(wchar_t character) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t _screenWidth = 0;
|
||||||
|
uint32_t _screenHeight = 0;
|
||||||
|
uint32_t _renderedFrameCount = 0;
|
||||||
|
|
||||||
|
bool IsMessageShown();
|
||||||
|
|
||||||
|
void DisplayMessage(string title, string message);
|
||||||
|
void DrawToasts();
|
||||||
|
|
||||||
|
void DrawToast(shared_ptr<ToastInfo> toast, int &lastHeight);
|
||||||
|
void DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
virtual void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255) = 0;
|
||||||
|
|
||||||
|
void ShowFpsCounter();
|
||||||
|
void ShowLagCounter();
|
||||||
|
};
|
|
@ -7,7 +7,8 @@ SdlRenderer::SdlRenderer(void* windowHandle) : _windowHandle(windowHandle)
|
||||||
{
|
{
|
||||||
_frameBuffer = nullptr;
|
_frameBuffer = nullptr;
|
||||||
SetScreenSize(256,240);
|
SetScreenSize(256,240);
|
||||||
VideoRenderer::GetInstance()->RegisterRenderingDevice(this);
|
VideoRenderer::GetInstance()->RegisterRenderingDevice(this);
|
||||||
|
MessageManager::RegisterMessageManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SdlRenderer::~SdlRenderer()
|
SdlRenderer::~SdlRenderer()
|
||||||
|
@ -28,6 +29,11 @@ void SdlRenderer::Init()
|
||||||
_sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, SDL_RENDERER_ACCELERATED);
|
_sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, SDL_RENDERER_ACCELERATED);
|
||||||
_sdlTexture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, _nesFrameWidth, _nesFrameHeight);
|
_sdlTexture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, _nesFrameWidth, _nesFrameHeight);
|
||||||
|
|
||||||
|
_spriteFont.reset(new SpriteFont(_sdlRenderer, "Resources/Font.24.spritefont"));
|
||||||
|
_largeFont.reset(new SpriteFont(_sdlRenderer, "Resources/Font.64.spritefont"));
|
||||||
|
|
||||||
|
SDL_SetWindowSize(_sdlWindow, _screenWidth, _screenHeight);
|
||||||
|
|
||||||
_frameBuffer = new uint8_t[_nesFrameHeight*_nesFrameWidth*4];
|
_frameBuffer = new uint8_t[_nesFrameHeight*_nesFrameWidth*4];
|
||||||
memset(_frameBuffer, 0, _nesFrameHeight*_nesFrameWidth*4);
|
memset(_frameBuffer, 0, _nesFrameHeight*_nesFrameWidth*4);
|
||||||
}
|
}
|
||||||
|
@ -59,10 +65,9 @@ void SdlRenderer::Reset()
|
||||||
void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height)
|
void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height)
|
||||||
{
|
{
|
||||||
ScreenSize screenSize;
|
ScreenSize screenSize;
|
||||||
VideoDecoder::GetInstance()->GetScreenSize(screenSize, true);
|
VideoDecoder::GetInstance()->GetScreenSize(screenSize, false);
|
||||||
|
|
||||||
double scale = EmulationSettings::GetVideoScale();
|
if(_screenHeight != (uint32_t)screenSize.Height || _screenWidth != (uint32_t)screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != EmulationSettings::GetVideoResizeFilter()) {
|
||||||
if(_scale != scale || _screenHeight != (uint32_t)screenSize.Height || _screenWidth != (uint32_t)screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != EmulationSettings::GetVideoResizeFilter()) {
|
|
||||||
_frameLock.Acquire();
|
_frameLock.Acquire();
|
||||||
|
|
||||||
_nesFrameHeight = height;
|
_nesFrameHeight = height;
|
||||||
|
@ -73,15 +78,13 @@ void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height)
|
||||||
_screenWidth = screenSize.Width;
|
_screenWidth = screenSize.Width;
|
||||||
|
|
||||||
_resizeFilter = EmulationSettings::GetVideoResizeFilter();
|
_resizeFilter = EmulationSettings::GetVideoResizeFilter();
|
||||||
_scale = scale;
|
|
||||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _resizeFilter == VideoResizeFilter::Bilinear ? "1" : "0");
|
|
||||||
SDL_RenderSetLogicalSize(_sdlRenderer, _nesFrameWidth, _nesFrameHeight);
|
|
||||||
|
|
||||||
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _resizeFilter == VideoResizeFilter::Bilinear ? "1" : "0");
|
||||||
_screenBufferSize = _screenHeight*_screenWidth;
|
_screenBufferSize = _screenHeight*_screenWidth;
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
_frameLock.Release();
|
_frameLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdlRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
|
void SdlRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
|
||||||
|
@ -89,26 +92,87 @@ void SdlRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height
|
||||||
_frameLock.Acquire();
|
_frameLock.Acquire();
|
||||||
SetScreenSize(width, height);
|
SetScreenSize(width, height);
|
||||||
memcpy(_frameBuffer, frameBuffer, width*height*4);
|
memcpy(_frameBuffer, frameBuffer, width*height*4);
|
||||||
|
_frameChanged = true;
|
||||||
_frameLock.Release();
|
_frameLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SdlRenderer::Render()
|
void SdlRenderer::Render()
|
||||||
{
|
{
|
||||||
auto lock = _frameLock.AcquireSafe();
|
bool paused = EmulationSettings::IsPaused();
|
||||||
|
if(_noUpdateCount > 10 || _frameChanged || paused || IsMessageShown()) {
|
||||||
|
auto lock = _frameLock.AcquireSafe();
|
||||||
|
|
||||||
SDL_RenderClear(_sdlRenderer);
|
SDL_RenderClear(_sdlRenderer);
|
||||||
|
|
||||||
uint8_t *textureBuffer;
|
uint8_t *textureBuffer;
|
||||||
int rowPitch;
|
int rowPitch;
|
||||||
SDL_LockTexture(_sdlTexture, nullptr, (void**)&textureBuffer, &rowPitch);
|
SDL_LockTexture(_sdlTexture, nullptr, (void**)&textureBuffer, &rowPitch);
|
||||||
uint32_t* ppuFrameBuffer = (uint32_t*)_frameBuffer;
|
uint32_t* ppuFrameBuffer = (uint32_t*)_frameBuffer;
|
||||||
for(uint32_t i = 0, iMax = _nesFrameHeight; i < iMax; i++) {
|
for(uint32_t i = 0, iMax = _nesFrameHeight; i < iMax; i++) {
|
||||||
memcpy(textureBuffer, ppuFrameBuffer, _nesFrameWidth*4);
|
memcpy(textureBuffer, ppuFrameBuffer, _nesFrameWidth*4);
|
||||||
ppuFrameBuffer += _nesFrameWidth;
|
ppuFrameBuffer += _nesFrameWidth;
|
||||||
textureBuffer += rowPitch;
|
textureBuffer += rowPitch;
|
||||||
|
}
|
||||||
|
SDL_UnlockTexture(_sdlTexture);
|
||||||
|
|
||||||
|
if(_frameChanged) {
|
||||||
|
_renderedFrameCount++;
|
||||||
|
_frameChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect source = {0, 0, (int)_nesFrameWidth, (int)_nesFrameHeight };
|
||||||
|
SDL_Rect dest = {0, 0, (int)_screenWidth, (int)_screenHeight };
|
||||||
|
SDL_RenderCopy(_sdlRenderer, _sdlTexture, &source, &dest);
|
||||||
|
|
||||||
|
if(paused) {
|
||||||
|
DrawPauseScreen();
|
||||||
|
} else if(VideoDecoder::GetInstance()->IsRunning()) {
|
||||||
|
if(EmulationSettings::CheckFlag(EmulationFlags::ShowFPS)) {
|
||||||
|
ShowFpsCounter();
|
||||||
|
}
|
||||||
|
if(EmulationSettings::CheckFlag(EmulationFlags::ShowLagCounter)) {
|
||||||
|
ShowLagCounter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawToasts();
|
||||||
|
|
||||||
|
SDL_RenderPresent(_sdlRenderer);
|
||||||
|
} else {
|
||||||
|
_noUpdateCount++;
|
||||||
}
|
}
|
||||||
SDL_UnlockTexture(_sdlTexture);
|
|
||||||
|
|
||||||
SDL_RenderCopy(_sdlRenderer, _sdlTexture, nullptr, nullptr);
|
|
||||||
SDL_RenderPresent(_sdlRenderer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SdlRenderer::DrawPauseScreen()
|
||||||
|
{
|
||||||
|
uint32_t textureData = 0x222222AA;
|
||||||
|
SDL_Surface* surf = SDL_CreateRGBSurfaceFrom((void*)&textureData, 1, 1, 32, 4, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||||
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(_sdlRenderer, surf);
|
||||||
|
SDL_Rect source = {0, 0, 1, 1 };
|
||||||
|
SDL_Rect dest = {0, 0, (int)_screenWidth, (int)_screenHeight };
|
||||||
|
SDL_RenderCopy(_sdlRenderer, texture, &source, &dest);
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
SDL_FreeSurface(surf);
|
||||||
|
|
||||||
|
XMVECTOR stringDimensions = _largeFont->MeasureString(L"PAUSE");
|
||||||
|
float* measureF = (float*)&stringDimensions;
|
||||||
|
_largeFont->DrawString(_sdlRenderer, L"PAUSE", (int)(_screenWidth / 2 - measureF[0] / 2), (int)(_screenHeight / 2 - measureF[1] / 2 - 8), 250, 235, 215);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlRenderer::DrawString(std::wstring message, int x, int y, uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
const wchar_t *text = message.c_str();
|
||||||
|
_spriteFont->DrawString(_sdlRenderer, text, x, y, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
float SdlRenderer::MeasureString(std::wstring text)
|
||||||
|
{
|
||||||
|
XMVECTOR measure = _spriteFont->MeasureString(text.c_str());
|
||||||
|
float* measureF = (float*)&measure;
|
||||||
|
return measureF[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdlRenderer::ContainsCharacter(wchar_t character)
|
||||||
|
{
|
||||||
|
return _spriteFont->ContainsCharacter(character);
|
||||||
|
}
|
|
@ -4,6 +4,8 @@
|
||||||
#include "../Utilities/SimpleLock.h"
|
#include "../Utilities/SimpleLock.h"
|
||||||
#include "../Core/EmulationSettings.h"
|
#include "../Core/EmulationSettings.h"
|
||||||
#include "../Core/VideoRenderer.h"
|
#include "../Core/VideoRenderer.h"
|
||||||
|
#include "SpriteFont.h"
|
||||||
|
#include "BaseRenderer.h"
|
||||||
|
|
||||||
struct SDL_Window
|
struct SDL_Window
|
||||||
{
|
{
|
||||||
|
@ -19,24 +21,26 @@ struct SDL_Window
|
||||||
};
|
};
|
||||||
typedef struct SDL_Window SDL_Window;
|
typedef struct SDL_Window SDL_Window;
|
||||||
|
|
||||||
class SdlRenderer : public IRenderingDevice
|
class SdlRenderer : public IRenderingDevice, public BaseRenderer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
void* _windowHandle;
|
void* _windowHandle;
|
||||||
SDL_Window* _sdlWindow = nullptr;
|
SDL_Window* _sdlWindow = nullptr;
|
||||||
SDL_Renderer *_sdlRenderer = nullptr;
|
SDL_Renderer *_sdlRenderer = nullptr;
|
||||||
SDL_Texture *_sdlTexture = nullptr;
|
SDL_Texture *_sdlTexture = nullptr;
|
||||||
|
std::unique_ptr<SpriteFont> _spriteFont;
|
||||||
|
std::unique_ptr<SpriteFont> _largeFont;
|
||||||
|
|
||||||
VideoResizeFilter _resizeFilter = VideoResizeFilter::NearestNeighbor;
|
VideoResizeFilter _resizeFilter = VideoResizeFilter::NearestNeighbor;
|
||||||
|
|
||||||
SimpleLock _frameLock;
|
SimpleLock _frameLock;
|
||||||
uint8_t* _frameBuffer;
|
uint8_t* _frameBuffer;
|
||||||
|
|
||||||
const uint32_t _bytesPerPixel = 4;
|
const uint32_t _bytesPerPixel = 4;
|
||||||
uint32_t _screenWidth = 0;
|
|
||||||
uint32_t _screenHeight = 0;
|
|
||||||
uint32_t _screenBufferSize = 0;
|
uint32_t _screenBufferSize = 0;
|
||||||
double _scale = 0;
|
|
||||||
|
bool _frameChanged = true;
|
||||||
|
uint32_t _noUpdateCount = 0;
|
||||||
|
|
||||||
uint32_t _nesFrameHeight = 0;
|
uint32_t _nesFrameHeight = 0;
|
||||||
uint32_t _nesFrameWidth = 0;
|
uint32_t _nesFrameWidth = 0;
|
||||||
|
@ -46,6 +50,11 @@ private:
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
void SetScreenSize(uint32_t width, uint32_t height);
|
void SetScreenSize(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
void DrawPauseScreen();
|
||||||
|
|
||||||
|
float MeasureString(std::wstring text);
|
||||||
|
bool ContainsCharacter(wchar_t character);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SdlRenderer(void* windowHandle);
|
SdlRenderer(void* windowHandle);
|
||||||
virtual ~SdlRenderer();
|
virtual ~SdlRenderer();
|
||||||
|
@ -53,4 +62,6 @@ public:
|
||||||
void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height);
|
void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height);
|
||||||
void Render();
|
void Render();
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255);
|
||||||
};
|
};
|
359
Linux/SpriteFont.cpp
Normal file
359
Linux/SpriteFont.cpp
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// This a heavily modified version of SpriteFont.cpp from DirectX Toolkit (MIT license)
|
||||||
|
// It strips down a lot of options not needed for Mesen and implements the minimum
|
||||||
|
// required to use .spritefont files in SDL.
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// File: SpriteFont.cpp
|
||||||
|
//
|
||||||
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||||
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
#include "SpriteFont.h"
|
||||||
|
|
||||||
|
XMVECTOR XMVectorMax(FXMVECTOR V1,FXMVECTOR V2)
|
||||||
|
{
|
||||||
|
return _mm_max_ps( V1, V2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
XMVECTOR XMVectorSet(float x, float y, float z, float w)
|
||||||
|
{
|
||||||
|
return _mm_set_ps( w, z, y, x );
|
||||||
|
}
|
||||||
|
|
||||||
|
XMVECTOR XMVectorZero()
|
||||||
|
{
|
||||||
|
return _mm_setzero_ps();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define XM_PERMUTE_PS( v, c ) _mm_shuffle_ps( v, v, c )
|
||||||
|
void XMStoreFloat2(XMFLOAT2* pDestination, FXMVECTOR V)
|
||||||
|
{
|
||||||
|
XMVECTOR T = XM_PERMUTE_PS( V, _MM_SHUFFLE( 1, 1, 1, 1 ) );
|
||||||
|
_mm_store_ss( &pDestination->x, V );
|
||||||
|
_mm_store_ss( &pDestination->y, T );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal SpriteFont implementation class.
|
||||||
|
class SpriteFont::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl(SDL_Renderer* renderer, BinaryReader* reader);
|
||||||
|
virtual ~Impl();
|
||||||
|
|
||||||
|
Glyph const* FindGlyph(wchar_t character) const;
|
||||||
|
|
||||||
|
void SetDefaultCharacter(wchar_t character);
|
||||||
|
|
||||||
|
template<typename TAction>
|
||||||
|
void ForEachGlyph(wchar_t const* text, TAction action) const;
|
||||||
|
|
||||||
|
|
||||||
|
// Fields.
|
||||||
|
std::vector<uint8_t> textureData;
|
||||||
|
SDL_Texture* texture;
|
||||||
|
std::vector<Glyph> glyphs;
|
||||||
|
Glyph const* defaultGlyph;
|
||||||
|
float lineSpacing;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Constants.
|
||||||
|
const XMFLOAT2 SpriteFont::Float2Zero(0, 0);
|
||||||
|
|
||||||
|
static const char spriteFontMagic[] = "DXTKfont";
|
||||||
|
|
||||||
|
|
||||||
|
// Comparison operators make our sorted glyph vector work with std::binary_search and lower_bound.
|
||||||
|
static inline bool operator< (SpriteFont::Glyph const& left, SpriteFont::Glyph const& right)
|
||||||
|
{
|
||||||
|
return left.Character < right.Character;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool operator< (wchar_t left, SpriteFont::Glyph const& right)
|
||||||
|
{
|
||||||
|
return (uint32_t)left < right.Character;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool operator< (SpriteFont::Glyph const& left, wchar_t right)
|
||||||
|
{
|
||||||
|
return left.Character < (uint32_t)right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a SpriteFont from the binary format created by the MakeSpriteFont utility.
|
||||||
|
SpriteFont::Impl::Impl(SDL_Renderer* renderer, BinaryReader* reader) :
|
||||||
|
defaultGlyph(nullptr)
|
||||||
|
{
|
||||||
|
// Validate the header.
|
||||||
|
for (char const* magic = spriteFontMagic; *magic; magic++)
|
||||||
|
{
|
||||||
|
if (reader->Read<uint8_t>() != *magic)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Not a MakeSpriteFont output binary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the glyph data.
|
||||||
|
auto glyphCount = reader->Read<uint32_t>();
|
||||||
|
auto glyphData = reader->ReadArray<Glyph>(glyphCount);
|
||||||
|
|
||||||
|
glyphs.assign(glyphData, glyphData + glyphCount);
|
||||||
|
|
||||||
|
// Read font properties.
|
||||||
|
lineSpacing = reader->Read<float>();
|
||||||
|
|
||||||
|
SetDefaultCharacter((wchar_t)reader->Read<uint32_t>());
|
||||||
|
|
||||||
|
// Read the texture data.
|
||||||
|
auto textureWidth = reader->Read<uint32_t>();
|
||||||
|
auto textureHeight = reader->Read<uint32_t>();
|
||||||
|
reader->Read<uint32_t>(); //DXGI_FORMAT, ignored - assume 32-bit RBGA
|
||||||
|
auto textureStride = reader->Read<uint32_t>();
|
||||||
|
auto textureRows = reader->Read<uint32_t>();
|
||||||
|
auto pixelData = reader->ReadArray<uint8_t>(textureStride * textureRows);
|
||||||
|
|
||||||
|
textureData.insert(textureData.end(), pixelData, pixelData+textureStride*textureHeight);
|
||||||
|
|
||||||
|
SDL_Surface* surf = SDL_CreateRGBSurfaceFrom((void*)textureData.data(), textureWidth, textureHeight, 32, textureStride, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||||
|
texture = SDL_CreateTextureFromSurface(renderer, surf);
|
||||||
|
SDL_FreeSurface(surf);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteFont::Impl::~Impl()
|
||||||
|
{
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up the requested glyph, falling back to the default character if it is not in the font.
|
||||||
|
SpriteFont::Glyph const* SpriteFont::Impl::FindGlyph(wchar_t character) const
|
||||||
|
{
|
||||||
|
auto glyph = std::lower_bound(glyphs.begin(), glyphs.end(), character);
|
||||||
|
|
||||||
|
if (glyph != glyphs.end() && glyph->Character == (uint32_t)character)
|
||||||
|
{
|
||||||
|
return &*glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultGlyph)
|
||||||
|
{
|
||||||
|
return defaultGlyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Character not in font");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Sets the missing-character fallback glyph.
|
||||||
|
void SpriteFont::Impl::SetDefaultCharacter(wchar_t character)
|
||||||
|
{
|
||||||
|
defaultGlyph = nullptr;
|
||||||
|
|
||||||
|
if (character)
|
||||||
|
{
|
||||||
|
defaultGlyph = FindGlyph(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The core glyph layout algorithm, shared between DrawString and MeasureString.
|
||||||
|
template<typename TAction>
|
||||||
|
void SpriteFont::Impl::ForEachGlyph(wchar_t const* text, TAction action) const
|
||||||
|
{
|
||||||
|
float x = 0;
|
||||||
|
float y = 0;
|
||||||
|
|
||||||
|
for (; *text; text++)
|
||||||
|
{
|
||||||
|
wchar_t character = *text;
|
||||||
|
|
||||||
|
switch (character)
|
||||||
|
{
|
||||||
|
case '\r':
|
||||||
|
// Skip carriage returns.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
// New line.
|
||||||
|
x = 0;
|
||||||
|
y += lineSpacing;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Output this character.
|
||||||
|
auto glyph = FindGlyph(character);
|
||||||
|
|
||||||
|
x += glyph->XOffset;
|
||||||
|
|
||||||
|
if (x < 0)
|
||||||
|
x = 0;
|
||||||
|
|
||||||
|
float advance = glyph->Subrect.right - glyph->Subrect.left + glyph->XAdvance;
|
||||||
|
|
||||||
|
if(!std::iswspace(character) || (glyph->Subrect.right - glyph->Subrect.left) > 1 || (glyph->Subrect.bottom - glyph->Subrect.top ) > 1) {
|
||||||
|
action(glyph, x, y, advance);
|
||||||
|
}
|
||||||
|
|
||||||
|
x += advance;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Construct from a binary file created by the MakeSpriteFont utility.
|
||||||
|
SpriteFont::SpriteFont(SDL_Renderer* renderer, string fileName)
|
||||||
|
{
|
||||||
|
BinaryReader reader(fileName);
|
||||||
|
|
||||||
|
pImpl = std::make_unique<Impl>(renderer, &reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move constructor.
|
||||||
|
SpriteFont::SpriteFont(SpriteFont&& moveFrom)
|
||||||
|
: pImpl(std::move(moveFrom.pImpl))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Move assignment.
|
||||||
|
SpriteFont& SpriteFont::operator= (SpriteFont&& moveFrom)
|
||||||
|
{
|
||||||
|
pImpl = std::move(moveFrom.pImpl);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Public destructor.
|
||||||
|
SpriteFont::~SpriteFont()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteFont::DrawString(SDL_Renderer *renderer, wchar_t const* text, int x, int y, uint8_t r, uint8_t g, uint8_t b) const
|
||||||
|
{
|
||||||
|
SDL_SetTextureColorMod(pImpl->texture, r, g, b);
|
||||||
|
pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float offsetX, float offsetY, float advance)
|
||||||
|
{
|
||||||
|
int width = (int)(glyph->Subrect.right - glyph->Subrect.left);
|
||||||
|
int height = (int)(glyph->Subrect.bottom - glyph->Subrect.top);
|
||||||
|
|
||||||
|
SDL_Rect source = {(int)glyph->Subrect.left, (int)glyph->Subrect.top, width, height};
|
||||||
|
SDL_Rect dest = {x + (int)offsetX, y + (int)(offsetY + glyph->YOffset), width, height};
|
||||||
|
SDL_RenderCopy(renderer, pImpl->texture, &source, &dest);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
XMVECTOR SpriteFont::MeasureString(wchar_t const* text) const
|
||||||
|
{
|
||||||
|
XMVECTOR result = XMVectorZero();
|
||||||
|
|
||||||
|
pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance)
|
||||||
|
{
|
||||||
|
float w = (float)(glyph->Subrect.right - glyph->Subrect.left);
|
||||||
|
float h = (float)(glyph->Subrect.bottom - glyph->Subrect.top) + glyph->YOffset;
|
||||||
|
|
||||||
|
h = std::max(h, pImpl->lineSpacing);
|
||||||
|
|
||||||
|
result = XMVectorMax(result, XMVectorSet(x + w, y + h, 0, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RECT SpriteFont::MeasureDrawBounds(wchar_t const* text, XMFLOAT2 const& position) const
|
||||||
|
{
|
||||||
|
RECT result = { UINT32_MAX, UINT32_MAX, 0, 0 };
|
||||||
|
|
||||||
|
pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance)
|
||||||
|
{
|
||||||
|
float w = (float)(glyph->Subrect.right - glyph->Subrect.left);
|
||||||
|
float h = (float)(glyph->Subrect.bottom - glyph->Subrect.top);
|
||||||
|
|
||||||
|
float minX = position.x + x;
|
||||||
|
float minY = position.y + y + glyph->YOffset;
|
||||||
|
|
||||||
|
float maxX = std::max(minX + advance, minX + w);
|
||||||
|
float maxY = minY + h;
|
||||||
|
|
||||||
|
if (minX < result.left)
|
||||||
|
result.left = long(minX);
|
||||||
|
|
||||||
|
if (minY < result.top)
|
||||||
|
result.top = long(minY);
|
||||||
|
|
||||||
|
if (result.right < maxX)
|
||||||
|
result.right = long(maxX);
|
||||||
|
|
||||||
|
if (result.bottom < maxY)
|
||||||
|
result.bottom = long(maxY);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.left == LONG_MAX)
|
||||||
|
{
|
||||||
|
result.left = 0;
|
||||||
|
result.top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RECT SpriteFont::MeasureDrawBounds(wchar_t const* text, FXMVECTOR position) const
|
||||||
|
{
|
||||||
|
XMFLOAT2 pos;
|
||||||
|
XMStoreFloat2(&pos, position);
|
||||||
|
|
||||||
|
return MeasureDrawBounds(text, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Spacing properties
|
||||||
|
float SpriteFont::GetLineSpacing() const
|
||||||
|
{
|
||||||
|
return pImpl->lineSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpriteFont::SetLineSpacing(float spacing)
|
||||||
|
{
|
||||||
|
pImpl->lineSpacing = spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Font properties
|
||||||
|
wchar_t SpriteFont::GetDefaultCharacter() const
|
||||||
|
{
|
||||||
|
return pImpl->defaultGlyph ? (wchar_t)pImpl->defaultGlyph->Character : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpriteFont::SetDefaultCharacter(wchar_t character)
|
||||||
|
{
|
||||||
|
pImpl->SetDefaultCharacter(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SpriteFont::ContainsCharacter(wchar_t character) const
|
||||||
|
{
|
||||||
|
return std::binary_search(pImpl->glyphs.begin(), pImpl->glyphs.end(), character);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom layout/rendering
|
||||||
|
SpriteFont::Glyph const* SpriteFont::FindGlyph(wchar_t character) const
|
||||||
|
{
|
||||||
|
return pImpl->FindGlyph(character);
|
||||||
|
}
|
185
Linux/SpriteFont.h
Normal file
185
Linux/SpriteFont.h
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// This a heavily modified version of SpriteFont.h from DirectX Toolkit (MIT)
|
||||||
|
// It strips down a lot of options not needed for Mesen and implements the minimum
|
||||||
|
// required to use .spritefont files in SDL.
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// File: SpriteFont.h
|
||||||
|
//
|
||||||
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||||
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <fstream>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <string>
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
#define FXMVECTOR __m128
|
||||||
|
#define GXMVECTOR __m128
|
||||||
|
#define XMVECTOR __m128
|
||||||
|
|
||||||
|
struct RECT
|
||||||
|
{
|
||||||
|
uint32_t left;
|
||||||
|
uint32_t top;
|
||||||
|
uint32_t right;
|
||||||
|
uint32_t bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XMFLOAT2
|
||||||
|
{
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
|
||||||
|
XMFLOAT2() {}
|
||||||
|
XMFLOAT2(float _x, float _y) : x(_x), y(_y) {}
|
||||||
|
explicit XMFLOAT2(const float *pArray) : x(pArray[0]), y(pArray[1]) {}
|
||||||
|
|
||||||
|
XMFLOAT2& operator= (const XMFLOAT2& Float2) { x = Float2.x; y = Float2.y; return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
XMVECTOR XMVectorMax(FXMVECTOR V1,FXMVECTOR V2);
|
||||||
|
XMVECTOR XMVectorSet(float x, float y, float z, float w);
|
||||||
|
XMVECTOR XMVectorZero();
|
||||||
|
void XMStoreFloat2(XMFLOAT2* pDestination, FXMVECTOR V);
|
||||||
|
|
||||||
|
class SpriteFont
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Glyph;
|
||||||
|
|
||||||
|
SpriteFont(SDL_Renderer* renderer, string fileName);
|
||||||
|
|
||||||
|
SpriteFont(SpriteFont&& moveFrom);
|
||||||
|
SpriteFont& operator= (SpriteFont&& moveFrom);
|
||||||
|
|
||||||
|
SpriteFont(SpriteFont const&) = delete;
|
||||||
|
SpriteFont& operator= (SpriteFont const&) = delete;
|
||||||
|
|
||||||
|
virtual ~SpriteFont();
|
||||||
|
|
||||||
|
void DrawString(SDL_Renderer *renderer, wchar_t const* text, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255) const;
|
||||||
|
|
||||||
|
XMVECTOR MeasureString(wchar_t const* text) const;
|
||||||
|
|
||||||
|
RECT MeasureDrawBounds(wchar_t const* text, XMFLOAT2 const& position) const;
|
||||||
|
RECT MeasureDrawBounds(wchar_t const* text, FXMVECTOR position) const;
|
||||||
|
|
||||||
|
// Spacing properties
|
||||||
|
float GetLineSpacing() const;
|
||||||
|
void SetLineSpacing(float spacing);
|
||||||
|
|
||||||
|
// Font properties
|
||||||
|
wchar_t GetDefaultCharacter() const;
|
||||||
|
void SetDefaultCharacter(wchar_t character);
|
||||||
|
|
||||||
|
bool ContainsCharacter(wchar_t character) const;
|
||||||
|
|
||||||
|
// Custom layout/rendering
|
||||||
|
Glyph const* FindGlyph(wchar_t character) const;
|
||||||
|
|
||||||
|
// Describes a single character glyph.
|
||||||
|
struct Glyph
|
||||||
|
{
|
||||||
|
uint32_t Character;
|
||||||
|
RECT Subrect;
|
||||||
|
float XOffset;
|
||||||
|
float YOffset;
|
||||||
|
float XAdvance;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private implementation.
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
std::unique_ptr<Impl> pImpl;
|
||||||
|
|
||||||
|
static const XMFLOAT2 Float2Zero;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BinaryReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BinaryReader(string fileName) : mPos(nullptr), mEnd(nullptr)
|
||||||
|
{
|
||||||
|
size_t dataSize;
|
||||||
|
|
||||||
|
bool result = ReadEntireFile(fileName, mOwnedData, &dataSize);
|
||||||
|
if(!result) {
|
||||||
|
throw std::runtime_error( "BinaryReader" );
|
||||||
|
}
|
||||||
|
|
||||||
|
mPos = mOwnedData.get();
|
||||||
|
mEnd = mOwnedData.get() + dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a single value.
|
||||||
|
template<typename T> T const& Read()
|
||||||
|
{
|
||||||
|
return *ReadArray<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Reads an array of values.
|
||||||
|
template<typename T> T const* ReadArray(size_t elementCount)
|
||||||
|
{
|
||||||
|
static_assert(std::is_pod<T>::value, "Can only read plain-old-data types");
|
||||||
|
|
||||||
|
uint8_t const* newPos = mPos + sizeof(T) * elementCount;
|
||||||
|
|
||||||
|
if (newPos < mPos)
|
||||||
|
throw std::overflow_error("ReadArray");
|
||||||
|
|
||||||
|
if (newPos > mEnd)
|
||||||
|
throw std::runtime_error("End of file");
|
||||||
|
|
||||||
|
auto result = reinterpret_cast<T const*>(mPos);
|
||||||
|
|
||||||
|
mPos = newPos;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower level helper reads directly from the filesystem into memory.
|
||||||
|
static bool ReadEntireFile(string fileName, std::unique_ptr<uint8_t[]>& data, size_t* dataSize)
|
||||||
|
{
|
||||||
|
std::ifstream file(fileName, std::ios::binary | std::ios::in);
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
size_t filesize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
// Create enough space for the file data.
|
||||||
|
data.reset(new uint8_t[filesize]);
|
||||||
|
|
||||||
|
// Read the data in.
|
||||||
|
file.read((char*)data.get(), filesize);
|
||||||
|
|
||||||
|
*dataSize = filesize;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The data currently being read.
|
||||||
|
uint8_t const* mPos;
|
||||||
|
uint8_t const* mEnd;
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> mOwnedData;
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue