diff --git a/arm9/source/common/hid.c b/arm9/source/common/hid.c index 7360dfd..f63afe0 100644 --- a/arm9/source/common/hid.c +++ b/arm9/source/common/hid.c @@ -56,3 +56,24 @@ bool CheckButton(u32 button) { for(; (t_pressed < 0x13000) && ((HID_STATE & button) == button); t_pressed++); return (t_pressed >= 0x13000); } + +void ButtonToString(u32 button, char* str) { + const char* strings[] = { BUTTON_STRINGS }; + + *str = '\0'; + if (button) { + u32 b = 0; + for (b = 0; !((button>>b)&0x1); b++); + if (b < countof(strings)) strcpy(str, strings[b]); + } +} + +u32 StringToButton(char* str) { + const char* strings[] = { BUTTON_STRINGS }; + + u32 b = 0; + for (b = 0; b < countof(strings); b++) + if (strcmp(str, strings[b]) == 0) break; + + return (b == countof(strings)) ? 0 : 1<= n) ? 0 : sel + 1; } +u32 ShowHotkeyPrompt(u32 n, const char** options, const u32* keys, const char *format, ...) { + char str[STRBUF_SIZE] = { 0 }; + char* ptr = str; + va_list va; + va_start(va, format); + ptr += vsnprintf(ptr, STRBUF_SIZE, format, va); + va_end(va); + + ptr += snprintf(ptr, STRBUF_SIZE - (ptr-str), "\n "); + for (u32 i = 0; i < n; i++) { + char buttonstr[16]; + ButtonToString(keys[i], buttonstr); + ptr += snprintf(ptr, STRBUF_SIZE - (ptr-str), "\n<%s> %s", buttonstr, options[i]); + } + ptr += snprintf(ptr, STRBUF_SIZE - (ptr-str), "\n \n<%s> %s", "B", "cancel"); + + ClearScreenF(true, false, COLOR_STD_BG); + DrawStringCenter(MAIN_SCREEN, COLOR_STD_FONT, COLOR_STD_BG, "%s", str); + + u32 sel = 0; + while (!sel) { + u32 pad_state = InputWait(0); + if (pad_state & BUTTON_B) break; + for (u32 i = 0; i < n; i++) { + if (keys[i] & pad_state) { + sel = i+1; + break; + } + } + } + + ClearScreenF(true, false, COLOR_STD_BG); + return sel; +} + bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alphabet, const char *format, va_list va) { const u32 alphabet_size = strnlen(alphabet, 256); const u32 input_shown = 22; diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index e1d8794..5ac7748 100644 --- a/arm9/source/common/ui.h +++ b/arm9/source/common/ui.h @@ -73,6 +73,7 @@ void ShowString(const char *format, ...); void ShowIconString(u8* icon, int w, int h, const char *format, ...); bool ShowPrompt(bool ask, const char *format, ...); u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...); +u32 ShowHotkeyPrompt(u32 n, const char** options, const u32* keys, const char *format, ...); bool ShowStringPrompt(char* inputstr, u32 max_size, const char *format, ...); u64 ShowHexPrompt(u64 start_val, u32 n_digits, const char *format, ...); u64 ShowNumberPrompt(u64 start_val, const char *format, ...); diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index 829db0a..b9eb2d6 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -60,7 +60,7 @@ #define _FLG(c) ((c >= 'a') ? (1 << (c - 'a')) : 0) #define IS_CTRLFLOW_CMD(id) ((id == CMD_ID_IF) || (id == CMD_ID_ELIF) || (id == CMD_ID_ELSE) || (id == CMD_ID_END) || \ - (id == CMD_ID_GOTO) || (id == CMD_ID_LABELSEL) || \ + (id == CMD_ID_GOTO) || (id == CMD_ID_LABELSEL) || (id == CMD_ID_KEYSEL) || \ (id == CMD_ID_FOR) || (id == CMD_ID_NEXT)) // command ids (also entry into the cmd_list array below) @@ -75,6 +75,7 @@ typedef enum { CMD_ID_NEXT, CMD_ID_GOTO, CMD_ID_LABELSEL, + CMD_ID_KEYSEL, CMD_ID_ECHO, CMD_ID_QR, CMD_ID_ASK, @@ -143,6 +144,7 @@ Gm9ScriptCmd cmd_list[] = { { CMD_ID_NEXT , _CMD_NEXT , 0, 0 }, { CMD_ID_GOTO , "goto" , 1, 0 }, { CMD_ID_LABELSEL, "labelsel", 2, 0 }, + { CMD_ID_KEYSEL , "keysel" , 2, 0 }, { CMD_ID_ECHO , "echo" , 1, 0 }, { CMD_ID_QR , "qr" , 2, 0 }, { CMD_ID_ASK , "ask" , 1, 0 }, @@ -179,8 +181,8 @@ Gm9ScriptCmd cmd_list[] = { { CMD_ID_APPLYIPS, "applyips", 3, 0 }, { CMD_ID_APPLYBPS, "applybps", 3, 0 }, { CMD_ID_APPLYBPM, "applybpm", 3, 0 }, - { CMD_ID_ISDIR, "isdir" , 1, 0 }, - { CMD_ID_EXIST, "exist" , 1, 0 }, + { CMD_ID_ISDIR , "isdir" , 1, 0 }, + { CMD_ID_EXIST , "exist" , 1, 0 }, { CMD_ID_BOOT , "boot" , 1, 0 }, { CMD_ID_SWITCHSD, "switchsd", 1, 0 }, { CMD_ID_REBOOT , "reboot" , 0, 0 }, @@ -916,16 +918,16 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { if (err_str) snprintf(err_str, _ERR_STR_LEN, "label not found"); } } - else if (id == CMD_ID_LABELSEL) { + else if ((id == CMD_ID_LABELSEL) || (id == CMD_ID_KEYSEL)) { const char* options[_CHOICE_MAX_N] = { NULL }; char* options_jmp[_CHOICE_MAX_N] = { NULL }; char options_str[_CHOICE_MAX_N][_CHOICE_STR_LEN+1]; + u32 options_keys[_CHOICE_MAX_N] = { 0 }; char* ast = strchr(argv[1], '*'); char* ptr = NULL; u32 n_opt = 0; while ((ptr = find_label(argv[1], ptr))) { - options[n_opt] = options_str[n_opt]; options_jmp[n_opt] = ptr; while (*(ptr++) != '@'); @@ -937,10 +939,21 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) { else if (ptr[i] == '_') choice[i] = ' '; else choice[i] = ptr[i]; } + if (id == CMD_ID_KEYSEL) { + char* keystr = choice; + for (; *choice != ' ' && *choice != '\0'; choice++); + if (*choice != '\0') *(choice++) = '\0'; + options_keys[n_opt] = StringToButton(keystr); + if (!options_keys[n_opt]) continue; + } + + options[n_opt] = choice; if (++n_opt >= _CHOICE_MAX_N) break; } - u32 result = ShowSelectPrompt(n_opt, options, "%s", argv[0]); + u32 result = (id == CMD_ID_LABELSEL) ? ShowSelectPrompt(n_opt, options, "%s", argv[0]) : + ShowHotkeyPrompt(n_opt, options, options_keys, "%s", argv[0]); + if (!result) { ret = false; if (err_str) snprintf(err_str, _ERR_STR_LEN, "user abort"); diff --git a/data/HelloScript.gm9 b/data/HelloScript.gm9 deleted file mode 100644 index 172c4b3..0000000 --- a/data/HelloScript.gm9 +++ /dev/null @@ -1,308 +0,0 @@ -# GodMode9 "Hello Script" -# Tutorial script - read / run this to learn how it works -# last changed: 20170906 -# author: d0k3 - -# COMMENTS / SCRIPTING BASICS / 'echo' COMMAND -# Anything after '#' will be ignored by the scripting engine -# Commands and arguments are separated by whitespaces, any redundant whitespaces will be ignored -# Use '"' if an argument has to include whitespaces -# The echo command outputs info on screen - echo "'Hello Script'\nby d0k3\n \nThis is a sample script to\nshowcase the GM9 script engine." # comments work anywhere -# Unknown commands lead to script abort (remove the '#' below to test) -# iamunknown test test - -# 'qr' COMMAND -# The 'qr' command does the same as the echo command, but also displays a QR code on the top screen -qr "Scan for cool stuff!" https://github.com/d0k3/GodMode9 - -# 'ask' COMMAND -# The 'ask' command is similar to the 'echo' command, but will allow the user to abort -# Note that normally any failed command (like a negative user response on 'ask') will result in script abort -ask "Continue running this script?" - -# (-o/-s) SWITCHES -# You may use the -o / --optional and -s / --silent switches on any command -# -o / --optional continues on failures -# -s / --silent tries to suppress all error messages -# This would have completely ignored the user response on the previous command: -ask -o -s "Really continue running this script?\n(I will completely ignore your answer)" - -# ENVIRONMENTAL VARS -# GM9VER is the GodMode9 version number -# REGION is the region of your device's SysNAND (USA, EUR, JPN, KOR, CHN, TWN or UNK for unknown) -# SERIAL is the serial number of your device -# GM9OUT is the standard output path -# CURRDIR is the directory the script is running from -# HAX is the hax the system is currently running from ("ntrboot", "sighax", "a9lh" or empty) -# ONTYPE is the console type ("O3DS" or "N3DS") -# RDTYPE is the unit type ("devkit" or "retail") -# SYSID0 is the id0 belonging to your SysNAND -# EMUID0 is the id0 belonging to your EmuNAND (if available) -# TIMESTAMP is the current time in hhmmss format -# DATESTAMP is the current date in YYMMDD format -# Use $[VAR] to get the *content* of a variable VAR -echo "Your GodMode9 version is $[GM9VER]\nYour region is $[REGION]\nYour serial number is $[SERIAL]\nYour std output path is $[GM9OUT]\nCurrent dir is $[CURRDIR]\nCurrent hax is $[HAX]\nYour system is a $[RDTYPE] $[ONTYPE]\nCurrent datestamp is: $[DATESTAMP]\nCurrent timestamp is: $[TIMESTAMP]\n \nYour sys / emu ID0 is:\n$[SYSID0]\n$[EMUID0]" -qr "You can also have this as a QR code :)" "Your GodMode9 version is $[GM9VER]\nYour region is $[REGION]\nYour serial number is $[SERIAL]\nYour std output path is $[GM9OUT]\nCurrent dir is $[CURRDIR]\nCurrent hax is $[HAX]\nYour system is a $[RDTYPE] $[ONTYPE]\nCurrent datestamp is: $[DATESTAMP]\nCurrent timestamp is: $[TIMESTAMP]\n \nYour sys / emu ID0 is:\n$[SYSID0]\n$[EMUID0]" - -# ERRORMSG and SUCCESSMSG / 'set' COMMAND -# These two are special environment vars, allowing you to control the message on script failure or success -set ERRORMSG "HelloScript testing script failed" -set SUCCESSMSG "HelloScript testing script success" -# you can also reset these to return to default script messages -# set ERRORMSG "" -# set SUCCESSMSG "" - -# 'chk' COMMAND -# Using the check command, you can check if two arguments match (not case sensitive) -# -u / -unequal check if not matching instead -# These checks will (most likely) pass -chk "hello" "HeLlo" -chk "0:/gm9/out" $[GM9OUT] -chk $[SYSID0] $[SYSID0] -# unsure about these -chk -o $[HAX] "sighax" -chk -o -u $[HAX] "" - -# PREVIEW_MODE, PREVIEW_COLOR_ACTIVE, PREVIEW_COLOR_COMMENT and PREVIEW_COLOR_CODE -# PREVIEW_MODE controls how the preview on the top screen is displayed, set it it to off, quick or full -# PREVIEW_COLOR_ACTIVE, PREVIEW_COLOR_COMMENT and PREVIEW_COLOR_CODE set the colors of the preview (RGB, hex) -set PREVIEW_MODE quick -# we don't want these ugly colors -# set PREVIEW_COLOR_ACTIVE 00FF00 -# set PREVIEW_COLOR_COMMENT 0000FF -# set PREVIEW_COLOR_CODE FF0000 - -# CREATING VARS -# You can also create new variables using 'set' -# Notice how you can use variables when setting variables -set TESTPATH 0:/testdir/$[SERIAL]_tick.db -echo "Your testpath is:\n$[TESTPATH]" - -# 'input' COMMAND -# The 'input' command will allow the user to input a string and store that string in a variable -# Preset the variable to have an initial value -set USERINPUT "Hello World" -input "Enter something please?" USERINPUT -echo "You entered:\n$[USERINPUT]" - -# 'strsplit' COMMAND -# The 'strsplit' command extracts a substring from a string -# -b / --before extracts the substring *before* the split char -# -f / --first matches the first, not last occurence of the split char -strsplit TESTSPLIT1 "one/two/three" "/" -strsplit -b TESTSPLIT2 "one/two/three" "/" -strsplit -f TESTSPLIT3 "one/two/three" "/" -strsplit -b -f TESTSPLIT4 "one/two/three" "/" -echo "one/two/three\n$[TESTSPLIT1]\n$[TESTSPLIT2]\n$[TESTSPLIT3]\n$[TESTSPLIT4]" - -# strrep COMMAND -# The 'strrep' command replaces one char with another inside a string -# notice how we use TESTREP both as input and output -set TESTREP "Hello_World" -strrep TESTREP $[TESTREP] "_ " -echo $[TESTREP] - -# 'filesel' COMMAND -# The 'filesel' command allows the user to choose a file inside a directory -# The path is stored inside a variable, and the selection can be limited via wildcards -# -d / --include_dirs allows to browse folders and select files inside -filesel "Please select a file" 0:/*.* SELFILE - -# 'dirsel' COMMAND -# The 'dirsel' command works identically to the 'filesel' command, but allows selecting a dir -# Note that a final slash ('/') is not expected here and there can be no wildcard pattern. -dirsel "Now, select a dir" 0: SELDIR -echo "You selected $[SELFILE]\nand $[SELDIR]\nWe'll do nothing with either :)." - -# 'allow' COMMAND -# typically used at the beginning of a script, prompts to allow writes to the location given in the argument -# doing this from the beginning is preferable, so that no actions are taken unless all required write permissions are unlocked -# (note #1: without 'allow', write permissions are still in place and the user will be asked on demand) -# (note #2: this simple testing script requires no additional permissions, thus the command is hidden behind a '#') -# -a / --all allows writes to the specified location and all included files and directories -# allow -a M:/ - -# 'rm' COMMAND -# The 'rm' command is used to remove files and directories (recursively) -# Here, we remove the dir if it is still here from an earlier run of this script -# If it is not here, we ignore any errors and keep silent about it -rm -o -s 0:/testdir - -# 'mkdir' COMMAND -# Use this to create a directory at the specified location -mkdir 0:/testdir - -# 'imgmount' COMMAND -# The 'imgmount' command is used to mount an image. -# An image can be (among other things) FAT, NAND, NCCH, NCSD, FIRM, ticket.db... -imgmount S:/ctrnand_full.bin - -# 'cp' COMMAND -# Use 'cp' to copy a file or directory (recursively) -# -h / --hash also adds a .sha file containing the files SHA256 -# -w / --overwite forces overwrite on existing files -# -k / --skip forces skip on existing files -# -n / --no_cancel prevents user cancels (useful on critical operations) -cp -h -w -n 7:/dbs/ticket.db $[TESTPATH] - -# 'imgumount' COMMAND -# This unmounts the current mounted image. -imgumount - -# 'findnot' COMMAND -# Use 'findnot' in conjunction with '?' to find an unused filename -findnot $[TESTPATH]_???.rn RENPATH - -# 'mv' COMMAND -# The 'mv command renames or moves a file or directory -# -w / --overwite forces overwrite on existing files -# -k / --skip forces skip on existing files -# -n / --no_cancel prevents user cancels (useful on critical operations) -mv -w -k $[TESTPATH] $[RENPATH] - -# 'find' COMMAND -# The 'find' command has two main uses, (1) checking if files / dirs exist and (2) finding files / dirs -# Here we use it to check for RENPATH, thus we use NULL as second argument (we're not interested in the output) -find $[RENPATH] NULL -# Wildcards ('*' / '?') are allowed when searching for a file / directory name -# If wildcards are used, 'find' will return the last alphanumerical match -# -f / --first return the first alphanumerical match instead -find S:/nand.* NANDIMAGE - -# 'sha' COMMAND -# Use this to check a files' SHA256 -sha $[RENPATH] $[TESTPATH].sha -# Instead of an .sha file you can also use the SHA256 in hex as second argument -# sha S:/sector0x96.bin 82F2730D2C2DA3F30165F987FDCCAC5CBAB24B4E5F65C981CD7BE6F438E6D9D3 -# This also allows partial SHA checks (for @x:y handling see 'inject' below) -# sha S:/firm0.bin@100:100 078CC0CFD850A27093DDA2630C3603CA0C96969BD1F26DA48AC7B1BAE5DD5219 - -# 'shaget' COMMAND -# Use this to calculate (and store) a files' SHA256 -# If the second argument is a filename, the SHA256 will be stored there -# shaget 0:/boot.firm 0:/boot.firm.sha -# If it's a variable, it will be stored there temporarily instead -shaget S:/nand_hdr.bin NANDHDRSHA -sha S:/nand_hdr.bin $[NANDHDRSHA] -# Partial SHA calculation is also possible (for @x:y handling see 'inject' below) -# shaget 0:/boot.firm@100:100 0:/boot.firm.partial.sha - -# 'inject' COMMAND -# This command is used to inject part of one file into another -# The syntax is: inject origin@x:y destination@z -# x: origin offset (in hex) -# y: origin size, starting at x (in hex) -# z: destination offset (in hex) -# If destination does not exist or z is not given, a new destination file will be created(!) -# If x is not given, the full origin file size, starting from offset 0, is used to inject -# If y is not given, everything starting from offset x is used to inject -# -n / --no_cancel prevents user cancels (useful on critical operations) -inject S:/nand_hdr.bin@100:4 $[RENPATH]@200 # offsets and sizes are in hex -# As we just deliberately corrupted our test file, the subsequent SHA check will fail -set ERRORMSG "SHA check failed (this was expected)" -sha -o $[RENPATH] $[TESTPATH].sha -set ERRORMSG "" - -# 'fdummy' COMMAND -# This command creates a dummy file of the specified size (size in hex) -# Contents of the dummy file are undefined and existing files won't be overwritten -set DUMMY 0:/testdir/dummy.bin -fdummy $[DUMMY] 400 - -# 'fill' COMMAND -# This command fills (a portition of) a file with the specified byte value -# The syntax is: fill destination@x:y fillbyte -# x: destination offset (in hex) -# y: destination size, starting at x (in hex) -# If x is not given, the full file size, starting from offset 0, is overwritten -# If y is not given, everything starting from offset x is overwritten -# -n / --no_cancel prevents user cancels (useful on critical operations) -fill $[DUMMY]@100:100 FF -fill $[DUMMY]@300 80 - -# 'fset' COMMAND -# This command sets a portition of a file with the specified data in hex -# The syntax is: fset destination@x hexdata -# x: destination offset (in hex) -# y: bytes to write (in hex) (must be lower than hexdata size) -# If x is not given, data is inserted at the beginning of the file -# If y is not given, all of hexdata is written to the file -# -e / --flip_endian flips the order of the bytes -fset $[DUMMY]@100 48454c4c4f # 'HELLO' - -# 'fget' COMMAND -# This command reads data from a file and stores it in a var -# The syntax is: fset origin@x:y var -# x: origin offset (in hex) -# y: origin size, starting at x (in hex) -# Both x and y have to be set(!) -# -e / --flip_endian flips the order of the bytes -fget -e $[DUMMY]@100:5 OLLEH -echo "This is 'HELLO', in hex and reverse:\n$[OLLEH]" - -# 'fixcmac' COMMAND -# Use this to fix the CMACs for a file or a whole folder (recursively) -# This will count as success if a file does not contain a CMAC -# More info on CMACs: http://3dbrew.org/wiki/Savegames#AES_CMAC_header -# fixcmac 1:/data - -# 'verify' COMMAND -# Certain file formats (NAND, NCCH, NCSD, CIA, FIRM, ...) can also be verified. Use 'verify' to do so. -# verify -o s:/firm0.bin # As drive letters are case sensitive, this would fail -verify S:/firm1.bin - -# 'decrypt' COMMAND -# Certain file formats (NCCH, NCSD, CIA, FIRM, BOSS, ...) can be decrypted. Use 'decrypt' to do so. -# Take note that all crypto operations are done INPLACE and will overwrite the file(!) -# decrypt 0:/x.ncch - -# 'encrypt' COMMAND -# Certain file formats (NCCH, NCSD, CIA, BOSS, ...) can be encrypted. Use 'encrypt' to do so. -# Take note that all crypto operations are done INPLACE and will overwrite the file(!) -# encrypt 0:/x.ncch - -# 'buildcia' COMMAND -# You can build CIA files from certain file formats (TMD, NCCH, NCSD ...). Use 'buildcia' to do so. -# CIA files will always be built to the standard output directory (0:/gm9/out) -# -l / --legit force CIA to be legit (only works for legit system installed titles) -# buildcia 0:/x.ncch - -# 'sdump' COMMAND -# This command dumps a supported file to the standard output directory (0:/gm9/out) -# Supported files: encTitleKeys.bin, decTitleKeys.bin, seeddb.bin -# -w / --overwrite overwrite existing files without asking -# sdump encTitleKeys.bin -# sdump decTitleKeys.bin -# sdump seeddb.bin - -# 'applyips' COMMAND -# This will apply the given IPS-formatted delta patch (argument 1) to the specified file (argument 2) -# to produce the patched file (argument 3). 2 and 3 may be the same to perform an in-place patch. -# applyips 0:/example/patch.ips 0:/data/original.bin 0:/game/modded.bin - -# 'applybps' COMMAND -# This will apply the given BPS-formatted delta patch (argument 1) to the specified file (argument 2) -# to produce the patched file (argument 3). -# applybps 0:/example/patch.bps 0:/data/original.bin 0:/game/modded.bin - -# 'applybpm' COMMAND -# This will apply the given BPM-formatted delta patch (argument 1) to the specified directory (argument 2) -# to produce a directory containing patched files (argument 3). -# applybpm 0:/example/patch.bpm 0:/data/originalfolder 0:/game/moddedfolder - -# 'boot' COMMAND -# Use this command to chainload a compatible FIRM -# boot 0:/boot.firm -# disabled cause it would leave the script - -# 'switchsd' COMMAND -# Use this to allow the user to switch the SD card -# switchsd "Please switch SD card." - -# 'reboot' / 'poweroff' COMMAND -# These are used to reboot or power off the 3DS console -set ERRORMSG "Test script finished,\n(without reboot)\n \nIgnore the error message." -ask "Reboot now?" -reboot -poweroff # this line can never be reached diff --git a/data/echotest.gm9 b/data/echotest.gm9 deleted file mode 100644 index 4b15ff7..0000000 --- a/data/echotest.gm9 +++ /dev/null @@ -1,19 +0,0 @@ -# line 1: bad # of args -echo -o "#" - -# This works. -echo "check #" - -# The % symbol disappears. -echo "%" -echo "check %" - -# error message fail -echo "-" - -# This works. -echo "check -" - -# Probably not bug. Syntax abuse but adding it here anyway. -bkpt -echo """ \ No newline at end of file diff --git a/data/sdumptest.gm9 b/data/sdumptest.gm9 deleted file mode 100644 index 3e4dbbd..0000000 --- a/data/sdumptest.gm9 +++ /dev/null @@ -1,4 +0,0 @@ -sdump -w encTitleKeys.bin -sdump -w decTitleKeys.bin -sdump -w seeddb.bin -sdump -o -w shalalala.bin diff --git a/resources/sample/HelloSpaghetti.gm9 b/resources/sample/HelloSpaghetti.gm9 index 8e12a21..4941dfe 100644 --- a/resources/sample/HelloSpaghetti.gm9 +++ b/resources/sample/HelloSpaghetti.gm9 @@ -69,6 +69,38 @@ labelsel -o -s "Choose preview mode" choice_* goto outside +# keysel sample code +@spaghetti_keysel_example + +@kchoice_X_Preview_off +set PREVIEW_MODE off +goto kchooser + +@kchoice_Y_Preview_quick +set PREVIEW_MODE quick +goto kchooser + +@kchoice_R_Preview_full +set PREVIEW_MODE full +goto kchooser + +@kchoice_L_Preview_png +set PREVIEW_MODE V:/GodMode9_splash.png +goto kchooser + +@kchoice_SELECT_Preview_custom +set PREVIEW_MODE "Your text can be here" +input -o "Enter anything:" PREVIEW_MODE +goto kchooser + +@kchoice_START_Leave_script +goto outside + +@kchooser +keysel -o -s "Choose preview mode via key" kchoice_* +goto outside + + # for-next sample code @spaghetti_fornext_sample