Add splash screen and FAQ/troubleshooting page

This commit is contained in:
spicyjpeg 2024-07-12 20:14:35 +02:00
parent e9b389a82f
commit 57139b87b5
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
15 changed files with 218 additions and 99 deletions

View File

@ -1,6 +1,6 @@
<p align="center">
<img alt="573in1 logo" src="doc/assets/logo.png" width="512" height="336" />
<img alt="573in1 logo" src="doc/assets/logo.png" width="512" />
</p>
573in1 is a full-featured homebrew maintenance and troubleshooting tool, game
@ -43,17 +43,17 @@ instructions. **Reading the documentation before proceeding is highly**
## Screenshots
<p align="center">
<img alt="Main menu" src="doc/assets/mainMenu.png" width="320" height="240" />
<img alt="Main menu" src="doc/assets/mainMenu.png" width="320" />
<img alt="Cartridge information screen" src="doc/assets/cartInfo.png"
width="320" height="240" />
width="320" />
<img alt="Cartridge system ID editor" src="doc/assets/editSystemID.png"
width="320" height="240" />
width="320" />
<img alt="Storage device information screen" src="doc/assets/storageInfo.png"
width="320" height="240" />
width="320" />
<img alt="IDE drive file picker" src="doc/assets/ideDrivePicker.png"
width="320" height="240" />
width="320" />
<img alt="Monitor test pattern" src="doc/assets/monitorTestPattern.png"
width="320" height="240" />
width="320" />
</p>
## Project status
@ -78,6 +78,8 @@ to lowest priority:
flash or a PCMCIA card, allowing the 573 BIOS to boot them automatically even
with no CD-ROM drive present.
- Cleaning up the codebase, which currently contains *many* bad practices.
- Adding support for JVS bus scanning and using JVS I/O boards in place of the
JAMMA button inputs.
- Adding UTF-8 support to the font and text rendering code. This is a rather low
priority feature, but it would allow 573in1 to be translated to other
languages.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

67
doc/faq.md Normal file
View File

@ -0,0 +1,67 @@
# FAQ and troubleshooting
## Running 573in1
### I cannot get past the BIOS "hardware error" screen and into the main menu
- Make sure the disc image has been burned correctly (and not by e.g. dragging
the image file onto the disc). The disc should appear as `573IN1_xxxxx` and
contain a bunch of files named `README.TXT`, `PSX.EXE` and so on.
- Try using a different disc and/or another CD-ROM drive. A list of drives known
to be compatible with the stock System 573 BIOS can be found
[here](https://psx-spx.consoledev.net/konamisystem573/#known-working-replacement-drives).
- Ensure the BIOS is not reporting an actual hardware error with the 573. A
CD-ROM error will result in `CDR BAD` being displayed, while other errors will
be reported under the PCB location of the chip that failed (e.g. `18E BAD` for
a JVS MCU error). A list of BIOS errors can be found
[here](https://psx-spx.consoledev.net/konamisystem573/#boot-sequence).
### I get an error about a missing file or the CD-ROM being incorrect
- You have booted into the game currently installed on the 573's internal flash
rather than from the disc. Turn off DIP switch 4 (the rightmost one) and power
cycle the 573.
## Security cartridges
### My cartridge does not get recognized
- Make sure the cartridge's contacts are clean and the security cartridge
connector on the 573 has no bent pins and is not worn out, corroded or
otherwise damaged.
## IDE devices
### My IDE cable does not fit into the 573
- The 573 motherboard has pin 20 populated on the IDE connector, but most cables
use it as a keying pin (as per later versions of the IDE specification) and
thus have it blocked off. You may either try to find another cable or drill a
small hole in the cable's connector.
- You can also cut or desolder pin 20 on the 573, however this is not
recommended.
### My IDE drive, CF card or adapter does not show up
- Try using a different drive or adapter. Some CF cards, SD card adapters and
IDE-to-SATA converters are known to be problematic.
### My IDE drive, CF card or adapter shows up but is unreadable
- Ensure the drive supports LBA addressing. All CF cards, SD card adapters and
the vast majority of hard drives do, however older hard drives (typically ones
with a capacity lower than 8 GB or manufactured before the mid 1990s) may only
support CHS addressing.
- Ensure the drive is formatted with a single FAT12, FAT16, FAT32 or exFAT
partition. Other filesystems such as NTFS or ext4 are not supported.
## Miscellaneous
### How do I obtain more information about an error that happened?
- You may press the test button on your 573 or cabinet at any time to toggle the
log window, which will contain detailed information about errors.
- If an IDE hard drive is connected you can also take screenshots by holding
down the test button. Screenshots are saved to the `573in1` directory in the
root of the filesystem.

View File

@ -8,7 +8,7 @@
- TODO: ~~[Installing games and working with flash memory devices](flash.md)~~
- TODO: ~~[Connecting an IDE hard drive](hdd.md)~~
- TODO: ~~[Installing 573in1 as a BIOS ROM](bios.md)~~
- TODO: ~~[FAQ and troubleshooting](faq.md)~~
- [FAQ and troubleshooting](faq.md)
## Development resources

View File

@ -326,11 +326,6 @@ void Context::drawGradientRectD(
cmd[7] = gp0_xy(x + width, y + height);
}
void Context::drawBackdrop(Color color, BlendMode blendMode) {
setBlendMode(blendMode, true);
drawRect(0, 0, width, height, color, true);
}
/* Image class */
void Image::initFromVRAMRect(

View File

@ -143,6 +143,14 @@ public:
);
}
inline void drawBackdrop(Color color) {
drawRect(0, 0, width, height, color);
}
inline void drawBackdrop(Color color, BlendMode blendMode) {
setBlendMode(blendMode, true);
drawRect(0, 0, width, height, color, true);
}
void setResolution(
VideoMode mode, int width, int height, bool forceInterlace = false,
bool sideBySide = false
@ -169,7 +177,6 @@ public:
int x, int y, int width, int height, Color top, Color middle,
Color bottom, bool blend = false
);
void drawBackdrop(Color color, BlendMode blendMode);
};
/* Image class */

