diff --git a/Makefile b/Makefile index b691e74..dd1b052 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,10 @@ ifeq ($(SWITCH_SCREENS),1) CFLAGS += -DSWITCH_SCREENS endif +ifneq ("$(wildcard $(CURDIR)/../$(DATA)/README.md)","") + CFLAGS += -DHARDCODE_README +endif + ifneq ("$(wildcard $(CURDIR)/../$(DATA)/aeskeydb.bin)","") CFLAGS += -DHARDCODE_KEYS endif @@ -103,7 +107,8 @@ export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/aeskeydb.bin))) \ +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/README.md))) \ + $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/aeskeydb.bin))) \ $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/autorun.gm9))) ifeq ($(SAFEMODE),1) BINFILES += $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/sm9*.*))) @@ -219,6 +224,11 @@ $(OUTPUT).elf : $(OFILES) @$(bin2o) #--------------------------------------------------------------------------------- %_gm9.h %.gm9.o: %.gm9 +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) +#--------------------------------------------------------------------------------- +%_md.h %.md.o: %.md #--------------------------------------------------------------------------------- @echo $(notdir $<) @$(bin2o) diff --git a/source/godmode.c b/source/godmode.c index 253cb3b..621891e 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -31,6 +31,12 @@ #ifdef AUTORUN_SCRIPT #include "autorun_gm9.h" #endif +#ifdef HARDCODE_README +#include "README_md.h" +#else +#define README_md NULL +#define README_md_size 0 +#endif #define N_PANES 2 @@ -1490,6 +1496,7 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1; int clock = ++n_opt; int sysinfo = ++n_opt; + int readme = (README_md != NULL) ? (int) ++n_opt : -1; if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu"; if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup"; @@ -1498,6 +1505,7 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar if (hsrestore > 0) optionstr[hsrestore - 1] = "Restore H&S"; if (clock > 0) optionstr[clock - 1] = "Set RTC date&time"; if (sysinfo > 0) optionstr[sysinfo - 1] = "System info"; + if (readme > 0) optionstr[readme - 1] = "Show ReadMe"; int user_select = ShowSelectPrompt(n_opt, optionstr, promptstr); if (user_select == sdformat) { // format SD card @@ -1598,7 +1606,11 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar else if (user_select == sysinfo) { // Myria's system info char* sysinfo_txt = (char*) TEMP_BUFFER; MyriaSysinfo(sysinfo_txt); - MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, TEMP_BUFFER_SIZE), false); + MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, TEMP_BUFFER_SIZE), 1, false); + return 0; + } + else if (user_select == readme) { // Display GodMode9 readme + MemToCViewer((const char*) README_md, README_md_size, "GodMode9 ReadMe Table of Contents"); return 0; } else return 1; diff --git a/source/utils/scripting.c b/source/utils/scripting.c index f7b45d9..fbcf129 100644 --- a/source/utils/scripting.c +++ b/source/utils/scripting.c @@ -884,8 +884,8 @@ void MemTextView(const char* text, u32 len, char* line0, int off_disp, int lno, } } -bool MemTextViewer(const char* text, u32 len, bool as_script) { - u32 ww = 0; +bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) { + u32 ww = TV_LLEN_DISP; // check if this really is text if (!ValidateText(text, len)) { @@ -921,6 +921,7 @@ bool MemTextViewer(const char* text, u32 len, bool as_script) { char* line0 = (char*) text; int lcurr = 1; int off_disp = 0; + for (; lcurr < (int) start; line0 = line_seek(text, len, 0, line0, 1), lcurr++); while (true) { // display text on screen MemTextView(text, len, line0, off_disp, lcurr, ww, 0, as_script); @@ -967,6 +968,81 @@ bool MemTextViewer(const char* text, u32 len, bool as_script) { return true; } +// right now really only intended for use with the GodMode9 readme +// (misses safety checks for wider compatibility) +bool MemToCViewer(const char* text, u32 len, const char* title) { + const u32 max_captions = 24; // we assume this is enough + char* captions[max_captions]; + u32 lineno[max_captions]; + u32 ww = TV_LLEN_DISP; + + // check if this really is text + if (!ValidateText(text, len)) { + ShowPrompt(false, "Error: Invalid text data"); + return false; + } + + // clear screens / view start of readme on top + script_color_active = COLOR_TVRUN; + ClearScreenF(true, true, COLOR_STD_BG); + MemTextView(text, len, (char*) text, 0, 1, ww, 0, false); + + // parse text for markdown captions + u32 n_captions = 0; + char* ptr = (char*) text; + for (u32 lno = 1;; lno++) { + char* ptr_next = line_seek(text, len, 0, ptr, 1); + if (ptr == ptr_next) break; + if (*ptr == '#') { + captions[n_captions] = ptr; + lineno[n_captions] = lno; + if ((lno > 1) && (++n_captions >= max_captions)) break; + } + ptr = ptr_next; + } + + int cursor = -1; + while (true) { + // display ToC + u32 y0 = TV_VPAD; + u32 x0 = (SCREEN_WIDTH_BOT - GetDrawStringWidth(title)) / 2; + DrawStringF(BOT_SCREEN, x0, y0, COLOR_TVTEXT, COLOR_STD_BG, "%s\n%*.*s", title, + strnlen(title, 40), strnlen(title, 40), "========================================"); + y0 += 2 * (FONT_HEIGHT_EXT + (2*TV_VPAD)); + for (u32 i = 0; (i < n_captions) && (y0 < SCREEN_HEIGHT); i++) { + u32 text_color = ((int) i == cursor) ? COLOR_TVRUN : COLOR_TVTEXT; + char* caption = captions[i]; + u32 len = 0; + u32 lvl = 0; + for (; *caption == '#'; caption++, lvl++); + for (; IS_WHITESPACE(*caption); caption++); + for (; caption[len] != '\n' && caption[len] != '\r'; len++); + DrawStringF(BOT_SCREEN, x0 + (lvl-1) * (FONT_WIDTH_EXT/2), y0, text_color, COLOR_STD_BG, + "%*.*s", len, len, caption); + y0 += FONT_HEIGHT_EXT + (2*TV_VPAD); + } + + // handle user input + u32 pad_state = InputWait(0); + if ((cursor >= 0) && (pad_state & BUTTON_A)) { + return MemTextViewer(text, len, lineno[cursor], false); + } else if (pad_state & BUTTON_B) { + break; + } else if (pad_state & BUTTON_UP) { + cursor = (cursor <= 0) ? ((int) n_captions - 1) : cursor - 1; + MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false); + } else if (pad_state & BUTTON_DOWN) { + if (++cursor >= (int) n_captions) cursor = 0; + MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false); + } + } + + // 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; @@ -975,7 +1051,7 @@ bool FileTextViewer(const char* path, bool as_script) { for (len = 0; (len < flen) && text[len]; len++); // let MemTextViewer take over - return MemTextViewer(text, len, as_script); + return MemTextViewer(text, len, 1, as_script); } bool ExecuteGM9Script(const char* path_script) { diff --git a/source/utils/scripting.h b/source/utils/scripting.h index 1417860..309a118 100644 --- a/source/utils/scripting.h +++ b/source/utils/scripting.h @@ -8,6 +8,7 @@ #define SCRIPT_MAX_SIZE (SCRIPT_BUFFER_SIZE-VAR_BUFFER_SIZE-1) bool ValidateText(const char* text, u32 size); -bool MemTextViewer(const char* text, u32 len, bool as_script); +bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script); +bool MemToCViewer(const char* text, u32 len, const char* title); bool FileTextViewer(const char* path, bool as_script); bool ExecuteGM9Script(const char* path_script);