Add screen resolution switcher, minor cleanups

This commit is contained in:
spicyjpeg 2023-12-23 18:35:06 +01:00
parent 36b09d5865
commit d5c1f0065b
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
17 changed files with 212 additions and 68 deletions

View File

@ -79,6 +79,7 @@ target_compile_definitions(
ENABLE_CART_MENU=1
#ENABLE_X76F100_DRIVER=1
ENABLE_DUMMY_DRIVER=1
ENABLE_LOGGING=1
ENABLE_I2C_LOGGING=1
,
#ENABLE_ARGV=1
@ -86,6 +87,7 @@ target_compile_definitions(
ENABLE_CART_MENU=1
#ENABLE_X76F100_DRIVER=1
#ENABLE_DUMMY_DRIVER=1
ENABLE_LOGGING=1
#ENABLE_I2C_LOGGING=1
>
)

View File

@ -221,6 +221,10 @@
"name": "Restore flash, PCMCIA cards or RTC from dump",
"prompt": "Restore the contents of the internal ROMs or any PCMCIA cards from the IDE hard drive or CF card connected as secondary drive (if any)."
},
"setResolution": {
"name": "Change screen resolution",
"prompt": "Switch to a different screen resolution and aspect ratio."
},
"about": {
"name": "About this tool",
"prompt": "View information about this tool, including open source licenses."
@ -255,6 +259,22 @@
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back"
},
"ResolutionScreen": {
"title": "{CART_ICON} Change screen resolution",
"prompt": "Select a resolution appropriate for your monitor or upscaler setup. Note that interlaced modes may be subject to flickering.",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
"320x240p": "320x240 (4:3), progressive",
"320x240i": "320x240 (4:3), interlaced (line doubled)",
"368x240p": "368x240 (~16:10), progressive",
"368x240i": "368x240 (~16:10), interlaced (line doubled)",
"512x240p": "512x240 (~20:9), progressive",
"512x240i": "512x240 (~20:9), interlaced (line doubled)",
"640x240p": "640x240 (24:9), progressive",
"640x240i": "640x240 (24:9), interlaced (line doubled)",
"640x480i": "640x480 (4:3), interlaced"
},
"SystemIDEntryScreen": {
"title": "Edit system identifier",
"body": "Enter the new digital I/O board's identifier. To obtain the ID of another board, run this tool on its respective system.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move between digits, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",

View File

@ -156,7 +156,7 @@ void App::_interruptHandler(void) {
}
}
void App::run(void) {
[[noreturn]] void App::run(void) {
LOG("starting app @ 0x%08x", this);
_ctx.screenData = this;
@ -165,8 +165,10 @@ void App::run(void) {
_loadResources();
_backgroundLayer.text = "v" VERSION_STRING;
_ctx.setBackgroundLayer(_backgroundLayer);
_ctx.setOverlayLayer(_overlayLayer);
_ctx.background = &_backgroundLayer;
#ifdef ENABLE_LOGGING
_ctx.overlay = &_overlayLayer;
#endif
_ctx.show(_workerStatusScreen);
for (;;) {

View File

@ -6,6 +6,7 @@
#include "app/main.hpp"
#include "app/misc.hpp"
#include "app/cartunlock.hpp"
#include "ps1/system.h"
#include "cart.hpp"
#include "cartdata.hpp"
#include "cartio.hpp"
@ -56,6 +57,7 @@ class App {
friend class WarningScreen;
friend class ButtonMappingScreen;
friend class MainMenuScreen;
friend class ResolutionScreen;
friend class AboutScreen;
friend class CartInfoScreen;
friend class UnlockKeyScreen;
@ -73,6 +75,7 @@ private:
WarningScreen _warningScreen;
ButtonMappingScreen _buttonMappingScreen;
MainMenuScreen _mainMenuScreen;
ResolutionScreen _resolutionScreen;
AboutScreen _aboutScreen;
CartInfoScreen _cartInfoScreen;
UnlockKeyScreen _unlockKeyScreen;
@ -132,7 +135,7 @@ public:
}
~App(void);
void run(void);
[[noreturn]] void run(void);
};
#define APP (reinterpret_cast<App *>(ctx.screenData))

View File

@ -402,9 +402,7 @@ enum DumpBank {
BANK_NONE_16BIT = -2
};
static constexpr int _NUM_DUMP_REGIONS = 5;
static const DumpRegion _DUMP_REGIONS[_NUM_DUMP_REGIONS]{
static const DumpRegion _DUMP_REGIONS[]{
{
.prompt = "App.romDumpWorker.dumpBIOS"_h,
.path = EXTERNAL_DATA_DIR "/dump%d/bios.bin",
@ -468,7 +466,7 @@ bool App::_romDumpWorker(void) {
if (!_fileProvider.createDirectory(dirPath))
goto _initError;
for (int i = 0; i < _NUM_DUMP_REGIONS; i++) {
for (int i = 0; i < util::countOf(_DUMP_REGIONS); i++) {
auto &region = _DUMP_REGIONS[i];
// Skip PCMCIA slots if a card is not inserted.

View File

@ -13,10 +13,9 @@ public:
void (CartActionsScreen::*target)(ui::Context &ctx);
};
static constexpr int _NUM_SYSTEM_ID_ACTIONS = 8;
static constexpr int _NUM_NO_SYSTEM_ID_ACTIONS = 5;
static constexpr int _NUM_SYSTEM_ID_ACTIONS = 3;
static const Action _ACTIONS[_NUM_SYSTEM_ID_ACTIONS]{
static const Action _ACTIONS[]{
{
.name = "CartActionsScreen.qrDump.name"_h,
.prompt = "CartActionsScreen.qrDump.prompt"_h,
@ -163,11 +162,11 @@ void CartActionsScreen::show(ui::Context &ctx, bool goBack) {
_prompt = STRH(_ACTIONS[0].prompt);
_itemPrompt = STR("CartActionsScreen.itemPrompt");
_listLength = _NUM_NO_SYSTEM_ID_ACTIONS;
_listLength = util::countOf(_ACTIONS) - _NUM_SYSTEM_ID_ACTIONS;
if (APP->_parser) {
if (APP->_parser->flags & cart::DATA_HAS_SYSTEM_ID)
_listLength = _NUM_SYSTEM_ID_ACTIONS;
_listLength = util::countOf(_ACTIONS);
}
ListScreen::show(ctx, goBack);

View File

@ -1,12 +1,13 @@
#include "app/app.hpp"
#include "app/main.hpp"
#include "ps1/gpucmd.h"
#include "uibase.hpp"
#include "util.hpp"
/* Main menu screens */
static constexpr int WARNING_COOLDOWN = 15;
static constexpr int WARNING_COOLDOWN = 10;
void WarningScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("WarningScreen.title");
@ -81,13 +82,7 @@ public:
void (MainMenuScreen::*target)(ui::Context &ctx);
};
#ifdef ENABLE_CART_MENU
static constexpr int _NUM_MENU_ENTRIES = 5;
#else
static constexpr int _NUM_MENU_ENTRIES = 4;
#endif
static const MenuEntry _MENU_ENTRIES[_NUM_MENU_ENTRIES]{
static const MenuEntry _MENU_ENTRIES[]{
{
#ifdef ENABLE_CART_MENU
.name = "MainMenuScreen.cartInfo.name"_h,
@ -109,6 +104,10 @@ static const MenuEntry _MENU_ENTRIES[_NUM_MENU_ENTRIES]{
.target = &MainMenuScreen::systemInfo
}, {
#endif
.name = "MainMenuScreen.setResolution.name"_h,
.prompt = "MainMenuScreen.setResolution.prompt"_h,
.target = &MainMenuScreen::setResolution
}, {
.name = "MainMenuScreen.about.name"_h,
.prompt = "MainMenuScreen.about.prompt"_h,
.target = &MainMenuScreen::about
@ -157,6 +156,10 @@ void MainMenuScreen::systemInfo(ui::Context &ctx) {
//ctx.show(APP->systemInfoScreen, false, true);
}
void MainMenuScreen::setResolution(ui::Context &ctx) {
ctx.show(APP->_resolutionScreen, false, true);
}
void MainMenuScreen::about(ui::Context &ctx) {
ctx.show(APP->_aboutScreen, false, true);
}
@ -176,7 +179,7 @@ void MainMenuScreen::show(ui::Context &ctx, bool goBack) {
_prompt = STRH(_MENU_ENTRIES[0].prompt);
_itemPrompt = STR("MainMenuScreen.itemPrompt");
_listLength = _NUM_MENU_ENTRIES;
_listLength = util::countOf(_MENU_ENTRIES);
ListScreen::show(ctx, goBack);
}
@ -191,6 +194,91 @@ void MainMenuScreen::update(ui::Context &ctx) {
(this->*action.target)(ctx);
}
struct Resolution {
public:
util::Hash name;
int width, height;
bool forceInterlace;
};
static const Resolution _RESOLUTIONS[]{
{
.name = "ResolutionScreen.320x240p"_h,
.width = 320,
.height = 240,
.forceInterlace = false
}, {
.name = "ResolutionScreen.320x240i"_h,
.width = 320,
.height = 240,
.forceInterlace = true
}, {
.name = "ResolutionScreen.368x240p"_h,
.width = 368,
.height = 240,
.forceInterlace = false
}, {
.name = "ResolutionScreen.368x240i"_h,
.width = 368,
.height = 240,
.forceInterlace = true
}, {
.name = "ResolutionScreen.512x240p"_h,
.width = 512,
.height = 240,
.forceInterlace = false
}, {
.name = "ResolutionScreen.512x240i"_h,
.width = 512,
.height = 240,
.forceInterlace = true
}, {
.name = "ResolutionScreen.640x240p"_h,
.width = 640,
.height = 240,
.forceInterlace = false
}, {
.name = "ResolutionScreen.640x240i"_h,
.width = 640,
.height = 240,
.forceInterlace = true
}, {
.name = "ResolutionScreen.640x480i"_h,
.width = 640,
.height = 480,
.forceInterlace = true
}
};
const char *ResolutionScreen::_getItemName(ui::Context &ctx, int index) const {
return STRH(_RESOLUTIONS[index].name);
}
void ResolutionScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("ResolutionScreen.title");
_prompt = STR("ResolutionScreen.prompt");
_itemPrompt = STR("ResolutionScreen.itemPrompt");
_listLength = util::countOf(_RESOLUTIONS);
ListScreen::show(ctx, goBack);
}
void ResolutionScreen::update(ui::Context &ctx) {
auto &res = _RESOLUTIONS[_activeItem];
ListScreen::update(ctx);
if (ctx.buttons.pressed(ui::BTN_START)) {
if (!ctx.buttons.held(ui::BTN_LEFT) && !ctx.buttons.held(ui::BTN_RIGHT))
ctx.gpuCtx.setResolution(
GP1_MODE_NTSC, res.width, res.height, res.forceInterlace
);
ctx.show(APP->_mainMenuScreen, true, true);
}
}
void AboutScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("AboutScreen.title");
_prompt = STR("AboutScreen.prompt");

View File

@ -35,6 +35,7 @@ public:
void dump(ui::Context &ctx);
void restore(ui::Context &ctx);
void systemInfo(ui::Context &ctx);
void setResolution(ui::Context &ctx);
void about(ui::Context &ctx);
void ejectCD(ui::Context &ctx);
void reboot(ui::Context &ctx);
@ -43,6 +44,15 @@ public:
void update(ui::Context &ctx);
};
class ResolutionScreen : public ui::ListScreen {
protected:
const char *_getItemName(ui::Context &ctx, int index) const;
public:
void show(ui::Context &ctx, bool goBack = false);
void update(ui::Context &ctx);
};
class AboutScreen : public ui::TextScreen {
private:
util::Data _text;

View File

@ -4,6 +4,7 @@
#include "vendor/miniz.h"
#include "cart.hpp"
#include "util.hpp"
#include "utilerror.hpp"
namespace cart {
@ -247,7 +248,7 @@ size_t Dump::toQRString(char *output) const {
);
if (error != MZ_OK) {
LOG("compression error, code=%d", error);
LOG("%s, ptr=0x%08x", util::getErrorString(error), this);
return 0;
}
LOG(

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include "cart.hpp"
#include "cartdata.hpp"
#include "util.hpp"
namespace cart {
@ -211,9 +212,7 @@ public:
uint8_t flags;
};
static constexpr int _NUM_KNOWN_FORMATS = 11;
static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{
static const KnownFormat _KNOWN_FORMATS[]{
{
// Used by GCB48 (and possibly other games?)
.name = "region only",
@ -337,7 +336,7 @@ Parser *newCartParser(Dump &dump, FormatType formatType, uint8_t flags) {
Parser *newCartParser(Dump &dump) {
// Try all formats from the most complex one to the simplest.
for (int i = _NUM_KNOWN_FORMATS - 1; i >= 0; i--) {
for (int i = util::countOf(_KNOWN_FORMATS) - 1; i >= 0; i--) {
auto &format = _KNOWN_FORMATS[i];
Parser *parser = newCartParser(dump, format.format, format.flags);

View File

@ -56,11 +56,13 @@ size_t upload(const RectWH &rect, const void *data, bool wait) {
/* Rendering context */
void Context::_applyResolution(VideoMode mode, int shiftX, int shiftY) const {
void Context::_applyResolution(
VideoMode mode, bool forceInterlace, int shiftX, int shiftY
) const {
GP1HorizontalRes hres;
GP1VerticalRes vres;
int span, is480;
int span, vdiv;
if (width < 320) {
hres = GP1_HRES_256;
@ -80,17 +82,19 @@ void Context::_applyResolution(VideoMode mode, int shiftX, int shiftY) const {
}
if (height <= 256) {
vres = GP1_VRES_256;
is480 = false;
vres = GP1_VRES_256;
vdiv = 1;
} else {
vres = GP1_VRES_512;
is480 = true;
vres = GP1_VRES_512;
vdiv = 2;
forceInterlace = true;
}
int x = shiftX + 0x760, offsetX = span / 2;
int y = shiftY + (mode ? 0xa3 : 0x88), offsetY = height / (2 << is480);
int x = shiftX + 0x760, offsetX = span >> 1;
int y = shiftY + (mode ? 0xa3 : 0x88), offsetY = height >> vdiv;
GPU_GP1 = gp1_fbMode(hres, vres, mode, is480, GP1_COLOR_16BPP);
GPU_GP1 = gp1_fbMode(hres, vres, mode, forceInterlace, GP1_COLOR_16BPP);
GPU_GP1 = gp1_fbRangeH(x - offsetX, x + offsetX);
GPU_GP1 = gp1_fbRangeV(y - offsetY, y + offsetY);
}
@ -114,7 +118,8 @@ void Context::flip(void) {
}
void Context::setResolution(
VideoMode mode, int _width, int _height, bool sideBySide
VideoMode mode, int _width, int _height, bool forceInterlace,
bool sideBySide
) {
auto enable = disableInterrupts();
@ -143,7 +148,7 @@ void Context::setResolution(
_currentListPtr = _buffers[0].displayList;
_currentBuffer = 0;
_applyResolution(mode);
_applyResolution(mode, forceInterlace);
if (enable)
enableInterrupts();
}

View File

@ -79,15 +79,19 @@ private:
return _buffers[_currentBuffer ^ 1];
}
void _applyResolution(VideoMode mode, int shiftX = 0, int shiftY = 0) const;
void _applyResolution(
VideoMode mode, bool forceInterlace = false, int shiftX = 0,
int shiftY = 0
) const;
public:
int width, height, refreshRate;
inline Context(
VideoMode mode, int width, int height, bool sideBySide = false
VideoMode mode, int width, int height, bool forceInterlace = false,
bool sideBySide = false
) : _lastTexpage(0) {
setResolution(mode, width, height, sideBySide);
setResolution(mode, width, height, forceInterlace, sideBySide);
}
inline void newLayer(int x, int y) {
newLayer(x, y, width, height);
@ -115,7 +119,8 @@ public:
}
void setResolution(
VideoMode mode, int width, int height, bool sideBySide = false
VideoMode mode, int width, int height, bool forceInterlace = false,
bool sideBySide = false
);
void flip(void);

View File

@ -18,12 +18,14 @@ extern "C" const size_t _resourcesSize;
class Settings {
public:
int width, height;
bool forceInterlace;
int baudRate;
const void *resPtr;
size_t resLength;
inline Settings(void)
: width(320), height(240), baudRate(0), resPtr(nullptr), resLength(0) {}
: width(320), height(240), forceInterlace(false), baudRate(0),
resPtr(nullptr), resLength(0) {}
bool parse(const char *arg);
};
@ -53,6 +55,10 @@ bool Settings::parse(const char *arg) {
height = int(strtol(&arg[14], nullptr, 0));
return true;
case "screen.interlace"_h:
forceInterlace = bool(strtol(&arg[17], nullptr, 0));
return true;
// Allow the default assets to be overridden by passing a pointer to an
// in-memory ZIP file as a command-line argument.
case "resources.ptr"_h:
@ -99,26 +105,31 @@ int main(int argc, const char **argv) {
// Load the resource archive, first from memory if a pointer was given and
// then from the HDD. If both attempts fail, fall back to the archive
// embedded into the executable.
file::ZIPProvider resourceProvider;
auto resourceProvider = new file::ZIPProvider;
if (settings.resPtr && settings.resLength) {
if (resourceProvider.init(settings.resPtr, settings.resLength))
if (resourceProvider->init(settings.resPtr, settings.resLength))
goto _resourceInitDone;
}
resourceProvider.init(_resources, _resourcesSize);
resourceProvider->init(_resources, _resourcesSize);
_resourceInitDone:
io::clearWatchdog();
gpu::Context gpuCtx(GP1_MODE_NTSC, settings.width, settings.height);
ui::Context uiCtx(gpuCtx);
App app(uiCtx, resourceProvider);
auto gpuCtx = new gpu::Context(
GP1_MODE_NTSC, settings.width, settings.height, settings.forceInterlace
);
auto uiCtx = new ui::Context(*gpuCtx);
auto app = new App(*uiCtx, *resourceProvider);
gpu::enableDisplay(true);
spu::setVolume(0x3fff);
io::setMiscOutput(io::MISC_SPU_ENABLE, true);
app->run();
app.run();
delete app;
delete uiCtx;
delete gpuCtx;
return 0;
}

View File

@ -2,6 +2,7 @@
#include <assert.h>
#include <stdint.h>
#include "ps1/registers.h"
#include "ps1/system.h"
#include "spu.hpp"
#include "util.hpp"

View File

@ -128,7 +128,7 @@ void ButtonState::update(void) {
/* UI context */
Context::Context(gpu::Context &gpuCtx, void *screenData)
: _background(nullptr), _overlay(nullptr), _currentScreen(0), gpuCtx(gpuCtx),
: _currentScreen(0), gpuCtx(gpuCtx), background(nullptr), overlay(nullptr),
time(0), screenData(screenData) {
_screens[0] = nullptr;
_screens[1] = nullptr;
@ -151,21 +151,21 @@ void Context::draw(void) {
auto oldScreen = _screens[_currentScreen ^ 1];
auto newScreen = _screens[_currentScreen];
if (_background)
_background->draw(*this);
if (background)
background->draw(*this);
if (oldScreen)
oldScreen->draw(*this, false);
if (newScreen)
newScreen->draw(*this, true);
if (_overlay)
_overlay->draw(*this);
if (overlay)
overlay->draw(*this);
}
void Context::update(void) {
buttons.update();
if (_overlay)
_overlay->update(*this);
if (overlay)
overlay->update(*this);
if (_screens[_currentScreen])
_screens[_currentScreen]->update(*this);
}

View File

@ -138,11 +138,11 @@ class Screen;
class Context {
private:
Screen *_screens[2];
Layer *_background, *_overlay;
int _currentScreen;
public:
gpu::Context &gpuCtx;
Layer *background, *overlay;
gpu::Font font;
gpu::Color colors[NUM_UI_COLORS];
@ -157,12 +157,6 @@ public:
//buttons.update();
time++;
}
inline void setBackgroundLayer(Layer &layer) {
_background = &layer;
}
inline void setOverlayLayer(Layer &layer) {
_overlay = &layer;
}
Context(gpu::Context &gpuCtx, void *screenData = nullptr);
void show(Screen &screen, bool goBack = false, bool playSound = false);

View File

@ -5,7 +5,6 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "ps1/system.h"
namespace util {
@ -47,6 +46,10 @@ template<typename T, typename X> static inline void assertAligned(X *ptr) {
assert(!(reinterpret_cast<uintptr_t>(ptr) % alignof(T)));
}
template<typename T> static constexpr inline size_t countOf(T &array) {
return sizeof(array) / sizeof(array[0]);
}
template<typename T, typename X> static constexpr inline T forcedCast(X item) {
return reinterpret_cast<T>(reinterpret_cast<void *>(item));
}
@ -91,14 +94,14 @@ public:
if (ptr)
free(ptr);
ptr = malloc(_length);
ptr = new uint8_t[length];
length = _length;
return ptr;
}
inline void destroy(void) {
if (ptr) {
free(ptr);
delete[] as<uint8_t>();
ptr = nullptr;
}
}
@ -265,9 +268,12 @@ extern const char *const MINIZ_ZIP_ERROR_NAMES[];
}
//#define LOG(...) util::logger.log(__VA_ARGS__)
#ifdef ENABLE_LOGGING
#define LOG(fmt, ...) \
util::logger.log("%s(%d): " fmt, __func__, __LINE__ __VA_OPT__(,) __VA_ARGS__)
#else
#define LOG(fmt, ...)
#endif
static constexpr inline util::Hash operator""_h(
const char *const literal, size_t length