View File

@ -180,6 +180,8 @@ void FileIOManager::closeResourceFile(void) {
static constexpr size_t _WORKER_STACK_SIZE = 0x20000;
static constexpr int _SPLASH_SCREEN_TIMEOUT = 5;
App::App(ui::Context &ctx)
#ifdef ENABLE_LOG_BUFFER
: _logOverlay(_logBuffer),
@ -238,6 +240,7 @@ void App::_loadResources(void) {
res.loadTIM(_background.tile, "assets/textures/background.tim");
res.loadTIM(_ctx.font.image, "assets/textures/font.tim");
res.loadStruct(_ctx.font.metrics, "assets/textures/font.metrics");
res.loadTIM(_splashOverlay.image, "assets/textures/splash.tim");
res.loadStruct(_ctx.colors, "assets/app.palette");
res.loadData(_stringTable, "assets/app.strings");
@ -303,6 +306,36 @@ bool App::_takeScreenshot(void) {
return true;
}
void App::_updateOverlays(void) {
// Date and time overlay
static char dateString[24];
util::Date date;
io::getRTCTime(date);
date.toString(dateString);
_textOverlay.leftText = dateString;
// Splash screen overlay
int timeout = _ctx.gpuCtx.refreshRate * _SPLASH_SCREEN_TIMEOUT;
if ((_workerStatus.status == WORKER_DONE) || (_ctx.time > timeout))
_splashOverlay.hide(_ctx);
// Log overlay
if (
_ctx.buttons.released(ui::BTN_DEBUG) &&
!_ctx.buttons.longReleased(ui::BTN_DEBUG)
)
_logOverlay.toggle(_ctx);
// Screenshot overlay
if (_ctx.buttons.longPressed(ui::BTN_DEBUG)) {
if (_takeScreenshot())
_screenshotOverlay.animate(_ctx);
}
}
void App::_runWorker(
bool (App::*func)(void), ui::Screen &next, bool goBack, bool playSound
) {
@ -361,40 +394,27 @@ void App::_interruptHandler(void) {
_fileIO.loadResourceFile(nullptr);
_loadResources();
char dateString[24];
_textOverlay.leftText = dateString;
_textOverlay.rightText = "v" VERSION_STRING;
_screenshotOverlay.callback = [](ui::Context &ctx) -> bool {
return APP->_takeScreenshot();
};
_textOverlay.rightText = "v" VERSION_STRING;
_ctx.backgrounds[0] = &_background;
_ctx.backgrounds[1] = &_textOverlay;
_ctx.overlays[0] = &_splashOverlay;
#ifdef ENABLE_LOG_BUFFER
_ctx.overlays[0] = &_logOverlay;
_ctx.overlays[1] = &_logOverlay;
#endif
_ctx.overlays[1] = &_screenshotOverlay;
_ctx.overlays[2] = &_screenshotOverlay;
#ifdef ENABLE_AUTOBOOT
_runWorker(&App::_ideInitWorker, _warningScreen);
#else
// If autoboot is disabled, show the warning screen before initializing the
// drives in order to give them enough time to spin up.
_runWorker(nullptr, _warningScreen);
#endif
_setupInterrupts();
_splashOverlay.show(_ctx);
_ctx.sounds[ui::SOUND_STARTUP].play();
for (;;) {
util::Date date;
io::getRTCTime(date);
date.toString(dateString);
_ctx.update();
_ctx.draw();
_updateOverlays();
_ctx.draw();
switchThreadImmediate(&_workerThread);
_ctx.gpuCtx.flip();
}

View File

@ -180,12 +180,13 @@ private:
ResolutionScreen _resolutionScreen;
AboutScreen _aboutScreen;
ui::TiledBackground _background;
ui::TextOverlay _textOverlay;
ui::SplashOverlay _splashOverlay;
#ifdef ENABLE_LOG_BUFFER
util::LogBuffer _logBuffer;
ui::LogOverlay _logOverlay;
#endif
ui::TiledBackground _background;
ui::TextOverlay _textOverlay;
ui::ScreenshotOverlay _screenshotOverlay;
ui::Context &_ctx;
@ -214,6 +215,7 @@ private:
char *output, size_t length, const char *path, int maxIndex = 9999
);
bool _takeScreenshot(void);
void _updateOverlays(void);
void _runWorker(
bool (App::*func)(void), ui::Screen &next, bool goBack = false,
bool playSound = false

View File

@ -239,6 +239,7 @@ void QRCodeScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("QRCodeScreen.title");
_prompt = STR("QRCodeScreen.prompt");
_image = &_code;
_imageScale = _QR_CODE_SCALE;
_imagePadding = _QR_CODE_SCALE * _QR_CODE_PADDING;
_backdropColor = 0xffffff;

View File

@ -45,6 +45,9 @@ public:
};
class QRCodeScreen : public ui::ImageScreen {
private:
gpu::Image _code;
public:
bool valid;
@ -52,14 +55,14 @@ public:
: valid(false) {}
inline bool generateCode(const char *textInput) {
if (!gpu::generateQRCode(_image, 960, 256, textInput))
if (!gpu::generateQRCode(_code, 960, 256, textInput))
return false;
valid = true;
return true;
}
inline bool generateCode(const uint8_t *binaryInput, size_t length) {
if (!gpu::generateQRCode(_image, 960, 256, binaryInput, length))
if (!gpu::generateQRCode(_code, 960, 256, binaryInput, length))
return false;
valid = true;

View File

@ -303,9 +303,7 @@ void TestPatternScreen::_drawTextOverlay(
void TestPatternScreen::draw(ui::Context &ctx, bool active) const {
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
ctx.gpuCtx.drawRect(
0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height, _BACKGROUND_COLOR
);
ctx.gpuCtx.drawBackdrop(_BACKGROUND_COLOR);
}
void TestPatternScreen::update(ui::Context &ctx) {

View File

@ -234,11 +234,6 @@ void Context::draw(void) {
void Context::update(void) {
buttons.update();
for (auto layer : overlays) {
if (layer)
layer->update(*this);
}
auto screen = getCurrentScreen();
if (screen)
@ -294,22 +289,56 @@ void TextOverlay::draw(Context &ctx, bool active) const {
}
}
LogOverlay::LogOverlay(util::LogBuffer &buffer)
: _buffer(buffer) {
_slideAnim.setValue(0);
void SplashOverlay::draw(Context &ctx, bool active) const {
int brightness = _fadeAnim.getValue(ctx.time);
if (!brightness)
return;
// Backdrop
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
ctx.gpuCtx.drawBackdrop(
gp0_rgb(brightness, brightness, brightness), GP0_BLEND_SUBTRACT
);
if (brightness < 0xff)
return;
// Image
int x = (ctx.gpuCtx.width - image.width) / 2;
int y = (ctx.gpuCtx.height - image.height) / 2;
image.draw(ctx.gpuCtx, x, y);
}
void SplashOverlay::show(Context &ctx) {
if (!_fadeAnim.getTargetValue())
#if 0
_fadeAnim.setValue(ctx.time, 0, 0xff, SPEED_SLOWEST);
#else
_fadeAnim.setValue(0xff);
#endif
}
void SplashOverlay::hide(Context &ctx) {
if (_fadeAnim.getTargetValue())
_fadeAnim.setValue(ctx.time, 0xff, 0, SPEED_SLOWEST);
}
void LogOverlay::draw(Context &ctx, bool active) const {
int offset = _slideAnim.getValue(ctx.time);
if (!offset)
return;
// Backdrop
_newLayer(
ctx, 0, offset - ctx.gpuCtx.height, ctx.gpuCtx.width,
ctx.gpuCtx.height
);
ctx.gpuCtx.drawBackdrop(ctx.colors[COLOR_BACKDROP], GP0_BLEND_SUBTRACT);
// Text
int screenHeight = ctx.gpuCtx.height - SCREEN_MIN_MARGIN_Y * 2;
int linesShown = screenHeight / ctx.font.metrics.lineHeight;
@ -330,15 +359,11 @@ void LogOverlay::draw(Context &ctx, bool active) const {
}
}
void LogOverlay::update(Context &ctx) {
if (
ctx.buttons.released(BTN_DEBUG) && !ctx.buttons.longReleased(BTN_DEBUG)
) {
bool shown = !_slideAnim.getTargetValue();
void LogOverlay::toggle(Context &ctx) {
bool shown = !_slideAnim.getTargetValue();
_slideAnim.setValue(ctx.time, shown ? ctx.gpuCtx.height : 0, SPEED_SLOW);
ctx.sounds[shown ? SOUND_ENTER : SOUND_EXIT].play();
}
_slideAnim.setValue(ctx.time, shown ? ctx.gpuCtx.height : 0, SPEED_SLOW);
ctx.sounds[shown ? SOUND_ENTER : SOUND_EXIT].play();
}
void ScreenshotOverlay::draw(Context &ctx, bool active) const {
@ -353,13 +378,9 @@ void ScreenshotOverlay::draw(Context &ctx, bool active) const {
);
}
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();
}
}
void ScreenshotOverlay::animate(Context &ctx) {
_flashAnim.setValue(ctx.time, 0xff, 0, SPEED_SLOW);
ctx.sounds[ui::SOUND_SCREENSHOT].play();
}
/* Base screen classes */
@ -381,15 +402,17 @@ void AnimatedScreen::hide(Context &ctx, bool goBack) {
}
void BackdropScreen::show(Context &ctx, bool goBack) {
_backdropAnim.setValue(ctx.time, 0, 0x50, SPEED_FAST);
if (!_fadeAnim.getTargetValue())
_fadeAnim.setValue(ctx.time, 0, 0x50, SPEED_FAST);
}
void BackdropScreen::hide(Context &ctx, bool goBack) {
_backdropAnim.setValue(ctx.time, 0x50, 0, SPEED_FAST);
if (_fadeAnim.getTargetValue())
_fadeAnim.setValue(ctx.time, 0x50, 0, SPEED_FAST);
}
void BackdropScreen::draw(Context &ctx, bool active) const {
int brightness = _backdropAnim.getValue(ctx.time);
int brightness = _fadeAnim.getValue(ctx.time);
if (!brightness)
return;

View File

@ -64,7 +64,8 @@ enum Sound {
enum AnimationSpeed {
SPEED_FASTEST = 10,
SPEED_FAST = 15,
SPEED_SLOW = 20
SPEED_SLOW = 20,
SPEED_SLOWEST = 30
};
static constexpr int SCREEN_MARGIN_X = 16;
@ -214,7 +215,6 @@ protected:
public:
virtual void draw(Context &ctx, bool active = true) const {}
virtual void update(Context &ctx) {}
};
class TiledBackground : public Layer {
@ -234,15 +234,29 @@ public:
void draw(Context &ctx, bool active = true) const;
};
class SplashOverlay : public Layer {
private:
util::Tween<int, util::QuadOutEasing> _fadeAnim;
public:
gpu::Image image;
void draw(Context &ctx, bool active = true) const;
void show(Context &ctx);
void hide(Context &ctx);
};
class LogOverlay : public Layer {
private:
util::LogBuffer &_buffer;
util::Tween<int, util::QuadOutEasing> _slideAnim;
public:
LogOverlay(util::LogBuffer &buffer);
inline LogOverlay(util::LogBuffer &buffer)
: _buffer(buffer) {}
void draw(Context &ctx, bool active = true) const;
void update(Context &ctx);
void toggle(Context &ctx);
};
class ScreenshotOverlay : public Layer {
@ -250,13 +264,8 @@ 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);
void animate(Context &ctx);
};
/* Base screen classes */
@ -286,7 +295,7 @@ public:
class BackdropScreen : public Screen {
private:
util::Tween<int, util::LinearEasing> _backdropAnim;
util::Tween<int, util::LinearEasing> _fadeAnim;
public:
virtual void show(Context &ctx, bool goBack = false);

View File

@ -24,13 +24,6 @@ namespace ui {
/* Common screens */
void PlaceholderScreen::draw(Context &ctx, bool active) const {
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
ctx.gpuCtx.drawRect(
0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height, ctx.colors[COLOR_WINDOW2]
);
}
TextScreen::TextScreen(void)
: _title(nullptr), _body(nullptr), _prompt(nullptr) {}
@ -128,16 +121,17 @@ void TextScreen::update(Context &ctx) {
}
ImageScreen::ImageScreen(void)
: _imageScale(1), _imagePadding(0), _title(nullptr), _prompt(nullptr) {}
: _image(nullptr), _imageScale(1), _imagePadding(0), _title(nullptr),
_prompt(nullptr) {}
void ImageScreen::draw(Context &ctx, bool active) const {
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
if (_image.width && _image.height) {
if (_image) {
int x = ctx.gpuCtx.width / 2;
int y = ctx.gpuCtx.height / 2;
int width = _image.width * _imageScale / 2;
int height = _image.height * _imageScale / 2;
int width = _image->width * _imageScale / 2;
int height = _image->height * _imageScale / 2;
if (_prompt)
y -= (SCREEN_PROMPT_HEIGHT - ctx.font.metrics.lineHeight) / 2;
@ -153,9 +147,12 @@ void ImageScreen::draw(Context &ctx, bool active) const {
}
// Image
_image.drawScaled(
ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2
);
if (_imageScale > 1)
_image->drawScaled(
ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2
);
else
_image->draw(ctx.gpuCtx, x - width, y - height);
}
// Text

View File

@ -25,11 +25,6 @@ namespace ui {
/* Common screens */
class PlaceholderScreen : public AnimatedScreen {
public:
void draw(Context &ctx, bool active) const;
};
class TextScreen : public AnimatedScreen {
private:
util::Tween<int, util::QuadOutEasing> _scrollAnim;
@ -54,7 +49,7 @@ public:
class ImageScreen : public AnimatedScreen {
protected:
gpu::Image _image;
gpu::Image *_image;
int _imageScale, _imagePadding;
gpu::Color _backdropColor;