Clean up worker API, implement autobooting

This commit is contained in:
spicyjpeg 2024-05-29 12:20:32 +02:00
parent fd7bc1fa44
commit e75a7f2e42
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
17 changed files with 448 additions and 312 deletions

View File

@ -57,6 +57,15 @@ target_include_directories(
src src
src/libc src/libc
) )
target_compile_options(
common PUBLIC
-Wall
-Wextra
-Wno-unused-parameter
$<$<COMPILE_LANGUAGE:CXX>:
-Wno-pmf-conversions
>
)
target_compile_definitions(common PUBLIC VERSION="${PROJECT_VERSION}") target_compile_definitions(common PUBLIC VERSION="${PROJECT_VERSION}")
link_libraries(common) link_libraries(common)
@ -127,6 +136,7 @@ addExecutable(
target_compile_definitions( target_compile_definitions(
main PRIVATE main PRIVATE
$<IF:$<CONFIG:Debug>, $<IF:$<CONFIG:Debug>,
ENABLE_AUTOBOOT=1
ENABLE_DUMMY_DRIVER=1 ENABLE_DUMMY_DRIVER=1
ENABLE_FULL_IDE_DRIVER=1 ENABLE_FULL_IDE_DRIVER=1
ENABLE_I2C_LOGGING=1 ENABLE_I2C_LOGGING=1
@ -135,6 +145,7 @@ target_compile_definitions(
ENABLE_PS1_CONTROLLER=1 ENABLE_PS1_CONTROLLER=1
#ENABLE_X76F100_DRIVER=1 #ENABLE_X76F100_DRIVER=1
, ,
ENABLE_AUTOBOOT=1
#ENABLE_DUMMY_DRIVER=1 #ENABLE_DUMMY_DRIVER=1
ENABLE_FULL_IDE_DRIVER=1 ENABLE_FULL_IDE_DRIVER=1
#ENABLE_I2C_LOGGING=1 #ENABLE_I2C_LOGGING=1

View File

@ -8,7 +8,8 @@
"ideInitWorker": { "ideInitWorker": {
"initDrives": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.", "initDrives": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
"initFileIO": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.", "initFileIO": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.",
"loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives." "loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives.",
"autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives."
}, },
"cartDetectWorker": { "cartDetectWorker": {
"readDigitalIO": "Retrieving digital I/O board ID...", "readDigitalIO": "Retrieving digital I/O board ID...",
@ -110,6 +111,13 @@
} }
}, },
"AutobootScreen": {
"title": "Note",
"body": "A valid boot executable has been found and will be launched shortly. You may disable automatic booting by turning off DIP switch 1 or creating a file named noboot.txt in the root of the filesystem.\n\nFile: %s",
"cancel": "{START_BUTTON} Cancel (%ds)"
},
"ButtonMappingScreen": { "ButtonMappingScreen": {
"title": "{RIGHT_ARROW} Select button mapping", "title": "{RIGHT_ARROW} Select button mapping",
"prompt": "Use {START_BUTTON} or the Test button to select a mapping preset suitable for your cabinet or JAMMA setup. Other buttons will be enabled once a mapping is selected.", "prompt": "Use {START_BUTTON} or the Test button to select a mapping preset suitable for your cabinet or JAMMA setup. Other buttons will be enabled once a mapping is selected.",

View File

@ -81,8 +81,8 @@ static inline void clearWatchdog(void) {
SYS573_WATCHDOG = 0; SYS573_WATCHDOG = 0;
} }
static inline uint32_t getDIPSwitches(void) { static inline bool getDIPSwitch(int bit) {
return SYS573_DIP_CART & 0xf; return !((SYS573_DIP_CART >> bit) & 1);
} }
static inline bool getCartInsertionStatus(void) { static inline bool getCartInsertionStatus(void) {

View File

@ -18,12 +18,13 @@
/* Worker status class */ /* Worker status class */
void WorkerStatus::reset(void) { void WorkerStatus::reset(ui::Screen &next, bool goBack) {
status = WORKER_IDLE; status = WORKER_IDLE;
progress = 0; progress = 0;
progressTotal = 1; progressTotal = 1;
message = nullptr; message = nullptr;
nextScreen = nullptr; nextScreen = &next;
nextGoBack = goBack;
} }
void WorkerStatus::update(int part, int total, const char *text) { void WorkerStatus::update(int part, int total, const char *text) {
@ -38,30 +39,27 @@ void WorkerStatus::update(int part, int total, const char *text) {
enableInterrupts(); enableInterrupts();
} }
void WorkerStatus::setStatus(WorkerStatusType value) { ui::Screen &WorkerStatus::setNextScreen(ui::Screen &next, bool goBack) {
auto enable = disableInterrupts(); auto enable = disableInterrupts();
auto oldNext = nextScreen;
nextScreen = &next;
nextGoBack = goBack;
if (enable)
enableInterrupts();
return *oldNext;
}
WorkerStatusType WorkerStatus::setStatus(WorkerStatusType value) {
auto enable = disableInterrupts();
auto oldStatus = status;
status = value; status = value;
if (enable) if (enable)
enableInterrupts(); enableInterrupts();
}
void WorkerStatus::setNextScreen(ui::Screen &next, bool goBack) { return oldStatus;
auto enable = disableInterrupts();
_nextGoBack = goBack;
_nextScreen = &next;
if (enable)
enableInterrupts();
}
void WorkerStatus::finish(void) {
auto enable = disableInterrupts();
status = _nextGoBack ? WORKER_NEXT_BACK : WORKER_NEXT;
nextScreen = _nextScreen;
if (enable)
enableInterrupts();
} }
/* Filesystem manager class */ /* Filesystem manager class */
@ -97,10 +95,7 @@ void FileIOManager::initIDE(void) {
} }
ide[i] = iso; ide[i] = iso;
bool mapped = vfs.mount("cdrom:", iso, true); vfs.mount("cdrom:", iso, true);
if (mapped)
LOG("mapped cdrom: -> %s", name);
} else { } else {
auto fat = new file::FATProvider(); auto fat = new file::FATProvider();
@ -110,10 +105,7 @@ void FileIOManager::initIDE(void) {
} }
ide[i] = fat; ide[i] = fat;
bool mapped = vfs.mount("hdd:", fat, true); vfs.mount("hdd:", fat, true);
if (mapped)
LOG("mapped hdd: -> %s", name);
} }
vfs.mount(name, ide[i], true); vfs.mount(name, ide[i], true);
@ -199,26 +191,6 @@ void App::_unloadCartData(void) {
//_selectedEntry = nullptr; //_selectedEntry = nullptr;
} }
void App::_setupWorker(bool (App::*func)(void)) {
LOG("restarting worker, func=0x%08x", func);
auto enable = disableInterrupts();
_workerStack.allocate(_WORKER_STACK_SIZE);
_workerStatus.reset();
_workerFunction = func;
auto stackBottom = _workerStack.as<uint8_t>();
initThread(
// This is not how you implement delegates in C++.
&_workerThread, util::forcedCast<ArgFunction>(&App::_worker), this,
&stackBottom[(_WORKER_STACK_SIZE - 1) & ~7]
);
if (enable)
enableInterrupts();
}
void App::_setupInterrupts(void) { void App::_setupInterrupts(void) {
setInterruptHandler( setInterruptHandler(
util::forcedCast<ArgFunction>(&App::_interruptHandler), this util::forcedCast<ArgFunction>(&App::_interruptHandler), this
@ -282,7 +254,7 @@ bool App::_getNumberedPath(char *output, size_t length, const char *path) {
} }
bool App::_takeScreenshot(void) { bool App::_takeScreenshot(void) {
char path[32]; char path[file::MAX_PATH_LENGTH];
if (!_createDataDirectory()) if (!_createDataDirectory())
return false; return false;
@ -299,10 +271,31 @@ bool App::_takeScreenshot(void) {
return true; return true;
} }
void App::_runWorker(
bool (App::*func)(void), ui::Screen &next, bool goBack, bool playSound
) {
auto enable = disableInterrupts();
_workerStatus.reset(next, goBack);
_workerStack.allocate(_WORKER_STACK_SIZE);
_workerFunction = func;
auto stackBottom = _workerStack.as<uint8_t>();
initThread(
&_workerThread, util::forcedCast<ArgFunction>(&App::_worker), this,
&stackBottom[(_WORKER_STACK_SIZE - 1) & ~7]
);
if (enable)
enableInterrupts();
_ctx.show(_workerStatusScreen, false, playSound);
}
void App::_worker(void) { void App::_worker(void) {
if (_workerFunction) { if (_workerFunction) {
(this->*_workerFunction)(); (this->*_workerFunction)();
_workerStatus.finish(); _workerStatus.setStatus(WORKER_DONE);
} }
// Do nothing while waiting for vblank once the task is done. // Do nothing while waiting for vblank once the task is done.
@ -334,15 +327,6 @@ void App::_interruptHandler(void) {
_fileIO.resource.init(resourcePtr, resourceLength); _fileIO.resource.init(resourcePtr, resourceLength);
_loadResources(); _loadResources();
#ifdef NDEBUG
_workerStatus.setNextScreen(_warningScreen);
#else
// Skip the warning screen in debug builds.
_workerStatus.setNextScreen(_buttonMappingScreen);
#endif
_setupWorker(&App::_ideInitWorker);
_setupInterrupts();
char dateString[24]; char dateString[24];
_textOverlay.leftText = dateString; _textOverlay.leftText = dateString;
@ -357,7 +341,9 @@ void App::_interruptHandler(void) {
_ctx.overlays[0] = &_logOverlay; _ctx.overlays[0] = &_logOverlay;
#endif #endif
_ctx.overlays[1] = &_screenshotOverlay; _ctx.overlays[1] = &_screenshotOverlay;
_ctx.show(_workerStatusScreen);
_runWorker(&App::_ideInitWorker, _warningScreen);
_setupInterrupts();
for (;;) { for (;;) {
util::Date date; util::Date date;

View File

@ -25,17 +25,12 @@ enum WorkerStatusType {
WORKER_REBOOT = 1, WORKER_REBOOT = 1,
WORKER_BUSY = 2, WORKER_BUSY = 2,
WORKER_BUSY_SUSPEND = 3, // Prevent main thread from running WORKER_BUSY_SUSPEND = 3, // Prevent main thread from running
WORKER_NEXT = 4, // Go to next screen (goBack=false) WORKER_DONE = 4 // Go to next screen
WORKER_NEXT_BACK = 5 // Go to next screen (goBack=true)
}; };
// This class is used by the worker thread to report its current status back to // This class is used by the worker thread to report its current status back to
// the main thread and the WorkerStatusScreen. // the main thread and the WorkerStatusScreen.
class WorkerStatus { class WorkerStatus {
private:
volatile bool _nextGoBack;
ui::Screen *volatile _nextScreen;
public: public:
volatile WorkerStatusType status; volatile WorkerStatusType status;
@ -43,12 +38,12 @@ public:
const char *volatile message; const char *volatile message;
ui::Screen *volatile nextScreen; ui::Screen *volatile nextScreen;
volatile bool nextGoBack;
void reset(void); void reset(ui::Screen &next, bool goBack = false);
void update(int part, int total, const char *text = nullptr); void update(int part, int total, const char *text = nullptr);
void setStatus(WorkerStatusType value); ui::Screen &setNextScreen(ui::Screen &next, bool goBack = false);
void setNextScreen(ui::Screen &next, bool goBack = false); WorkerStatusType setStatus(WorkerStatusType value);
void finish(void);
}; };
/* Filesystem manager class */ /* Filesystem manager class */
@ -86,6 +81,7 @@ class App {
friend class ConfirmScreen; friend class ConfirmScreen;
friend class FilePickerScreen; friend class FilePickerScreen;
friend class FileBrowserScreen; friend class FileBrowserScreen;
friend class AutobootScreen;
friend class WarningScreen; friend class WarningScreen;
friend class ButtonMappingScreen; friend class ButtonMappingScreen;
friend class MainMenuScreen; friend class MainMenuScreen;
@ -112,6 +108,7 @@ private:
ConfirmScreen _confirmScreen; ConfirmScreen _confirmScreen;
FilePickerScreen _filePickerScreen; FilePickerScreen _filePickerScreen;
FileBrowserScreen _fileBrowserScreen; FileBrowserScreen _fileBrowserScreen;
AutobootScreen _autobootScreen;
WarningScreen _warningScreen; WarningScreen _warningScreen;
ButtonMappingScreen _buttonMappingScreen; ButtonMappingScreen _buttonMappingScreen;
MainMenuScreen _mainMenuScreen; MainMenuScreen _mainMenuScreen;
@ -159,12 +156,15 @@ private:
const cart::CartDBEntry *_identified, *_selectedEntry; const cart::CartDBEntry *_identified, *_selectedEntry;
void _unloadCartData(void); void _unloadCartData(void);
void _setupWorker(bool (App::*func)(void));
void _setupInterrupts(void); void _setupInterrupts(void);
void _loadResources(void); void _loadResources(void);
bool _createDataDirectory(void); bool _createDataDirectory(void);
bool _getNumberedPath(char *output, size_t length, const char *path); bool _getNumberedPath(char *output, size_t length, const char *path);
bool _takeScreenshot(void); bool _takeScreenshot(void);
void _runWorker(
bool (App::*func)(void), ui::Screen &next, bool goBack = false,
bool playSound = false
);
// cartworkers.cpp // cartworkers.cpp
bool _cartDetectWorker(void); bool _cartDetectWorker(void);

View File

@ -59,17 +59,18 @@ const char *CartActionsScreen::_getItemName(ui::Context &ctx, int index) const {
} }
void CartActionsScreen::qrDump(ui::Context &ctx) { void CartActionsScreen::qrDump(ui::Context &ctx) {
if (APP->_qrCodeScreen.valid) { if (APP->_qrCodeScreen.valid)
ctx.show(APP->_qrCodeScreen, false, true); ctx.show(APP->_qrCodeScreen, false, true);
} else { else
APP->_setupWorker(&App::_qrCodeWorker); APP->_runWorker(&App::_qrCodeWorker, APP->_qrCodeScreen, false, true);
ctx.show(APP->_workerStatusScreen, false, true);
}
} }
void CartActionsScreen::hddDump(ui::Context &ctx) { void CartActionsScreen::hddDump(ui::Context &ctx) {
APP->_setupWorker(&App::_cartDumpWorker); APP->_messageScreen.previousScreens[MESSAGE_SUCCESS] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_cartInfoScreen);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_runWorker(&App::_cartDumpWorker, APP->_messageScreen, false, true);
} }
void CartActionsScreen::hexdump(ui::Context &ctx) { void CartActionsScreen::hexdump(ui::Context &ctx) {
@ -77,18 +78,23 @@ void CartActionsScreen::hexdump(ui::Context &ctx) {
} }
void CartActionsScreen::hddRestore(ui::Context &ctx) { void CartActionsScreen::hddRestore(ui::Context &ctx) {
APP->_filePickerScreen.previousScreen = this;
APP->_filePickerScreen.setMessage( APP->_filePickerScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
ctx.show(APP->_confirmScreen, false, true); ctx.show(APP->_confirmScreen, false, true);
}, },
STR("CartActionsScreen.hddRestore.filePrompt") STR("CartActionsScreen.hddRestore.filePrompt")
); );
APP->_confirmScreen.previousScreen = &(APP->_fileBrowserScreen);
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
APP->_fileBrowserScreen,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_cartRestoreWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_fileBrowserScreen);
APP->_runWorker(
&App::_cartRestoreWorker, APP->_cartInfoScreen, true, true
);
}, },
STR("CartActionsScreen.hddRestore.confirm") STR("CartActionsScreen.hddRestore.confirm")
); );
@ -101,11 +107,15 @@ void CartActionsScreen::reflash(ui::Context &ctx) {
} }
void CartActionsScreen::erase(ui::Context &ctx) { void CartActionsScreen::erase(ui::Context &ctx) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_cartEraseWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_cartActionsScreen);
APP->_runWorker(
&App::_cartEraseWorker, APP->_cartInfoScreen, true, true
);
}, },
STR("CartActionsScreen.erase.confirm") STR("CartActionsScreen.erase.confirm")
); );
@ -115,22 +125,27 @@ void CartActionsScreen::erase(ui::Context &ctx) {
void CartActionsScreen::resetSystemID(ui::Context &ctx) { void CartActionsScreen::resetSystemID(ui::Context &ctx) {
if (!(APP->_cartParser->getIdentifiers()->systemID.isEmpty())) { if (!(APP->_cartParser->getIdentifiers()->systemID.isEmpty())) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
util::clear(APP->_cartParser->getIdentifiers()->systemID); util::clear(APP->_cartParser->getIdentifiers()->systemID);
APP->_cartParser->flush(); APP->_cartParser->flush();
APP->_setupWorker(&App::_cartWriteWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_cartActionsScreen);
APP->_runWorker(
&App::_cartWriteWorker, APP->_cartInfoScreen, true, true
);
}, },
STR("CartActionsScreen.resetSystemID.confirm") STR("CartActionsScreen.resetSystemID.confirm")
); );
ctx.show(APP->_confirmScreen, false, true); ctx.show(APP->_confirmScreen, false, true);
} else { } else {
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_messageScreen.setMessage( APP->_messageScreen.setMessage(
MESSAGE_ERROR, *this, STR("CartActionsScreen.resetSystemID.error") MESSAGE_ERROR, STR("CartActionsScreen.resetSystemID.error")
); );
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
@ -139,24 +154,29 @@ void CartActionsScreen::resetSystemID(ui::Context &ctx) {
void CartActionsScreen::matchSystemID(ui::Context &ctx) { void CartActionsScreen::matchSystemID(ui::Context &ctx) {
if (APP->_cartDump.flags & cart::DUMP_SYSTEM_ID_OK) { if (APP->_cartDump.flags & cart::DUMP_SYSTEM_ID_OK) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_cartParser->getIdentifiers()->systemID.copyFrom( APP->_cartParser->getIdentifiers()->systemID.copyFrom(
APP->_cartDump.systemID.data APP->_cartDump.systemID.data
); );
APP->_cartParser->flush(); APP->_cartParser->flush();
APP->_setupWorker(&App::_cartWriteWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_cartActionsScreen);
APP->_runWorker(
&App::_cartWriteWorker, APP->_cartInfoScreen, true, true
);
}, },
STR("CartActionsScreen.matchSystemID.confirm") STR("CartActionsScreen.matchSystemID.confirm")
); );
ctx.show(APP->_confirmScreen, false, true); ctx.show(APP->_confirmScreen, false, true);
} else { } else {
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_messageScreen.setMessage( APP->_messageScreen.setMessage(
MESSAGE_ERROR, *this, STR("CartActionsScreen.matchSystemID.error") MESSAGE_ERROR, STR("CartActionsScreen.matchSystemID.error")
); );
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
@ -164,22 +184,6 @@ void CartActionsScreen::matchSystemID(ui::Context &ctx) {
} }
void CartActionsScreen::editSystemID(ui::Context &ctx) { void CartActionsScreen::editSystemID(ui::Context &ctx) {
APP->_confirmScreen.setMessage(
APP->_systemIDEntryScreen,
[](ui::Context &ctx) {
APP->_systemIDEntryScreen.setSystemID(*(APP->_cartParser));
APP->_setupWorker(&App::_cartWriteWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("CartActionsScreen.editSystemID.confirm")
);
APP->_messageScreen.setMessage(
MESSAGE_ERROR, APP->_systemIDEntryScreen,
STR("CartActionsScreen.editSystemID.error")
);
APP->_systemIDEntryScreen.getSystemID(*(APP->_cartParser)); APP->_systemIDEntryScreen.getSystemID(*(APP->_cartParser));
ctx.show(APP->_systemIDEntryScreen, false, true); ctx.show(APP->_systemIDEntryScreen, false, true);
} }
@ -284,11 +288,16 @@ void ReflashGameScreen::update(ui::Context &ctx) {
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) { if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
ctx.show(APP->_cartActionsScreen, true, true); ctx.show(APP->_cartActionsScreen, true, true);
} else { } else {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_cartReflashWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_reflashGameScreen);
APP->_runWorker(
&App::_cartReflashWorker, APP->_cartInfoScreen, true,
true
);
}, },
STR("CartActionsScreen.reflash.confirm") STR("CartActionsScreen.reflash.confirm")
); );
@ -319,10 +328,35 @@ void SystemIDEntryScreen::update(ui::Context &ctx) {
if (_activeButton == _buttonIndexOffset) { if (_activeButton == _buttonIndexOffset) {
ctx.show(APP->_cartActionsScreen, true, true); ctx.show(APP->_cartActionsScreen, true, true);
} else if (_activeButton == (_buttonIndexOffset + 1)) { } else if (_activeButton == (_buttonIndexOffset + 1)) {
if (util::dsCRC8(_buffer, 7) == _buffer[7]) if (util::dsCRC8(_buffer, 7) == _buffer[7]) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage(
[](ui::Context &ctx) {
APP->_systemIDEntryScreen.setSystemID(
*(APP->_cartParser)
);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
&(APP->_systemIDEntryScreen);
APP->_runWorker(
&App::_cartWriteWorker, APP->_cartInfoScreen, true,
true
);
},
STR("CartActionsScreen.editSystemID.confirm")
);
ctx.show(APP->_confirmScreen, false, true); ctx.show(APP->_confirmScreen, false, true);
else } else {
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_messageScreen.setMessage(
MESSAGE_ERROR,
STR("CartActionsScreen.editSystemID.error")
);
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
} }
} }
} }
}

