mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 11:43:39 +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",
|
||||
"compression": "none"
|
||||
},
|
||||
{
|
||||
"type": "binary",
|
||||
"name": "assets/sounds/screenshot.vag",
|
||||
"source": "${PROJECT_SOURCE_DIR}/assets/sounds/screenshot.vag",
|
||||
"compression": "none"
|
||||
},
|
||||
{
|
||||
"type": "palette",
|
||||
"name": "assets/app.palette",
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common/file.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/pcdrv.h"
|
||||
#include "vendor/ff.h"
|
||||
#include "vendor/miniz.h"
|
||||
@ -256,6 +258,8 @@ size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO: these *really* belong somewhere else
|
||||
|
||||
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
||||
util::Data data;
|
||||
|
||||
@ -308,6 +312,89 @@ size_t Provider::loadVAG(spu::Sound &output, const char *path) {
|
||||
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) {
|
||||
int error = pcdrvInit();
|
||||
|
||||
|
@ -148,8 +148,10 @@ public:
|
||||
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 saveData(const void *input, size_t length, const char *path);
|
||||
|
||||
size_t loadTIM(gpu::Image &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 {
|
||||
|
@ -54,6 +54,44 @@ size_t upload(const RectWH &rect, const void *data, bool wait) {
|
||||
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 */
|
||||
|
||||
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 download(const RectWH &rect, void *data, bool wait);
|
||||
|
||||
/* Rendering context */
|
||||
|
||||
@ -93,6 +94,15 @@ public:
|
||||
) : _lastTexpage(0) {
|
||||
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) {
|
||||
newLayer(x, y, width, height);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/registers573.h"
|
||||
|
||||
namespace ide {
|
||||
@ -241,17 +242,14 @@ public:
|
||||
uint8_t param[11];
|
||||
uint8_t _reserved[4];
|
||||
|
||||
inline void clear(void) {
|
||||
__builtin_memset(this, 0, sizeof(Packet));
|
||||
}
|
||||
inline void setStartStopUnit(ATAPIStartStopMode mode) {
|
||||
clear();
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_START_STOP_UNIT;
|
||||
param[3] = mode & 3;
|
||||
}
|
||||
inline void setRead(uint32_t lba, size_t count) {
|
||||
clear();
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_READ12;
|
||||
param[1] = (lba >> 24) & 0xff;
|
||||
@ -264,7 +262,7 @@ public:
|
||||
param[8] = (count >> 0) & 0xff;
|
||||
}
|
||||
inline void setSetCDSpeed(uint16_t value) {
|
||||
clear();
|
||||
util::clear(*this);
|
||||
|
||||
command = ATAPI_SET_CD_SPEED;
|
||||
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)));
|
||||
}
|
||||
|
||||
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) {
|
||||
return sizeof(array) / sizeof(array[0]);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
#include "common/defs.hpp"
|
||||
#include "common/file.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/util.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)
|
||||
#ifdef ENABLE_LOG_BUFFER
|
||||
: _overlayLayer(_logBuffer),
|
||||
: _logOverlay(_logBuffer),
|
||||
#else
|
||||
:
|
||||
#endif
|
||||
@ -94,7 +95,7 @@ void App::_unloadCartData(void) {
|
||||
_cartDump.chipType = cart::NONE;
|
||||
_cartDump.flags = 0;
|
||||
_cartDump.clearIdentifiers();
|
||||
_cartDump.clearData();
|
||||
util::clear(_cartDump.data);
|
||||
|
||||
_identified = 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/enter.vag", // ui::SOUND_ENTER
|
||||
"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) {
|
||||
_resourceProvider.loadTIM(_backgroundLayer.tile, "assets/textures/background.tim");
|
||||
_resourceProvider.loadTIM(_ctx.font.image, "assets/textures/font.tim");
|
||||
_resourceProvider.loadStruct(_ctx.font.metrics, "assets/textures/font.metrics");
|
||||
_resourceProvider.loadStruct(_ctx.colors, "assets/app.palette");
|
||||
_resourceProvider.loadData(_stringTable, "assets/app.strings");
|
||||
_resourceProvider.loadTIM(_background.tile, "assets/textures/background.tim");
|
||||
_resourceProvider.loadTIM(_ctx.font.image, "assets/textures/font.tim");
|
||||
_resourceProvider.loadStruct(_ctx.font.metrics, "assets/textures/font.metrics");
|
||||
_resourceProvider.loadStruct(_ctx.colors, "assets/app.palette");
|
||||
_resourceProvider.loadData(_stringTable, "assets/app.strings");
|
||||
|
||||
for (int i = 0; i < ui::NUM_UI_SOUNDS; 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) {
|
||||
if (_workerFunction) {
|
||||
(this->*_workerFunction)();
|
||||
@ -189,13 +214,18 @@ void App::_interruptHandler(void) {
|
||||
|
||||
char dateString[24];
|
||||
|
||||
_backgroundLayer.leftText = dateString;
|
||||
_backgroundLayer.rightText = "v" VERSION_STRING;
|
||||
_textOverlay.leftText = dateString;
|
||||
_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
|
||||
_ctx.overlay = &_overlayLayer;
|
||||
_ctx.overlays[0] = &_logOverlay;
|
||||
#endif
|
||||
_ctx.overlays[1] = &_screenshotOverlay;
|
||||
_ctx.show(_workerStatusScreen);
|
||||
|
||||
for (;;) {
|
||||
|
@ -100,10 +100,12 @@ private:
|
||||
ChecksumScreen _checksumScreen;
|
||||
|
||||
#ifdef ENABLE_LOG_BUFFER
|
||||
util::LogBuffer _logBuffer;
|
||||
ui::LogOverlay _overlayLayer;
|
||||
util::LogBuffer _logBuffer;
|
||||
ui::LogOverlay _logOverlay;
|
||||
#endif
|
||||
ui::TiledBackground _backgroundLayer;
|
||||
ui::TiledBackground _background;
|
||||
ui::TextOverlay _textOverlay;
|
||||
ui::ScreenshotOverlay _screenshotOverlay;
|
||||
|
||||
ui::Context &_ctx;
|
||||
file::ZIPProvider &_resourceProvider;
|
||||
@ -129,6 +131,7 @@ private:
|
||||
void _setupWorker(bool (App::*func)(void));
|
||||
void _setupInterrupts(void);
|
||||
void _loadResources(void);
|
||||
bool _takeScreenshot(void);
|
||||
|
||||
// cartworkers.cpp
|
||||
bool _cartDetectWorker(void);
|
||||
|
@ -118,7 +118,7 @@ void CartActionsScreen::resetSystemID(ui::Context &ctx) {
|
||||
APP->_confirmScreen.setMessage(
|
||||
*this,
|
||||
[](ui::Context &ctx) {
|
||||
APP->_cartParser->getIdentifiers()->systemID.clear();
|
||||
util::clear(APP->_cartParser->getIdentifiers()->systemID);
|
||||
APP->_cartParser->flush();
|
||||
|
||||
APP->_setupWorker(&App::_cartWriteWorker);
|
||||
|
@ -278,18 +278,14 @@ void UnlockKeyScreen::useCustomKey(ui::Context &ctx) {
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::use00Key(ui::Context &ctx) {
|
||||
__builtin_memset(
|
||||
APP->_cartDump.dataKey, 0x00, sizeof(APP->_cartDump.dataKey)
|
||||
);
|
||||
util::clear(APP->_cartDump.dataKey, 0x00);
|
||||
|
||||
APP->_selectedEntry = nullptr;
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::useFFKey(ui::Context &ctx) {
|
||||
__builtin_memset(
|
||||
APP->_cartDump.dataKey, 0xff, sizeof(APP->_cartDump.dataKey)
|
||||
);
|
||||
util::clear(APP->_cartDump.dataKey, 0xff);
|
||||
|
||||
APP->_selectedEntry = nullptr;
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
|
@ -230,7 +230,7 @@ bool App::_cartDumpWorker(void) {
|
||||
do {
|
||||
index++;
|
||||
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));
|
||||
}
|
||||
@ -389,7 +389,7 @@ bool App::_cartReflashWorker(void) {
|
||||
auto pri = _cartParser->getIdentifiers();
|
||||
auto pub = _cartParser->getPublicIdentifiers();
|
||||
|
||||
_cartDump.clearData();
|
||||
util::clear(_cartDump.data);
|
||||
_cartDump.initConfig(
|
||||
9, _selectedEntry->flags & cart::DATA_HAS_PUBLIC_SECTION
|
||||
);
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include "common/util.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
#include "main/app/romactions.hpp"
|
||||
#include "main/cartdata.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
|
||||
@ -264,7 +263,7 @@ void StorageActionsScreen::resetFlashHeader(ui::Context &ctx) {
|
||||
APP->_confirmScreen.setMessage(
|
||||
*this,
|
||||
[](ui::Context &ctx) {
|
||||
APP->_romHeaderDump.clearData();
|
||||
util::clear(APP->_romHeaderDump.data);
|
||||
APP->_setupWorker(&App::_flashHeaderWriteWorker);
|
||||
ctx.show(APP->_workerStatusScreen, false, true);
|
||||
},
|
||||
|
@ -117,7 +117,7 @@ bool App::_romDumpWorker(void) {
|
||||
|
||||
do {
|
||||
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));
|
||||
|
||||
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) {
|
||||
clearConfig();
|
||||
util::clear(config);
|
||||
|
||||
switch (chipType) {
|
||||
case X76F041:
|
||||
@ -121,8 +121,8 @@ size_t CartDump::toQRString(char *output) const {
|
||||
size_t uncompLength = getDumpLength();
|
||||
size_t compLength = MAX_QR_STRING_LENGTH;
|
||||
|
||||
__builtin_memset(compressed, 0, MAX_QR_STRING_LENGTH);
|
||||
int error = mz_compress2(
|
||||
util::clear(compressed);
|
||||
auto error = mz_compress2(
|
||||
compressed, reinterpret_cast<mz_ulong *>(&compLength),
|
||||
reinterpret_cast<const uint8_t *>(this), uncompLength,
|
||||
MZ_BEST_COMPRESSION
|
||||
|
@ -43,9 +43,6 @@ public:
|
||||
inline void copyTo(uint8_t *dest) const {
|
||||
__builtin_memcpy(dest, data, sizeof(data));
|
||||
}
|
||||
inline void clear(void) {
|
||||
__builtin_memset(data, 0, sizeof(data));
|
||||
}
|
||||
inline bool isEmpty(void) const {
|
||||
return (util::sum(data, sizeof(data)) == 0);
|
||||
}
|
||||
@ -102,9 +99,9 @@ public:
|
||||
return (sizeof(CartDump) - sizeof(data)) + getChipSize().dataLength;
|
||||
}
|
||||
inline void clearIdentifiers(void) {
|
||||
systemID.clear();
|
||||
cartID.clear();
|
||||
zsID.clear();
|
||||
util::clear(systemID);
|
||||
util::clear(cartID);
|
||||
util::clear(zsID);
|
||||
}
|
||||
inline void copyDataFrom(const uint8_t *source) {
|
||||
__builtin_memcpy(data, source, getChipSize().dataLength);
|
||||
@ -112,27 +109,18 @@ public:
|
||||
inline void copyDataTo(uint8_t *dest) const {
|
||||
__builtin_memcpy(dest, data, getChipSize().dataLength);
|
||||
}
|
||||
inline void clearData(void) {
|
||||
__builtin_memset(data, 0, sizeof(data));
|
||||
}
|
||||
inline void copyKeyFrom(const uint8_t *source) {
|
||||
__builtin_memcpy(dataKey, source, sizeof(dataKey));
|
||||
}
|
||||
inline void copyKeyTo(uint8_t *dest) const {
|
||||
__builtin_memcpy(dest, dataKey, sizeof(dataKey));
|
||||
}
|
||||
inline void clearKey(void) {
|
||||
__builtin_memset(dataKey, 0, sizeof(dataKey));
|
||||
}
|
||||
inline void copyConfigFrom(const uint8_t *source) {
|
||||
__builtin_memcpy(config, source, sizeof(config));
|
||||
}
|
||||
inline void copyConfigTo(uint8_t *dest) const {
|
||||
__builtin_memcpy(dest, config, sizeof(config));
|
||||
}
|
||||
inline void clearConfig(void) {
|
||||
__builtin_memset(config, 0, sizeof(config));
|
||||
}
|
||||
|
||||
void initConfig(uint8_t maxAttempts, bool hasPublicSection = false);
|
||||
bool isPublicDataEmpty(void) const;
|
||||
@ -158,9 +146,6 @@ public:
|
||||
inline bool validateMagic(void) const {
|
||||
return (magic == ROM_HEADER_DUMP_HEADER_MAGIC);
|
||||
}
|
||||
inline void clearData(void) {
|
||||
__builtin_memset(data, 0xff, sizeof(data));
|
||||
}
|
||||
|
||||
bool isDataEmpty(void) const;
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ uint8_t IdentifierSet::getFlags(void) const {
|
||||
}
|
||||
|
||||
void IdentifierSet::setInstallID(uint8_t prefix) {
|
||||
installID.clear();
|
||||
util::clear(installID);
|
||||
|
||||
installID.data[0] = prefix;
|
||||
installID.updateChecksum();
|
||||
@ -34,7 +34,7 @@ void IdentifierSet::setInstallID(uint8_t prefix) {
|
||||
void IdentifierSet::updateTraceID(
|
||||
TraceIDType type, int param, const Identifier *_cartID
|
||||
) {
|
||||
traceID.clear();
|
||||
util::clear(traceID);
|
||||
|
||||
const uint8_t *input = _cartID ? &_cartID->data[1] : &cartID.data[1];
|
||||
uint16_t checksum = 0;
|
||||
@ -93,7 +93,7 @@ uint8_t PublicIdentifierSet::getFlags(void) const {
|
||||
}
|
||||
|
||||
void PublicIdentifierSet::setInstallID(uint8_t prefix) {
|
||||
installID.clear();
|
||||
util::clear(installID);
|
||||
|
||||
installID.data[0] = prefix;
|
||||
installID.updateChecksum();
|
||||
|
@ -57,10 +57,6 @@ class [[gnu::packed]] IdentifierSet {
|
||||
public:
|
||||
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;
|
||||
void setInstallID(uint8_t prefix);
|
||||
void updateTraceID(
|
||||
@ -72,10 +68,6 @@ class [[gnu::packed]] PublicIdentifierSet {
|
||||
public:
|
||||
Identifier installID, systemID; // aka MID, XID
|
||||
|
||||
inline void clear(void) {
|
||||
__builtin_memset(this, 0, sizeof(PublicIdentifierSet));
|
||||
}
|
||||
|
||||
uint8_t getFlags(void) const;
|
||||
void setInstallID(uint8_t prefix);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common/io.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/cart.hpp"
|
||||
#include "main/cartio.hpp"
|
||||
#include "main/zs01.hpp"
|
||||
@ -90,11 +91,11 @@ DriverError DummyDriver::erase(void) {
|
||||
if (!__builtin_memcmp(
|
||||
_dump.dataKey, dummyDriverDump.dataKey, sizeof(_dump.dataKey)
|
||||
)) {
|
||||
dummyDriverDump.clearData();
|
||||
dummyDriverDump.clearKey();
|
||||
util::clear(dummyDriverDump.data);
|
||||
util::clear(dummyDriverDump.dataKey);
|
||||
// TODO: clear config registers as well
|
||||
|
||||
_dump.clearKey();
|
||||
util::clear(_dump.dataKey);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@ -276,7 +277,7 @@ DriverError X76F041Driver::readPrivateData(void) {
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_dump.clearConfig();
|
||||
util::clear(_dump.config);
|
||||
io::i2cReadBytes(_dump.config, 5);
|
||||
io::i2cStopWithCS();
|
||||
|
||||
@ -328,7 +329,7 @@ DriverError X76F041Driver::erase(void) {
|
||||
|
||||
io::i2cStopWithCS(_X76_WRITE_DELAY);
|
||||
|
||||
_dump.clearKey();
|
||||
util::clear(_dump.dataKey);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@ -607,7 +608,7 @@ DriverError ZS01Driver::erase(void) {
|
||||
|
||||
key.unpackFrom(_dump.dataKey);
|
||||
|
||||
__builtin_memset(request.data, 0, sizeof(request.data));
|
||||
util::clear(request.data);
|
||||
request.address = zs01::ADDR_ERASE;
|
||||
request.encodeWriteRequest(key, _encoderState);
|
||||
|
||||
@ -615,7 +616,7 @@ DriverError ZS01Driver::erase(void) {
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
_dump.clearKey();
|
||||
util::clear(_dump.dataKey);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "common/gpufont.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/pad.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "ps1/gpucmd.h"
|
||||
|
||||
@ -51,8 +52,9 @@ static const uint32_t _BUTTON_MAPPINGS[NUM_BUTTON_MAPS][NUM_BUTTONS]{
|
||||
};
|
||||
|
||||
ButtonState::ButtonState(void)
|
||||
: _held(0), _prevHeld(0), _pressed(0), _released(0), _repeating(0),
|
||||
_repeatTimer(0), buttonMap(MAP_JOYSTICK) {}
|
||||
: _held(0), _prevHeld(0), _longHeld(0), _prevLongHeld(0), _pressed(0),
|
||||
_released(0), _longPressed(0), _longReleased(0), _repeatTimer(0),
|
||||
buttonMap(MAP_JOYSTICK) {}
|
||||
|
||||
uint8_t ButtonState::_getHeld(void) const {
|
||||
uint32_t inputs = io::getJAMMAInputs();
|
||||
@ -94,25 +96,29 @@ uint8_t ButtonState::_getHeld(void) const {
|
||||
}
|
||||
|
||||
void ButtonState::reset(void) {
|
||||
_held = _getHeld();
|
||||
_prevHeld = _held;
|
||||
_held = _getHeld();
|
||||
_prevHeld = _held;
|
||||
_longHeld = 0;
|
||||
_prevLongHeld = 0;
|
||||
|
||||
_pressed = 0;
|
||||
_released = 0;
|
||||
_repeating = 0;
|
||||
_repeatTimer = 0;
|
||||
_pressed = 0;
|
||||
_released = 0;
|
||||
_longPressed = 0;
|
||||
_longReleased = 0;
|
||||
_repeatTimer = 0;
|
||||
}
|
||||
|
||||
void ButtonState::update(void) {
|
||||
_prevHeld = _held;
|
||||
_held = _getHeld();
|
||||
_prevHeld = _held;
|
||||
_prevLongHeld = _longHeld;
|
||||
_held = _getHeld();
|
||||
|
||||
uint32_t changed = _prevHeld ^ _held;
|
||||
|
||||
if (buttonMap == MAP_SINGLE_BUTTON) {
|
||||
_pressed = 0;
|
||||
_released = 0;
|
||||
_repeating = 0;
|
||||
_pressed = 0;
|
||||
_released = 0;
|
||||
_longHeld = 0;
|
||||
|
||||
// In single-button mode, interpret a short button press as the right
|
||||
// button and a long press as start.
|
||||
@ -135,19 +141,24 @@ void ButtonState::update(void) {
|
||||
else if (_held)
|
||||
_repeatTimer++;
|
||||
|
||||
_pressed = (changed & _held) & ~_pressed;
|
||||
_released = (changed & _prevHeld) & ~_released;
|
||||
_repeating = (_repeatTimer >= REPEAT_DELAY) ? _held : 0;
|
||||
_pressed = (changed & _held) & ~_pressed;
|
||||
_released = (changed & _prevHeld) & ~_released;
|
||||
_longHeld = (_repeatTimer >= REPEAT_DELAY) ? _held : 0;
|
||||
}
|
||||
|
||||
changed = _prevLongHeld ^ _longHeld;
|
||||
|
||||
_longPressed = (changed & _longHeld) & ~_longPressed;
|
||||
_longReleased = (changed & _prevLongHeld) & ~_longReleased;
|
||||
}
|
||||
|
||||
/* UI context */
|
||||
|
||||
Context::Context(gpu::Context &gpuCtx, void *screenData)
|
||||
: _currentScreen(0), gpuCtx(gpuCtx), background(nullptr), overlay(nullptr),
|
||||
time(0), screenData(screenData) {
|
||||
_screens[0] = nullptr;
|
||||
_screens[1] = nullptr;
|
||||
: _currentScreen(0), gpuCtx(gpuCtx), time(0), screenData(screenData) {
|
||||
util::clear(_screens);
|
||||
util::clear(backgrounds);
|
||||
util::clear(overlays);
|
||||
}
|
||||
|
||||
void Context::show(Screen &screen, bool goBack, bool playSound) {
|
||||
@ -169,21 +180,30 @@ void Context::draw(void) {
|
||||
auto oldScreen = _screens[_currentScreen ^ 1];
|
||||
auto newScreen = _screens[_currentScreen];
|
||||
|
||||
if (background)
|
||||
background->draw(*this);
|
||||
for (auto layer : backgrounds) {
|
||||
if (layer)
|
||||
layer->draw(*this);
|
||||
}
|
||||
|
||||
if (oldScreen)
|
||||
oldScreen->draw(*this, false);
|
||||
if (newScreen)
|
||||
newScreen->draw(*this, true);
|
||||
if (overlay)
|
||||
overlay->draw(*this);
|
||||
|
||||
for (auto layer : overlays) {
|
||||
if (layer)
|
||||
layer->draw(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::update(void) {
|
||||
buttons.update();
|
||||
|
||||
if (overlay)
|
||||
overlay->update(*this);
|
||||
for (auto layer : overlays) {
|
||||
if (layer)
|
||||
layer->update(*this);
|
||||
}
|
||||
|
||||
if (_screens[_currentScreen])
|
||||
_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)
|
||||
tile.draw(ctx.gpuCtx, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void TextOverlay::draw(Context &ctx, bool active) const {
|
||||
gpu::RectWH rect;
|
||||
|
||||
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) {
|
||||
if (ctx.buttons.pressed(BTN_DEBUG)) {
|
||||
if (
|
||||
ctx.buttons.released(BTN_DEBUG) && !ctx.buttons.longReleased(BTN_DEBUG)
|
||||
) {
|
||||
bool shown = !_slideAnim.getTargetValue();
|
||||
|
||||
_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 */
|
||||
|
||||
void AnimatedScreen::_newLayer(
|
||||
@ -308,6 +353,7 @@ void BackdropScreen::hide(Context &ctx, bool goBack) {
|
||||
|
||||
void BackdropScreen::draw(Context &ctx, bool active) const {
|
||||
int brightness = _backdropAnim.getValue(ctx.time);
|
||||
|
||||
if (!brightness)
|
||||
return;
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace ui {
|
||||
/* Public constants */
|
||||
|
||||
static constexpr int NUM_UI_COLORS = 18;
|
||||
static constexpr int NUM_UI_SOUNDS = 9;
|
||||
static constexpr int NUM_UI_SOUNDS = 10;
|
||||
|
||||
enum Color {
|
||||
COLOR_DEFAULT = 0,
|
||||
@ -44,7 +44,8 @@ enum Sound {
|
||||
SOUND_MOVE_RIGHT = 5,
|
||||
SOUND_ENTER = 6,
|
||||
SOUND_EXIT = 7,
|
||||
SOUND_CLICK = 8
|
||||
SOUND_CLICK = 8,
|
||||
SOUND_SCREENSHOT = 9
|
||||
};
|
||||
|
||||
enum AnimationSpeed {
|
||||
@ -106,7 +107,9 @@ class ButtonState {
|
||||
private:
|
||||
uint32_t _mappings[NUM_BUTTONS];
|
||||
uint8_t _held, _prevHeld;
|
||||
uint8_t _pressed, _released, _repeating;
|
||||
uint8_t _longHeld, _prevLongHeld;
|
||||
uint8_t _pressed, _released;
|
||||
uint8_t _longPressed, _longReleased;
|
||||
|
||||
int _repeatTimer;
|
||||
|
||||
@ -115,20 +118,24 @@ private:
|
||||
public:
|
||||
ButtonMap buttonMap;
|
||||
|
||||
inline bool pressed(Button button) const {
|
||||
return _pressed & (1 << button);
|
||||
inline bool held(Button button) const {
|
||||
return (_held >> button) & 1;
|
||||
}
|
||||
inline bool pressedRepeating(Button button) const {
|
||||
return (_pressed | _repeating) & (1 << button);
|
||||
inline bool pressed(Button button) const {
|
||||
return (_pressed >> button) & 1;
|
||||
}
|
||||
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 {
|
||||
return _held & (1 << button);
|
||||
inline bool longPressed(Button button) const {
|
||||
return (_longPressed >> button) & 1;
|
||||
}
|
||||
inline bool longReleased(Button button) const {
|
||||
return (_longReleased >> button) & 1;
|
||||
}
|
||||
|
||||
ButtonState(void);
|
||||
@ -148,7 +155,8 @@ private:
|
||||
|
||||
public:
|
||||
gpu::Context &gpuCtx;
|
||||
Layer *background, *overlay;
|
||||
|
||||
Layer *backgrounds[4], *overlays[4];
|
||||
|
||||
gpu::Font font;
|
||||
gpu::Color colors[NUM_UI_COLORS];
|
||||
@ -186,9 +194,15 @@ public:
|
||||
class TiledBackground : public Layer {
|
||||
public:
|
||||
gpu::Image tile;
|
||||
|
||||
void draw(Context &ctx, bool active = true) const;
|
||||
};
|
||||
|
||||
class TextOverlay : public Layer {
|
||||
public:
|
||||
const char *leftText, *rightText;
|
||||
|
||||
inline TiledBackground(void)
|
||||
inline TextOverlay(void)
|
||||
: leftText(nullptr), rightText(nullptr) {}
|
||||
|
||||
void draw(Context &ctx, bool active = true) const;
|
||||
@ -205,6 +219,20 @@ public:
|
||||
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 */
|
||||
|
||||
// This is probably the most stripped-down way to implement something that
|
||||
|
@ -84,7 +84,7 @@ void TextScreen::update(Context &ctx) {
|
||||
|
||||
if (
|
||||
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) {
|
||||
value = scrollHeight;
|
||||
@ -98,7 +98,7 @@ void TextScreen::update(Context &ctx) {
|
||||
}
|
||||
if (
|
||||
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) {
|
||||
value = 0;
|
||||
@ -281,7 +281,7 @@ void ListScreen::draw(Context &ctx, bool active) const {
|
||||
void ListScreen::update(Context &ctx) {
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeItem > 0))
|
||||
(ctx.buttons.longHeld(ui::BTN_LEFT) && (_activeItem > 0))
|
||||
) {
|
||||
_activeItem--;
|
||||
if (_activeItem < 0) {
|
||||
@ -296,7 +296,7 @@ void ListScreen::update(Context &ctx) {
|
||||
}
|
||||
if (
|
||||
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++;
|
||||
if (_activeItem >= _listLength) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
@ -82,7 +83,7 @@ void MessageBoxScreen::update(Context &ctx) {
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeButton > 0))
|
||||
(ctx.buttons.longHeld(ui::BTN_LEFT) && (_activeButton > 0))
|
||||
) {
|
||||
_activeButton--;
|
||||
if (_activeButton < 0) {
|
||||
@ -96,7 +97,7 @@ void MessageBoxScreen::update(Context &ctx) {
|
||||
}
|
||||
if (
|
||||
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++;
|
||||
if (_activeButton >= numButtons) {
|
||||
@ -112,13 +113,13 @@ void MessageBoxScreen::update(Context &ctx) {
|
||||
|
||||
HexEntryScreen::HexEntryScreen(void)
|
||||
: _bufferLength(0) {
|
||||
__builtin_memset(_buffer, 0, sizeof(_buffer));
|
||||
util::clear(_buffer);
|
||||
}
|
||||
|
||||
void HexEntryScreen::show(Context &ctx, bool goBack) {
|
||||
MessageBoxScreen::show(ctx, goBack);
|
||||
|
||||
//__builtin_memset(_buffer, 0, _bufferLength);
|
||||
//util::clear(_buffer);
|
||||
|
||||
_buttonIndexOffset = _bufferLength * 2;
|
||||
_charWidth = ctx.font.getCharacterWidth('0');
|
||||
@ -187,7 +188,7 @@ void HexEntryScreen::update(Context &ctx) {
|
||||
|
||||
if (
|
||||
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) {
|
||||
value = 0xf;
|
||||
@ -198,7 +199,7 @@ void HexEntryScreen::update(Context &ctx) {
|
||||
}
|
||||
if (
|
||||
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) {
|
||||
value = 0;
|
||||
@ -365,7 +366,7 @@ void DateEntryScreen::update(Context &ctx) {
|
||||
|
||||
if (
|
||||
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) {
|
||||
value = field.maxValue;
|
||||
@ -376,7 +377,7 @@ void DateEntryScreen::update(Context &ctx) {
|
||||
}
|
||||
if (
|
||||
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) {
|
||||
value = field.minValue;
|
||||
|
@ -64,9 +64,6 @@ public:
|
||||
inline void copyTo(uint8_t *dest) const {
|
||||
__builtin_memcpy(dest, data, sizeof(data));
|
||||
}
|
||||
inline void clear(void) {
|
||||
__builtin_memset(data, 0, sizeof(data));
|
||||
}
|
||||
|
||||
void updateCRC(void);
|
||||
bool validateCRC(void) const;
|
||||
|
Loading…
x
Reference in New Issue
Block a user