mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Scripting: Live preview for scripts
This commit also adds limited syntax highlighting for scripts
This commit is contained in:
parent
730cff8554
commit
5f0ab648aa
@ -1,6 +1,6 @@
|
|||||||
# GodMode9 "Hello Script"
|
# GodMode9 "Hello Script"
|
||||||
# Tutorial script - read / run this to learn how it works
|
# Tutorial script - read / run this to learn how it works
|
||||||
# last changed: 20170706
|
# last changed: 20170906
|
||||||
# author: d0k3
|
# author: d0k3
|
||||||
|
|
||||||
# COMMENTS / SCRIPTING BASICS / 'echo' COMMAND
|
# COMMENTS / SCRIPTING BASICS / 'echo' COMMAND
|
||||||
|
@ -94,7 +94,7 @@ u32 IdentifyFileType(const char* path) {
|
|||||||
return BIN_KEYDB; // key database
|
return BIN_KEYDB; // key database
|
||||||
} else if ((sscanf(fname, "slot%02lXKey", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0) && (fsize = 16) && (id < 0x40)) {
|
} else if ((sscanf(fname, "slot%02lXKey", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0) && (fsize = 16) && (id < 0x40)) {
|
||||||
return BIN_LEGKEY; // legacy key file
|
return BIN_LEGKEY; // legacy key file
|
||||||
} else if (ValidateText((char*) data, (fsize > 0X200) ? 0x200 : fsize)) {
|
} else if (ValidateText((char*) data, (fsize > 0x200) ? 0x200 : fsize)) {
|
||||||
u32 type = 0;
|
u32 type = 0;
|
||||||
if ((fsize <= SCRIPT_MAX_SIZE) && ext && (strncasecmp(ext, SCRIPT_EXT, strnlen(SCRIPT_EXT, 16) + 1) == 0))
|
if ((fsize <= SCRIPT_MAX_SIZE) && ext && (strncasecmp(ext, SCRIPT_EXT, strnlen(SCRIPT_EXT, 16) + 1) == 0))
|
||||||
type |= TXT_SCRIPT; // should be a script (which is also generic text)
|
type |= TXT_SCRIPT; // should be a script (which is also generic text)
|
||||||
|
217
source/godmode.c
217
source/godmode.c
@ -44,87 +44,12 @@
|
|||||||
#define COLOR_HVASCII RGB(0x40, 0x80, 0x50)
|
#define COLOR_HVASCII RGB(0x40, 0x80, 0x50)
|
||||||
#define COLOR_HVHEX(i) ((i % 2) ? RGB(0x30, 0x90, 0x30) : RGB(0x30, 0x80, 0x30))
|
#define COLOR_HVHEX(i) ((i % 2) ? RGB(0x30, 0x90, 0x30) : RGB(0x30, 0x80, 0x30))
|
||||||
|
|
||||||
#define COLOR_TVOFFS RGB(0x40, 0x60, 0x50)
|
|
||||||
#define COLOR_TVOFFSL RGB(0x20, 0x40, 0x30)
|
|
||||||
#define COLOR_TVTEXT RGB(0x30, 0x85, 0x30)
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char path[256];
|
char path[256];
|
||||||
u32 cursor;
|
u32 cursor;
|
||||||
u32 scroll;
|
u32 scroll;
|
||||||
} PaneData;
|
} PaneData;
|
||||||
|
|
||||||
static inline u32 LineLen(const char* text, u32 len, u32 ww, const char* line) {
|
|
||||||
char* line0 = (char*) line;
|
|
||||||
char* line1 = (char*) line;
|
|
||||||
u32 llen = 0;
|
|
||||||
|
|
||||||
// non wordwrapped length
|
|
||||||
while ((line1 < (text + len)) && (*line1 != '\n') && *line1) line1++;
|
|
||||||
while ((line1 > line0) && (*(line1-1) <= ' ')) line1--;
|
|
||||||
llen = line1 - line0;
|
|
||||||
if (ww && (llen > ww)) { // wordwrapped length
|
|
||||||
for (llen = ww; (llen > 0) && (line[llen] != ' '); llen--);
|
|
||||||
if (!llen) llen = ww; // workaround for long strings
|
|
||||||
}
|
|
||||||
return llen;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char* LineSeek(const char* text, u32 len, u32 ww, const char* line, int add) {
|
|
||||||
// safety checks /
|
|
||||||
if (line < text) return NULL;
|
|
||||||
if ((line >= (text + len)) && (add >= 0)) return (char*) line;
|
|
||||||
|
|
||||||
if (!ww) { // non wordwrapped mode
|
|
||||||
char* lf = ((char*) line - 1);
|
|
||||||
|
|
||||||
// ensure we are at the start of the line
|
|
||||||
while ((lf > text) && (*lf != '\n')) lf--;
|
|
||||||
|
|
||||||
// handle backwards search
|
|
||||||
for (; (add < 0) && (lf >= text); add++)
|
|
||||||
for (lf--; (lf >= text) && (*lf != '\n'); lf--);
|
|
||||||
|
|
||||||
// handle forwards search
|
|
||||||
for (; (add > 0) && (lf < text + len); add--)
|
|
||||||
for (lf++; (lf < text + len) && (*lf != '\n'); lf++);
|
|
||||||
|
|
||||||
return lf + 1;
|
|
||||||
} else { // wordwrapped mode
|
|
||||||
char* l0 = (char*) line;
|
|
||||||
|
|
||||||
// handle forwards wordwrapped search
|
|
||||||
while ((add > 0) && (l0 < text + len)) {
|
|
||||||
u32 llen = LineLen(text, len, 0, l0);
|
|
||||||
for (; (add > 0) && (llen > ww); add--) {
|
|
||||||
u32 llenww = LineLen(text, len, ww, l0);
|
|
||||||
llen -= llenww;
|
|
||||||
l0 += llenww;
|
|
||||||
}
|
|
||||||
if (add > 0) {
|
|
||||||
l0 = LineSeek(text, len, 0, l0, 1);
|
|
||||||
add--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle backwards wordwrapped search
|
|
||||||
while ((add < 0) && (l0 > text)) {
|
|
||||||
char* l1 = LineSeek(text, len, 0, l0, -1);
|
|
||||||
int nlww = 0; // count wordwrapped lines in paragraph
|
|
||||||
for (char* ld = l1; ld < l0; ld = LineSeek(text, len, ww, ld, 1), nlww++);
|
|
||||||
if (add + nlww < 0) {
|
|
||||||
add += nlww;
|
|
||||||
l0 = l1;
|
|
||||||
} else {
|
|
||||||
l0 = LineSeek(text, len, ww, l1, nlww + add);
|
|
||||||
add = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return l0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetTimeString(char* timestr, bool forced_update) {
|
void GetTimeString(char* timestr, bool forced_update) {
|
||||||
static DsTime dstime;
|
static DsTime dstime;
|
||||||
static u64 timer = (u64) -1; // this ensures we don't check the time too often
|
static u64 timer = (u64) -1; // this ensures we don't check the time too often
|
||||||
@ -370,137 +295,6 @@ u32 SdFormatMenu(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 FileTextViewer(const char* path) {
|
|
||||||
const u32 vpad = 1;
|
|
||||||
const u32 hpad = 0;
|
|
||||||
const u32 lnos = 4;
|
|
||||||
u32 ww = 0;
|
|
||||||
|
|
||||||
// load text file (completely into memory)
|
|
||||||
char* text = (char*) TEMP_BUFFER;
|
|
||||||
u32 flen = FileGetData(path, text, TEMP_BUFFER_SIZE, 0);
|
|
||||||
u32 len = 0; // actual length may be shorter due to zero symbol
|
|
||||||
for (len = 0; (len < flen) && text[len]; len++);
|
|
||||||
|
|
||||||
// check if this really is a text file
|
|
||||||
if (!ValidateText(text, len)) {
|
|
||||||
ShowPrompt(false, "Error: Not a valid text file");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear screens
|
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
|
||||||
|
|
||||||
// instructions
|
|
||||||
static const char* instr = "Textviewer Controls:\n \n\x18\x19\x1A\x1B(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nB - Exit\n";
|
|
||||||
ShowString(instr);
|
|
||||||
|
|
||||||
// no of lines and length to display
|
|
||||||
u32 nlin_disp = SCREEN_HEIGHT / (FONT_HEIGHT_EXT + (2*vpad));
|
|
||||||
u32 llen_disp = (SCREEN_WIDTH_TOP - (2*hpad)) / FONT_WIDTH_EXT;
|
|
||||||
|
|
||||||
// block placements
|
|
||||||
const char* al_str = "<< ";
|
|
||||||
const char* ar_str = " >>";
|
|
||||||
if (lnos) llen_disp -= (lnos + 1); // make room for line numbers
|
|
||||||
u32 x_txt = (lnos) ? hpad + ((lnos+1)*FONT_WIDTH_EXT) : hpad;
|
|
||||||
u32 x_lno = hpad;
|
|
||||||
u32 p_al = 0;
|
|
||||||
u32 p_ar = llen_disp - strnlen(ar_str, 16);
|
|
||||||
u32 x_al = x_txt + (p_al * FONT_WIDTH_EXT);
|
|
||||||
u32 x_ar = x_txt + (p_ar * FONT_WIDTH_EXT);
|
|
||||||
|
|
||||||
// find maximum line len
|
|
||||||
u32 llen_max = 0;
|
|
||||||
for (char* ptr = (char*) text; ptr < (text + len); ptr = LineSeek(text, len, 0, ptr, 1)) {
|
|
||||||
u32 llen = LineLen(text, len, 0, ptr);
|
|
||||||
if (llen > llen_max) llen_max = llen;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find last allowed lines (ww and nonww)
|
|
||||||
char* llast_nww = LineSeek(text, len, 0, text + len, -nlin_disp);
|
|
||||||
char* llast_ww = LineSeek(text, len, llen_disp, text + len, -nlin_disp);
|
|
||||||
|
|
||||||
char* line0 = (char*) text;
|
|
||||||
int lcurr = 1;
|
|
||||||
int off_disp = 0;
|
|
||||||
while (true) {
|
|
||||||
// display text on screen
|
|
||||||
char txtstr[128]; // should be more than enough
|
|
||||||
char* ptr = line0;
|
|
||||||
u32 nln = lcurr;
|
|
||||||
for (u32 y = vpad; y < SCREEN_HEIGHT; y += FONT_HEIGHT_EXT + (2*vpad)) {
|
|
||||||
char* ptr_next = LineSeek(text, len, ww, ptr, 1);
|
|
||||||
u32 llen = LineLen(text, len, ww, ptr);
|
|
||||||
u32 ncpy = ((int) llen < off_disp) ? 0 : (llen - off_disp);
|
|
||||||
if (ncpy > llen_disp) ncpy = llen_disp;
|
|
||||||
bool al = !ww && off_disp && (ptr != ptr_next);
|
|
||||||
bool ar = !ww && (llen > off_disp + llen_disp);
|
|
||||||
|
|
||||||
// build text string
|
|
||||||
snprintf(txtstr, llen_disp + 1, "%-*.*s", (int) llen_disp, (int) llen_disp, "");
|
|
||||||
if (ncpy) memcpy(txtstr, ptr + off_disp, ncpy);
|
|
||||||
for (char* d = txtstr; *d; d++) if (*d < ' ') *d = ' ';
|
|
||||||
if (al) memcpy(txtstr + p_al, al_str, strnlen(al_str, 16));
|
|
||||||
if (ar) memcpy(txtstr + p_ar, ar_str, strnlen(ar_str, 16));
|
|
||||||
|
|
||||||
// draw line number & text
|
|
||||||
DrawStringF(TOP_SCREEN, x_txt, y, COLOR_TVTEXT, COLOR_STD_BG, txtstr);
|
|
||||||
if (lnos && (ptr != ptr_next)) DrawStringF(TOP_SCREEN, x_lno, y,
|
|
||||||
((ptr == text) || (*(ptr-1) == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", lnos, nln);
|
|
||||||
else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", lnos, lnos, " ");
|
|
||||||
|
|
||||||
// colorize arrows
|
|
||||||
if (al) DrawStringF(TOP_SCREEN, x_al, y, COLOR_TVOFFS, COLOR_TRANSPARENT, al_str);
|
|
||||||
if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, ar_str);
|
|
||||||
|
|
||||||
// advance pointer / line number
|
|
||||||
for (char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln;
|
|
||||||
ptr = ptr_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle user input
|
|
||||||
u32 pad_state = InputWait(0);
|
|
||||||
if ((pad_state & BUTTON_R1) && (pad_state & BUTTON_L1)) CreateScreenshot();
|
|
||||||
else { // standard viewer mode
|
|
||||||
char* line0_next = line0;
|
|
||||||
u32 step_ud = (pad_state & BUTTON_R1) ? nlin_disp : 1;
|
|
||||||
u32 step_lr = (pad_state & BUTTON_R1) ? llen_disp : 1;
|
|
||||||
bool switched = (pad_state & BUTTON_R1);
|
|
||||||
if (pad_state & BUTTON_DOWN) line0_next = LineSeek(text, len, ww, line0, step_ud);
|
|
||||||
else if (pad_state & BUTTON_UP) line0_next = LineSeek(text, len, ww, line0, -step_ud);
|
|
||||||
else if (pad_state & BUTTON_RIGHT) off_disp += step_lr;
|
|
||||||
else if (pad_state & BUTTON_LEFT) off_disp -= step_lr;
|
|
||||||
else if (switched && (pad_state & BUTTON_X)) {
|
|
||||||
u64 lnext64 = ShowNumberPrompt(lcurr, "Current line: %i\nEnter new line below.", lcurr);
|
|
||||||
if (lnext64 && (lnext64 != (u64) -1)) line0_next = LineSeek(text, len, 0, line0, (int) lnext64 - lcurr);
|
|
||||||
ShowString(instr);
|
|
||||||
} else if (switched && (pad_state & BUTTON_Y)) {
|
|
||||||
ww = ww ? 0 : llen_disp;
|
|
||||||
line0_next = LineSeek(text, len, ww, line0, 0);
|
|
||||||
} else if (pad_state & (BUTTON_B|BUTTON_START)) break;
|
|
||||||
|
|
||||||
// check for problems, apply changes
|
|
||||||
if (!ww && (line0_next > llast_nww)) line0_next = llast_nww;
|
|
||||||
else if (ww && (line0_next > llast_ww)) line0_next = llast_ww;
|
|
||||||
if (line0_next < line0) { // fix line number for decrease
|
|
||||||
do if (*(--line0) == '\n') lcurr--;
|
|
||||||
while (line0 > line0_next);
|
|
||||||
} else { // fix line number for increase / same
|
|
||||||
for (; line0_next > line0; line0++)
|
|
||||||
if (*line0 == '\n') lcurr++;
|
|
||||||
}
|
|
||||||
if (off_disp + llen_disp > llen_max) off_disp = llen_max - llen_disp;
|
|
||||||
if ((off_disp < 0) || ww) off_disp = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear screens
|
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 FileHexViewer(const char* path) {
|
u32 FileHexViewer(const char* path) {
|
||||||
static const u32 max_data = (SCREEN_HEIGHT / 8) * 16;
|
static const u32 max_data = (SCREEN_HEIGHT / 8) * 16;
|
||||||
static u32 mode = 0;
|
static u32 mode = 0;
|
||||||
@ -980,7 +774,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
|||||||
FileHexViewer(curr_entry->path);
|
FileHexViewer(curr_entry->path);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (user_select == textviewer) { // -> show in text viewer
|
} else if (user_select == textviewer) { // -> show in text viewer
|
||||||
FileTextViewer(curr_entry->path);
|
FileTextViewer(curr_entry->path, scriptable);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (user_select == calcsha) { // -> calculate SHA-256
|
} else if (user_select == calcsha) { // -> calculate SHA-256
|
||||||
Sha256Calculator(curr_entry->path);
|
Sha256Calculator(curr_entry->path);
|
||||||
@ -1485,6 +1279,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
|||||||
if (ShowPrompt(true, "%s\nWarning: Do not run scripts\nfrom untrusted sources.\n \nExecute script?", pathstr))
|
if (ShowPrompt(true, "%s\nWarning: Do not run scripts\nfrom untrusted sources.\n \nExecute script?", pathstr))
|
||||||
ShowPrompt(false, "%s\nScript execute %s", pathstr, ExecuteGM9Script(curr_entry->path) ? "success" : "failure");
|
ShowPrompt(false, "%s\nScript execute %s", pathstr, ExecuteGM9Script(curr_entry->path) ? "success" : "failure");
|
||||||
GetDirContents(current_dir, current_path);
|
GetDirContents(current_dir, current_path);
|
||||||
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1605,13 +1400,9 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else if (user_select == sysinfo) { // Myria's system info
|
} else if (user_select == sysinfo) { // Myria's system info
|
||||||
const char* sysinfo_path = OUTPUT_PATH "/sysinfo.txt";
|
|
||||||
char* sysinfo_txt = (char*) TEMP_BUFFER;
|
char* sysinfo_txt = (char*) TEMP_BUFFER;
|
||||||
ShowString("Calculating system info...");
|
|
||||||
MyriaSysinfo(sysinfo_txt);
|
MyriaSysinfo(sysinfo_txt);
|
||||||
FileSetData(sysinfo_path, sysinfo_txt, strnlen(sysinfo_txt, TEMP_BUFFER_SIZE), 0, true);
|
MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, TEMP_BUFFER_SIZE), false);
|
||||||
FileTextViewer(sysinfo_path);
|
|
||||||
ShowPrompt(false, "System info written to\n%s", sysinfo_path);
|
|
||||||
return 0;
|
return 0;
|
||||||
} else return 1;
|
} else return 1;
|
||||||
|
|
||||||
@ -2055,6 +1846,8 @@ u32 GodMode(bool is_b9s) {
|
|||||||
if ((user_select == more) && (HomeMoreMenu(current_path, current_dir, clipboard) == 0)) break; // more... menu
|
if ((user_select == more) && (HomeMoreMenu(current_path, current_dir, clipboard) == 0)) break; // more... menu
|
||||||
else if ((user_select == scripts) && (FileSelector(loadpath, "HOME scripts... menu.\nSelect script:", SCRIPT_PATH, "*.gm9", true, false))) {
|
else if ((user_select == scripts) && (FileSelector(loadpath, "HOME scripts... menu.\nSelect script:", SCRIPT_PATH, "*.gm9", true, false))) {
|
||||||
ExecuteGM9Script(loadpath);
|
ExecuteGM9Script(loadpath);
|
||||||
|
GetDirContents(current_dir, current_path);
|
||||||
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
break;
|
break;
|
||||||
} else if ((user_select == payloads) && (FileSelector(loadpath, "HOME payloads... menu.\nSelect payload:", PAYLOAD_PATH, "*.firm", true, false))) {
|
} else if ((user_select == payloads) && (FileSelector(loadpath, "HOME payloads... menu.\nSelect payload:", PAYLOAD_PATH, "*.firm", true, false))) {
|
||||||
size_t firm_size = FileGetData(loadpath, TEMP_BUFFER, TEMP_BUFFER_SIZE, 0);
|
size_t firm_size = FileGetData(loadpath, TEMP_BUFFER, TEMP_BUFFER_SIZE, 0);
|
||||||
|
@ -24,6 +24,23 @@
|
|||||||
|
|
||||||
#define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE)
|
#define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE)
|
||||||
|
|
||||||
|
// macros for textviewer
|
||||||
|
#define TV_VPAD 1 // vertical padding per line (above / below)
|
||||||
|
#define TV_HPAD 0 // horizontal padding per line (left)
|
||||||
|
#define TV_LNOS 4 // # of digits in line numbers (0 to disable)
|
||||||
|
|
||||||
|
#define TV_NLIN_DISP (SCREEN_HEIGHT / (FONT_HEIGHT_EXT + (2*TV_VPAD)))
|
||||||
|
#define TV_LLEN_DISP (((SCREEN_WIDTH_TOP - (2*TV_HPAD)) / FONT_WIDTH_EXT) - (TV_LNOS + 1))
|
||||||
|
|
||||||
|
// text viewer colors
|
||||||
|
#define COLOR_TVOFFS RGB(0x40, 0x60, 0x50)
|
||||||
|
#define COLOR_TVOFFSL RGB(0x20, 0x40, 0x30)
|
||||||
|
#define COLOR_TVTEXT RGB(0x30, 0x85, 0x30)
|
||||||
|
#define COLOR_TVRUN RGB(0xC0, 0x00, 0x00)
|
||||||
|
#define COLOR_TVCMT RGB(0x10, 0x40, 0x10)
|
||||||
|
#define COLOR_TVCMD RGB(0x30, 0x85, 0x45)
|
||||||
|
|
||||||
|
|
||||||
// some useful macros
|
// some useful macros
|
||||||
#define IS_WHITESPACE(c) ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'))
|
#define IS_WHITESPACE(c) ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n'))
|
||||||
#define _FLG(c) (1 << (c - 'a'))
|
#define _FLG(c) (1 << (c - 'a'))
|
||||||
@ -117,6 +134,77 @@ static inline bool strntohex(const char* str, u8* hex, u32 len) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 line_len(const char* text, u32 len, u32 ww, const char* line) {
|
||||||
|
char* line0 = (char*) line;
|
||||||
|
char* line1 = (char*) line;
|
||||||
|
u32 llen = 0;
|
||||||
|
|
||||||
|
// non wordwrapped length
|
||||||
|
while ((line1 < (text + len)) && (*line1 != '\n') && *line1) line1++;
|
||||||
|
while ((line1 > line0) && (*(line1-1) <= ' ')) line1--;
|
||||||
|
llen = line1 - line0;
|
||||||
|
if (ww && (llen > ww)) { // wordwrapped length
|
||||||
|
for (llen = ww; (llen > 0) && (line[llen] != ' '); llen--);
|
||||||
|
if (!llen) llen = ww; // workaround for long strings
|
||||||
|
}
|
||||||
|
return llen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char* line_seek(const char* text, u32 len, u32 ww, const char* line, int add) {
|
||||||
|
// safety checks /
|
||||||
|
if (line < text) return NULL;
|
||||||
|
if ((line >= (text + len)) && (add >= 0)) return (char*) line;
|
||||||
|
|
||||||
|
if (!ww) { // non wordwrapped mode
|
||||||
|
char* lf = ((char*) line - 1);
|
||||||
|
|
||||||
|
// ensure we are at the start of the line
|
||||||
|
while ((lf > text) && (*lf != '\n')) lf--;
|
||||||
|
|
||||||
|
// handle backwards search
|
||||||
|
for (; (add < 0) && (lf >= text); add++)
|
||||||
|
for (lf--; (lf >= text) && (*lf != '\n'); lf--);
|
||||||
|
|
||||||
|
// handle forwards search
|
||||||
|
for (; (add > 0) && (lf < text + len); add--)
|
||||||
|
for (lf++; (lf < text + len) && (*lf != '\n'); lf++);
|
||||||
|
|
||||||
|
return lf + 1;
|
||||||
|
} else { // wordwrapped mode
|
||||||
|
char* l0 = (char*) line;
|
||||||
|
|
||||||
|
// handle forwards wordwrapped search
|
||||||
|
while ((add > 0) && (l0 < text + len)) {
|
||||||
|
u32 llen = line_len(text, len, 0, l0);
|
||||||
|
for (; (add > 0) && (llen > ww); add--) {
|
||||||
|
u32 llenww = line_len(text, len, ww, l0);
|
||||||
|
llen -= llenww;
|
||||||
|
l0 += llenww;
|
||||||
|
}
|
||||||
|
if (add > 0) {
|
||||||
|
l0 = line_seek(text, len, 0, l0, 1);
|
||||||
|
add--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle backwards wordwrapped search
|
||||||
|
while ((add < 0) && (l0 > text)) {
|
||||||
|
char* l1 = line_seek(text, len, 0, l0, -1);
|
||||||
|
int nlww = 0; // count wordwrapped lines in paragraph
|
||||||
|
for (char* ld = l1; ld < l0; ld = line_seek(text, len, ww, ld, 1), nlww++);
|
||||||
|
if (add + nlww < 0) {
|
||||||
|
add += nlww;
|
||||||
|
l0 = l1;
|
||||||
|
} else {
|
||||||
|
l0 = line_seek(text, len, ww, l1, nlww + add);
|
||||||
|
add = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char* set_var(const char* name, const char* content) {
|
char* set_var(const char* name, const char* content) {
|
||||||
Gm9ScriptVar* vars = (Gm9ScriptVar*) VAR_BUFFER;
|
Gm9ScriptVar* vars = (Gm9ScriptVar*) VAR_BUFFER;
|
||||||
u32 max_vars = VAR_BUFFER_SIZE / sizeof(Gm9ScriptVar);
|
u32 max_vars = VAR_BUFFER_SIZE / sizeof(Gm9ScriptVar);
|
||||||
@ -592,6 +680,158 @@ bool ValidateText(const char* text, u32 len) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemTextView(const char* text, u32 len, char* line0, int off_disp, int lno, u32 ww, u32 mno, bool is_script) {
|
||||||
|
// block placements
|
||||||
|
const char* al_str = "<< ";
|
||||||
|
const char* ar_str = " >>";
|
||||||
|
u32 x_txt = (TV_LNOS >= 0) ? TV_HPAD + ((TV_LNOS+1)*FONT_WIDTH_EXT) : TV_HPAD;
|
||||||
|
u32 x_lno = TV_HPAD;
|
||||||
|
u32 p_al = 0;
|
||||||
|
u32 p_ar = TV_LLEN_DISP - strnlen(ar_str, 16);
|
||||||
|
u32 x_al = x_txt + (p_al * FONT_WIDTH_EXT);
|
||||||
|
u32 x_ar = x_txt + (p_ar * FONT_WIDTH_EXT);
|
||||||
|
|
||||||
|
// display text on screen
|
||||||
|
char txtstr[128]; // should be more than enough
|
||||||
|
char* ptr = line0;
|
||||||
|
u32 nln = lno;
|
||||||
|
for (u32 y = TV_VPAD; y < SCREEN_HEIGHT; y += FONT_HEIGHT_EXT + (2*TV_VPAD)) {
|
||||||
|
char* ptr_next = line_seek(text, len, ww, ptr, 1);
|
||||||
|
u32 llen = line_len(text, len, ww, ptr);
|
||||||
|
u32 ncpy = ((int) llen < off_disp) ? 0 : (llen - off_disp);
|
||||||
|
if (ncpy > TV_LLEN_DISP) ncpy = TV_LLEN_DISP;
|
||||||
|
bool al = !ww && off_disp && (ptr != ptr_next);
|
||||||
|
bool ar = !ww && ((int) llen > off_disp + TV_LLEN_DISP);
|
||||||
|
|
||||||
|
// set text color / find start of comment of scripts
|
||||||
|
u32 color_text = (nln == mno) ? COLOR_TVRUN : (is_script) ? COLOR_TVCMD : COLOR_TVTEXT;
|
||||||
|
int cmt_start = llen;
|
||||||
|
if (is_script && (nln != mno)) {
|
||||||
|
char* hash = line_seek(text, len, 0, ptr, 0);
|
||||||
|
for (; *hash != '#' && (hash - ptr < (int) llen); hash++);
|
||||||
|
cmt_start = (hash - ptr) - off_disp;
|
||||||
|
}
|
||||||
|
if (cmt_start <= 0) color_text = COLOR_TVCMT;
|
||||||
|
|
||||||
|
// build text string
|
||||||
|
snprintf(txtstr, TV_LLEN_DISP + 1, "%-*.*s", (int) TV_LLEN_DISP, (int) TV_LLEN_DISP, "");
|
||||||
|
if (ncpy) memcpy(txtstr, ptr + off_disp, ncpy);
|
||||||
|
for (char* d = txtstr; *d; d++) if (*d < ' ') *d = ' ';
|
||||||
|
if (al) memcpy(txtstr + p_al, al_str, strnlen(al_str, 16));
|
||||||
|
if (ar) memcpy(txtstr + p_ar, ar_str, strnlen(ar_str, 16));
|
||||||
|
|
||||||
|
// draw line number & text
|
||||||
|
DrawStringF(TOP_SCREEN, x_txt, y, color_text, COLOR_STD_BG, txtstr);
|
||||||
|
if (TV_LNOS > 0) { // line number
|
||||||
|
if (ptr != ptr_next)
|
||||||
|
DrawStringF(TOP_SCREEN, x_lno, y, ((ptr == text) || (*(ptr-1) == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", TV_LNOS, nln);
|
||||||
|
else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// colorize comment if is_script
|
||||||
|
if (cmt_start < (int) llen) {
|
||||||
|
for (int i = 0; i < (int) llen; i++)
|
||||||
|
if (i < cmt_start) txtstr[i] = ' ';
|
||||||
|
DrawStringF(TOP_SCREEN, x_txt, y, COLOR_TVCMT, COLOR_TRANSPARENT, txtstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// colorize arrows
|
||||||
|
if (al) DrawStringF(TOP_SCREEN, x_al, y, COLOR_TVOFFS, COLOR_TRANSPARENT, al_str);
|
||||||
|
if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, ar_str);
|
||||||
|
|
||||||
|
// advance pointer / line number
|
||||||
|
for (char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln;
|
||||||
|
ptr = ptr_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemTextViewer(const char* text, u32 len, bool as_script) {
|
||||||
|
u32 ww = 0;
|
||||||
|
|
||||||
|
// check if this really is text
|
||||||
|
if (!ValidateText(text, len)) {
|
||||||
|
ShowPrompt(false, "Error: Invalid text data");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear screens
|
||||||
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
|
|
||||||
|
// instructions
|
||||||
|
static const char* instr = "Textviewer Controls:\n \n\x18\x19\x1A\x1B(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nB - Exit\n";
|
||||||
|
ShowString(instr);
|
||||||
|
|
||||||
|
// find maximum line len
|
||||||
|
u32 llen_max = 0;
|
||||||
|
for (char* ptr = (char*) text; ptr < (text + len); ptr = line_seek(text, len, 0, ptr, 1)) {
|
||||||
|
u32 llen = line_len(text, len, 0, ptr);
|
||||||
|
if (llen > llen_max) llen_max = llen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find last allowed lines (ww and nonww)
|
||||||
|
char* llast_nww = line_seek(text, len, 0, text + len, -TV_NLIN_DISP);
|
||||||
|
char* llast_ww = line_seek(text, len, TV_LLEN_DISP, text + len, -TV_NLIN_DISP);
|
||||||
|
|
||||||
|
char* line0 = (char*) text;
|
||||||
|
int lcurr = 1;
|
||||||
|
int off_disp = 0;
|
||||||
|
while (true) {
|
||||||
|
// display text on screen
|
||||||
|
MemTextView(text, len, line0, off_disp, lcurr, ww, 0, as_script);
|
||||||
|
|
||||||
|
// handle user input
|
||||||
|
u32 pad_state = InputWait(0);
|
||||||
|
if ((pad_state & BUTTON_R1) && (pad_state & BUTTON_L1)) CreateScreenshot();
|
||||||
|
else { // standard viewer mode
|
||||||
|
char* line0_next = line0;
|
||||||
|
u32 step_ud = (pad_state & BUTTON_R1) ? TV_NLIN_DISP : 1;
|
||||||
|
u32 step_lr = (pad_state & BUTTON_R1) ? TV_LLEN_DISP : 1;
|
||||||
|
bool switched = (pad_state & BUTTON_R1);
|
||||||
|
if (pad_state & BUTTON_DOWN) line0_next = line_seek(text, len, ww, line0, step_ud);
|
||||||
|
else if (pad_state & BUTTON_UP) line0_next = line_seek(text, len, ww, line0, -step_ud);
|
||||||
|
else if (pad_state & BUTTON_RIGHT) off_disp += step_lr;
|
||||||
|
else if (pad_state & BUTTON_LEFT) off_disp -= step_lr;
|
||||||
|
else if (switched && (pad_state & BUTTON_X)) {
|
||||||
|
u64 lnext64 = ShowNumberPrompt(lcurr, "Current line: %i\nEnter new line below.", lcurr);
|
||||||
|
if (lnext64 && (lnext64 != (u64) -1)) line0_next = line_seek(text, len, 0, line0, (int) lnext64 - lcurr);
|
||||||
|
ShowString(instr);
|
||||||
|
} else if (switched && (pad_state & BUTTON_Y)) {
|
||||||
|
ww = ww ? 0 : TV_LLEN_DISP;
|
||||||
|
line0_next = line_seek(text, len, ww, line0, 0);
|
||||||
|
} else if (pad_state & (BUTTON_B|BUTTON_START)) break;
|
||||||
|
|
||||||
|
// check for problems, apply changes
|
||||||
|
if (!ww && (line0_next > llast_nww)) line0_next = llast_nww;
|
||||||
|
else if (ww && (line0_next > llast_ww)) line0_next = llast_ww;
|
||||||
|
if (line0_next < line0) { // fix line number for decrease
|
||||||
|
do if (*(--line0) == '\n') lcurr--;
|
||||||
|
while (line0 > line0_next);
|
||||||
|
} else { // fix line number for increase / same
|
||||||
|
for (; line0_next > line0; line0++)
|
||||||
|
if (*line0 == '\n') lcurr++;
|
||||||
|
}
|
||||||
|
if (off_disp + TV_LLEN_DISP > (int) llen_max) off_disp = llen_max - TV_LLEN_DISP;
|
||||||
|
if ((off_disp < 0) || ww) off_disp = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear screens
|
||||||
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileTextViewer(const char* path, bool as_script) {
|
||||||
|
// load text file (completely into memory)
|
||||||
|
char* text = (char*) TEMP_BUFFER;
|
||||||
|
u32 flen = FileGetData(path, text, TEMP_BUFFER_SIZE, 0);
|
||||||
|
u32 len = 0; // actual length may be shorter due to zero symbol
|
||||||
|
for (len = 0; (len < flen) && text[len]; len++);
|
||||||
|
|
||||||
|
// let MemTextViewer take over
|
||||||
|
return MemTextViewer(text, len, as_script);
|
||||||
|
}
|
||||||
|
|
||||||
bool ExecuteGM9Script(const char* path_script) {
|
bool ExecuteGM9Script(const char* path_script) {
|
||||||
// revert mount state?
|
// revert mount state?
|
||||||
char* script = (char*) SCRIPT_BUFFER;
|
char* script = (char*) SCRIPT_BUFFER;
|
||||||
@ -607,13 +847,29 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
// initialise variables
|
// initialise variables
|
||||||
init_vars(path_script);
|
init_vars(path_script);
|
||||||
|
|
||||||
for (u32 line = 1; ptr < end; line++) {
|
// clear screen (only if script viewer is used
|
||||||
|
if (MAIN_SCREEN != TOP_SCREEN)
|
||||||
|
ClearScreen(TOP_SCREEN, COLOR_STD_BG);
|
||||||
|
|
||||||
|
// script execute loop
|
||||||
|
for (u32 lno = 1; ptr < end; lno++) {
|
||||||
u32 flags = 0;
|
u32 flags = 0;
|
||||||
|
|
||||||
// find line end
|
// find line end
|
||||||
char* line_end = strchr(ptr, '\n');
|
char* line_end = strchr(ptr, '\n');
|
||||||
if (!line_end) line_end = ptr + strlen(ptr);
|
if (!line_end) line_end = ptr + strlen(ptr);
|
||||||
|
|
||||||
|
// update script viewer
|
||||||
|
if (MAIN_SCREEN != TOP_SCREEN) {
|
||||||
|
if (lno <= (TV_NLIN_DISP/2)) {
|
||||||
|
MemTextView(script, script_size, script, 0, 1, 0, lno, true);
|
||||||
|
} else {
|
||||||
|
char* ptr_view = line_seek(script, script_size, 0, ptr, -(TV_NLIN_DISP/2));
|
||||||
|
u32 lno_view = lno - (TV_NLIN_DISP/2);
|
||||||
|
MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// run command
|
// run command
|
||||||
char err_str[_ERR_STR_LEN+1] = { 0 };
|
char err_str[_ERR_STR_LEN+1] = { 0 };
|
||||||
if (!run_line(ptr, line_end, &flags, err_str)) { // error handling
|
if (!run_line(ptr, line_end, &flags, err_str)) { // error handling
|
||||||
@ -633,7 +889,7 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
else snprintf(line_str, 32+1, "%.*s", lptr1 - lptr0, lptr0);
|
else snprintf(line_str, 32+1, "%.*s", lptr1 - lptr0, lptr0);
|
||||||
char path_str[32+1];
|
char path_str[32+1];
|
||||||
TruncateString(path_str, path_script, 32, 12);
|
TruncateString(path_str, path_script, 32, 12);
|
||||||
ShowPrompt(false, "%s\nline %lu: %s\n%s", path_str, line, err_str, line_str);
|
ShowPrompt(false, "%s\nline %lu: %s\n%s", path_str, lno, err_str, line_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(flags & _FLG('o'))) return false; // failed if not optional
|
if (!(flags & _FLG('o'))) return false; // failed if not optional
|
||||||
@ -645,5 +901,6 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
|
|
||||||
char* msg_okay = get_var("SUCCESSMSG", NULL);
|
char* msg_okay = get_var("SUCCESSMSG", NULL);
|
||||||
if (msg_okay && *msg_okay) ShowPrompt(false, msg_okay);
|
if (msg_okay && *msg_okay) ShowPrompt(false, msg_okay);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,6 @@
|
|||||||
#define SCRIPT_MAX_SIZE (SCRIPT_BUFFER_SIZE-VAR_BUFFER_SIZE-1)
|
#define SCRIPT_MAX_SIZE (SCRIPT_BUFFER_SIZE-VAR_BUFFER_SIZE-1)
|
||||||
|
|
||||||
bool ValidateText(const char* text, u32 size);
|
bool ValidateText(const char* text, u32 size);
|
||||||
|
bool MemTextViewer(const char* text, u32 len, bool as_script);
|
||||||
|
bool FileTextViewer(const char* path, bool as_script);
|
||||||
bool ExecuteGM9Script(const char* path_script);
|
bool ExecuteGM9Script(const char* path_script);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user