mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-02-02 12:37:19 +01:00
Add screenshot functionality
This commit is contained in:
parent
88528c1e72
commit
d85582d9b5
BIN
assets/sounds/screenshot.vag
Normal file
BIN
assets/sounds/screenshot.vag
Normal file
Binary file not shown.
@ -73,6 +73,12 @@
|
|||||||
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/click.vag",
|
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/click.vag",
|
||||||
"compression": "none"
|
"compression": "none"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "binary",
|
||||||
|
"name": "assets/sounds/screenshot.vag",
|
||||||
|
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/screenshot.vag",
|
||||||
|
"compression": "none"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "palette",
|
"type": "palette",
|
||||||
"name": "assets/app.palette",
|
"name": "assets/app.palette",
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "common/file.hpp"
|
#include "common/file.hpp"
|
||||||
|
#include "common/gpu.hpp"
|
||||||
|
#include "common/util.hpp"
|
||||||
#include "ps1/pcdrv.h"
|
#include "ps1/pcdrv.h"
|
||||||
#include "vendor/ff.h"
|
#include "vendor/ff.h"
|
||||||
#include "vendor/miniz.h"
|
#include "vendor/miniz.h"
|
||||||
@ -256,6 +258,8 @@ size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: these *really* belong somewhere else
|
||||||
|
|
||||||
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
||||||
util::Data data;
|
util::Data data;
|
||||||
|
|
||||||
@ -308,6 +312,89 @@ size_t Provider::loadVAG(spu::Sound &output, const char *path) {
|
|||||||
return data.length;
|
return data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct [[gnu::packed]] BMPHeader {
|
||||||
|
public:
|
||||||
|
uint16_t magic;
|
||||||
|
uint32_t fileLength;
|
||||||
|
uint8_t _reserved[4];
|
||||||
|
uint32_t dataOffset;
|
||||||
|
|
||||||
|
uint32_t headerLength, width, height;
|
||||||
|
uint16_t numPlanes, bpp;
|
||||||
|
uint32_t compType, dataLength, ppmX, ppmY, numColors, numColors2;
|
||||||
|
|
||||||
|
inline void init(int _width, int _height, int _bpp) {
|
||||||
|
util::clear(*this);
|
||||||
|
|
||||||
|
size_t length = _width * _height * _bpp / 8;
|
||||||
|
|
||||||
|
magic = 0x4d42;
|
||||||
|
fileLength = sizeof(BMPHeader) + length;
|
||||||
|
dataOffset = sizeof(BMPHeader);
|
||||||
|
headerLength = sizeof(BMPHeader) - offsetof(BMPHeader, headerLength);
|
||||||
|
width = _width;
|
||||||
|
height = _height;
|
||||||
|
numPlanes = 1;
|
||||||
|
bpp = _bpp;
|
||||||
|
dataLength = length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
||||||
|
#ifdef ENABLE_FILE_WRITING
|
||||||
|
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
||||||
|
|
||||||
|
if (!_file)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
BMPHeader header;
|
||||||
|
|
||||||
|
header.init(rect.w, rect.h, 16);
|
||||||
|
|
||||||
|
size_t length = _file->write(&header, sizeof(header));
|
||||||
|
util::Data buffer;
|
||||||
|
|
||||||
|
if (buffer.allocate(rect.w * 2 + 32)) {
|
||||||
|
// Read the image from VRAM one line at a time from the bottom up, as
|
||||||
|
// the BMP format stores lines in reversed order.
|
||||||
|
gpu::RectWH slice;
|
||||||
|
|
||||||
|
slice.x = rect.x;
|
||||||
|
slice.w = rect.w;
|
||||||
|
slice.h = 1;
|
||||||
|
|
||||||
|
for (int y = rect.y + rect.h - 1; y >= rect.y; y--) {
|
||||||
|
slice.y = y;
|
||||||
|
auto lineLength = gpu::download(slice, buffer.ptr, true);
|
||||||
|
|
||||||
|
// BMP stores channels in BGR order as opposed to RGB, so the red
|
||||||
|
// and blue channels must be swapped.
|
||||||
|
auto ptr = buffer.as<uint16_t>();
|
||||||
|
|
||||||
|
for (int i = lineLength; i > 0; i -= 2) {
|
||||||
|
uint16_t value = *ptr, newValue;
|
||||||
|
|
||||||
|
newValue = (value & (31 << 5));
|
||||||
|
newValue |= (value & (31 << 10)) >> 10;
|
||||||
|
newValue |= (value & (31 << 0)) << 10;
|
||||||
|
*(ptr++) = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
length += _file->write(buffer.ptr, lineLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
_file->close();
|
||||||
|
delete _file;
|
||||||
|
|
||||||
|
return length;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool HostProvider::init(void) {
|
bool HostProvider::init(void) {
|
||||||
int error = pcdrvInit();
|
int error = pcdrvInit();
|
||||||
|
|
||||||
|
@ -148,8 +148,10 @@ public:
|
|||||||
virtual size_t loadData(util::Data &output, const char *path);
|
virtual size_t loadData(util::Data &output, const char *path);
|
||||||
virtual size_t loadData(void *output, size_t length, const char *path);
|
virtual size_t loadData(void *output, size_t length, const char *path);
|
||||||
virtual size_t saveData(const void *input, size_t length, const char *path);
|
virtual size_t saveData(const void *input, size_t length, const char *path);
|
||||||
|
|
||||||
size_t loadTIM(gpu::Image &output, const char *path);
|
size_t loadTIM(gpu::Image &output, const char *path);
|
||||||
size_t loadVAG(spu::Sound &output, const char *path);
|
size_t loadVAG(spu::Sound &output, const char *path);
|
||||||
|
size_t saveVRAMBMP(gpu::RectWH &rect, const char *path);
|
||||||
};
|
};
|
||||||
|
|
||||||
class HostProvider : public Provider {
|
class HostProvider : public Provider {
|
||||||
|
@ -54,6 +54,44 @@ size_t upload(const RectWH &rect, const void *data, bool wait) {
|
|||||||
return length * _DMA_CHUNK_SIZE * 4;
|
return length * _DMA_CHUNK_SIZE * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t download(const RectWH &rect, void *data, bool wait) {
|
||||||
|
size_t length = (rect.w * rect.h) / 2;
|
||||||
|
|
||||||
|
util::assertAligned<uint32_t>(data);
|
||||||
|
//assert(!(length % _DMA_CHUNK_SIZE));
|
||||||
|
length = (length + _DMA_CHUNK_SIZE - 1) / _DMA_CHUNK_SIZE;
|
||||||
|
|
||||||
|
if (!waitForDMATransfer(DMA_GPU, _DMA_TIMEOUT))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto enable = disableInterrupts();
|
||||||
|
GPU_GP1 = gp1_dmaRequestMode(GP1_DREQ_NONE);
|
||||||
|
|
||||||
|
while (!(GPU_GP1 & GP1_STAT_CMD_READY))
|
||||||
|
__asm__ volatile("");
|
||||||
|
|
||||||
|
GPU_GP0 = gp0_flushCache();
|
||||||
|
GPU_GP0 = gp0_vramRead();
|
||||||
|
GPU_GP0 = gp0_xy(rect.x, rect.y);
|
||||||
|
GPU_GP0 = gp0_xy(rect.w, rect.h);
|
||||||
|
|
||||||
|
GPU_GP1 = gp1_dmaRequestMode(GP1_DREQ_GP0_READ);
|
||||||
|
|
||||||
|
while (!(GPU_GP1 & GP1_STAT_READ_READY))
|
||||||
|
__asm__ volatile("");
|
||||||
|
|
||||||
|
DMA_MADR(DMA_GPU) = reinterpret_cast<uint32_t>(data);
|
||||||
|
DMA_BCR (DMA_GPU) = _DMA_CHUNK_SIZE | (length << 16);
|
||||||
|
DMA_CHCR(DMA_GPU) = DMA_CHCR_READ | DMA_CHCR_MODE_SLICE | DMA_CHCR_ENABLE;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
enableInterrupts();
|
||||||
|
if (wait)
|
||||||
|
waitForDMATransfer(DMA_GPU, _DMA_TIMEOUT);
|
||||||
|
|
||||||
|
return length * _DMA_CHUNK_SIZE * 4;
|
||||||
|
}
|
||||||
|
|
||||||
/* Rendering context */
|
/* Rendering context */
|
||||||
|
|
||||||
void Context::_applyResolution(
|
void Context::_applyResolution(
|
||||||
|
@ -52,6 +52,7 @@ static inline void enableDisplay(bool enable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t upload(const RectWH &rect, const void *data, bool wait);
|
size_t upload(const RectWH &rect, const void *data, bool wait);
|
||||||
|
size_t download(const RectWH &rect, void *data, bool wait);
|
||||||
|
|
||||||
/* Rendering context */
|
/* Rendering context */
|
||||||
|
|
||||||
@ -93,6 +94,15 @@ public:
|
|||||||
) : _lastTexpage(0) {
|
) : _lastTexpage(0) {
|
||||||
setResolution(mode, width, height, forceInterlace, sideBySide);
|
setResolution(mode, width, height, forceInterlace, sideBySide);
|
||||||
}
|
}
|
||||||
|
inline void getVRAMClipRect(RectWH &output) const {
|
||||||
|
auto &clip = _buffers[_currentBuffer ^ 1].clip;
|
||||||
|
|
||||||
|
output.x = clip.x1;
|
||||||
|
output.y = clip.y1;
|
||||||
|
output.w = width;
|
||||||
|
output.h = height;
|
||||||
|
}
|
||||||
|
|
||||||
inline void newLayer(int x, int y) {
|
inline void newLayer(int x, int y) {
|
||||||
newLayer(x, y, width, height);
|
newLayer(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "common/util.hpp"
|
||||||
#include "ps1/registers573.h"
|
#include "ps1/registers573.h"
|
||||||
|
|
||||||
namespace ide {
|
namespace ide {
|
||||||
@ -241,17 +242,14 @@ public:
|
|||||||
uint8_t param[11];
|
uint8_t param[11];
|
||||||
uint8_t _reserved[4];
|
uint8_t _reserved[4];
|
||||||
|
|
||||||
inline void clear(void) {
|
|
||||||
__builtin_memset(this, 0, sizeof(Packet));
|
|
||||||
}
|
|
||||||
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
||||||
clear();
|
util::clear(*this);
|
||||||
|
|
||||||
command = ATAPI_START_STOP_UNIT;
|
command = ATAPI_START_STOP_UNIT;
|
||||||
param[3] = mode & 3;
|
param[3] = mode & 3;
|
||||||
}
|
}
|
||||||
inline void setRead(uint32_t lba, size_t count) {
|
inline void setRead(uint32_t lba, size_t count) {
|
||||||
clear();
|
util::clear(*this);
|
||||||
|
|
||||||
command = ATAPI_READ12;
|
command = ATAPI_READ12;
|
||||||
param[1] = (lba >> 24) & 0xff;
|
param[1] = (lba >> 24) & 0xff;
|
||||||
@ -264,7 +262,7 @@ public:
|
|||||||
param[8] = (count >> 0) & 0xff;
|
param[8] = (count >> 0) & 0xff;
|
||||||
}
|
}
|
||||||
inline void setSetCDSpeed(uint16_t value) {
|
inline void setSetCDSpeed(uint16_t value) {
|
||||||
clear();
|
util::clear(*this);
|
||||||
|
|
||||||
command = ATAPI_SET_CD_SPEED;
|
command = ATAPI_SET_CD_SPEED;
|
||||||
param[1] = (value >> 8) & 0xff;
|
param[1] = (value >> 8) & 0xff;
|
||||||
|
@ -53,6 +53,10 @@ template<typename T, typename X> static inline void assertAligned(X *ptr) {
|
|||||||
//assert(!(reinterpret_cast<uintptr_t>(ptr) % alignof(T)));
|
//assert(!(reinterpret_cast<uintptr_t>(ptr) % alignof(T)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T> static inline void clear(T &obj, uint8_t value = 0) {
|
||||||
|
__builtin_memset(&obj, value, sizeof(obj));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T> static constexpr inline size_t countOf(T &array) {
|
template<typename T> static constexpr inline size_t countOf(T &array) {
|
||||||
return sizeof(array) / sizeof(array[0]);
|
return sizeof(array) / sizeof(array[0]);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
#include "common/defs.hpp"
|
#include "common/defs.hpp"
|
||||||
#include "common/file.hpp"
|
#include "common/file.hpp"
|
||||||
|
#include "common/gpu.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "main/app/app.hpp"
|
#include "main/app/app.hpp"
|
||||||
@ -62,7 +63,7 @@ static constexpr size_t _WORKER_STACK_SIZE = 0x20000;
|
|||||||
|
|
||||||
App::App(ui::Context &ctx, file::ZIPProvider &resourceProvider)
|
App::App(ui::Context &ctx, file::ZIPProvider &resourceProvider)
|
||||||
#ifdef ENABLE_LOG_BUFFER
|
#ifdef ENABLE_LOG_BUFFER
|
||||||
: _overlayLayer(_logBuffer),
|
: _logOverlay(_logBuffer),
|
||||||
#else
|
#else
|
||||||
:
|
:
|
||||||
#endif
|
#endif
|
||||||
@ -94,7 +95,7 @@ void App::_unloadCartData(void) {
|
|||||||
_cartDump.chipType = cart::NONE;
|
_cartDump.chipType = cart::NONE;
|
||||||
_cartDump.flags = 0;
|
_cartDump.flags = 0;
|
||||||
_cartDump.clearIdentifiers();
|
_cartDump.clearIdentifiers();
|
||||||
_cartDump.clearData();
|
util::clear(_cartDump.data);
|
||||||
|
|
||||||
_identified = nullptr;
|
_identified = nullptr;
|
||||||
//_selectedEntry = nullptr;
|
//_selectedEntry = nullptr;
|
||||||
@ -138,20 +139,44 @@ static const char *const _UI_SOUND_PATHS[ui::NUM_UI_SOUNDS]{
|
|||||||
"assets/sounds/moveright.vag", // ui::SOUND_MOVE_RIGHT
|
"assets/sounds/moveright.vag", // ui::SOUND_MOVE_RIGHT
|
||||||
"assets/sounds/enter.vag", // ui::SOUND_ENTER
|
"assets/sounds/enter.vag", // ui::SOUND_ENTER
|
||||||
"assets/sounds/exit.vag", // ui::SOUND_EXIT
|
"assets/sounds/exit.vag", // ui::SOUND_EXIT
|
||||||
"assets/sounds/click.vag" // ui::SOUND_CLICK
|
"assets/sounds/click.vag", // ui::SOUND_CLICK
|
||||||
|
"assets/sounds/screenshot.vag" // ui::SOUND_SCREENSHOT
|
||||||
};
|
};
|
||||||
|
|
||||||
void App::_loadResources(void) {
|
void App::_loadResources(void) {
|
||||||
_resourceProvider.loadTIM(_backgroundLayer.tile, "assets/textures/background.tim");
|
_resourceProvider.loadTIM(_background.tile, "assets/textures/background.tim");
|
||||||
_resourceProvider.loadTIM(_ctx.font.image, "assets/textures/font.tim");
|
_resourceProvider.loadTIM(_ctx.font.image, "assets/textures/font.tim");
|
||||||
_resourceProvider.loadStruct(_ctx.font.metrics, "assets/textures/font.metrics");
|
_resourceProvider.loadStruct(_ctx.font.metrics, "assets/textures/font.metrics");
|
||||||
_resourceProvider.loadStruct(_ctx.colors, "assets/app.palette");
|
_resourceProvider.loadStruct(_ctx.colors, "assets/app.palette");
|
||||||
_resourceProvider.loadData(_stringTable, "assets/app.strings");
|
_resourceProvider.loadData(_stringTable, "assets/app.strings");
|
||||||
|
|
||||||
for (int i = 0; i < ui::NUM_UI_SOUNDS; i++)
|
for (int i = 0; i < ui::NUM_UI_SOUNDS; i++)
|
||||||
_resourceProvider.loadVAG(_ctx.sounds[i], _UI_SOUND_PATHS[i]);
|
_resourceProvider.loadVAG(_ctx.sounds[i], _UI_SOUND_PATHS[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool App::_takeScreenshot(void) {
|
||||||
|
file::FileInfo info;
|
||||||
|
char path[32];
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
index++;
|
||||||
|
snprintf(path, sizeof(path), EXTERNAL_DATA_DIR "/shot%04d.bmp", index);
|
||||||
|
} while (_fileProvider.getFileInfo(info, path));
|
||||||
|
|
||||||
|
gpu::RectWH clip;
|
||||||
|
|
||||||
|
_ctx.gpuCtx.getVRAMClipRect(clip);
|
||||||
|
|
||||||
|
if (_fileProvider.saveVRAMBMP(clip, path)) {
|
||||||
|
LOG("%s saved", path);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG("%s saving failed", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void App::_worker(void) {
|
void App::_worker(void) {
|
||||||
if (_workerFunction) {
|
if (_workerFunction) {
|
||||||
(this->*_workerFunction)();
|
(this->*_workerFunction)();
|
||||||
@ -189,13 +214,18 @@ void App::_interruptHandler(void) {
|
|||||||
|
|
||||||
char dateString[24];
|
char dateString[24];
|
||||||
|
|
||||||
_backgroundLayer.leftText = dateString;
|
_textOverlay.leftText = dateString;
|
||||||
_backgroundLayer.rightText = "v" VERSION_STRING;
|
_textOverlay.rightText = "v" VERSION_STRING;
|
||||||
|
_screenshotOverlay.callback = [](ui::Context &ctx) -> bool {
|
||||||
|
return APP->_takeScreenshot();
|
||||||
|
};
|
||||||
|
|
||||||
_ctx.background = &_backgroundLayer;
|
_ctx.backgrounds[0] = &_background;
|
||||||
|
_ctx.backgrounds[1] = &_textOverlay;
|
||||||
#ifdef ENABLE_LOG_BUFFER
|
#ifdef ENABLE_LOG_BUFFER
|
||||||
_ctx.overlay = &_overlayLayer;
|
_ctx.overlays[0] = &_logOverlay;
|
||||||
#endif
|
#endif
|
||||||
|
_ctx.overlays[1] = &_screenshotOverlay;
|
||||||
_ctx.show(_workerStatusScreen);
|
_ctx.show(_workerStatusScreen);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -100,10 +100,12 @@ private:
|
|||||||
ChecksumScreen _checksumScreen;
|
ChecksumScreen _checksumScreen;
|
||||||
|
|
||||||
#ifdef ENABLE_LOG_BUFFER
|
#ifdef ENABLE_LOG_BUFFER
|
||||||
util::LogBuffer _logBuffer;
|
util::LogBuffer _logBuffer;
|
||||||
ui::LogOverlay _overlayLayer;
|
ui::LogOverlay _logOverlay;
|
||||||
#endif
|
#endif
|
||||||
ui::TiledBackground _backgroundLayer;
|
ui::TiledBackground _background;
|
||||||
|
ui::TextOverlay _textOverlay;
|
||||||
|
ui::ScreenshotOverlay _screenshotOverlay;
|
||||||
|
|
||||||
ui::Context &_ctx;
|
ui::Context &_ctx;
|
||||||
file::ZIPProvider &_resourceProvider;
|
file::ZIPProvider &_resourceProvider;
|
||||||
@ -129,6 +131,7 @@ private:
|
|||||||
void _setupWorker(bool (App::*func)(void));
|
void _setupWorker(bool (App::*func)(void));
|
||||||
void _setupInterrupts(void);
|
void _setupInterrupts(void);
|
||||||
void _loadResources(void);
|
void _loadResources(void);
|
||||||
|
bool _takeScreenshot(void);
|
||||||
|
|
||||||
// cartworkers.cpp
|
// cartworkers.cpp
|
||||||
bool _cartDetectWorker(void);
|
bool _cartDetectWorker(void);
|
||||||
|
@ -118,7 +118,7 @@ void CartActionsScreen::resetSystemID(ui::Context &ctx) {
|
|||||||
APP->_confirmScreen.setMessage(
|
APP->_confirmScreen.setMessage(
|
||||||
*this,
|
*this,
|
||||||
[](ui::Context &ctx) {
|
[](ui::Context &ctx) {
|
||||||
APP->_cartParser->getIdentifiers()->systemID.clear();
|
util::clear(APP->_cartParser->getIdentifiers()->systemID);
|
||||||
APP->_cartParser->flush();
|
APP->_cartParser->flush();
|
||||||
|
|
||||||
APP->_setupWorker(&App::_cartWriteWorker);
|
APP->_setupWorker(&App::_cartWriteWorker);
|
||||||
|
@ -278,18 +278,14 @@ void UnlockKeyScreen::useCustomKey(ui::Context &ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UnlockKeyScreen::use00Key(ui::Context &ctx) {
|
void UnlockKeyScreen::use00Key(ui::Context &ctx) {
|
||||||
__builtin_memset(
|
util::clear(APP->_cartDump.dataKey, 0x00);
|
||||||
APP->_cartDump.dataKey, 0x00, sizeof(APP->_cartDump.dataKey)
|
|
||||||
);
|
|
||||||
|
|
||||||
APP->_selectedEntry = nullptr;
|
APP->_selectedEntry = nullptr;
|
||||||
ctx.show(APP->_confirmScreen, false, true);
|
ctx.show(APP->_confirmScreen, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnlockKeyScreen::useFFKey(ui::Context &ctx) {
|
void UnlockKeyScreen::useFFKey(ui::Context &ctx) {
|
||||||
__builtin_memset(
|
util::clear(APP->_cartDump.dataKey, 0xff);
|
||||||
APP->_cartDump.dataKey, 0xff, sizeof(APP->_cartDump.dataKey)
|
|
||||||
);
|
|
||||||
|
|
||||||
APP->_selectedEntry = nullptr;
|
APP->_selectedEntry = nullptr;
|
||||||
ctx.show(APP->_confirmScreen, false, true);
|
ctx.show(APP->_confirmScreen, false, true);
|
||||||
|
@ -230,7 +230,7 @@ bool App::_cartDumpWorker(void) {
|
|||||||
do {
|
do {
|
||||||
index++;
|
index++;
|
||||||
snprintf(
|
snprintf(
|
||||||
path, sizeof(path), EXTERNAL_DATA_DIR "/cart%d.573", index
|
path, sizeof(path), EXTERNAL_DATA_DIR "/cart%04d.573", index
|
||||||
);
|
);
|
||||||
} while (_fileProvider.getFileInfo(info, path));
|
} while (_fileProvider.getFileInfo(info, path));
|
||||||
}
|
}
|
||||||
@ -389,7 +389,7 @@ bool App::_cartReflashWorker(void) {
|
|||||||
auto pri = _cartParser->getIdentifiers();
|
auto pri = _cartParser->getIdentifiers();
|
||||||
auto pub = _cartParser->getPublicIdentifiers();
|
auto pub = _cartParser->getPublicIdentifiers();
|
||||||
|
|
||||||
_cartDump.clearData();
|
util::clear(_cartDump.data);
|
||||||
_cartDump.initConfig(
|
_cartDump.initConfig(
|
||||||
9, _selectedEntry->flags & cart::DATA_HAS_PUBLIC_SECTION
|
9, _selectedEntry->flags & cart::DATA_HAS_PUBLIC_SECTION
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include "common/util.hpp"
|
#include "common/util.hpp"
|
||||||
#include "main/app/app.hpp"
|
#include "main/app/app.hpp"
|
||||||
#include "main/app/romactions.hpp"
|
#include "main/app/romactions.hpp"
|
||||||
#include "main/cartdata.hpp"
|
|
||||||
#include "main/uibase.hpp"
|
#include "main/uibase.hpp"
|
||||||
#include "main/uicommon.hpp"
|
#include "main/uicommon.hpp"
|
||||||
|
|
||||||
@ -264,7 +263,7 @@ void StorageActionsScreen::resetFlashHeader(ui::Context &ctx) {
|
|||||||
APP->_confirmScreen.setMessage(
|
APP->_confirmScreen.setMessage(
|
||||||
*this,
|
*this,
|
||||||
[](ui::Context &ctx) {
|
[](ui::Context &ctx) {
|
||||||
APP->_romHeaderDump.clearData();
|
util::clear(APP->_romHeaderDump.data);
|
||||||
APP->_setupWorker(&App::_flashHeaderWriteWorker);
|
APP->_setupWorker(&App::_flashHeaderWriteWorker);
|
||||||
ctx.show(APP->_workerStatusScreen, false, true);
|
ctx.show(APP->_workerStatusScreen, false, true);
|
||||||
},
|
},
|
||||||
|
@ -117,7 +117,7 @@ bool App::_romDumpWorker(void) {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
index++;
|
index++;
|
||||||
snprintf(dirPath, sizeof(dirPath), EXTERNAL_DATA_DIR "/dump%d", index);
|
snprintf(dirPath, sizeof(dirPath), EXTERNAL_DATA_DIR "/dump%04d", index);
|
||||||
} while (_fileProvider.getFileInfo(info, dirPath));
|
} while (_fileProvider.getFileInfo(info, dirPath));
|
||||||
|
|
||||||
LOG("saving dumps to %s", dirPath);
|
LOG("saving dumps to %s", dirPath);
|
||||||
|
@ -54,7 +54,7 @@ const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]{
|
|||||||
};
|
};
|
||||||
|
|
||||||
void CartDump::initConfig(uint8_t maxAttempts, bool hasPublicSection) {
|
void CartDump::initConfig(uint8_t maxAttempts, bool hasPublicSection) {
|
||||||
clearConfig();
|
util::clear(config);
|
||||||
|
|
||||||
switch (chipType) {
|
switch (chipType) {
|
||||||
case X76F041:
|
case X76F041:
|
||||||
@ -121,8 +121,8 @@ size_t CartDump::toQRString(char *output) const {
|
|||||||
size_t uncompLength = getDumpLength();
|
size_t uncompLength = getDumpLength();
|
||||||
size_t compLength = MAX_QR_STRING_LENGTH;
|
size_t compLength = MAX_QR_STRING_LENGTH;
|
||||||
|
|
||||||
__builtin_memset(compressed, 0, MAX_QR_STRING_LENGTH);
|
util::clear(compressed);
|
||||||
int error = mz_compress2(
|
auto error = mz_compress2(
|
||||||
compressed, reinterpret_cast<mz_ulong *>(&compLength),
|
compressed, reinterpret_cast<mz_ulong *>(&compLength),
|
||||||
reinterpret_cast<const uint8_t *>(this), uncompLength,
|
reinterpret_cast<const uint8_t *>(this), uncompLength,
|
||||||
MZ_BEST_COMPRESSION
|
MZ_BEST_COMPRESSION
|
||||||
|
@ -43,9 +43,6 @@ public:
|
|||||||
inline void copyTo(uint8_t *dest) const {
|
inline void copyTo(uint8_t *dest) const {
|
||||||
__builtin_memcpy(dest, data, sizeof(data));
|
__builtin_memcpy(dest, data, sizeof(data));
|
||||||
}
|
}
|
||||||
inline void clear(void) {
|
|
||||||
__builtin_memset(data, 0, sizeof(data));
|
|
||||||
}
|
|
||||||
inline bool isEmpty(void) const {
|
inline bool isEmpty(void) const {
|
||||||
return (util::sum(data, sizeof(data)) == 0);
|
return (util::sum(data, sizeof(data)) == 0);
|
||||||
}
|
}
|
||||||
@ -102,9 +99,9 @@ public:
|
|||||||
return (sizeof(CartDump) - sizeof(data)) + getChipSize().dataLength;
|
return (sizeof(CartDump) - sizeof(data)) + getChipSize().dataLength;
|
||||||
}
|
}
|
||||||
inline void clearIdentifiers(void) {
|
inline void clearIdentifiers(void) {
|
||||||
systemID.clear();
|
util::clear(systemID);
|
||||||
cartID.clear();
|
util::clear(cartID);
|
||||||
zsID.clear();
|
util::clear(zsID);
|
||||||
}
|
}
|
||||||
inline void copyDataFrom(const uint8_t *source) {
|
inline void copyDataFrom(const uint8_t *source) {
|
||||||
__builtin_memcpy(data, source, getChipSize().dataLength);
|
__builtin_memcpy(data, source, getChipSize().dataLength);
|
||||||
@ -112,27 +109,18 @@ public:
|
|||||||
inline void copyDataTo(uint8_t *dest) const {
|
inline void copyDataTo(uint8_t *dest) const {
|
||||||
__builtin_memcpy(dest, data, getChipSize().dataLength);
|
__builtin_memcpy(dest, data, getChipSize().dataLength);
|
||||||
}
|
}
|
||||||
inline void clearData(void) {
|
|
||||||
__builtin_memset(data, 0, sizeof(data));
|
|
||||||
}
|
|
||||||
inline void copyKeyFrom(const uint8_t *source) {
|
inline void copyKeyFrom(const uint8_t *source) {
|
||||||
__builtin_memcpy(dataKey, source, sizeof(dataKey));
|
__builtin_memcpy(dataKey, source, sizeof(dataKey));
|
||||||
}
|
}
|
||||||
inline void copyKeyTo(uint8_t *dest) const {
|
inline void copyKeyTo(uint8_t *dest) const {
|
||||||
__builtin_memcpy(dest, dataKey, sizeof(dataKey));
|
__builtin_memcpy(dest, dataKey, sizeof(dataKey));
|
||||||
}
|
}
|
||||||
inline void clearKey(void) {
|
|
||||||
__builtin_memset(dataKey, 0, sizeof(dataKey));
|
|
||||||
}
|
|
||||||
inline void copyConfigFrom(const uint8_t *source) {
|
inline void copyConfigFrom(const uint8_t *source) {
|
||||||
__builtin_memcpy(config, source, sizeof(config));
|
__builtin_memcpy(config, source, sizeof(config));
|
||||||
}
|
}
|
||||||
inline void copyConfigTo(uint8_t *dest) const {
|
inline void copyConfigTo(uint8_t *dest) const {
|
||||||
__builtin_memcpy(dest, config, sizeof(config));
|
__builtin_memcpy(dest, config, sizeof(config));
|
||||||
}
|
}
|
||||||
inline void clearConfig(void) {
|
|
||||||
__builtin_memset(config, 0, sizeof(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
void initConfig(uint8_t maxAttempts, bool hasPublicSection = false);
|
void initConfig(uint8_t maxAttempts, bool hasPublicSection = false);
|
||||||
bool isPublicDataEmpty(void) const;
|
bool isPublicDataEmpty(void) const;
|
||||||
@ -158,9 +146,6 @@ public:
|
|||||||
inline bool validateMagic(void) const {
|
inline bool validateMagic(void) const {
|
||||||
return (magic == ROM_HEADER_DUMP_HEADER_MAGIC);
|
return (magic == ROM_HEADER_DUMP_HEADER_MAGIC);
|
||||||
}
|
}
|
||||||
inline void clearData(void) {
|
|
||||||
__builtin_memset(data, 0xff, sizeof(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDataEmpty(void) const;
|
bool isDataEmpty(void) const;
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,7 @@ uint8_t IdentifierSet::getFlags(void) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IdentifierSet::setInstallID(uint8_t prefix) {
|
void IdentifierSet::setInstallID(uint8_t prefix) {
|
||||||
installID.clear();
|
util::clear(installID);
|
||||||
|
|
||||||
installID.data[0] = prefix;
|
installID.data[0] = prefix;
|
||||||
installID.updateChecksum();
|
installID.updateChecksum();
|
||||||
@ -34,7 +34,7 @@ void IdentifierSet::setInstallID(uint8_t prefix) {
|
|||||||
void IdentifierSet::updateTraceID(
|
void IdentifierSet::updateTraceID(
|
||||||
TraceIDType type, int param, const Identifier *_cartID
|
TraceIDType type, int param, const Identifier *_cartID
|
||||||
) {
|
) {
|
||||||
traceID.clear();
|
util::clear(traceID);
|
||||||
|
|
||||||
const uint8_t *input = _cartID ? &_cartID->data[1] : &cartID.data[1];
|
const uint8_t *input = _cartID ? &_cartID->data[1] : &cartID.data[1];
|
||||||
uint16_t checksum = 0;
|
uint16_t checksum = 0;
|
||||||
@ -93,7 +93,7 @@ uint8_t PublicIdentifierSet::getFlags(void) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PublicIdentifierSet::setInstallID(uint8_t prefix) {
|
void PublicIdentifierSet::setInstallID(uint8_t prefix) {
|
||||||
installID.clear();
|
util::clear(installID);
|
||||||
|
|
||||||
installID.data[0] = prefix;
|
installID.data[0] = prefix;
|
||||||
installID.updateChecksum();
|
installID.updateChecksum();
|
||||||
|
@ -57,10 +57,6 @@ class [[gnu::packed]] IdentifierSet {
|
|||||||
public:
|
public:
|
||||||
Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID
|
Identifier traceID, cartID, installID, systemID; // aka TID, SID, MID, XID
|
||||||
|
|
||||||
inline void clear(void) {
|
|
||||||
__builtin_memset(this, 0, sizeof(IdentifierSet));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t getFlags(void) const;
|
uint8_t getFlags(void) const;
|
||||||
void setInstallID(uint8_t prefix);
|
void setInstallID(uint8_t prefix);
|
||||||
void updateTraceID(
|
void updateTraceID(
|
||||||
@ -72,10 +68,6 @@ class [[gnu::packed]] PublicIdentifierSet {
|
|||||||
public:
|
public:
|
||||||
Identifier installID, systemID; // aka MID, XID
|
Identifier installID, systemID; // aka MID, XID
|
||||||
|
|
||||||
inline void clear(void) {
|
|
||||||
__builtin_memset(this, 0, sizeof(PublicIdentifierSet));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t getFlags(void) const;
|
uint8_t getFlags(void) const;
|
||||||
void setInstallID(uint8_t prefix);
|
void setInstallID(uint8_t prefix);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
|
#include "common/util.hpp"
|
||||||
#include "main/cart.hpp"
|
#include "main/cart.hpp"
|
||||||
#include "main/cartio.hpp"
|
#include "main/cartio.hpp"
|
||||||
#include "main/zs01.hpp"
|
#include "main/zs01.hpp"
|
||||||
@ -90,11 +91,11 @@ DriverError DummyDriver::erase(void) {
|
|||||||
if (!__builtin_memcmp(
|
if (!__builtin_memcmp(
|
||||||
_dump.dataKey, dummyDriverDump.dataKey, sizeof(_dump.dataKey)
|
_dump.dataKey, dummyDriverDump.dataKey, sizeof(_dump.dataKey)
|
||||||
)) {
|
)) {
|
||||||
dummyDriverDump.clearData();
|
util::clear(dummyDriverDump.data);
|
||||||
dummyDriverDump.clearKey();
|
util::clear(dummyDriverDump.dataKey);
|
||||||
// TODO: clear config registers as well
|
// TODO: clear config registers as well
|
||||||
|
|
||||||
_dump.clearKey();
|
util::clear(_dump.dataKey);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +277,7 @@ DriverError X76F041Driver::readPrivateData(void) {
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
_dump.clearConfig();
|
util::clear(_dump.config);
|
||||||
io::i2cReadBytes(_dump.config, 5);
|
io::i2cReadBytes(_dump.config, 5);
|
||||||
io::i2cStopWithCS();
|
io::i2cStopWithCS();
|
||||||
|
|
||||||
@ -328,7 +329,7 @@ DriverError X76F041Driver::erase(void) {
|
|||||||
|
|
||||||
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
||||||
|
|
||||||
_dump.clearKey();
|
util::clear(_dump.dataKey);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,7 +608,7 @@ DriverError ZS01Driver::erase(void) {
|
|||||||
|
|
||||||
key.unpackFrom(_dump.dataKey);
|
key.unpackFrom(_dump.dataKey);
|
||||||
|
|
||||||
__builtin_memset(request.data, 0, sizeof(request.data));
|
util::clear(request.data);
|
||||||
request.address = zs01::ADDR_ERASE;
|
request.address = zs01::ADDR_ERASE;
|
||||||
request.encodeWriteRequest(key, _encoderState);
|
request.encodeWriteRequest(key, _encoderState);
|
||||||
|
|
||||||
@ -615,7 +616,7 @@ DriverError ZS01Driver::erase(void) {
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
_dump.clearKey();
|
util::clear(_dump.dataKey);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "common/gpufont.hpp"
|
#include "common/gpufont.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/pad.hpp"
|
#include "common/pad.hpp"
|
||||||
|
#include "common/util.hpp"
|
||||||
#include "main/uibase.hpp"
|
#include "main/uibase.hpp"
|
||||||
#include "ps1/gpucmd.h"
|
#include "ps1/gpucmd.h"
|
||||||
|
|
||||||
@ -51,8 +52,9 @@ static const uint32_t _BUTTON_MAPPINGS[NUM_BUTTON_MAPS][NUM_BUTTONS]{
|
|||||||
};
|
};
|
||||||
|
|
||||||
ButtonState::ButtonState(void)
|
ButtonState::ButtonState(void)
|
||||||
: _held(0), _prevHeld(0), _pressed(0), _released(0), _repeating(0),
|
: _held(0), _prevHeld(0), _longHeld(0), _prevLongHeld(0), _pressed(0),
|
||||||
_repeatTimer(0), buttonMap(MAP_JOYSTICK) {}
|
_released(0), _longPressed(0), _longReleased(0), _repeatTimer(0),
|
||||||
|
buttonMap(MAP_JOYSTICK) {}
|
||||||
|
|
||||||
uint8_t ButtonState::_getHeld(void) const {
|
uint8_t ButtonState::_getHeld(void) const {
|
||||||
uint32_t inputs = io::getJAMMAInputs();
|
uint32_t inputs = io::getJAMMAInputs();
|
||||||
@ -94,25 +96,29 @@ uint8_t ButtonState::_getHeld(void) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ButtonState::reset(void) {
|
void ButtonState::reset(void) {
|
||||||
_held = _getHeld();
|
_held = _getHeld();
|
||||||
_prevHeld = _held;
|
_prevHeld = _held;
|
||||||
|
_longHeld = 0;
|
||||||
|
_prevLongHeld = 0;
|
||||||
|
|
||||||
_pressed = 0;
|
_pressed = 0;
|
||||||
_released = 0;
|
_released = 0;
|
||||||
_repeating = 0;
|
_longPressed = 0;
|
||||||
_repeatTimer = 0;
|
_longReleased = 0;
|
||||||
|
_repeatTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonState::update(void) {
|
void ButtonState::update(void) {
|
||||||
_prevHeld = _held;
|
_prevHeld = _held;
|
||||||
_held = _getHeld();
|
_prevLongHeld = _longHeld;
|
||||||
|
_held = _getHeld();
|
||||||
|
|
||||||
uint32_t changed = _prevHeld ^ _held;
|
uint32_t changed = _prevHeld ^ _held;
|
||||||
|
|
||||||
if (buttonMap == MAP_SINGLE_BUTTON) {
|
if (buttonMap == MAP_SINGLE_BUTTON) {
|
||||||
_pressed = 0;
|
_pressed = 0;
|
||||||
_released = 0;
|
_released = 0;
|
||||||
_repeating = 0;
|
_longHeld = 0;
|
||||||
|
|
||||||
// In single-button mode, interpret a short button press as the right
|
// In single-button mode, interpret a short button press as the right
|
||||||
// button and a long press as start.
|
// button and a long press as start.
|
||||||
@ -135,19 +141,24 @@ void ButtonState::update(void) {
|
|||||||
else if (_held)
|
else if (_held)
|
||||||
_repeatTimer++;
|
_repeatTimer++;
|
||||||
|
|
||||||
_pressed = (changed & _held) & ~_pressed;
|
_pressed = (changed & _held) & ~_pressed;
|
||||||
_released = (changed & _prevHeld) & ~_released;
|
_released = (changed & _prevHeld) & ~_released;
|
||||||
_repeating = (_repeatTimer >= REPEAT_DELAY) ? _held : 0;
|
_longHeld = (_repeatTimer >= REPEAT_DELAY) ? _held : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changed = _prevLongHeld ^ _longHeld;
|
||||||
|
|
||||||
|
_longPressed = (changed & _longHeld) & ~_longPressed;
|
||||||
|
_longReleased = (changed & _prevLongHeld) & ~_longReleased;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UI context */
|
/* UI context */
|
||||||
|
|
||||||
Context::Context(gpu::Context &gpuCtx, void *screenData)
|
Context::Context(gpu::Context &gpuCtx, void *screenData)
|
||||||
: _currentScreen(0), gpuCtx(gpuCtx), background(nullptr), overlay(nullptr),
|
: _currentScreen(0), gpuCtx(gpuCtx), time(0), screenData(screenData) {
|
||||||
time(0), screenData(screenData) {
|
util::clear(_screens);
|
||||||
_screens[0] = nullptr;
|
util::clear(backgrounds);
|
||||||
_screens[1] = nullptr;
|
util::clear(overlays);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::show(Screen &screen, bool goBack, bool playSound) {
|
void Context::show(Screen &screen, bool goBack, bool playSound) {
|
||||||
@ -169,21 +180,30 @@ void Context::draw(void) {
|
|||||||
auto oldScreen = _screens[_currentScreen ^ 1];
|
auto oldScreen = _screens[_currentScreen ^ 1];
|
||||||
auto newScreen = _screens[_currentScreen];
|
auto newScreen = _screens[_currentScreen];
|
||||||
|
|
||||||
if (background)
|
for (auto layer : backgrounds) {
|
||||||
background->draw(*this);
|
if (layer)
|
||||||
|
layer->draw(*this);
|
||||||
|
}
|
||||||
|
|
||||||
if (oldScreen)
|
if (oldScreen)
|
||||||
oldScreen->draw(*this, false);
|
oldScreen->draw(*this, false);
|
||||||
if (newScreen)
|
if (newScreen)
|
||||||
newScreen->draw(*this, true);
|
newScreen->draw(*this, true);
|
||||||
if (overlay)
|
|
||||||
overlay->draw(*this);
|
for (auto layer : overlays) {
|
||||||
|
if (layer)
|
||||||
|
layer->draw(*this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::update(void) {
|
void Context::update(void) {
|
||||||
buttons.update();
|
buttons.update();
|
||||||
|
|
||||||
if (overlay)
|
for (auto layer : overlays) {
|
||||||
overlay->update(*this);
|
if (layer)
|
||||||
|
layer->update(*this);
|
||||||
|
}
|
||||||
|
|
||||||
if (_screens[_currentScreen])
|
if (_screens[_currentScreen])
|
||||||
_screens[_currentScreen]->update(*this);
|
_screens[_currentScreen]->update(*this);
|
||||||
}
|
}
|
||||||
@ -215,7 +235,9 @@ void TiledBackground::draw(Context &ctx, bool active) const {
|
|||||||
for (int y = -offsetY; y < ctx.gpuCtx.height; y += tile.height)
|
for (int y = -offsetY; y < ctx.gpuCtx.height; y += tile.height)
|
||||||
tile.draw(ctx.gpuCtx, x, y);
|
tile.draw(ctx.gpuCtx, x, y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextOverlay::draw(Context &ctx, bool active) const {
|
||||||
gpu::RectWH rect;
|
gpu::RectWH rect;
|
||||||
|
|
||||||
rect.y = ctx.gpuCtx.height - (8 + ctx.font.metrics.lineHeight);
|
rect.y = ctx.gpuCtx.height - (8 + ctx.font.metrics.lineHeight);
|
||||||
@ -272,7 +294,9 @@ void LogOverlay::draw(Context &ctx, bool active) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LogOverlay::update(Context &ctx) {
|
void LogOverlay::update(Context &ctx) {
|
||||||
if (ctx.buttons.pressed(BTN_DEBUG)) {
|
if (
|
||||||
|
ctx.buttons.released(BTN_DEBUG) && !ctx.buttons.longReleased(BTN_DEBUG)
|
||||||
|
) {
|
||||||
bool shown = !_slideAnim.getTargetValue();
|
bool shown = !_slideAnim.getTargetValue();
|
||||||
|
|
||||||
_slideAnim.setValue(ctx.time, shown ? ctx.gpuCtx.height : 0, SPEED_SLOW);
|
_slideAnim.setValue(ctx.time, shown ? ctx.gpuCtx.height : 0, SPEED_SLOW);
|
||||||
@ -280,6 +304,27 @@ void LogOverlay::update(Context &ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScreenshotOverlay::draw(Context &ctx, bool active) const {
|
||||||
|
int brightness = _flashAnim.getValue(ctx.time);
|
||||||
|
|
||||||
|
if (!brightness)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
|
||||||
|
ctx.gpuCtx.drawBackdrop(
|
||||||
|
gp0_rgb(brightness, brightness, brightness), GP0_BLEND_ADD
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotOverlay::update(Context &ctx) {
|
||||||
|
if (ctx.buttons.longPressed(BTN_DEBUG)) {
|
||||||
|
if (callback(ctx)) {
|
||||||
|
_flashAnim.setValue(ctx.time, 0xff, 0, SPEED_SLOW);
|
||||||
|
ctx.sounds[ui::SOUND_SCREENSHOT].play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Base screen classes */
|
/* Base screen classes */
|
||||||
|
|
||||||
void AnimatedScreen::_newLayer(
|
void AnimatedScreen::_newLayer(
|
||||||
@ -308,6 +353,7 @@ void BackdropScreen::hide(Context &ctx, bool goBack) {
|
|||||||
|
|
||||||
void BackdropScreen::draw(Context &ctx, bool active) const {
|
void BackdropScreen::draw(Context &ctx, bool active) const {
|
||||||
int brightness = _backdropAnim.getValue(ctx.time);
|
int brightness = _backdropAnim.getValue(ctx.time);
|
||||||
|
|
||||||
if (!brightness)
|
if (!brightness)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace ui {
|
|||||||
/* Public constants */
|
/* Public constants */
|
||||||
|
|
||||||
static constexpr int NUM_UI_COLORS = 18;
|
static constexpr int NUM_UI_COLORS = 18;
|
||||||
static constexpr int NUM_UI_SOUNDS = 9;
|
static constexpr int NUM_UI_SOUNDS = 10;
|
||||||
|
|
||||||
enum Color {
|
enum Color {
|
||||||
COLOR_DEFAULT = 0,
|
COLOR_DEFAULT = 0,
|
||||||
@ -44,7 +44,8 @@ enum Sound {
|
|||||||
SOUND_MOVE_RIGHT = 5,
|
SOUND_MOVE_RIGHT = 5,
|
||||||
SOUND_ENTER = 6,
|
SOUND_ENTER = 6,
|
||||||
SOUND_EXIT = 7,
|
SOUND_EXIT = 7,
|
||||||
SOUND_CLICK = 8
|
SOUND_CLICK = 8,
|
||||||
|
SOUND_SCREENSHOT = 9
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AnimationSpeed {
|
enum AnimationSpeed {
|
||||||
@ -106,7 +107,9 @@ class ButtonState {
|
|||||||
private:
|
private:
|
||||||
uint32_t _mappings[NUM_BUTTONS];
|
uint32_t _mappings[NUM_BUTTONS];
|
||||||
uint8_t _held, _prevHeld;
|
uint8_t _held, _prevHeld;
|
||||||
uint8_t _pressed, _released, _repeating;
|
uint8_t _longHeld, _prevLongHeld;
|
||||||
|
uint8_t _pressed, _released;
|
||||||
|
uint8_t _longPressed, _longReleased;
|
||||||
|
|
||||||
int _repeatTimer;
|
int _repeatTimer;
|
||||||
|
|
||||||
@ -115,20 +118,24 @@ private:
|
|||||||
public:
|
public:
|
||||||
ButtonMap buttonMap;
|
ButtonMap buttonMap;
|
||||||
|
|
||||||
inline bool pressed(Button button) const {
|
inline bool held(Button button) const {
|
||||||
return _pressed & (1 << button);
|
return (_held >> button) & 1;
|
||||||
}
|
}
|
||||||
inline bool pressedRepeating(Button button) const {
|
inline bool pressed(Button button) const {
|
||||||
return (_pressed | _repeating) & (1 << button);
|
return (_pressed >> button) & 1;
|
||||||
}
|
}
|
||||||
inline bool released(Button button) const {
|
inline bool released(Button button) const {
|
||||||
return _released & (1 << button);
|
return (_released >> button) & 1;
|
||||||
}
|
}
|
||||||
inline bool repeating(Button button) const {
|
|
||||||
return _repeating & (1 << button);
|
inline bool longHeld(Button button) const {
|
||||||
|
return (_longHeld >> button) & 1;
|
||||||
}
|
}
|
||||||
inline bool held(Button button) const {
|
inline bool longPressed(Button button) const {
|
||||||
return _held & (1 << button);
|
return (_longPressed >> button) & 1;
|
||||||
|
}
|
||||||
|
inline bool longReleased(Button button) const {
|
||||||
|
return (_longReleased >> button) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonState(void);
|
ButtonState(void);
|
||||||
@ -148,7 +155,8 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
gpu::Context &gpuCtx;
|
gpu::Context &gpuCtx;
|
||||||
Layer *background, *overlay;
|
|
||||||
|
Layer *backgrounds[4], *overlays[4];
|
||||||
|
|
||||||
gpu::Font font;
|
gpu::Font font;
|
||||||
gpu::Color colors[NUM_UI_COLORS];
|
gpu::Color colors[NUM_UI_COLORS];
|
||||||
@ -186,9 +194,15 @@ public:
|
|||||||
class TiledBackground : public Layer {
|
class TiledBackground : public Layer {
|
||||||
public:
|
public:
|
||||||
gpu::Image tile;
|
gpu::Image tile;
|
||||||
|
|
||||||
|
void draw(Context &ctx, bool active = true) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextOverlay : public Layer {
|
||||||
|
public:
|
||||||
const char *leftText, *rightText;
|
const char *leftText, *rightText;
|
||||||
|
|
||||||
inline TiledBackground(void)
|
inline TextOverlay(void)
|
||||||
: leftText(nullptr), rightText(nullptr) {}
|
: leftText(nullptr), rightText(nullptr) {}
|
||||||
|
|
||||||
void draw(Context &ctx, bool active = true) const;
|
void draw(Context &ctx, bool active = true) const;
|
||||||
@ -205,6 +219,20 @@ public:
|
|||||||
void update(Context &ctx);
|
void update(Context &ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ScreenshotOverlay : public Layer {
|
||||||
|
private:
|
||||||
|
util::Tween<int, util::QuadOutEasing> _flashAnim;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool (*callback)(ui::Context &ctx);
|
||||||
|
|
||||||
|
inline ScreenshotOverlay(void)
|
||||||
|
: callback(nullptr) {}
|
||||||
|
|
||||||
|
void draw(Context &ctx, bool active = true) const;
|
||||||
|
void update(Context &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
/* Base screen classes */
|
/* Base screen classes */
|
||||||
|
|
||||||
// This is probably the most stripped-down way to implement something that
|
// This is probably the most stripped-down way to implement something that
|
||||||
|
@ -84,7 +84,7 @@ void TextScreen::update(Context &ctx) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (value > 0))
|
(ctx.buttons.longHeld(ui::BTN_LEFT) && (value > 0))
|
||||||
) {
|
) {
|
||||||
if (value <= 0) {
|
if (value <= 0) {
|
||||||
value = scrollHeight;
|
value = scrollHeight;
|
||||||
@ -98,7 +98,7 @@ void TextScreen::update(Context &ctx) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (value < scrollHeight))
|
(ctx.buttons.longHeld(ui::BTN_RIGHT) && (value < scrollHeight))
|
||||||
) {
|
) {
|
||||||
if (value >= scrollHeight) {
|
if (value >= scrollHeight) {
|
||||||
value = 0;
|
value = 0;
|
||||||
@ -281,7 +281,7 @@ void ListScreen::draw(Context &ctx, bool active) const {
|
|||||||
void ListScreen::update(Context &ctx) {
|
void ListScreen::update(Context &ctx) {
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeItem > 0))
|
(ctx.buttons.longHeld(ui::BTN_LEFT) && (_activeItem > 0))
|
||||||
) {
|
) {
|
||||||
_activeItem--;
|
_activeItem--;
|
||||||
if (_activeItem < 0) {
|
if (_activeItem < 0) {
|
||||||
@ -296,7 +296,7 @@ void ListScreen::update(Context &ctx) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (_activeItem < (_listLength - 1)))
|
(ctx.buttons.longHeld(ui::BTN_RIGHT) && (_activeItem < (_listLength - 1)))
|
||||||
) {
|
) {
|
||||||
_activeItem++;
|
_activeItem++;
|
||||||
if (_activeItem >= _listLength) {
|
if (_activeItem >= _listLength) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "common/gpu.hpp"
|
#include "common/gpu.hpp"
|
||||||
|
#include "common/util.hpp"
|
||||||
#include "main/uibase.hpp"
|
#include "main/uibase.hpp"
|
||||||
#include "main/uimodals.hpp"
|
#include "main/uimodals.hpp"
|
||||||
|
|
||||||
@ -82,7 +83,7 @@ void MessageBoxScreen::update(Context &ctx) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeButton > 0))
|
(ctx.buttons.longHeld(ui::BTN_LEFT) && (_activeButton > 0))
|
||||||
) {
|
) {
|
||||||
_activeButton--;
|
_activeButton--;
|
||||||
if (_activeButton < 0) {
|
if (_activeButton < 0) {
|
||||||
@ -96,7 +97,7 @@ void MessageBoxScreen::update(Context &ctx) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (_activeButton < (numButtons - 1)))
|
(ctx.buttons.longHeld(ui::BTN_RIGHT) && (_activeButton < (numButtons - 1)))
|
||||||
) {
|
) {
|
||||||
_activeButton++;
|
_activeButton++;
|
||||||
if (_activeButton >= numButtons) {
|
if (_activeButton >= numButtons) {
|
||||||
@ -112,13 +113,13 @@ void MessageBoxScreen::update(Context &ctx) {
|
|||||||
|
|
||||||
HexEntryScreen::HexEntryScreen(void)
|
HexEntryScreen::HexEntryScreen(void)
|
||||||
: _bufferLength(0) {
|
: _bufferLength(0) {
|
||||||
__builtin_memset(_buffer, 0, sizeof(_buffer));
|
util::clear(_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HexEntryScreen::show(Context &ctx, bool goBack) {
|
void HexEntryScreen::show(Context &ctx, bool goBack) {
|
||||||
MessageBoxScreen::show(ctx, goBack);
|
MessageBoxScreen::show(ctx, goBack);
|
||||||
|
|
||||||
//__builtin_memset(_buffer, 0, _bufferLength);
|
//util::clear(_buffer);
|
||||||
|
|
||||||
_buttonIndexOffset = _bufferLength * 2;
|
_buttonIndexOffset = _bufferLength * 2;
|
||||||
_charWidth = ctx.font.getCharacterWidth('0');
|
_charWidth = ctx.font.getCharacterWidth('0');
|
||||||
@ -187,7 +188,7 @@ void HexEntryScreen::update(Context &ctx) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (value > 0))
|
(ctx.buttons.longHeld(ui::BTN_LEFT) && (value > 0))
|
||||||
) {
|
) {
|
||||||
if (--value < 0) {
|
if (--value < 0) {
|
||||||
value = 0xf;
|
value = 0xf;
|
||||||
@ -198,7 +199,7 @@ void HexEntryScreen::update(Context &ctx) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (value < 0xf))
|
(ctx.buttons.longHeld(ui::BTN_RIGHT) && (value < 0xf))
|
||||||
) {
|
) {
|
||||||
if (++value > 0xf) {
|
if (++value > 0xf) {
|
||||||
value = 0;
|
value = 0;
|
||||||
@ -365,7 +366,7 @@ void DateEntryScreen::update(Context &ctx) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (value > field.minValue))
|
(ctx.buttons.longHeld(ui::BTN_LEFT) && (value > field.minValue))
|
||||||
) {
|
) {
|
||||||
if (--value < field.minValue) {
|
if (--value < field.minValue) {
|
||||||
value = field.maxValue;
|
value = field.maxValue;
|
||||||
@ -376,7 +377,7 @@ void DateEntryScreen::update(Context &ctx) {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (value < field.maxValue))
|
(ctx.buttons.longHeld(ui::BTN_RIGHT) && (value < field.maxValue))
|
||||||
) {
|
) {
|
||||||
if (++value > field.maxValue) {
|
if (++value > field.maxValue) {
|
||||||
value = field.minValue;
|
value = field.minValue;
|
||||||
|
@ -64,9 +64,6 @@ public:
|
|||||||
inline void copyTo(uint8_t *dest) const {
|
inline void copyTo(uint8_t *dest) const {
|
||||||
__builtin_memcpy(dest, data, sizeof(data));
|
__builtin_memcpy(dest, data, sizeof(data));
|
||||||
}
|
}
|
||||||
inline void clear(void) {
|
|
||||||
__builtin_memset(data, 0, sizeof(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCRC(void);
|
void updateCRC(void);
|
||||||
bool validateCRC(void) const;
|
bool validateCRC(void) const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user