From 24b24674aedbdcb4432ab2ed823c3d9c97852da4 Mon Sep 17 00:00:00 2001 From: notaz Date: Sun, 22 Feb 2009 22:58:59 +0000 Subject: [PATCH] unified menu wip and some reorganization for it git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@638 be3aeb3a-fb24-0410-a615-afba39da0efa --- common/common.h | 2 + common/emu.h | 22 +-- common/menu.c | 400 ++++++++++++++++++++++++++++++++++++++++++++---- common/menu.h | 33 ++-- common/plat.h | 23 +++ gp2x/emu.h | 12 -- gp2x/menu.c | 24 +-- gp2x/plat.c | 43 ++++++ linux/Makefile | 17 +- psp/emu.h | 13 -- 10 files changed, 487 insertions(+), 102 deletions(-) create mode 100644 common/plat.h create mode 100644 gp2x/plat.c diff --git a/common/common.h b/common/common.h index d641c38..8904ecd 100644 --- a/common/common.h +++ b/common/common.h @@ -1,5 +1,7 @@ // platform specific things for common menu code +/* TODO: get rid of this file */ + #ifdef __GP2X__ #include "../gp2x/gp2x.h" diff --git a/common/emu.h b/common/emu.h index ba5cf43..15643b9 100644 --- a/common/emu.h +++ b/common/emu.h @@ -54,6 +54,19 @@ extern char loadedRomFName[512]; // last loaded ROM filename extern int kb_combo_keys, kb_combo_acts; // keys and actions which need button combos extern int pico_inp_mode; +// engine states +enum TPicoGameState { + PGS_Paused = 1, + PGS_Running, + PGS_Quit, + PGS_KeyConfig, + PGS_ReloadRom, + PGS_Menu, + PGS_RestartRun, + PGS_Suspending, /* PSP */ + PGS_SuspendWake, /* PSP */ +}; + int emu_ReloadRom(char *rom_fname); int emu_SaveLoadGame(int load, int sram); @@ -78,15 +91,6 @@ void emu_packConfig(void); void emu_unpackConfig(void); void emu_shutdownMCD(void); -/* not in common */ -extern const char * const keyNames[]; -void emu_prepareDefaultConfig(void); -void emu_platformDebugCat(char *str); -void emu_forcedFrame(int opts); -void emu_startSound(void); -void emu_endSound(void); -void emu_waitSound(void); - #ifdef __cplusplus } // extern "C" #endif diff --git a/common/menu.c b/common/menu.c index c3ef87f..8674897 100644 --- a/common/menu.c +++ b/common/menu.c @@ -15,7 +15,9 @@ #include "common.h" #include "input.h" #include "emu.h" +#include "plat.h" +#include char menuErrorMsg[64] = { 0, }; @@ -98,12 +100,17 @@ static void text_out16_(int x, int y, const char *text, int color) void text_out16(int x, int y, const char *texto, ...) { va_list args; - char buffer[512]; + char buffer[256]; + int maxw = (SCREEN_WIDTH - x) / 8; - va_start(args,texto); - vsprintf(buffer,texto,args); + va_start(args, texto); + vsnprintf(buffer, sizeof(buffer), texto, args); va_end(args); + if (maxw > 255) + maxw = 255; + buffer[maxw] = 0; + text_out16_(x,y,buffer,menu_text_color); } @@ -246,52 +253,51 @@ void menu_init(void) } -int me_id2offset(const menu_entry *entries, int count, menu_id id) +int me_id2offset(const menu_entry *ent, menu_id id) { int i; - for (i = 0; i < count; i++) - { - if (entries[i].id == id) return i; - } + for (i = 0; ent->name; ent++, i++) + if (ent->id == id) return i; lprintf("%s: id %i not found\n", __FUNCTION__, id); return 0; } -void me_enable(menu_entry *entries, int count, menu_id id, int enable) +void me_enable(menu_entry *entries, menu_id id, int enable) { - int i = me_id2offset(entries, count, id); + int i = me_id2offset(entries, id); entries[i].enabled = enable; } -int me_count_enabled(const menu_entry *entries, int count) +int me_count(const menu_entry *ent) { - int i, ret = 0; + int ret; - for (i = 0; i < count; i++) - { - if (entries[i].enabled) ret++; - } + for (ret = 0; ent->name; ent++, ret++) + ; return ret; } -menu_id me_index2id(const menu_entry *entries, int count, int index) +menu_id me_index2id(const menu_entry *ent, int index) { - int i; + const menu_entry *last; - for (i = 0; i < count; i++) + for (; ent->name; ent++) { - if (entries[i].enabled) + if (ent->enabled) { if (index == 0) break; index--; } + last = ent; } - if (i >= count) i = count - 1; - return entries[i].id; + if (ent->name == NULL) + ent = last; + return ent->id; } +/* TODO rm */ void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param) { int i, y1 = y; @@ -307,24 +313,132 @@ void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_ continue; } text_out16(x, y1, entries[i].name); - if (entries[i].beh == MB_ONOFF) + if (entries[i].beh == MB_OPT_ONOFF) text_out16(x + 27*8, y1, (*(int *)entries[i].var & entries[i].mask) ? "ON" : "OFF"); - else if (entries[i].beh == MB_RANGE) + else if (entries[i].beh == MB_OPT_RANGE) text_out16(x + 27*8, y1, "%i", *(int *)entries[i].var); y1 += 10; } + +} + +static void me_draw2(const menu_entry *entries, int sel) +{ + const menu_entry *ent; + int x, y, w = 0, h = 0; + int opt_offs = 27*8; + const char *name; + int asel = 0; + int i, n; + + /* calculate size of menu rect */ + for (ent = entries, i = n = 0; ent->name; ent++, i++) + { + int wt; + + if (!ent->enabled) + continue; + + if (i == sel) + asel = n; + + name = NULL; + wt = strlen(ent->name) * 8; /* FIXME: unhardcode font width */ + if (wt == 0 && ent->generate_name) + name = ent->generate_name(1); + if (name != NULL) + wt = strlen(name) * 8; + + if (ent->beh != MB_NONE) + { + if (wt > opt_offs) + opt_offs = wt + 8; + wt = opt_offs; + + switch (ent->beh) { + case MB_NONE: break; + case MB_OPT_ONOFF: + case MB_OPT_RANGE: wt += 8*3; break; + case MB_OPT_CUSTOM: + name = NULL; + if (ent->generate_name != NULL) + name = ent->generate_name(0); + if (name != NULL) + wt += strlen(name) * 8; + break; + } + } + + if (wt > w) + w = wt; + n++; + } + h = n * 10; + w += 16; /* selector */ + + if (w > SCREEN_WIDTH) { + lprintf("width %d > %d\n", w, SCREEN_WIDTH); + w = SCREEN_WIDTH; + } + if (h > SCREEN_HEIGHT) { + lprintf("height %d > %d\n", w, SCREEN_HEIGHT); + h = SCREEN_HEIGHT; + } + + x = SCREEN_WIDTH / 2 - w / 2; + y = SCREEN_HEIGHT / 2 - h / 2; + + /* draw */ + plat_video_menu_begin(); + menu_draw_selection(x, y + asel * 10, w); + + for (ent = entries; ent->name; ent++) + { + if (!ent->enabled) + continue; + + name = ent->name; + if (strlen(name) == 0) { + if (ent->generate_name) + name = ent->generate_name(1); + } + if (name != NULL) + text_out16(x + 16, y, name); + + switch (ent->beh) { + case MB_NONE: + break; + case MB_OPT_ONOFF: + text_out16(x + 16 + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF"); + break; + case MB_OPT_RANGE: + text_out16(x + 16 + opt_offs, y, "%i", *(int *)ent->var); + break; + case MB_OPT_CUSTOM: + name = NULL; + if (ent->generate_name) + name = ent->generate_name(0); + if (name != NULL) + text_out16(x + 16 + opt_offs, y, "%s", name); + break; + } + + y += 10; + } + + plat_video_menu_end(); } -int me_process(menu_entry *entries, int count, menu_id id, int is_next) +int me_process(menu_entry *entries, menu_id id, int is_next) { - int i = me_id2offset(entries, count, id); + int i = me_id2offset(entries, id); menu_entry *entry = &entries[i]; switch (entry->beh) { - case MB_ONOFF: + case MB_OPT_ONOFF: *(int *)entry->var ^= entry->mask; return 1; - case MB_RANGE: + case MB_OPT_RANGE: *(int *)entry->var += is_next ? 1 : -1; if (*(int *)entry->var < (int)entry->min) *(int *)entry->var = (int)entry->min; if (*(int *)entry->var > (int)entry->max) *(int *)entry->var = (int)entry->max; @@ -334,6 +448,210 @@ int me_process(menu_entry *entries, int count, menu_id id, int is_next) } } +static void me_loop(menu_entry *menu, int *menu_sel) +{ + int ret, inp, sel = *menu_sel, menu_sel_max; + + menu_sel_max = me_count(menu) - 1; + if (menu_sel_max < 1) { + lprintf("no enabled menu entries\n"); + return; + } + + while (!menu[sel].enabled && sel < menu_sel_max) + sel++; + + /* make sure action buttons are not pressed on entering menu */ + me_draw2(menu, sel); + while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU)); + + for (;;) + { + me_draw2(menu, sel); + inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R); + if (inp & PBTN_UP ) { + do { + sel--; + if (sel < 0) + sel = menu_sel_max; + } + while (!menu[sel].enabled); + } + if (inp & PBTN_DOWN) { + do { + sel++; + if (sel > menu_sel_max) + sel = 0; + } + while (!menu[sel].enabled); + } +// if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R)) debug_menu_loop(); // TODO + if (inp & (PBTN_MENU|PBTN_MBACK)) + break; + + if (inp & PBTN_MOK) + { + if (menu[sel].submenu_handler != NULL) { + ret = menu[sel].submenu_handler(menu[sel].id); + if (ret) break; + } + } +// menuErrorMsg[0] = 0; // TODO: clear error msg + } + *menu_sel = sel; +} + +/* ***************************************** */ + +/* TODO s */ +int menu_loop_tray(void) { return 0; } +void menu_romload_prepare(const char *rom_name) {} +void menu_romload_end(void) {} +me_bind_action emuctrl_actions[1]; +menu_entry opt_entries[1]; +menu_entry opt2_entries[1]; +menu_entry cdopt_entries[1]; +menu_entry ctrlopt_entries[1]; +const int opt_entry_count = 0; +const int opt2_entry_count = 0; +const int cdopt_entry_count = 0; +const int ctrlopt_entry_count = 0; + +extern int engineState; + +int savestate_menu_loop(int a) { return 1; } +int menu_loop_options() { return 1; } +void kc_sel_loop() {} +void draw_menu_credits() {} +void patches_menu_loop() {} + +// ------------ main menu ------------ + +static int main_menu_handler(menu_id id) +{ + int ret; + + switch (id) + { + case MA_MAIN_RESUME_GAME: + if (rom_loaded) { + while (in_menu_wait_any(50) & PBTN_MOK); + engineState = PGS_Running; + return 1; + } + break; + case MA_MAIN_SAVE_STATE: + if (rom_loaded) { + if (savestate_menu_loop(0)) + break; + engineState = PGS_Running; + return 1; + } + break; + case MA_MAIN_LOAD_STATE: + if (rom_loaded) { + if (savestate_menu_loop(1)) + break; + while (in_menu_wait_any(50) & PBTN_MOK); + engineState = PGS_Running; + return 1; + } + break; + case MA_MAIN_RESET_GAME: + if (rom_loaded) { + emu_ResetGame(); + while (in_menu_wait_any(50) & PBTN_MOK); + engineState = PGS_Running; + return 1; + } + break; + case MA_MAIN_LOAD_ROM: + { +/* char curr_path[PATH_MAX], *selfname; + FILE *tstf; + if ( (tstf = fopen(loadedRomFName, "rb")) ) + { + fclose(tstf); + strcpy(curr_path, loadedRomFName); + } + else + getcwd(curr_path, PATH_MAX); + selfname = romsel_loop(curr_path); + if (selfname) { + printf("selected file: %s\n", selfname); + engineState = PGS_ReloadRom; + return; + }*/ + break; + } + case MA_MAIN_OPTIONS: + ret = menu_loop_options(); + if (ret == 1) break; // status update + if (engineState == PGS_ReloadRom) + return 1; // BIOS test + break; + case MA_MAIN_CONTROLS: + kc_sel_loop(); + break; + case MA_MAIN_CREDITS: + draw_menu_credits(); + usleep(500*1000); /* FIXME */ + in_menu_wait(PBTN_MOK|PBTN_MBACK); + break; + case MA_MAIN_EXIT: + engineState = PGS_Quit; + return 1; + case MA_MAIN_PATCHES: + if (rom_loaded && PicoPatches) { + patches_menu_loop(); + PicoPatchApply(); + strcpy(menuErrorMsg, "Patches applied"); + } + break; + default: + lprintf("%s: something unknown selected\n", __FUNCTION__); + break; + } + + return 0; +} + +menu_entry e_main_menu[] = +{ + mee_submenu_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler), + mee_submenu_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler), + mee_submenu_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler), + mee_submenu_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler), + mee_submenu_id("Load new ROM/ISO", MA_MAIN_LOAD_ROM, main_menu_handler), + mee_submenu_id("Change options", MA_MAIN_OPTIONS, main_menu_handler), + mee_submenu_id("Credits", MA_MAIN_CREDITS, main_menu_handler), + mee_submenu_id("Patches / GameGenie",MA_MAIN_PATCHES, main_menu_handler), + mee_submenu_id("Exit", MA_MAIN_EXIT, main_menu_handler), + mee_end, +}; + +void menu_loop(void) +{ + static int sel = 0; + + me_enable(e_main_menu, MA_MAIN_RESUME_GAME, rom_loaded); + me_enable(e_main_menu, MA_MAIN_SAVE_STATE, rom_loaded); + me_enable(e_main_menu, MA_MAIN_LOAD_STATE, rom_loaded); + me_enable(e_main_menu, MA_MAIN_RESET_GAME, rom_loaded); + me_enable(e_main_menu, MA_MAIN_PATCHES, PicoPatches != NULL); + + plat_video_menu_enter(rom_loaded); + in_set_blocking(1); + me_loop(e_main_menu, &sel); + in_set_blocking(0); + + if (rom_loaded) { + while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MBACK)); // wait until select is released + engineState = PGS_Running; + } + +} + // ------------ debug menu ------------ #include @@ -406,7 +724,7 @@ void debug_menu_loop(void) { switch (mode) { - case 0: menu_draw_begin(); + case 0: plat_video_menu_begin(); tmp = PDebugMain(); emu_platformDebugCat(tmp); draw_text_debug(tmp, 0, 0); @@ -428,7 +746,7 @@ void debug_menu_loop(void) draw_text_debug(PDebugSpriteList(), spr_offs, 6); break; } - menu_draw_end(); + plat_video_menu_end(); inp = in_menu_wait(PBTN_EAST|PBTN_MBACK|PBTN_WEST|PBTN_NORTH|PBTN_L|PBTN_R|PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT); if (inp & PBTN_MBACK) return; @@ -497,4 +815,26 @@ const char *me_region_name(unsigned int code, int auto_order) } } +/* TODO: rename */ +void menu_darken_bg(void *dst, int pixels, int darker) +{ + unsigned int *screen = dst; + pixels /= 2; + if (darker) + { + while (pixels--) + { + unsigned int p = *screen; + *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3); + } + } + else + { + while (pixels--) + { + unsigned int p = *screen; + *screen++ = (p&0xf79ef79e)>>1; + } + } +} diff --git a/common/menu.h b/common/menu.h index b17f2dd..0227d52 100644 --- a/common/menu.h +++ b/common/menu.h @@ -13,8 +13,9 @@ extern char menuErrorMsg[64]; typedef enum { MB_NONE = 1, /* no auto processing */ - MB_ONOFF, /* ON/OFF setting */ - MB_RANGE, /* [min-max] setting */ + MB_OPT_ONOFF, /* ON/OFF setting */ + MB_OPT_RANGE, /* [min-max] setting */ + MB_OPT_CUSTOM, } menu_behavior; typedef enum @@ -104,14 +105,25 @@ typedef struct char *name; menu_behavior beh; menu_id id; - void *var; /* for on-off settings */ - int mask; + void *var; /* for on-off/range settings */ + int mask; /* bit to toggle for on/off */ signed char min; /* for ranged integer settings, to be sign-extended */ signed char max; char enabled; char need_to_save; + int (*submenu_handler)(menu_id id); + const char * (*generate_name)(int is_left); } menu_entry; +#define mee_submenu_id(name, id, handler) \ + { name, MB_NONE, id, NULL, 0, 0, 0, 1, 0, handler, NULL } + +#define mee_submenu(name, handler) \ + mee_submenu_id(name, MA_NONE, handler) + +#define mee_end \ + { NULL, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, NULL } + typedef struct { char *name; @@ -124,12 +136,15 @@ extern me_bind_action emuctrl_actions[]; // platform code typedef void (me_draw_custom_f)(const menu_entry *entry, int x, int y, void *param); -int me_id2offset(const menu_entry *entries, int count, menu_id id); -void me_enable(menu_entry *entries, int count, menu_id id, int enable); -int me_count_enabled(const menu_entry *entries, int count); -menu_id me_index2id(const menu_entry *entries, int count, int index); +/* TODO: move? */ +int me_id2offset(const menu_entry *entries, menu_id id); +void me_enable(menu_entry *entries, menu_id id, int enable); +int me_count_enabled(const menu_entry *ent); +menu_id me_index2id(const menu_entry *entries, int index); void me_draw(const menu_entry *entries, int count, int x, int y, me_draw_custom_f *cust_draw, void *param); -int me_process(menu_entry *entries, int count, menu_id id, int is_next); +int me_process(menu_entry *entries, menu_id id, int is_next); const char *me_region_name(unsigned int code, int auto_order); +void menu_darken_bg(void *dst, int pixels, int darker); + diff --git a/common/plat.h b/common/plat.h new file mode 100644 index 0000000..35ceaeb --- /dev/null +++ b/common/plat.h @@ -0,0 +1,23 @@ +/* stuff to be implemented by platform code */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char * const keyNames[]; +void emu_prepareDefaultConfig(void); +void emu_platformDebugCat(char *str); +void emu_forcedFrame(int opts); +void emu_startSound(void); +void emu_endSound(void); +void emu_waitSound(void); + +/* menu: enter (switch bpp, etc), begin/end drawing */ +void plat_video_menu_enter(int is_rom_loaded); +void plat_video_menu_begin(void); +void plat_video_menu_end(void); + +#ifdef __cplusplus +} // extern "C" +#endif + diff --git a/gp2x/emu.h b/gp2x/emu.h index 88947b5..21190c0 100644 --- a/gp2x/emu.h +++ b/gp2x/emu.h @@ -4,18 +4,6 @@ // For commercial use, separate licencing terms must be obtained. - -// engine states -enum TPicoGameState { - PGS_Paused = 1, - PGS_Running, - PGS_Quit, - PGS_KeyConfig, - PGS_ReloadRom, - PGS_Menu, - PGS_RestartRun, -}; - extern char romFileName[]; extern int engineState; diff --git a/gp2x/menu.c b/gp2x/menu.c index b44e514..5221543 100644 --- a/gp2x/menu.c +++ b/gp2x/menu.c @@ -1483,28 +1483,6 @@ static void menu_loop_root(void) } } -void menu_darken_bg(void *dst, int pixels, int darker) -{ - unsigned int *screen = dst; - pixels /= 2; - if (darker) - { - while (pixels--) - { - unsigned int p = *screen; - *screen++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3); - } - } - else - { - while (pixels--) - { - unsigned int p = *screen; - *screen++ = (p&0xf79ef79e)>>1; - } - } -} - static void menu_prepare_bg(int use_game_bg) { if (use_game_bg) @@ -1535,7 +1513,7 @@ static void menu_gfx_prepare(void) } -void menu_loop(void) +void menu_loop_old(void) { in_set_blocking(1); menu_gfx_prepare(); diff --git a/gp2x/plat.c b/gp2x/plat.c new file mode 100644 index 0000000..16fa183 --- /dev/null +++ b/gp2x/plat.c @@ -0,0 +1,43 @@ +#include + +#include "gp2x.h" +#include "../common/plat.h" +#include "../common/readpng.h" +#include "../common/menu.h" + + +void plat_video_menu_enter(int is_rom_loaded) +{ + if (is_rom_loaded) + { + // darken the active framebuffer + memset(gp2x_screen, 0, 320*8*2); + menu_darken_bg((char *)gp2x_screen + 320*8*2, 320*224, 1); + memset((char *)gp2x_screen + 320*232*2, 0, 320*8*2); + } + else + { + // should really only happen once, on startup.. + readpng(gp2x_screen, "skin/background.png", READPNG_BG); + } + + // copy to buffer2 + gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2); + + // switch to 16bpp + gp2x_video_changemode2(16); + gp2x_video_RGB_setscaling(0, 320, 240); + gp2x_video_flip2(); +} + +void plat_video_menu_begin(void) +{ + gp2x_pd_clone_buffer2(); +} + +void plat_video_menu_end(void) +{ + gp2x_video_flush_cache(); + gp2x_video_flip2(); +} + diff --git a/linux/Makefile b/linux/Makefile index 07ec24e..f7b6827 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -3,11 +3,11 @@ use_musashi = 1 #use_fame = 1 #use_mz80 = 1 +#profile = 1 +fake_in_gp2x = 1 -# profile = 1 - -DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT -DIO_STATS -DIN_EVDEV -DIN_GP2X -DFAKE_IN_GP2X # -DBENCHMARK +DEFINC = -I../.. -I. -D__GP2X__ -D_UNZIP_SUPPORT -DIO_STATS -DIN_EVDEV # -DBENCHMARK GCC = gcc STRIP = strip AS = gcc @@ -27,9 +27,14 @@ COPT += `pkg-config --cflags gthread-2.0` LDFLAGS += `pkg-config --libs gthread-2.0` # frontend -OBJS += platform/gp2x/main.o platform/gp2x/menu.o platform/gp2x/emu.o usbjoy.o blit.o \ - in_evdev.o sndout_oss.o gp2x.o 940ctl_ym2612.o log_io.o \ - platform/gp2x/in_gp2x.o +OBJS += platform/gp2x/main.o platform/gp2x/emu.o platform/gp2x/plat.o usbjoy.o blit.o \ + in_evdev.o sndout_oss.o gp2x.o 940ctl_ym2612.o log_io.o +# platform/gp2x/menu.o + +ifeq "$(fake_in_gp2x)" "1" +DEFINC += -DIN_GP2X -DFAKE_IN_GP2X +OBJS += platform/gp2x/in_gp2x.o +endif # common OBJS += platform/common/emu.o platform/common/menu.o platform/common/config.o platform/common/fonts.o \ diff --git a/psp/emu.h b/psp/emu.h index c27c566..fc0a36f 100644 --- a/psp/emu.h +++ b/psp/emu.h @@ -5,19 +5,6 @@ -// engine states -enum TPicoGameState { - PGS_Paused = 1, - PGS_Running, - PGS_Quit, - PGS_KeyConfig, - PGS_ReloadRom, - PGS_Menu, - PGS_RestartRun, - PGS_Suspending, - PGS_SuspendWake, -}; - extern char romFileName[]; extern int engineState, engineStateSuspend; -- 2.39.2