Skip to content

Commit

Permalink
Adding text editor functionality to text viewer.
Browse files Browse the repository at this point in the history
  • Loading branch information
nevumx committed Jun 30, 2024
1 parent 7b6b478 commit ab681aa
Show file tree
Hide file tree
Showing 11 changed files with 503 additions and 186 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ With the possibilites GodMode9 provides, not everything may be obvious at first
* __Search drives and folders__: Just press R+A on the drive / folder you want to search.
* __Compare and verify files__: Press the A button on the first file, select `Calculate SHA-256`. Do the same for the second file. If the two files are identical, you will get a message about them being identical. On the SDCARD drive (`0:`) you can also write an SHA file, so you can check for any modifications at a later point.
* __Hexview and hexedit any file__: Press the A button on a file and select `Show in Hexeditor`. A button again enables edit mode, hold the A button and press arrow buttons to edit bytes. You will get an additional confirmation prompt to take over changes. Take note that for certain files, write permissions can't be enabled.
* __View text files in a text viewer__: Press the A button on a file and select `Show in Textviewer` (only shows up for actual text files). You can enable wordwrapped mode via R+Y, and navigate around the file via R+X and the dpad.
* __View/edit text files in a text editor__: Press the A button on a file and select `Show in Text Editor` (only shows up for actual text files). You can enable wordwrapped mode via R+Y, and navigate around the file via R+X and the dpad.
* __Chainload FIRM payloads__: Press the A button on a FIRM file, select `FIRM options` -> `Boot FIRM`. Keep in mind you should not run FIRMs from dubious sources and that the write permissions system is no longer in place after booting a payload.
* __Chainload FIRM payloads from a neat menu__: The `payloads` menu is found inside the HOME button menu. It provides any FIRM found in `0:/gm9/payloads` for quick chainloading.
* __Inject a file to another file__: Put exactly one file (the file to be injected from) into the clipboard (via the Y button). Press A on the file to be injected to. There will be an option to inject the first file into it.
Expand Down Expand Up @@ -203,6 +203,7 @@ This tool would not have been possible without the help of numerous people. Than
* **Lilith Valentine** for testing and helpful advice
* **Project Nayuki** for [qrcodegen](https://github.com/nayuki/QR-Code-generator)
* **Amazingmax fonts** for the Amazdoom font
* **nevumx** for turning the text viewer into a text editor with UTF-8 and LF/CRLF support
* The fine folks on **the official GodMode9 IRC channel and Discord server**
* The fine folks on **freenode #Cakey**
* All **[3dbrew.org](https://www.3dbrew.org/wiki/Main_Page) editors**
Expand Down
99 changes: 69 additions & 30 deletions arm9/source/common/swkbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include "language.h"
#include "swkbd.h"
#include "timer.h"
#include "hid.h"
#include "utf.h"


Expand All @@ -12,7 +11,7 @@ static inline char to_uppercase(char c) {
return c;
}

static bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout) {
bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout, bool multi_line) {
// count # of rows
u32 n_rows = 0;
for (u32 i = 0;; i++) {
Expand All @@ -26,16 +25,18 @@ static bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout) {
u32 height = (n_rows) ? (n_rows * SWKBD_STDKEY_HEIGHT) + ((n_rows-1) * SWKDB_KEY_SPACING) : 0;
u32 p_y = SCREEN_HEIGHT - height - SWKBD_STDKEY_HEIGHT - SWKDB_KEY_SPACING;

// set up the textbox
TouchBox* txtbox = swkbd;
txtbox->x = (SCREEN_WIDTH_BOT - SWKBD_TEXTBOX_WIDTH) / 2;
txtbox->y = p_y - 30;
txtbox->w = SWKBD_TEXTBOX_WIDTH;
txtbox->h = 30;
txtbox->id = KEY_TXTBOX;

// set button positions
TouchBox* tb = swkbd + 1;
TouchBox* tb = swkbd;
if (!multi_line) {
// set up the textbox
TouchBox* txtbox = tb++;
txtbox->x = (SCREEN_WIDTH_BOT - SWKBD_TEXTBOX_WIDTH) / 2;
txtbox->y = p_y - 30;
txtbox->w = SWKBD_TEXTBOX_WIDTH;
txtbox->h = 30;
txtbox->id = KEY_TXTBOX;
}

for (u32 l = 0, k = 0; layout[l] != 0; ) {
// calculate width of current row
u32 n_keys = layout[l++];
Expand Down Expand Up @@ -70,8 +71,9 @@ static bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout) {
return true;
}

static void DrawKey(const TouchBox* key, const bool pressed, const u32 uppercase) {
static void DrawKey(const TouchBox* key, const bool pressed, const u32 uppercase, const bool multi_line) {
const char* keystrs[] = { SWKBD_KEYSTR };
const char* ml_keystrs[] = { SWKBD_ML_KEYSTR };
const u32 color = (pressed) ? COLOR_SWKBD_PRESSED :
(key->id == KEY_ENTER) ? COLOR_SWKBD_ENTER :
((key->id == KEY_CAPS) && (uppercase > 1)) ? COLOR_SWKBD_CAPS :
Expand All @@ -81,7 +83,7 @@ static void DrawKey(const TouchBox* key, const bool pressed, const u32 uppercase
if (key->id == KEY_TXTBOX) return;

char keystr[16];
if (key->id >= 0x80) snprintf(keystr, sizeof(keystr), "%s", keystrs[key->id - 0x80]);
if (key->id >= 0x80) snprintf(keystr, sizeof(keystr), "%s", (multi_line ? ml_keystrs : keystrs)[key->id - 0x80]);
else {
keystr[0] = (uppercase) ? to_uppercase(key->id) : key->id;
keystr[1] = 0;
Expand Down Expand Up @@ -111,12 +113,12 @@ static void DrawKeyBoardBox(TouchBox* swkbd, u32 color) {
DrawRectangle(BOT_SCREEN, x0-1, y0-1, x1-x0+2, y1-y0+2, color);
}

static void DrawKeyBoard(TouchBox* swkbd, const u32 uppercase) {
static void DrawKeyBoard(TouchBox* swkbd, const u32 uppercase, const bool multi_line) {
// we need to make sure to skip the textbox here(first entry)

// draw keyboard
for (TouchBox* tb = swkbd + 1; tb->id != 0; tb++) {
DrawKey(tb, false, uppercase);
for (TouchBox* tb = swkbd + (multi_line ? 0 : 1); tb->id != 0; tb++) {
DrawKey(tb, false, uppercase, multi_line);
}
}

Expand Down Expand Up @@ -209,21 +211,24 @@ static void MoveTextBoxCursor(const TouchBox* txtbox, const char* inputstr, cons
}
}

static char KeyboardWait(TouchBox* swkbd, bool uppercase) {
static char KeyboardWait(TouchBox* swkbd, bool uppercase, const bool multi_line) {
u32 id = 0;
u16 x, y;

// wait for touch input (handle key input, too)
while (true) {
u32 pressed = InputWait(0);
if (pressed & BUTTON_B) return KEY_ESCAPE;
if (multi_line && pressed & TIMEOUT_HID) return 0;
else if (pressed & BUTTON_B) return KEY_ESCAPE;
else if (pressed & BUTTON_A) return KEY_ENTER;
else if (pressed & BUTTON_X) return KEY_BKSPC;
else if (pressed & BUTTON_Y) return KEY_INSERT;
else if (pressed & BUTTON_R1) return KEY_CAPS;
else if (pressed & BUTTON_Y) return multi_line ? KEY_CAPS : KEY_INSERT;
else if (!multi_line && pressed & BUTTON_R1) return KEY_CAPS;
else if (pressed & BUTTON_RIGHT) return KEY_RIGHT;
else if (pressed & BUTTON_LEFT) return KEY_LEFT;
else if (pressed & BUTTON_SELECT) return KEY_SWITCH;
else if (multi_line && pressed & BUTTON_UP) return KEY_UP;
else if (multi_line && pressed & BUTTON_DOWN) return KEY_DOWN;
else if (!multi_line && pressed & BUTTON_SELECT) return KEY_SWITCH;
else if (pressed & BUTTON_TOUCH) break;
}

Expand All @@ -232,9 +237,9 @@ static char KeyboardWait(TouchBox* swkbd, bool uppercase) {
const TouchBox* tb = TouchBoxGet(&id, x, y, swkbd, 0);
if (tb) {
if (id == KEY_TXTBOX) break; // immediately break on textbox
DrawKey(tb, true, uppercase);
DrawKey(tb, true, uppercase, multi_line);
while(HID_ReadTouchState(&x, &y) && (tb == TouchBoxGet(NULL, x, y, swkbd, 0)));
DrawKey(tb, false, uppercase);
DrawKey(tb, false, uppercase, multi_line);
}
}

Expand All @@ -261,9 +266,9 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
}

// generate keyboards
if (!BuildKeyboard(swkbd_alphabet, keys_alphabet, layout_alphabet)) return false;
if (!BuildKeyboard(swkbd_special, keys_special, layout_special)) return false;
if (!BuildKeyboard(swkbd_numpad, keys_numpad, layout_numpad)) return false;
if (!BuildKeyboard(swkbd_alphabet, keys_alphabet, layout_alphabet, false)) return false;
if (!BuildKeyboard(swkbd_special, keys_special, layout_special, false)) return false;
if (!BuildKeyboard(swkbd_numpad, keys_numpad, layout_numpad, false)) return false;

// (instructional) text
char str[512]; // arbitrary limit, should be more than enough
Expand Down Expand Up @@ -292,19 +297,19 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
// draw keyboard if required
if (swkbd != swkbd_prev) {
DrawKeyBoardBox(swkbd, COLOR_SWKBD_BOX);
DrawKeyBoard(swkbd, uppercase);
DrawKeyBoard(swkbd, uppercase, false);
DrawTextBox(textbox, inputstr, cursor, &scroll);
swkbd_prev = swkbd;
}

// handle user input
char key = KeyboardWait(swkbd, uppercase);
char key = KeyboardWait(swkbd, uppercase, false);
if (key == KEY_INSERT) key = ' '; // impromptu replacement
if (key == KEY_TXTBOX) {
MoveTextBoxCursor(textbox, inputstr, max_size, &cursor, &scroll);
} else if (key == KEY_CAPS) {
uppercase = (uppercase + 1) % 3;
DrawKeyBoard(swkbd, uppercase);
DrawKeyBoard(swkbd, uppercase, false);
continue;
} else if (key == KEY_ENTER) {
ret = true;
Expand Down Expand Up @@ -375,7 +380,7 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
}
if (uppercase == 1) {
uppercase = 0;
DrawKeyBoard(swkbd, uppercase);
DrawKeyBoard(swkbd, uppercase, false);
}
}

Expand All @@ -385,4 +390,38 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {

ClearScreen(BOT_SCREEN, COLOR_STD_BG);
return ret;
}

char ShowMultiLineKeyboard(TouchBox* swkbd_alphabet, TouchBox* swkbd_special, TouchBox* swkbd_numpad, TouchBox** swkbd, TouchBox** swkbd_prev, u32* uppercase) {
if (!*swkbd) {
u32 str_width = GetDrawStringWidth(STR_TEXTEDITOR_CONTROLS_KEYBOARD);
if (str_width < (24 * FONT_WIDTH_EXT)) str_width = 24 * FONT_WIDTH_EXT;
u32 str_x = (str_width >= SCREEN_WIDTH_BOT) ? 0 : (SCREEN_WIDTH_BOT - str_width) / 2;
ClearScreen(BOT_SCREEN, COLOR_STD_BG);
DrawStringF(BOT_SCREEN, str_x, 20, COLOR_STD_FONT, COLOR_STD_BG, "%s", STR_TEXTEDITOR_CONTROLS_KEYBOARD);
*swkbd = swkbd_alphabet;
}

// handle keyboard
while (true) {
// draw keyboard if required
if (*swkbd != *swkbd_prev) {
DrawKeyBoardBox(*swkbd, COLOR_SWKBD_BOX);
DrawKeyBoard(*swkbd, *uppercase, true);
*swkbd_prev = *swkbd;
}

// handle user input
char key = KeyboardWait(*swkbd, *uppercase, true);
if (key == KEY_ALPHA) {
*swkbd = swkbd_alphabet;
} else if (key == KEY_SPECIAL) {
*swkbd = swkbd_special;
} else if (key == KEY_NUMPAD) {
*swkbd = swkbd_numpad;
} else if (key == KEY_CAPS) {
*uppercase = (*uppercase + 1) % 3;
DrawKeyBoard(*swkbd, *uppercase, true);
} else return key;
}
}
32 changes: 27 additions & 5 deletions arm9/source/common/swkbd.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "common.h"
#include "hid.h"
#include "ui.h"
#include "touchcal.h"

Expand All @@ -20,11 +21,15 @@ enum {
KEY_ESCAPE = 0x8A,
KEY_SWITCH = 0x8B,
KEY_UNICODE = 0x8C,
KEY_UP = 0x8D,
KEY_DOWN = 0x8E,
KEY_TXTBOX = 0xFF
};

// special key strings
#define SWKBD_KEYSTR "", "DEL", "INS", "SUBMIT", "CAPS", "#$@", "123", "ABC", "←", "→", "ESC", "SWITCH", "U+"
#define SWKBD_KEYSTR "", "DEL", "INS", "SUBMIT", "CAPS", "#$@", "123", "ABC", "←", "→", "ESC", "SWITCH", "U+", "↑", "↓"
// multiline special key stings
#define SWKBD_ML_KEYSTR "", "DEL", "INS", "ENTER", "CAPS", "#$@", "123", "ABC", "←", "→", "ESC", "SWITCH", "U+", "↑", "↓"

#define COLOR_SWKBD_NORMAL COLOR_GREY
#define COLOR_SWKBD_PRESSED COLOR_LIGHTGREY
Expand All @@ -45,6 +50,13 @@ enum {
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '_', '#', '!', \
KEY_CAPS, ' ', KEY_NUMPAD, KEY_SPECIAL, KEY_LEFT, KEY_RIGHT

#define SWKBD_KEYS_ML_ALPHABET \
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '+', '-', KEY_BKSPC, \
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '&', KEY_ENTER, \
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '(', ')', '[', ']', \
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '_', '#', '!', \
KEY_CAPS, ' ', KEY_NUMPAD, KEY_SPECIAL, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN

#define SWKBD_KEYS_SPECIAL \
'(', ')', '{', '}', '[', ']', \
'.', ',', '?', '!', '`', '\'', \
Expand All @@ -53,9 +65,9 @@ enum {
KEY_ALPHA, ' ', KEY_BKSPC

#define SWKBD_KEYS_NUMPAD \
'7', '8', '9', 'F', 'E', \
'4', '5', '6', 'D', 'C', \
'3', '2', '1', 'B', 'A', \
'7', '8', '9', 'E', 'F', \
'4', '5', '6', 'C', 'D', \
'1', '2', '3', 'A', 'B', \
'0', '.', '_', KEY_LEFT, KEY_RIGHT, \
KEY_ALPHA, KEY_UNICODE, ' ', KEY_BKSPC

Expand All @@ -68,12 +80,20 @@ enum {
6, 32, 123, 32, 32, 18, 18, 0, \
0

#define SWKBD_LAYOUT_ML_ALPHABET \
13, 32, 0, \
12, 51, 0, \
13, 0, \
12, 0, \
8, 32, 85, 32, 32, 18, 18, 18, 18, 0, \
0

#define SWKBD_LAYOUT_SPECIAL \
6, 0, \
6, 0, \
6, 0, \
6, 0, \
3, 32, 46, 32, 0, \
3, 32, 47, 32, 0, \
0

#define SWKBD_LAYOUT_NUMPAD \
Expand All @@ -87,3 +107,5 @@ enum {

#define ShowKeyboardOrPrompt (TouchIsCalibrated() ? ShowKeyboard : ShowStringPrompt)
bool PRINTF_ARGS(3) ShowKeyboard(char* inputstr, u32 max_size, const char *format, ...);
bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout, bool multi_line);
char ShowMultiLineKeyboard(TouchBox* swkbd_alphabet, TouchBox* swkbd_special, TouchBox* swkbd_numpad, TouchBox** swkbd, TouchBox** swkbd_prev, u32* uppercase);
34 changes: 23 additions & 11 deletions arm9/source/common/ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,22 +489,34 @@ u32 GetDrawStringHeight(const char* str) {
return height;
}

u32 GetCharSize(const char* str) {
const char *start = str;
do {
str++;
} while ((*str & 0xC0) == 0x80);
static inline bool IsIntermediateByte(const char* chr) {
return (*chr & 0xC0) == 0x80 || (chr[-1] == '\r' && chr[0] == '\n');
}

const char* GetNextChar(const char* chr) {
do ++chr; while (IsIntermediateByte(chr));
return chr;
}

const char* GetPrevChar(const char* chr) {
do --chr; while (IsIntermediateByte(chr));
return chr;
}

return str - start;
u32 GetCharSize(const char* str) {
return GetNextChar(str) - str;
}

u32 GetPrevCharSize(const char* str) {
const char *start = str;
do {
str--;
} while ((*str & 0xC0) == 0x80);
return str - GetPrevChar(str);
}

void IncChar(const char** chr) {
*chr = GetNextChar(*chr);
}

return start - str;
void DecChar(const char** chr) {
*chr = GetPrevChar(*chr);
}

u32 GetDrawStringWidth(const char* str) {
Expand Down
4 changes: 4 additions & 0 deletions arm9/source/common/ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ void PRINTF_ARGS(4) DrawStringCenter(u16 *screen, u32 color, u32 bgcolor, const

u32 GetCharSize(const char* str);
u32 GetPrevCharSize(const char* str);
const char* GetNextChar(const char* chr);
const char* GetPrevChar(const char* chr);
void IncChar(const char** chr);
void DecChar(const char** chr);

u32 GetDrawStringHeight(const char* str);
u32 GetDrawStringWidth(const char* str);
Expand Down
5 changes: 3 additions & 2 deletions arm9/source/godmode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
int n_opt = 0;
int special = (special_opt) ? ++n_opt : -1;
int hexviewer = ++n_opt;
int textviewer = (filetype & TXT_GENERIC) ? ++n_opt : -1;
int textviewer = (filetype & TXT_GENERIC || FileGetSize(file_path) == 0) ? ++n_opt : -1;
int calcsha256 = ++n_opt;
int calcsha1 = ++n_opt;
int calccmac = (CheckCmacPath(file_path) == 0) ? ++n_opt : -1;
Expand Down Expand Up @@ -1316,6 +1316,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
}
else if (user_select == textviewer) { // -> show in text viewer
FileTextViewer(file_path, scriptable);
GetDirContents(current_dir, current_path);
return 0;
}
else if (user_select == calcsha256) { // -> calculate SHA-256
Expand Down Expand Up @@ -2340,7 +2341,7 @@ u32 HomeMoreMenu(char* current_path) {
char* sysinfo_txt = (char*) malloc(STD_BUFFER_SIZE);
if (!sysinfo_txt) return 1;
MyriaSysinfo(sysinfo_txt);
MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, STD_BUFFER_SIZE), 1, false);
MemTextViewer(sysinfo_txt, strnlen(sysinfo_txt, STD_BUFFER_SIZE), 1, false, 0, NULL);
free(sysinfo_txt);
return 0;
}
Expand Down
Loading

0 comments on commit ab681aa

Please sign in to comment.