View File

@ -308,11 +308,16 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
auto &dump = APP->_cartDump; auto &dump = APP->_cartDump;
int offset = _getNumSpecialEntries(ctx); int offset = _getNumSpecialEntries(ctx);
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
APP->_unlockKeyScreen,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_cartUnlockWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_unlockKeyScreen);
APP->_runWorker(
&App::_cartUnlockWorker, APP->_cartInfoScreen, false,
true
);
}, },
STRH(_UNLOCK_WARNINGS[dump.chipType]) STRH(_UNLOCK_WARNINGS[dump.chipType])
); );
@ -353,11 +358,16 @@ void KeyEntryScreen::update(ui::Context &ctx) {
auto &dump = APP->_cartDump; auto &dump = APP->_cartDump;
// TODO: deduplicate this code (it is the same as UnlockKeyScreen) // TODO: deduplicate this code (it is the same as UnlockKeyScreen)
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
APP->_unlockKeyScreen,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_cartUnlockWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_keyEntryScreen);
APP->_runWorker(
&App::_cartUnlockWorker, APP->_cartInfoScreen, false,
true
);
}, },
STRH(_UNLOCK_WARNINGS[dump.chipType]) STRH(_UNLOCK_WARNINGS[dump.chipType])
); );

View File

@ -141,11 +141,9 @@ bool App::_cartUnlockWorker(void) {
if (error) { if (error) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _cartInfoScreen, MESSAGE_ERROR, WSTRH(_UNLOCK_ERRORS[_cartDump.chipType]),
WSTRH(_UNLOCK_ERRORS[_cartDump.chipType]),
cart::getErrorString(error) cart::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;
} }
@ -189,7 +187,6 @@ bool App::_cartUnlockWorker(void) {
bool App::_qrCodeWorker(void) { bool App::_qrCodeWorker(void) {
char qrString[cart::MAX_QR_STRING_LENGTH]; char qrString[cart::MAX_QR_STRING_LENGTH];
_workerStatus.setNextScreen(_qrCodeScreen);
_workerStatus.update(0, 2, WSTR("App.qrCodeWorker.compress")); _workerStatus.update(0, 2, WSTR("App.qrCodeWorker.compress"));
_cartDump.toQRString(qrString); _cartDump.toQRString(qrString);
@ -202,7 +199,7 @@ bool App::_qrCodeWorker(void) {
bool App::_cartDumpWorker(void) { bool App::_cartDumpWorker(void) {
_workerStatus.update(0, 1, WSTR("App.cartDumpWorker.save")); _workerStatus.update(0, 1, WSTR("App.cartDumpWorker.save"));
char path[32], code[8], region[8]; char path[file::MAX_PATH_LENGTH], code[8], region[8];
size_t length = _cartDump.getDumpLength(); size_t length = _cartDump.getDumpLength();
if (!_createDataDirectory()) if (!_createDataDirectory())
@ -228,17 +225,14 @@ bool App::_cartDumpWorker(void) {
goto _error; goto _error;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_SUCCESS, _cartInfoScreen, WSTR("App.cartDumpWorker.success"), MESSAGE_SUCCESS, WSTR("App.cartDumpWorker.success"), path
path
); );
_workerStatus.setNextScreen(_messageScreen);
return true; return true;
_error: _error:
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _cartInfoScreen, WSTR("App.cartDumpWorker.error"), path MESSAGE_ERROR, WSTR("App.cartDumpWorker.error"), path
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
} }
@ -255,7 +249,7 @@ bool App::_cartWriteWorker(void) {
if (error) { if (error) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _cartInfoScreen, WSTR("App.cartWriteWorker.error"), MESSAGE_ERROR, WSTR("App.cartWriteWorker.error"),
cart::getErrorString(error) cart::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
@ -292,8 +286,7 @@ bool App::_cartRestoreWorker(void) {
if (_cartDump.chipType != newDump.chipType) { if (_cartDump.chipType != newDump.chipType) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _fileBrowserScreen, MESSAGE_ERROR, WSTR("App.cartRestoreWorker.typeError"), path
WSTR("App.cartRestoreWorker.typeError"), path
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;
@ -322,8 +315,7 @@ bool App::_cartRestoreWorker(void) {
if (error) { if (error) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _fileBrowserScreen, MESSAGE_ERROR, WSTR("App.cartRestoreWorker.writeError"),
WSTR("App.cartRestoreWorker.writeError"),
cart::getErrorString(error) cart::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
@ -338,8 +330,7 @@ _fileError:
_fileOpenError: _fileOpenError:
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _fileBrowserScreen, MESSAGE_ERROR, WSTR("App.cartRestoreWorker.fileError"), path
WSTR("App.cartRestoreWorker.fileError"), path
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;
@ -352,8 +343,7 @@ bool App::_cartReflashWorker(void) {
!(_cartDump.flags & cart::DUMP_CART_ID_OK) !(_cartDump.flags & cart::DUMP_CART_ID_OK)
) { ) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _cartInfoScreen, MESSAGE_ERROR, WSTR("App.cartReflashWorker.idError")
WSTR("App.cartReflashWorker.idError")
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;
@ -418,8 +408,7 @@ bool App::_cartReflashWorker(void) {
if (error) { if (error) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _cartInfoScreen, MESSAGE_ERROR, WSTR("App.cartReflashWorker.writeError"),
WSTR("App.cartReflashWorker.writeError"),
cart::getErrorString(error) cart::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
@ -437,7 +426,7 @@ bool App::_cartEraseWorker(void) {
if (error) { if (error) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _cartInfoScreen, WSTR("App.cartEraseWorker.error"), MESSAGE_ERROR, WSTR("App.cartEraseWorker.error"),
cart::getErrorString(error) cart::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);

View File

@ -8,6 +8,7 @@
/* Main menu screens */ /* Main menu screens */
static constexpr int _WARNING_COOLDOWN = 10; static constexpr int _WARNING_COOLDOWN = 10;
static constexpr int _AUTOBOOT_DELAY = 5;
void WarningScreen::show(ui::Context &ctx, bool goBack) { void WarningScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("WarningScreen.title"); _title = STR("WarningScreen.title");
@ -17,18 +18,20 @@ void WarningScreen::show(ui::Context &ctx, bool goBack) {
_locked = true; _locked = true;
_numButtons = 1; _numButtons = 1;
_cooldownTimer = ctx.time + ctx.gpuCtx.refreshRate * _WARNING_COOLDOWN; #ifdef NDEBUG
_timer = ctx.time + ctx.gpuCtx.refreshRate * _WARNING_COOLDOWN;
#else
_timer = 0;
#endif
_buttonText[0] = 0;
MessageBoxScreen::show(ctx, goBack); MessageBoxScreen::show(ctx, goBack);
ctx.buttons.buttonMap = ui::MAP_SINGLE_BUTTON;
ctx.buttons.reset();
} }
void WarningScreen::update(ui::Context &ctx) { void WarningScreen::update(ui::Context &ctx) {
MessageBoxScreen::update(ctx); MessageBoxScreen::update(ctx);
int time = _cooldownTimer - ctx.time; int time = _timer - ctx.time;
_locked = (time > 0); _locked = (time > 0);
if (_locked) { if (_locked) {
@ -43,10 +46,53 @@ void WarningScreen::update(ui::Context &ctx) {
_buttons[0] = STR("WarningScreen.ok"); _buttons[0] = STR("WarningScreen.ok");
if (ctx.buttons.pressed(ui::BTN_RIGHT) || ctx.buttons.pressed(ui::BTN_START)) if (ctx.buttons.pressed(ui::BTN_START))
ctx.show(APP->_buttonMappingScreen, false, true); ctx.show(APP->_buttonMappingScreen, false, true);
} }
void AutobootScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("AutobootScreen.title");
_body = _bodyText;
_buttons[0] = _buttonText;
_numButtons = 1;
_timer = ctx.time + ctx.gpuCtx.refreshRate * _AUTOBOOT_DELAY;
_buttonText[0] = 0;
snprintf(_bodyText, sizeof(_bodyText), STR("AutobootScreen.body"), path);
MessageBoxScreen::show(ctx, goBack);
}
void AutobootScreen::update(ui::Context &ctx) {
MessageBoxScreen::update(ctx);
int time = _timer - ctx.time;
if (time < 0) {
__builtin_strncpy(
APP->_fileBrowserScreen.selectedPath, path,
sizeof(APP->_fileBrowserScreen.selectedPath)
);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
&(APP->_warningScreen);
APP->_runWorker(&App::_executableWorker, APP->_mainMenuScreen, true);
return;
}
time = (time / ctx.gpuCtx.refreshRate) + 1;
snprintf(
_buttonText, sizeof(_buttonText), STR("AutobootScreen.cancel"), time
);
if (ctx.buttons.pressed(ui::BTN_START))
ctx.show(APP->_warningScreen, false, true);
}
static const util::Hash _MAPPING_NAMES[]{ static const util::Hash _MAPPING_NAMES[]{
"ButtonMappingScreen.joystick"_h, "ButtonMappingScreen.joystick"_h,
"ButtonMappingScreen.ddrCab"_h, "ButtonMappingScreen.ddrCab"_h,
@ -55,7 +101,9 @@ static const util::Hash _MAPPING_NAMES[]{
"ButtonMappingScreen.dmxCab"_h "ButtonMappingScreen.dmxCab"_h
}; };
const char *ButtonMappingScreen::_getItemName(ui::Context &ctx, int index) const { const char *ButtonMappingScreen::_getItemName(
ui::Context &ctx, int index
) const {
return STRH(_MAPPING_NAMES[index]); return STRH(_MAPPING_NAMES[index]);
} }
@ -67,18 +115,14 @@ void ButtonMappingScreen::show(ui::Context &ctx, bool goBack) {
_listLength = ui::NUM_BUTTON_MAPS - 1; _listLength = ui::NUM_BUTTON_MAPS - 1;
ListScreen::show(ctx, goBack); ListScreen::show(ctx, goBack);
ctx.buttons.setButtonMap(ui::MAP_SINGLE_BUTTON);
ctx.buttons.buttonMap = ui::MAP_SINGLE_BUTTON;
ctx.buttons.reset();
} }
void ButtonMappingScreen::update(ui::Context &ctx) { void ButtonMappingScreen::update(ui::Context &ctx) {
ListScreen::update(ctx); ListScreen::update(ctx);
if (ctx.buttons.pressed(ui::BTN_START)) { if (ctx.buttons.pressed(ui::BTN_START)) {
ctx.buttons.buttonMap = ui::ButtonMap(_activeItem); ctx.buttons.setButtonMap(ui::ButtonMap(_activeItem));
ctx.buttons.reset();
ctx.show(APP->_mainMenuScreen, false, true); ctx.show(APP->_mainMenuScreen, false, true);
} }
} }
@ -134,12 +178,12 @@ const char *MainMenuScreen::_getItemName(ui::Context &ctx, int index) const {
} }
void MainMenuScreen::cartInfo(ui::Context &ctx) { void MainMenuScreen::cartInfo(ui::Context &ctx) {
if (APP->_cartDriver) { if (APP->_cartDriver)
ctx.show(APP->_cartInfoScreen, false, true); ctx.show(APP->_cartInfoScreen, false, true);
} else { else
APP->_setupWorker(&App::_cartDetectWorker); APP->_runWorker(
ctx.show(APP->_workerStatusScreen, false, true); &App::_cartDetectWorker, APP->_cartInfoScreen, false, true
} );
} }
void MainMenuScreen::storageInfo(ui::Context &ctx) { void MainMenuScreen::storageInfo(ui::Context &ctx) {
@ -151,11 +195,15 @@ void MainMenuScreen::ideInfo(ui::Context &ctx) {
} }
void MainMenuScreen::runExecutable(ui::Context &ctx) { void MainMenuScreen::runExecutable(ui::Context &ctx) {
APP->_filePickerScreen.previousScreen = this;
APP->_filePickerScreen.setMessage( APP->_filePickerScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_executableWorker); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_fileBrowserScreen);
APP->_runWorker(
&App::_executableWorker, APP->_mainMenuScreen, true, true
);
}, },
STR("MainMenuScreen.runExecutable.filePrompt") STR("MainMenuScreen.runExecutable.filePrompt")
); );
@ -176,13 +224,14 @@ void MainMenuScreen::about(ui::Context &ctx) {
} }
void MainMenuScreen::ejectCD(ui::Context &ctx) { void MainMenuScreen::ejectCD(ui::Context &ctx) {
APP->_setupWorker(&App::_atapiEjectWorker); APP->_messageScreen.previousScreens[MESSAGE_SUCCESS] = this;
ctx.show(APP->_workerStatusScreen, false, true); APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_runWorker(&App::_atapiEjectWorker, *this, true, true);
} }
void MainMenuScreen::reboot(ui::Context &ctx) { void MainMenuScreen::reboot(ui::Context &ctx) {
APP->_setupWorker(&App::_rebootWorker); APP->_runWorker(&App::_rebootWorker, *this, true, true);
ctx.show(APP->_workerStatusScreen, false, true);
} }
void MainMenuScreen::show(ui::Context &ctx, bool goBack) { void MainMenuScreen::show(ui::Context &ctx, bool goBack) {

View File

@ -9,7 +9,7 @@
class WarningScreen : public ui::MessageBoxScreen { class WarningScreen : public ui::MessageBoxScreen {
private: private:
int _cooldownTimer; int _timer;
char _buttonText[16]; char _buttonText[16];
public: public:
@ -17,6 +17,18 @@ public:
void update(ui::Context &ctx); void update(ui::Context &ctx);
}; };
class AutobootScreen : public ui::MessageBoxScreen {
private:
int _timer;
char _bodyText[512], _buttonText[16];
public:
const char *path;
void show(ui::Context &ctx, bool goBack = false);
void update(ui::Context &ctx);
};
class ButtonMappingScreen : public ui::ListScreen { class ButtonMappingScreen : public ui::ListScreen {
protected: protected:
const char *_getItemName(ui::Context &ctx, int index) const; const char *_getItemName(ui::Context &ctx, int index) const;

View File

@ -4,12 +4,23 @@
#include "common/defs.hpp" #include "common/defs.hpp"
#include "common/file.hpp" #include "common/file.hpp"
#include "common/ide.hpp" #include "common/ide.hpp"
#include "common/io.hpp"
#include "common/util.hpp" #include "common/util.hpp"
#include "main/app/app.hpp" #include "main/app/app.hpp"
#include "ps1/system.h" #include "ps1/system.h"
static const char *const _AUTOBOOT_PATHS[][2]{
{ "hdd:/noboot.txt", "hdd:/psx.exe" },
{ "cdrom:/noboot.txt", "cdrom:/psx.exe" },
{ "cdrom:/noboot.txt", "cdrom:/qsy.dxd" },
{ "cdrom:/noboot.txt", "cdrom:/ssw.bxf" },
{ "cdrom:/noboot.txt", "cdrom:/tsv.axg" },
{ "cdrom:/noboot.txt", "cdrom:/gse.nxx" },
{ "cdrom:/noboot.txt", "cdrom:/nse.gxx" }
};
bool App::_ideInitWorker(void) { bool App::_ideInitWorker(void) {
_workerStatus.update(0, 3, WSTR("App.ideInitWorker.initDrives")); _workerStatus.update(0, 4, WSTR("App.ideInitWorker.initDrives"));
for (size_t i = 0; i < util::countOf(ide::devices); i++) { for (size_t i = 0; i < util::countOf(ide::devices); i++) {
auto &dev = ide::devices[i]; auto &dev = ide::devices[i];
@ -21,13 +32,33 @@ bool App::_ideInitWorker(void) {
dev.goIdle(); dev.goIdle();
} }
_workerStatus.update(1, 3, WSTR("App.ideInitWorker.initFileIO")); _workerStatus.update(1, 4, WSTR("App.ideInitWorker.initFileIO"));
_fileIO.initIDE(); _fileIO.initIDE();
_workerStatus.update(2, 3, WSTR("App.ideInitWorker.loadResources")); _workerStatus.update(2, 4, WSTR("App.ideInitWorker.loadResources"));
if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip")) if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip"))
_loadResources(); _loadResources();
#ifdef ENABLE_AUTOBOOT
// Only try to autoboot if DIP switch 1 is on.
if (io::getDIPSwitch(0)) {
_workerStatus.update(3, 4, WSTR("App.ideInitWorker.autoboot"));
for (auto path : _AUTOBOOT_PATHS) {
file::FileInfo info;
if (_fileIO.vfs.getFileInfo(info, path[0]))
continue;
if (!_fileIO.vfs.getFileInfo(info, path[1]))
continue;
_autobootScreen.path = path[1];
_workerStatus.setNextScreen(_autobootScreen);
break;
}
}
#endif
_ctx.sounds[ui::SOUND_STARTUP].play(); _ctx.sounds[ui::SOUND_STARTUP].play();
return true; return true;
} }
@ -69,8 +100,7 @@ bool App::_executableWorker(void) {
if (!header.validateMagic()) { if (!header.validateMagic()) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _fileBrowserScreen, MESSAGE_ERROR, WSTR("App.executableWorker.fileError"), path
WSTR("App.executableWorker.fileError"), path
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;
@ -79,7 +109,7 @@ bool App::_executableWorker(void) {
auto executableEnd = header.textOffset + header.textLength; auto executableEnd = header.textOffset + header.textLength;
auto stackTop = uintptr_t(header.getStackPtr()); auto stackTop = uintptr_t(header.getStackPtr());
LOG("ptr=0x%08x, length=0x%x", header.textOffset, header.textLength); LOG("load=0x%08x, length=0x%x", header.textOffset, header.textLength);
// Find a launcher that does not overlap the new executable and can thus be // Find a launcher that does not overlap the new executable and can thus be
// used to load it. Note that this implicitly assumes that none of the // used to load it. Note that this implicitly assumes that none of the
@ -150,16 +180,14 @@ bool App::_executableWorker(void) {
} }
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _fileBrowserScreen, MESSAGE_ERROR, WSTR("App.executableWorker.addressError"), path,
WSTR("App.executableWorker.addressError"), path, header.textOffset, header.textOffset, executableEnd - 1, stackTop
executableEnd - 1, stackTop
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;
} }
bool App::_atapiEjectWorker(void) { bool App::_atapiEjectWorker(void) {
_workerStatus.setNextScreen(_mainMenuScreen, true);
_workerStatus.update(0, 1, WSTR("App.atapiEjectWorker.eject")); _workerStatus.update(0, 1, WSTR("App.atapiEjectWorker.eject"));
for (auto &dev : ide::devices) { for (auto &dev : ide::devices) {
@ -173,8 +201,7 @@ bool App::_atapiEjectWorker(void) {
if (error) { if (error) {
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _mainMenuScreen, MESSAGE_ERROR, WSTR("App.atapiEjectWorker.ejectError"),
WSTR("App.atapiEjectWorker.ejectError"),
ide::getErrorString(error) ide::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
@ -185,7 +212,7 @@ bool App::_atapiEjectWorker(void) {
} }
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _mainMenuScreen, WSTR("App.atapiEjectWorker.noDrive") MESSAGE_ERROR, WSTR("App.atapiEjectWorker.noDrive")
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;

View File

@ -21,13 +21,10 @@ void WorkerStatusScreen::show(ui::Context &ctx, bool goBack) {
void WorkerStatusScreen::update(ui::Context &ctx) { void WorkerStatusScreen::update(ui::Context &ctx) {
auto &worker = APP->_workerStatus; auto &worker = APP->_workerStatus;
auto nextScreen = worker.nextScreen;
if ((worker.status == WORKER_NEXT) || (worker.status == WORKER_NEXT_BACK)) { if (worker.status == WORKER_DONE) {
worker.reset(); worker.setStatus(WORKER_IDLE);
ctx.show(*nextScreen, worker.status == WORKER_NEXT_BACK); ctx.show(*worker.nextScreen, worker.nextGoBack);
LOG("worker finished, next=0x%08x", nextScreen);
return; return;
} }
@ -41,11 +38,8 @@ static const util::Hash _MESSAGE_TITLES[]{
"MessageScreen.title.error"_h "MessageScreen.title.error"_h
}; };
void MessageScreen::setMessage( void MessageScreen::setMessage(MessageType type, const char *format, ...) {
MessageType type, ui::Screen &prev, const char *format, ...
) {
_type = type; _type = type;
_prevScreen = &prev;
va_list ap; va_list ap;
@ -60,7 +54,7 @@ void MessageScreen::show(ui::Context &ctx, bool goBack) {
_buttons[0] = STR("MessageScreen.ok"); _buttons[0] = STR("MessageScreen.ok");
_numButtons = 1; _numButtons = 1;
_locked = _prevScreen ? false : true; //_locked = !previousScreen;
MessageBoxScreen::show(ctx, goBack); MessageBoxScreen::show(ctx, goBack);
ctx.sounds[ui::SOUND_ALERT].play(); ctx.sounds[ui::SOUND_ALERT].play();
@ -70,14 +64,12 @@ void MessageScreen::update(ui::Context &ctx) {
MessageBoxScreen::update(ctx); MessageBoxScreen::update(ctx);
if (ctx.buttons.pressed(ui::BTN_START)) if (ctx.buttons.pressed(ui::BTN_START))
ctx.show(*_prevScreen, true, true); ctx.show(*previousScreens[_type], true, true);
} }
void ConfirmScreen::setMessage( void ConfirmScreen::setMessage(
ui::Screen &prev, void (*callback)(ui::Context &ctx), const char *format, void (*callback)(ui::Context &ctx), const char *format, ...
...
) { ) {
_prevScreen = &prev;
_callback = callback; _callback = callback;
va_list ap; va_list ap;
@ -106,7 +98,7 @@ void ConfirmScreen::update(ui::Context &ctx) {
if (_activeButton) if (_activeButton)
_callback(ctx); _callback(ctx);
else else
ctx.show(*_prevScreen, true, true); ctx.show(*previousScreen, true, true);
} }
} }
@ -161,10 +153,8 @@ const char *FilePickerScreen::_getItemName(ui::Context &ctx, int index) const {
} }
void FilePickerScreen::setMessage( void FilePickerScreen::setMessage(
ui::Screen &prev, void (*callback)(ui::Context &ctx), const char *format, void (*callback)(ui::Context &ctx), const char *format, ...
...
) { ) {
_prevScreen = &prev;
_callback = callback; _callback = callback;
va_list ap; va_list ap;
@ -181,9 +171,9 @@ void FilePickerScreen::reloadAndShow(ui::Context &ctx) {
if (!dev.atapiPoll()) if (!dev.atapiPoll())
continue; continue;
APP->_workerStatus.setNextScreen(*this); APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_setupWorker(&App::_ideInitWorker);
ctx.show(APP->_workerStatusScreen, false, true); APP->_runWorker(&App::_ideInitWorker, *this, false, true);
return; return;
} }
@ -213,8 +203,9 @@ void FilePickerScreen::update(ui::Context &ctx) {
ListScreen::update(ctx); ListScreen::update(ctx);
if (!_listLength) { if (!_listLength) {
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = previousScreen;
APP->_messageScreen.setMessage( APP->_messageScreen.setMessage(
MESSAGE_ERROR, *_prevScreen, STR("FilePickerScreen.noDeviceError") MESSAGE_ERROR, STR("FilePickerScreen.noDeviceError")
); );
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
return; return;
@ -222,7 +213,7 @@ void FilePickerScreen::update(ui::Context &ctx) {
if (ctx.buttons.pressed(ui::BTN_START)) { if (ctx.buttons.pressed(ui::BTN_START)) {
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) { if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
ctx.show(*_prevScreen, true, true); ctx.show(*previousScreen, true, true);
} else { } else {
int index = _activeItem; int index = _activeItem;
#ifndef NDEBUG #ifndef NDEBUG
@ -259,9 +250,8 @@ void FilePickerScreen::update(ui::Context &ctx) {
else else
error = "FilePickerScreen.ideError"_h; error = "FilePickerScreen.ideError"_h;
APP->_messageScreen.setMessage( APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
MESSAGE_ERROR, *this, STRH(error) APP->_messageScreen.setMessage(MESSAGE_ERROR, STRH(error));
);
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
} }
} }
@ -427,9 +417,10 @@ void FileBrowserScreen::update(ui::Context &ctx) {
if (loadDirectory(ctx, selectedPath) < 0) { if (loadDirectory(ctx, selectedPath) < 0) {
loadDirectory(ctx, _currentPath, false); loadDirectory(ctx, _currentPath, false);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_messageScreen.setMessage( APP->_messageScreen.setMessage(
MESSAGE_ERROR, *this, MESSAGE_ERROR, STR("FileBrowserScreen.subdirError"),
STR("FileBrowserScreen.subdirError"), selectedPath selectedPath
); );
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
} }

View File

@ -16,6 +16,8 @@ public:
void update(ui::Context &ctx); void update(ui::Context &ctx);
}; };
static constexpr size_t NUM_MESSAGE_TYPES = 3;
enum MessageType { enum MessageType {
MESSAGE_SUCCESS = 0, MESSAGE_SUCCESS = 0,
MESSAGE_WARNING = 1, MESSAGE_WARNING = 1,
@ -26,12 +28,11 @@ class MessageScreen : public ui::MessageBoxScreen {
private: private:
MessageType _type; MessageType _type;
char _bodyText[512]; char _bodyText[512];
ui::Screen *_prevScreen;
public: public:
void setMessage( ui::Screen *previousScreens[NUM_MESSAGE_TYPES];
MessageType type, ui::Screen &prev, const char *format, ...
); void setMessage(MessageType type, const char *format, ...);
void show(ui::Context &ctx, bool goBack = false); void show(ui::Context &ctx, bool goBack = false);
void update(ui::Context &ctx); void update(ui::Context &ctx);
@ -40,13 +41,13 @@ public:
class ConfirmScreen : public ui::MessageBoxScreen { class ConfirmScreen : public ui::MessageBoxScreen {
private: private:
char _bodyText[512]; char _bodyText[512];
ui::Screen *_prevScreen;
void (*_callback)(ui::Context &ctx); void (*_callback)(ui::Context &ctx);
public: public:
ui::Screen *previousScreen;
void setMessage( void setMessage(
ui::Screen &prev, void (*callback)(ui::Context &ctx), void (*callback)(ui::Context &ctx), const char *format, ...
const char *format, ...
); );
void show(ui::Context &ctx, bool goBack = false); void show(ui::Context &ctx, bool goBack = false);
@ -60,7 +61,6 @@ class FilePickerScreen : public ui::ListScreen {
private: private:
char _promptText[512]; char _promptText[512];
ui::Screen *_prevScreen;
void (*_callback)(ui::Context &ctx); void (*_callback)(ui::Context &ctx);
int _drives[util::countOf(ide::devices)]; int _drives[util::countOf(ide::devices)];
@ -69,9 +69,10 @@ protected:
const char *_getItemName(ui::Context &ctx, int index) const; const char *_getItemName(ui::Context &ctx, int index) const;
public: public:
ui::Screen *previousScreen;
void setMessage( void setMessage(
ui::Screen &prev, void (*callback)(ui::Context &ctx), void (*callback)(ui::Context &ctx), const char *format, ...
const char *format, ...
); );
void reloadAndShow(ui::Context &ctx); void reloadAndShow(ui::Context &ctx);

View File

@ -205,20 +205,26 @@ const char *StorageActionsScreen::_getItemName(
} }
void StorageActionsScreen::checksum(ui::Context &ctx) { void StorageActionsScreen::checksum(ui::Context &ctx) {
if (APP->_checksumScreen.valid) { if (APP->_checksumScreen.valid)
ctx.show(APP->_checksumScreen, false, true); ctx.show(APP->_checksumScreen, false, true);
} else { else
APP->_setupWorker(&App::_romChecksumWorker); APP->_runWorker(
ctx.show(APP->_workerStatusScreen, false, true); &App::_romChecksumWorker, APP->_checksumScreen, false, true
} );
} }
void StorageActionsScreen::dump(ui::Context &ctx) { void StorageActionsScreen::dump(ui::Context &ctx) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_romDumpWorker); APP->_messageScreen.previousScreens[MESSAGE_SUCCESS] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_storageInfoScreen);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
&(APP->_storageActionsScreen);
APP->_runWorker(
&App::_romDumpWorker, APP->_messageScreen, false, true
);
}, },
STR("StorageActionsScreen.dump.confirm") STR("StorageActionsScreen.dump.confirm")
); );
@ -227,18 +233,25 @@ void StorageActionsScreen::dump(ui::Context &ctx) {
} }
void StorageActionsScreen::restore(ui::Context &ctx) { void StorageActionsScreen::restore(ui::Context &ctx) {
APP->_filePickerScreen.previousScreen = this;
APP->_filePickerScreen.setMessage( APP->_filePickerScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
ctx.show(APP->_confirmScreen, false, true); ctx.show(APP->_confirmScreen, false, true);
}, },
STR("StorageActionsScreen.restore.filePrompt") STR("StorageActionsScreen.restore.filePrompt")
); );
APP->_confirmScreen.previousScreen = &(APP->_fileBrowserScreen);
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
APP->_fileBrowserScreen,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_romRestoreWorker); APP->_messageScreen.previousScreens[MESSAGE_SUCCESS] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_storageInfoScreen);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
&(APP->_fileBrowserScreen);
APP->_runWorker(
&App::_romRestoreWorker, APP->_messageScreen, false, true
);
}, },
STR("StorageActionsScreen.restore.confirm") STR("StorageActionsScreen.restore.confirm")
); );
@ -247,11 +260,17 @@ void StorageActionsScreen::restore(ui::Context &ctx) {
} }
void StorageActionsScreen::erase(ui::Context &ctx) { void StorageActionsScreen::erase(ui::Context &ctx) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
APP->_setupWorker(&App::_romEraseWorker); APP->_messageScreen.previousScreens[MESSAGE_SUCCESS] =
ctx.show(APP->_workerStatusScreen, false, true); &(APP->_storageInfoScreen);
APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
&(APP->_storageActionsScreen);
APP->_runWorker(
&App::_romEraseWorker, APP->_messageScreen, false, true
);
}, },
STR("StorageActionsScreen.erase.confirm") STR("StorageActionsScreen.erase.confirm")
); );
@ -260,12 +279,18 @@ void StorageActionsScreen::erase(ui::Context &ctx) {
} }
void StorageActionsScreen::resetFlashHeader(ui::Context &ctx) { void StorageActionsScreen::resetFlashHeader(ui::Context &ctx) {
APP->_confirmScreen.previousScreen = this;
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) { [](ui::Context &ctx) {
util::clear(APP->_romHeaderDump.data); util::clear(APP->_romHeaderDump.data);
APP->_setupWorker(&App::_flashHeaderWriteWorker);
ctx.show(APP->_workerStatusScreen, false, true); APP->_messageScreen.previousScreens[MESSAGE_ERROR] =
&(APP->_storageActionsScreen);
APP->_runWorker(
&App::_flashHeaderWriteWorker, APP->_storageInfoScreen, true,
true
);
}, },
STR("StorageActionsScreen.resetFlashHeader.confirm") STR("StorageActionsScreen.resetFlashHeader.confirm")
); );
@ -313,8 +338,9 @@ void StorageActionsScreen::update(ui::Context &ctx) {
(this->*action.target)(ctx); (this->*action.target)(ctx);
} }
} else { } else {
APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this;
APP->_messageScreen.setMessage( APP->_messageScreen.setMessage(
MESSAGE_ERROR, *this, STR("StorageActionsScreen.cardError") MESSAGE_ERROR, STR("StorageActionsScreen.cardError")
); );
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);

View File

@ -57,7 +57,6 @@ static constexpr size_t _DUMP_CHUNKS_PER_CRC = 32; // Save CRC32 every 16 MB
// TODO: all these *really* need a cleanup... // TODO: all these *really* need a cleanup...
bool App::_romChecksumWorker(void) { bool App::_romChecksumWorker(void) {
_workerStatus.setNextScreen(_checksumScreen);
_checksumScreen.valid = false; _checksumScreen.valid = false;
for (auto &entry : _REGION_INFO) { for (auto &entry : _REGION_INFO) {
@ -100,7 +99,7 @@ bool App::_romDumpWorker(void) {
// Store all dumps in a subdirectory named "dumpNNNN" within the main data // Store all dumps in a subdirectory named "dumpNNNN" within the main data
// folder. // folder.
char dirPath[32], filePath[32]; char dirPath[file::MAX_PATH_LENGTH], filePath[file::MAX_PATH_LENGTH];
if (!_createDataDirectory()) if (!_createDataDirectory())
goto _initError; goto _initError;
@ -158,26 +157,20 @@ bool App::_romDumpWorker(void) {
} }
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_SUCCESS, _storageInfoScreen, WSTR("App.romDumpWorker.success"), MESSAGE_SUCCESS, WSTR("App.romDumpWorker.success"), dirPath
dirPath
); );
_workerStatus.setNextScreen(_messageScreen);
return true; return true;
_initError: _initError:
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, WSTR("App.romDumpWorker.initError"), MESSAGE_ERROR, WSTR("App.romDumpWorker.initError"), dirPath
dirPath
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
_fileError: _fileError:
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, WSTR("App.romDumpWorker.fileError"), MESSAGE_ERROR, WSTR("App.romDumpWorker.fileError"), filePath
filePath
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
} }
@ -299,18 +292,13 @@ bool App::_romRestoreWorker(void) {
delete _file; delete _file;
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(MESSAGE_SUCCESS, WSTRH(message), bytesWritten);
MESSAGE_SUCCESS, _storageInfoScreen, WSTRH(message), bytesWritten
);
_workerStatus.setNextScreen(_messageScreen);
return true; return true;
_fileError: _fileError:
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, MESSAGE_ERROR, WSTR("App.romRestoreWorker.fileError"), path
WSTR("App.romRestoreWorker.fileError"), path
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
_flashError: _flashError:
@ -321,11 +309,9 @@ _flashError:
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, MESSAGE_ERROR, WSTR("App.romRestoreWorker.flashError"),
WSTR("App.romRestoreWorker.flashError"), rom::getErrorString(error), rom::getErrorString(error), bytesWritten
bytesWritten
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
} }
@ -366,31 +352,25 @@ bool App::_romEraseWorker(void) {
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_SUCCESS, _storageInfoScreen, WSTR("App.romEraseWorker.success"), MESSAGE_SUCCESS, WSTR("App.romEraseWorker.success"), sectorsErased
sectorsErased
); );
_workerStatus.setNextScreen(_messageScreen);
return true; return true;
_flashError: _flashError:
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, MESSAGE_ERROR, WSTR("App.romEraseWorker.flashError"),
WSTR("App.romEraseWorker.flashError"), rom::getErrorString(error), rom::getErrorString(error), sectorsErased
sectorsErased
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
_unsupported: _unsupported:
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, MESSAGE_ERROR, WSTR("App.romEraseWorker.unsupported")
WSTR("App.romEraseWorker.unsupported")
); );
_workerStatus.setNextScreen(_messageScreen);
return false; return false;
} }
@ -460,8 +440,6 @@ bool App::_flashHeaderWriteWorker(void) {
buffer.destroy(); buffer.destroy();
delete driver; delete driver;
_workerStatus.setNextScreen(_storageInfoScreen);
return true; return true;
_flashError: _flashError:
@ -469,8 +447,7 @@ _flashError:
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, MESSAGE_ERROR, WSTR("App.flashHeaderWriteWorker.flashError"),
WSTR("App.flashHeaderWriteWorker.flashError"),
rom::getErrorString(error) rom::getErrorString(error)
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
@ -480,8 +457,7 @@ _unsupported:
delete driver; delete driver;
_messageScreen.setMessage( _messageScreen.setMessage(
MESSAGE_ERROR, _storageInfoScreen, MESSAGE_ERROR, WSTR("App.flashHeaderWriteWorker.unsupported")
WSTR("App.flashHeaderWriteWorker.unsupported")
); );
_workerStatus.setNextScreen(_messageScreen); _workerStatus.setNextScreen(_messageScreen);
return false; return false;

View File

@ -52,14 +52,14 @@ static const uint32_t _BUTTON_MAPPINGS[NUM_BUTTON_MAPS][NUM_BUTTONS]{
}; };
ButtonState::ButtonState(void) ButtonState::ButtonState(void)
: _held(0), _prevHeld(0), _longHeld(0), _prevLongHeld(0), _pressed(0), : _buttonMap(MAP_JOYSTICK), _held(0), _prevHeld(0), _longHeld(0),
_released(0), _longPressed(0), _longReleased(0), _repeatTimer(0), _prevLongHeld(0), _pressed(0), _released(0), _longPressed(0), _longReleased(0),
buttonMap(MAP_JOYSTICK) {} _repeatTimer(0) {}
uint8_t ButtonState::_getHeld(void) const { uint8_t ButtonState::_getHeld(void) const {
uint32_t inputs = io::getJAMMAInputs(); uint32_t inputs = io::getJAMMAInputs();
uint8_t held = 0; uint8_t held = 0;
auto map = _BUTTON_MAPPINGS[buttonMap]; auto map = _BUTTON_MAPPINGS[_buttonMap];
#ifdef ENABLE_PS1_CONTROLLER #ifdef ENABLE_PS1_CONTROLLER
if (pad::ports[0].pollPad() || pad::ports[1].pollPad()) { if (pad::ports[0].pollPad() || pad::ports[1].pollPad()) {
@ -115,30 +115,33 @@ void ButtonState::update(void) {
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;
_longHeld = 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. Note that the repeat timer is not
if (_held) { // started if single button mode is enabled while a button is held down.
if (changed & _held) {
_repeatTimer = 1;
} else if (changed & _prevHeld) {
if (_repeatTimer && (_repeatTimer < REPEAT_DELAY))
_pressed |= 1 << BTN_RIGHT;
_repeatTimer = 0;
} else if (_held && _repeatTimer) {
if (_repeatTimer == REPEAT_DELAY) if (_repeatTimer == REPEAT_DELAY)
_pressed |= 1 << BTN_START; _pressed |= 1 << BTN_START;
_repeatTimer++; _repeatTimer++;
} else if (_prevHeld) {
if (_repeatTimer >= REPEAT_DELAY)
_released |= 1 << BTN_START;
else
_pressed |= 1 << BTN_RIGHT;
_repeatTimer = 0;
} }
} else { } else {
if (changed) if (changed & _held)
_repeatTimer = 1;
else if (changed & _prevHeld)
_repeatTimer = 0; _repeatTimer = 0;
else if (_held) else if (_held && _repeatTimer)
_repeatTimer++; _repeatTimer++;
_pressed = (changed & _held) & ~_pressed; _pressed = (changed & _held) & ~_pressed;
@ -162,7 +165,7 @@ Context::Context(gpu::Context &gpuCtx, void *screenData)
} }
void Context::show(Screen &screen, bool goBack, bool playSound) { void Context::show(Screen &screen, bool goBack, bool playSound) {
auto oldScreen = _screens[_currentScreen]; auto oldScreen = getCurrentScreen();
if (oldScreen) if (oldScreen)
oldScreen->hide(*this, goBack); oldScreen->hide(*this, goBack);
@ -177,8 +180,8 @@ void Context::show(Screen &screen, bool goBack, bool playSound) {
} }
void Context::draw(void) { void Context::draw(void) {
auto oldScreen = _screens[_currentScreen ^ 1]; auto oldScreen = getInactiveScreen();
auto newScreen = _screens[_currentScreen]; auto newScreen = getCurrentScreen();
for (auto layer : backgrounds) { for (auto layer : backgrounds) {
if (layer) if (layer)
@ -204,8 +207,10 @@ void Context::update(void) {
layer->update(*this); layer->update(*this);
} }
if (_screens[_currentScreen]) auto screen = getCurrentScreen();
_screens[_currentScreen]->update(*this);
if (screen)
screen->update(*this);
} }
/* Layer classes */ /* Layer classes */

View File

@ -105,7 +105,9 @@ enum ButtonMap {
class ButtonState { class ButtonState {
private: private:
ButtonMap _buttonMap;
uint32_t _mappings[NUM_BUTTONS]; uint32_t _mappings[NUM_BUTTONS];
uint8_t _held, _prevHeld; uint8_t _held, _prevHeld;
uint8_t _longHeld, _prevLongHeld; uint8_t _longHeld, _prevLongHeld;
uint8_t _pressed, _released; uint8_t _pressed, _released;
@ -116,7 +118,10 @@ private:
uint8_t _getHeld(void) const; uint8_t _getHeld(void) const;
public: public:
ButtonMap buttonMap; inline void setButtonMap(ButtonMap map) {
reset();
_buttonMap = map;
}
inline bool held(Button button) const { inline bool held(Button button) const {
return (_held >> button) & 1; return (_held >> button) & 1;
@ -167,6 +172,12 @@ public:
int time; int time;
void *screenData; // Opaque, can be accessed by screens void *screenData; // Opaque, can be accessed by screens
inline Screen *getCurrentScreen(void) const {
return _screens[_currentScreen];
}
inline Screen *getInactiveScreen(void) const {
return _screens[_currentScreen ^ 1];
}
inline void tick(void) { inline void tick(void) {
//buttons.update(); //buttons.update();
time++; time++;