mirror of
https://github.com/d0k3/GodMode9.git
synced 2026-05-30 22:36:55 +00:00
Adding text editor functionality to text viewer.
This commit is contained in:
parent
1bbd8193d1
commit
d65a5b6771
@ -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.
|
* __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.
|
* __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.
|
* __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__: 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.
|
* __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.
|
* __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.
|
||||||
@ -208,6 +208,7 @@ This tool would not have been possible without the help of numerous people. Than
|
|||||||
* **Project Nayuki** for [qrcodegen](https://github.com/nayuki/QR-Code-generator)
|
* **Project Nayuki** for [qrcodegen](https://github.com/nayuki/QR-Code-generator)
|
||||||
* **Amazingmax fonts** for the Amazdoom font
|
* **Amazingmax fonts** for the Amazdoom font
|
||||||
* **TakWolf** for [fusion-pixel-font](https://github.com/TakWolf/fusion-pixel-font) used for Chinese and Korean
|
* **TakWolf** for [fusion-pixel-font](https://github.com/TakWolf/fusion-pixel-font) used for Chinese and Korean
|
||||||
|
* **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 **the official GodMode9 IRC channel and Discord server**
|
||||||
* The fine folks on **freenode #Cakey**
|
* The fine folks on **freenode #Cakey**
|
||||||
* All **[3dbrew.org](https://www.3dbrew.org/wiki/Main_Page) editors**
|
* All **[3dbrew.org](https://www.3dbrew.org/wiki/Main_Page) editors**
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "swkbd.h"
|
#include "swkbd.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "hid.h"
|
|
||||||
#include "utf.h"
|
#include "utf.h"
|
||||||
|
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ static inline char to_uppercase(char c) {
|
|||||||
return 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
|
// count # of rows
|
||||||
u32 n_rows = 0;
|
u32 n_rows = 0;
|
||||||
for (u32 i = 0;; i++) {
|
for (u32 i = 0;; i++) {
|
||||||
@ -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 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;
|
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
|
// 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; ) {
|
for (u32 l = 0, k = 0; layout[l] != 0; ) {
|
||||||
// calculate width of current row
|
// calculate width of current row
|
||||||
u32 n_keys = layout[l++];
|
u32 n_keys = layout[l++];
|
||||||
@ -70,8 +71,9 @@ static bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout) {
|
|||||||
return true;
|
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* keystrs[] = { SWKBD_KEYSTR };
|
||||||
|
const char* ml_keystrs[] = { SWKBD_ML_KEYSTR };
|
||||||
const u32 color = (pressed) ? COLOR_SWKBD_PRESSED :
|
const u32 color = (pressed) ? COLOR_SWKBD_PRESSED :
|
||||||
(key->id == KEY_ENTER) ? COLOR_SWKBD_ENTER :
|
(key->id == KEY_ENTER) ? COLOR_SWKBD_ENTER :
|
||||||
((key->id == KEY_CAPS) && (uppercase > 1)) ? COLOR_SWKBD_CAPS :
|
((key->id == KEY_CAPS) && (uppercase > 1)) ? COLOR_SWKBD_CAPS :
|
||||||
@ -81,7 +83,7 @@ static void DrawKey(const TouchBox* key, const bool pressed, const u32 uppercase
|
|||||||
if (key->id == KEY_TXTBOX) return;
|
if (key->id == KEY_TXTBOX) return;
|
||||||
|
|
||||||
char keystr[16];
|
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 {
|
else {
|
||||||
keystr[0] = (uppercase) ? to_uppercase(key->id) : key->id;
|
keystr[0] = (uppercase) ? to_uppercase(key->id) : key->id;
|
||||||
keystr[1] = 0;
|
keystr[1] = 0;
|
||||||
@ -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);
|
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)
|
// we need to make sure to skip the textbox here(first entry)
|
||||||
|
|
||||||
// draw keyboard
|
// draw keyboard
|
||||||
for (TouchBox* tb = swkbd + 1; tb->id != 0; tb++) {
|
for (TouchBox* tb = swkbd + (multi_line ? 0 : 1); tb->id != 0; tb++) {
|
||||||
DrawKey(tb, false, uppercase);
|
DrawKey(tb, false, uppercase, multi_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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;
|
u32 id = 0;
|
||||||
u16 x, y;
|
u16 x, y;
|
||||||
|
|
||||||
// wait for touch input (handle key input, too)
|
// wait for touch input (handle key input, too)
|
||||||
while (true) {
|
while (true) {
|
||||||
u32 pressed = InputWait(0);
|
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_A) return KEY_ENTER;
|
||||||
else if (pressed & BUTTON_X) return KEY_BKSPC;
|
else if (pressed & BUTTON_X) return KEY_BKSPC;
|
||||||
else if (pressed & BUTTON_Y) return KEY_INSERT;
|
else if (pressed & BUTTON_Y) return multi_line ? KEY_CAPS : KEY_INSERT;
|
||||||
else if (pressed & BUTTON_R1) return KEY_CAPS;
|
else if (!multi_line && pressed & BUTTON_R1) return KEY_CAPS;
|
||||||
else if (pressed & BUTTON_RIGHT) return KEY_RIGHT;
|
else if (pressed & BUTTON_RIGHT) return KEY_RIGHT;
|
||||||
else if (pressed & BUTTON_LEFT) return KEY_LEFT;
|
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;
|
else if (pressed & BUTTON_TOUCH) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,9 +237,9 @@ static char KeyboardWait(TouchBox* swkbd, bool uppercase) {
|
|||||||
const TouchBox* tb = TouchBoxGet(&id, x, y, swkbd, 0);
|
const TouchBox* tb = TouchBoxGet(&id, x, y, swkbd, 0);
|
||||||
if (tb) {
|
if (tb) {
|
||||||
if (id == KEY_TXTBOX) break; // immediately break on textbox
|
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)));
|
while(HID_ReadTouchState(&x, &y) && (tb == TouchBoxGet(NULL, x, y, swkbd, 0)));
|
||||||
DrawKey(tb, false, uppercase);
|
DrawKey(tb, false, uppercase, multi_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,9 +266,9 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate keyboards
|
// generate keyboards
|
||||||
if (!BuildKeyboard(swkbd_alphabet, keys_alphabet, layout_alphabet)) return false;
|
if (!BuildKeyboard(swkbd_alphabet, keys_alphabet, layout_alphabet, false)) return false;
|
||||||
if (!BuildKeyboard(swkbd_special, keys_special, layout_special)) return false;
|
if (!BuildKeyboard(swkbd_special, keys_special, layout_special, false)) return false;
|
||||||
if (!BuildKeyboard(swkbd_numpad, keys_numpad, layout_numpad)) return false;
|
if (!BuildKeyboard(swkbd_numpad, keys_numpad, layout_numpad, false)) return false;
|
||||||
|
|
||||||
// (instructional) text
|
// (instructional) text
|
||||||
char str[512]; // arbitrary limit, should be more than enough
|
char str[512]; // arbitrary limit, should be more than enough
|
||||||
@ -292,19 +297,19 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
|
|||||||
// draw keyboard if required
|
// draw keyboard if required
|
||||||
if (swkbd != swkbd_prev) {
|
if (swkbd != swkbd_prev) {
|
||||||
DrawKeyBoardBox(swkbd, COLOR_SWKBD_BOX);
|
DrawKeyBoardBox(swkbd, COLOR_SWKBD_BOX);
|
||||||
DrawKeyBoard(swkbd, uppercase);
|
DrawKeyBoard(swkbd, uppercase, false);
|
||||||
DrawTextBox(textbox, inputstr, cursor, &scroll);
|
DrawTextBox(textbox, inputstr, cursor, &scroll);
|
||||||
swkbd_prev = swkbd;
|
swkbd_prev = swkbd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle user input
|
// handle user input
|
||||||
char key = KeyboardWait(swkbd, uppercase);
|
char key = KeyboardWait(swkbd, uppercase, false);
|
||||||
if (key == KEY_INSERT) key = ' '; // impromptu replacement
|
if (key == KEY_INSERT) key = ' '; // impromptu replacement
|
||||||
if (key == KEY_TXTBOX) {
|
if (key == KEY_TXTBOX) {
|
||||||
MoveTextBoxCursor(textbox, inputstr, max_size, &cursor, &scroll);
|
MoveTextBoxCursor(textbox, inputstr, max_size, &cursor, &scroll);
|
||||||
} else if (key == KEY_CAPS) {
|
} else if (key == KEY_CAPS) {
|
||||||
uppercase = (uppercase + 1) % 3;
|
uppercase = (uppercase + 1) % 3;
|
||||||
DrawKeyBoard(swkbd, uppercase);
|
DrawKeyBoard(swkbd, uppercase, false);
|
||||||
continue;
|
continue;
|
||||||
} else if (key == KEY_ENTER) {
|
} else if (key == KEY_ENTER) {
|
||||||
ret = true;
|
ret = true;
|
||||||
@ -375,7 +380,7 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
|
|||||||
}
|
}
|
||||||
if (uppercase == 1) {
|
if (uppercase == 1) {
|
||||||
uppercase = 0;
|
uppercase = 0;
|
||||||
DrawKeyBoard(swkbd, uppercase);
|
DrawKeyBoard(swkbd, uppercase, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,4 +390,38 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
|
|||||||
|
|
||||||
ClearScreen(BOT_SCREEN, COLOR_STD_BG);
|
ClearScreen(BOT_SCREEN, COLOR_STD_BG);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "hid.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "touchcal.h"
|
#include "touchcal.h"
|
||||||
|
|
||||||
@ -20,11 +21,15 @@ enum {
|
|||||||
KEY_ESCAPE = 0x8A,
|
KEY_ESCAPE = 0x8A,
|
||||||
KEY_SWITCH = 0x8B,
|
KEY_SWITCH = 0x8B,
|
||||||
KEY_UNICODE = 0x8C,
|
KEY_UNICODE = 0x8C,
|
||||||
|
KEY_UP = 0x8D,
|
||||||
|
KEY_DOWN = 0x8E,
|
||||||
KEY_TXTBOX = 0xFF
|
KEY_TXTBOX = 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
// special key strings
|
// 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_NORMAL COLOR_GREY
|
||||||
#define COLOR_SWKBD_PRESSED COLOR_LIGHTGREY
|
#define COLOR_SWKBD_PRESSED COLOR_LIGHTGREY
|
||||||
@ -45,6 +50,13 @@ enum {
|
|||||||
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '_', '#', '!', \
|
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '_', '#', '!', \
|
||||||
KEY_CAPS, ' ', KEY_NUMPAD, KEY_SPECIAL, KEY_LEFT, KEY_RIGHT
|
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 \
|
#define SWKBD_KEYS_SPECIAL \
|
||||||
'(', ')', '{', '}', '[', ']', \
|
'(', ')', '{', '}', '[', ']', \
|
||||||
'.', ',', '?', '!', '`', '\'', \
|
'.', ',', '?', '!', '`', '\'', \
|
||||||
@ -53,9 +65,9 @@ enum {
|
|||||||
KEY_ALPHA, ' ', KEY_BKSPC
|
KEY_ALPHA, ' ', KEY_BKSPC
|
||||||
|
|
||||||
#define SWKBD_KEYS_NUMPAD \
|
#define SWKBD_KEYS_NUMPAD \
|
||||||
'7', '8', '9', 'F', 'E', \
|
'7', '8', '9', 'E', 'F', \
|
||||||
'4', '5', '6', 'D', 'C', \
|
'4', '5', '6', 'C', 'D', \
|
||||||
'3', '2', '1', 'B', 'A', \
|
'1', '2', '3', 'A', 'B', \
|
||||||
'0', '.', '_', KEY_LEFT, KEY_RIGHT, \
|
'0', '.', '_', KEY_LEFT, KEY_RIGHT, \
|
||||||
KEY_ALPHA, KEY_UNICODE, ' ', KEY_BKSPC
|
KEY_ALPHA, KEY_UNICODE, ' ', KEY_BKSPC
|
||||||
|
|
||||||
@ -68,12 +80,20 @@ enum {
|
|||||||
6, 32, 123, 32, 32, 18, 18, 0, \
|
6, 32, 123, 32, 32, 18, 18, 0, \
|
||||||
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 \
|
#define SWKBD_LAYOUT_SPECIAL \
|
||||||
6, 0, \
|
6, 0, \
|
||||||
6, 0, \
|
6, 0, \
|
||||||
6, 0, \
|
6, 0, \
|
||||||
6, 0, \
|
6, 0, \
|
||||||
3, 32, 46, 32, 0, \
|
3, 32, 47, 32, 0, \
|
||||||
0
|
0
|
||||||
|
|
||||||
#define SWKBD_LAYOUT_NUMPAD \
|
#define SWKBD_LAYOUT_NUMPAD \
|
||||||
@ -87,3 +107,5 @@ enum {
|
|||||||
|
|
||||||
#define ShowKeyboardOrPrompt (TouchIsCalibrated() ? ShowKeyboard : ShowStringPrompt)
|
#define ShowKeyboardOrPrompt (TouchIsCalibrated() ? ShowKeyboard : ShowStringPrompt)
|
||||||
bool PRINTF_ARGS(3) ShowKeyboard(char* inputstr, u32 max_size, const char *format, ...);
|
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);
|
||||||
|
|||||||
@ -489,22 +489,34 @@ u32 GetDrawStringHeight(const char* str) {
|
|||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetCharSize(const char* str) {
|
static inline bool IsIntermediateByte(const char* chr) {
|
||||||
const char *start = str;
|
return (*chr & 0xC0) == 0x80 || (chr[-1] == '\r' && chr[0] == '\n');
|
||||||
do {
|
}
|
||||||
str++;
|
|
||||||
} while ((*str & 0xC0) == 0x80);
|
|
||||||
|
|
||||||
return str - start;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetCharSize(const char* str) {
|
||||||
|
return GetNextChar(str) - str;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetPrevCharSize(const char* str) {
|
u32 GetPrevCharSize(const char* str) {
|
||||||
const char *start = str;
|
return str - GetPrevChar(str);
|
||||||
do {
|
}
|
||||||
str--;
|
|
||||||
} while ((*str & 0xC0) == 0x80);
|
|
||||||
|
|
||||||
return start - str;
|
void IncChar(const char** chr) {
|
||||||
|
*chr = GetNextChar(*chr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecChar(const char** chr) {
|
||||||
|
*chr = GetPrevChar(*chr);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetDrawStringWidth(const char* str) {
|
u32 GetDrawStringWidth(const char* str) {
|
||||||
|
|||||||
@ -70,6 +70,10 @@ void PRINTF_ARGS(4) DrawStringCenter(u16 *screen, u32 color, u32 bgcolor, const
|
|||||||
|
|
||||||
u32 GetCharSize(const char* str);
|
u32 GetCharSize(const char* str);
|
||||||
u32 GetPrevCharSize(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 GetDrawStringHeight(const char* str);
|
||||||
u32 GetDrawStringWidth(const char* str);
|
u32 GetDrawStringWidth(const char* str);
|
||||||
|
|||||||
@ -1285,7 +1285,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
int n_opt = 0;
|
int n_opt = 0;
|
||||||
int special = (special_opt) ? ++n_opt : -1;
|
int special = (special_opt) ? ++n_opt : -1;
|
||||||
int hexviewer = ++n_opt;
|
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 calcsha256 = ++n_opt;
|
||||||
int calcsha1 = ++n_opt;
|
int calcsha1 = ++n_opt;
|
||||||
int calccmac = (CheckCmacPath(file_path) == 0) ? ++n_opt : -1;
|
int calccmac = (CheckCmacPath(file_path) == 0) ? ++n_opt : -1;
|
||||||
@ -1377,6 +1377,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
}
|
}
|
||||||
else if (user_select == textviewer) { // -> show in text viewer
|
else if (user_select == textviewer) { // -> show in text viewer
|
||||||
FileTextViewer(file_path, scriptable);
|
FileTextViewer(file_path, scriptable);
|
||||||
|
GetDirContents(current_dir, current_path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (user_select == calcsha256) { // -> calculate SHA-256
|
else if (user_select == calcsha256) { // -> calculate SHA-256
|
||||||
@ -2439,7 +2440,7 @@ u32 HomeMoreMenu(char* current_path) {
|
|||||||
char* sysinfo_txt = (char*) malloc(STD_BUFFER_SIZE);
|
char* sysinfo_txt = (char*) malloc(STD_BUFFER_SIZE);
|
||||||
if (!sysinfo_txt) return 1;
|
if (!sysinfo_txt) return 1;
|
||||||
MyriaSysinfo(sysinfo_txt);
|
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);
|
free(sysinfo_txt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include "bps.h"
|
#include "bps.h"
|
||||||
#include "pxi.h"
|
#include "pxi.h"
|
||||||
#include "gyro.h"
|
#include "gyro.h"
|
||||||
|
#include "utf.h"
|
||||||
|
|
||||||
|
|
||||||
#define _MAX_ARGS 4
|
#define _MAX_ARGS 4
|
||||||
@ -57,6 +58,7 @@
|
|||||||
|
|
||||||
#define TV_NLIN_DISP (SCREEN_HEIGHT / (FONT_HEIGHT_EXT + (2*TV_VPAD)))
|
#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))
|
#define TV_LLEN_DISP (((SCREEN_WIDTH_TOP - (2*TV_HPAD)) / FONT_WIDTH_EXT) - (TV_LNOS + 1))
|
||||||
|
#define MAX_CHAR_SIZE 4 // Max number of bytes needed for a UTF-8 character.
|
||||||
|
|
||||||
// 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'))
|
||||||
@ -206,6 +208,10 @@ static const Gm9ScriptCmd cmd_list[] = {
|
|||||||
{ CMD_ID_BKPT , "bkpt" , 0, 0 }
|
{ CMD_ID_BKPT , "bkpt" , 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// off-screen string indicators
|
||||||
|
static const char al_str[] = "<< ";
|
||||||
|
static const char ar_str[] = " >>";
|
||||||
|
|
||||||
// global vars for preview
|
// global vars for preview
|
||||||
static u32 preview_mode = 0; // 0 -> off 1 -> quick 2 -> full
|
static u32 preview_mode = 0; // 0 -> off 1 -> quick 2 -> full
|
||||||
static u32 script_color_active = 0;
|
static u32 script_color_active = 0;
|
||||||
@ -260,86 +266,125 @@ static inline u32 hexntostr(const u8* hex, char* str, u32 len) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 line_len(const char* text, u32 len, u32 ww, const char* line, char** eol) {
|
static inline bool is_crlf(const char* str) {
|
||||||
u32 last = ((text + len) - line);
|
u32 crlf = 0, lf = 0;
|
||||||
u32 llen = 0;
|
do if (str[0] == '\n') ++lf; else if (str[0] == '\r' && str[1] == '\n') ++crlf, ++str;
|
||||||
char* lf = NULL;
|
while (*str++);
|
||||||
char* spc = NULL;
|
return crlf > lf;
|
||||||
|
}
|
||||||
|
|
||||||
if (line >= (text + len))
|
static inline bool is_newline(const char* chr) {
|
||||||
|
return chr[0] == '\n' || (chr[0] == '\r' && chr[1] == '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 bytes_in_chars_u32(const char* str, u32 nchars) {
|
||||||
|
u32 bytes = 0;
|
||||||
|
for (u32 i = 0; str[bytes] && i < nchars; bytes += GetCharSize(str + bytes), ++i);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bytes_in_chars_int(const char* str, int nchars) {
|
||||||
|
int bytes = 0;
|
||||||
|
for (int i = 0; str[bytes] && i < nchars; bytes += GetCharSize(str + bytes), ++i);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int chars_in_bytes(const char* str, int nbytes) {
|
||||||
|
int chars = 0;
|
||||||
|
for (int i = 0; str[chars] && i < nbytes; i += GetCharSize(str + i), ++chars);
|
||||||
|
return chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 chars_between_pointers(const char* start, const char* end) {
|
||||||
|
u32 chars = 0;
|
||||||
|
for (const char* i = start; *i && i < end; IncChar(&i), ++chars);
|
||||||
|
return chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 line_len_chars(const char* text, u32 len, u32 ww, const char* line, const char** eol) {
|
||||||
|
u32 llen = 0;
|
||||||
|
const char* lf = NULL;
|
||||||
|
const char* spc = NULL;
|
||||||
|
u32 spc_len = 0;
|
||||||
|
const char* lptr = line;
|
||||||
|
|
||||||
|
if (line > text + len)
|
||||||
return 0; // early exit
|
return 0; // early exit
|
||||||
|
|
||||||
// search line feeds, spaces (only relevant for wordwrapped)
|
// search line feeds, spaces (only relevant for wordwrapped)
|
||||||
for (llen = 0; !ww || (llen < ww); llen++) {
|
for (; !ww || (llen < ww); IncChar(&lptr), ++llen) {
|
||||||
if (ww && (line[llen] == ' ')) spc = (char*) (line + llen);
|
if (ww && (*lptr == ' ')) {
|
||||||
if (!line[llen] || (line[llen] == '\n') || (llen >= last)) {
|
spc = lptr;
|
||||||
lf = (char*) (line + llen);
|
spc_len = llen;
|
||||||
|
}
|
||||||
|
if (is_newline(lptr) || lptr >= text + len) {
|
||||||
|
lf = lptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// line feed found, truncate trailing "empty" chars
|
// line feed found, truncate trailing "empty" chars
|
||||||
// for wordwrapped, stop line after last space (if any)
|
// for wordwrapped, stop line after last space (if any)
|
||||||
if (lf) for (; (llen > 0) && (line[llen-1] <= ' '); llen--);
|
if (lf) for (; lptr > line && *GetPrevChar(lptr) < ' '; DecChar(&lptr), --llen);
|
||||||
else if (ww && spc) llen = (spc - line) + 1;
|
else if (ww && spc) llen = spc_len + 1;
|
||||||
|
|
||||||
// signal eol if required
|
// signal eol if required
|
||||||
if (eol) *eol = lf;
|
if (eol) *eol = lf;
|
||||||
return llen;
|
return llen;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char* line_seek(const char* text, u32 len, u32 ww, const char* line, int add) {
|
static inline const char* line_seek_chars(const char* text, u32 len, u32 ww, const char* line, int add) {
|
||||||
// safety checks /
|
// safety check
|
||||||
if (line < text) return NULL;
|
if ((line <= text && add <= 0) || (line >= text + len && add >= 0)) return line;
|
||||||
if ((line >= (text + len)) && (add >= 0)) return (char*) line;
|
|
||||||
|
const char* l0 = line;
|
||||||
|
|
||||||
if (!ww) { // non wordwrapped mode
|
if (!ww) { // non wordwrapped mode
|
||||||
char* lf = ((char*) line - 1);
|
for (; add < 0 && l0 > text; add++)
|
||||||
|
for (DecChar(&l0); l0 > text && l0[-1] != '\n'; DecChar(&l0));
|
||||||
|
|
||||||
// ensure we are at the start of the line
|
for (; add > 0 && l0 < text + len; add--)
|
||||||
while ((lf > text) && (*lf != '\n')) lf--;
|
for (IncChar(&l0); l0 < text + len && l0[-1] != '\n'; IncChar(&l0));
|
||||||
|
|
||||||
// handle backwards search
|
return l0;
|
||||||
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
|
} else { // wordwrapped mode
|
||||||
char* l0 = (char*) line;
|
|
||||||
|
|
||||||
// handle forwards wordwrapped search
|
// handle forwards wordwrapped search
|
||||||
for (; (add > 0) && (l0 < text + len); add--) {
|
for (; add > 0 && l0 < text + len; add--) {
|
||||||
char* eol = NULL;
|
const char* eol = NULL;
|
||||||
u32 llenww = line_len(text, len, ww, l0, &eol);
|
u32 llenww_chars = line_len_chars(text, len, ww, l0, &eol);
|
||||||
if (eol || !llenww) l0 = line_seek(text, len, 0, l0, 1);
|
if (eol || !llenww_chars) l0 = line_seek_chars(text, len, 0, l0, 1);
|
||||||
else l0 += llenww;
|
else l0 += bytes_in_chars_u32(l0, llenww_chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle backwards wordwrapped search
|
// handle backwards wordwrapped search
|
||||||
while ((add < 0) && (l0 > text)) {
|
while (add < 0 && l0 > text) {
|
||||||
char* l1 = line_seek(text, len, 0, l0, -1);
|
const char* l1 = line_seek_chars(text, len, 0, l0, -1);
|
||||||
char* l0_minus1 = l1;
|
if (l0 > text + len) {
|
||||||
|
l0 = text + len;
|
||||||
|
if (l1 == l0) ++add;
|
||||||
|
}
|
||||||
|
const char* l0_minus1 = l1;
|
||||||
int nlww = 0; // no of wordwrapped lines in paragraph
|
int nlww = 0; // no of wordwrapped lines in paragraph
|
||||||
for (char* ld = l1; ld < l0; ld = line_seek(text, len, ww, ld, 1), nlww++)
|
for (const char* ld = l1; ld < l0; ld = line_seek_chars(text, len, ww, ld, 1), nlww++)
|
||||||
l0_minus1 = ld;
|
l0_minus1 = ld;
|
||||||
|
if (line > text + len && l0 == text + len && chars_in_bytes(l0_minus1, l0 - l0_minus1) == (int) ww) ++add;
|
||||||
if (add + nlww < 0) { // haven't reached the desired line yet
|
if (add + nlww < 0) { // haven't reached the desired line yet
|
||||||
add += nlww;
|
add += nlww;
|
||||||
l0 = l1;
|
l0 = l1;
|
||||||
} else { // reached the desired line
|
} else { // reached the desired line
|
||||||
l0 = (add == -1) ? l0_minus1 : line_seek(text, len, ww, l1, nlww + add);
|
l0 = (add == -1) ? l0_minus1 : line_seek_chars(text, len, ww, l1, nlww + add);
|
||||||
add = 0;
|
add = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return l0;
|
return l0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char* line_start(const char* text, u32 len, u32 ww, const char* ptr) {
|
||||||
|
return line_seek_chars(text, len, ww, GetNextChar(ptr), -1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline u32 get_lno(const char* text, u32 len, const char* line) {
|
static inline u32 get_lno(const char* text, u32 len, const char* line) {
|
||||||
u32 lno = 1;
|
u32 lno = 1;
|
||||||
|
|
||||||
@ -1616,7 +1661,7 @@ bool run_line(const char* line_start, const char* line_end, u32* flags, char* er
|
|||||||
|
|
||||||
// checks for illegal ASCII symbols
|
// checks for illegal ASCII symbols
|
||||||
bool ValidateText(const char* text, u32 len) {
|
bool ValidateText(const char* text, u32 len) {
|
||||||
if (!len) return false;
|
if (!len) return true;
|
||||||
for (u32 i = 0; i < len; i++) {
|
for (u32 i = 0; i < len; i++) {
|
||||||
char c = text[i];
|
char c = text[i];
|
||||||
if ((c == '\r') && ((i+1) < len) && (text[i+1] != '\n')) return false; // CR without LF
|
if ((c == '\r') && ((i+1) < len) && (text[i+1] != '\n')) return false; // CR without LF
|
||||||
@ -1626,57 +1671,89 @@ 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) {
|
void MemTextView(const char* text, u32 len, const char* line0, int off_disp_chars, int lno, u32 ww, u32 mno, bool is_script, const char* cursor) {
|
||||||
// block placements
|
// 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_txt = (TV_LNOS >= 0) ? TV_HPAD + ((TV_LNOS+1)*FONT_WIDTH_EXT) : TV_HPAD;
|
||||||
u32 x_lno = TV_HPAD;
|
u32 x_lno = TV_HPAD;
|
||||||
u32 p_al = 0;
|
u32 x_al = x_txt;
|
||||||
u32 p_ar = TV_LLEN_DISP - strlen(ar_str);
|
u32 x_ar = x_txt + ((TV_LLEN_DISP - strlen(ar_str)) * FONT_WIDTH_EXT);
|
||||||
u32 x_al = x_txt + (p_al * FONT_WIDTH_EXT);
|
|
||||||
u32 x_ar = x_txt + (p_ar * FONT_WIDTH_EXT);
|
|
||||||
|
|
||||||
// display text on screen
|
// display text on screen
|
||||||
char txtstr[TV_LLEN_DISP + 1];
|
char txtstr[TV_LLEN_DISP * MAX_CHAR_SIZE + 1];
|
||||||
char* ptr = line0;
|
const char* ptr = line0;
|
||||||
u32 nln = lno;
|
u32 nln = lno;
|
||||||
|
bool last_empty_line_drawn = false;
|
||||||
for (u32 y = TV_VPAD; y < SCREEN_HEIGHT; y += FONT_HEIGHT_EXT + (2*TV_VPAD)) {
|
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);
|
int off_disp_bytes = bytes_in_chars_int(ptr, off_disp_chars);
|
||||||
u32 llen = line_len(text, len, ww, ptr, NULL);
|
const char* ptr_next = line_seek_chars(text, len, ww, ptr, 1);
|
||||||
u32 ncpy = ((int) llen < off_disp) ? 0 : (llen - off_disp);
|
u32 llen_chars = line_len_chars(text, len, ww, ptr, NULL);
|
||||||
if (ncpy > TV_LLEN_DISP) ncpy = TV_LLEN_DISP;
|
u32 llen_bytes = bytes_in_chars_u32(ptr, llen_chars);
|
||||||
bool al = !ww && off_disp && (ptr != ptr_next);
|
u32 tv_llen_disp_chars = llen_chars;
|
||||||
bool ar = !ww && (llen > off_disp + TV_LLEN_DISP);
|
if (tv_llen_disp_chars > TV_LLEN_DISP) tv_llen_disp_chars = TV_LLEN_DISP;
|
||||||
|
u32 tv_llen_disp_bytes = bytes_in_chars_u32(ptr + off_disp_bytes, tv_llen_disp_chars);
|
||||||
|
u32 ncpy_bytes = ((int) llen_bytes < off_disp_bytes) ? 0 : (llen_bytes - off_disp_bytes);
|
||||||
|
if (ncpy_bytes > tv_llen_disp_bytes) ncpy_bytes = tv_llen_disp_bytes;
|
||||||
|
bool al = !ww && off_disp_chars && (ptr != ptr_next);
|
||||||
|
bool ar = !ww && (llen_chars + 1 > off_disp_chars + TV_LLEN_DISP);
|
||||||
|
|
||||||
// set text color / find start of comment of scripts
|
// set text color / find start of comment of scripts
|
||||||
u32 color_text = (nln == mno) ? script_color_active : (is_script) ? script_color_code : (u32) COLOR_TVTEXT;
|
u32 color_text = (nln == mno) ? script_color_active : (is_script) ? script_color_code : (u32) COLOR_TVTEXT;
|
||||||
int cmt_start = TV_LLEN_DISP; // start of comment in current displayed line (may be negative)
|
int cmt_start_bytes = TV_LLEN_DISP; // start of comment in current displayed line (may be negative)
|
||||||
|
int cmt_start_chars = 0;
|
||||||
if (is_script && (nln != mno)) {
|
if (is_script && (nln != mno)) {
|
||||||
char* hash = line_seek(text, len, 0, ptr, 0);
|
const char* hash = line_start(text, len, 0, ptr);
|
||||||
for (; *hash != '#' && (hash - ptr < (int) llen); hash++);
|
for (; *hash != '#' && hash - ptr < (int) llen_bytes; hash++);
|
||||||
cmt_start = (hash - ptr) - off_disp;
|
cmt_start_bytes = hash - (ptr + off_disp_bytes);
|
||||||
|
if (cmt_start_bytes <= 0) color_text = script_color_comment;
|
||||||
|
else cmt_start_chars = chars_in_bytes(ptr + off_disp_bytes, cmt_start_bytes);
|
||||||
}
|
}
|
||||||
if (cmt_start <= 0) color_text = script_color_comment;
|
|
||||||
|
|
||||||
// build text string
|
// build text string
|
||||||
snprintf(txtstr, sizeof(txtstr), "%-*.*s", (int) TV_LLEN_DISP, (int) TV_LLEN_DISP, "");
|
snprintf(txtstr, sizeof(txtstr), "%-*.*s", (int) (TV_LLEN_DISP * MAX_CHAR_SIZE), (int) (TV_LLEN_DISP * MAX_CHAR_SIZE), "");
|
||||||
if (ncpy) memcpy(txtstr, ptr + off_disp, ncpy);
|
if (ncpy_bytes) {
|
||||||
|
memcpy(txtstr, ptr + off_disp_bytes, ncpy_bytes);
|
||||||
|
}
|
||||||
for (char* d = txtstr; *d; d++) if (*d < ' ') *d = ' ';
|
for (char* d = txtstr; *d; d++) if (*d < ' ') *d = ' ';
|
||||||
if (al) memcpy(txtstr + p_al, al_str, strlen(al_str));
|
if (ar) {
|
||||||
if (ar) memcpy(txtstr + p_ar, ar_str, strlen(ar_str));
|
char* textstr_end = txtstr + ncpy_bytes;
|
||||||
|
u32 txtstr_ar_bytes = 0;
|
||||||
|
for (int txtstr_ar_i = 0; txtstr_ar_i < (int) strlen(ar_str); txtstr_ar_bytes += GetPrevCharSize(textstr_end - txtstr_ar_bytes), ++txtstr_ar_i);
|
||||||
|
memcpy(textstr_end - txtstr_ar_bytes, ar_str, sizeof(ar_str));
|
||||||
|
}
|
||||||
|
if (al) {
|
||||||
|
u32 txtstr_al_bytes = bytes_in_chars_u32(txtstr, strlen(al_str));
|
||||||
|
memmove(txtstr + strlen(al_str), txtstr + txtstr_al_bytes, sizeof(txtstr) - txtstr_al_bytes);
|
||||||
|
memcpy(txtstr, al_str, strlen(al_str));
|
||||||
|
}
|
||||||
|
|
||||||
// draw line number & text
|
// draw line number & text
|
||||||
DrawString(TOP_SCREEN, txtstr, x_txt, y, color_text, COLOR_STD_BG);
|
DrawString(TOP_SCREEN, txtstr, x_txt, y, color_text, COLOR_STD_BG);
|
||||||
if (TV_LNOS > 0) { // line number
|
if (TV_LNOS > 0) { // line number
|
||||||
if (ptr != ptr_next)
|
bool prev_ww_line_full = ww && ww == chars_between_pointers(line_seek_chars(text, len, ww, ptr, -1), ptr);
|
||||||
DrawStringF(TOP_SCREEN, x_lno, y, ((ptr == text) || (*(ptr-1) == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", TV_LNOS, nln);
|
bool last_line_empty = (ptr == text + len && (!len || ptr[-1] == '\n' || prev_ww_line_full) && !last_empty_line_drawn);
|
||||||
|
if (ptr != ptr_next || last_line_empty) {
|
||||||
|
DrawStringF(TOP_SCREEN, x_lno, y, ((ptr == text) || (ptr[-1] == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", TV_LNOS, nln);
|
||||||
|
if (last_line_empty) last_empty_line_drawn = true;
|
||||||
|
}
|
||||||
else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " ");
|
else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cursor) {
|
||||||
|
u32 cursor_line_offset_chars = chars_between_pointers(ptr + off_disp_bytes, cursor);
|
||||||
|
if (cursor >= ptr + off_disp_bytes && cursor <= ptr + off_disp_bytes + ncpy_bytes && cursor_line_offset_chars < TV_LLEN_DISP
|
||||||
|
&& (cursor != ptr + off_disp_bytes + ncpy_bytes || is_newline(cursor) || cursor == text + len)) {
|
||||||
|
DrawRectangle(TOP_SCREEN, x_txt + cursor_line_offset_chars * FONT_WIDTH_EXT, y, FONT_WIDTH_EXT, 1, COLOR_RED);
|
||||||
|
DrawRectangle(TOP_SCREEN, x_txt + (cursor_line_offset_chars + 1) * FONT_WIDTH_EXT - ((cursor_line_offset_chars == TV_LLEN_DISP - 1) ? 1 : 0), y, 1, FONT_HEIGHT_EXT, COLOR_RED);
|
||||||
|
DrawRectangle(TOP_SCREEN, x_txt + cursor_line_offset_chars * FONT_WIDTH_EXT, y, 1, FONT_HEIGHT_EXT, COLOR_RED);
|
||||||
|
DrawRectangle(TOP_SCREEN, x_txt + cursor_line_offset_chars * FONT_WIDTH_EXT, y + FONT_HEIGHT_EXT - 1, FONT_WIDTH_EXT, 1, COLOR_RED);
|
||||||
|
cursor = NULL; // prevent cursor from being drawn multiple times at the end of a file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// colorize comment if is_script
|
// colorize comment if is_script
|
||||||
if ((cmt_start > 0) && ((u32) cmt_start < TV_LLEN_DISP)) {
|
if (cmt_start_chars > 0 && cmt_start_chars < (int) TV_LLEN_DISP) {
|
||||||
memset(txtstr, ' ', cmt_start);
|
memset(txtstr, ' ', cmt_start_bytes);
|
||||||
|
memmove(txtstr + cmt_start_chars, txtstr + cmt_start_bytes, sizeof(txtstr) - cmt_start_bytes);
|
||||||
DrawString(TOP_SCREEN, txtstr, x_txt, y, script_color_comment, COLOR_TRANSPARENT);
|
DrawString(TOP_SCREEN, txtstr, x_txt, y, script_color_comment, COLOR_TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1685,12 +1762,12 @@ void MemTextView(const char* text, u32 len, char* line0, int off_disp, int lno,
|
|||||||
if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, "%s", ar_str);
|
if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, "%s", ar_str);
|
||||||
|
|
||||||
// advance pointer / line number
|
// advance pointer / line number
|
||||||
for (char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln;
|
for (const char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln;
|
||||||
ptr = ptr_next;
|
ptr = ptr_next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
|
bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max_len, const char* save_path) {
|
||||||
u32 ww = TV_LLEN_DISP;
|
u32 ww = TV_LLEN_DISP;
|
||||||
|
|
||||||
// check if this really is text
|
// check if this really is text
|
||||||
@ -1699,11 +1776,37 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear screens
|
static const char keys_ml_alphabet[] = { SWKBD_KEYS_ML_ALPHABET };
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
static const char keys_special[] = { SWKBD_KEYS_SPECIAL };
|
||||||
|
static const char keys_numpad[] = { SWKBD_KEYS_NUMPAD };
|
||||||
|
static const u8 layout_ml_alphabet[] = { SWKBD_LAYOUT_ML_ALPHABET };
|
||||||
|
static const u8 layout_special[] = { SWKBD_LAYOUT_SPECIAL };
|
||||||
|
static const u8 layout_numpad[] = { SWKBD_LAYOUT_NUMPAD };
|
||||||
|
TouchBox swkbd_alphabet[64];
|
||||||
|
TouchBox swkbd_special[32];
|
||||||
|
TouchBox swkbd_numpad[32];
|
||||||
|
|
||||||
// instructions
|
u32 uppercase = 0;
|
||||||
ShowString("%s", STR_TEXTVIEWER_CONTROLS_DETAILS);
|
TouchBox* swkbd = NULL;
|
||||||
|
TouchBox* swkbd_prev = NULL;
|
||||||
|
|
||||||
|
// generate keyboards
|
||||||
|
if (!BuildKeyboard(swkbd_alphabet, keys_ml_alphabet, layout_ml_alphabet, true)) return false;
|
||||||
|
if (!BuildKeyboard(swkbd_special, keys_special, layout_special, true)) return false;
|
||||||
|
if (!BuildKeyboard(swkbd_numpad, keys_numpad, layout_numpad, true)) return false;
|
||||||
|
|
||||||
|
char* text_cpy = NULL;
|
||||||
|
u32 text_cpy_len = 0;
|
||||||
|
if (max_len) {
|
||||||
|
// create a copy to check for changes against on exit
|
||||||
|
text_cpy = malloc(len + 1);
|
||||||
|
text_cpy_len = len;
|
||||||
|
if (!text_cpy) return false;
|
||||||
|
memcpy(text_cpy, text, len + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear screens
|
||||||
|
ClearScreen(TOP_SCREEN, COLOR_STD_BG);
|
||||||
|
|
||||||
// set script colors
|
// set script colors
|
||||||
if (as_script) {
|
if (as_script) {
|
||||||
@ -1712,56 +1815,181 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
|
|||||||
script_color_code = COLOR_TVCMD;
|
script_color_code = COLOR_TVCMD;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find maximum line len
|
bool crlf = is_crlf(text);
|
||||||
u32 llen_max = 0;
|
bool display_view_instructions = true;
|
||||||
for (char* ptr = (char*) text; ptr < (text + len); ptr = line_seek(text, len, 0, ptr, 1)) {
|
const char* cursor = NULL;
|
||||||
u32 llen = line_len(text, len, 0, ptr, NULL);
|
const char* line0 = text;
|
||||||
if (llen > llen_max) llen_max = llen;
|
int lcurr = 1; // Current line number
|
||||||
}
|
int off_disp_chars = 0; // non-word-wrapped offset
|
||||||
|
for (; lcurr < (int) start; line0 = line_seek_chars(text, len, 0, line0, 1), lcurr++);
|
||||||
// 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;
|
|
||||||
for (; lcurr < (int) start; line0 = line_seek(text, len, 0, line0, 1), lcurr++);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// display text on screen
|
// display text on screen
|
||||||
MemTextView(text, len, line0, off_disp, lcurr, ww, 0, as_script);
|
MemTextView(text, len, line0, off_disp_chars, lcurr, ww, 0, as_script, cursor);
|
||||||
|
|
||||||
// handle user input
|
const char* line0_next = line0;
|
||||||
u32 pad_state = InputWait(0);
|
|
||||||
char* line0_next = line0;
|
if (!cursor) { // view mode
|
||||||
u32 step_ud = (pad_state & BUTTON_R1) ? TV_NLIN_DISP : 1;
|
if (display_view_instructions) {
|
||||||
u32 step_lr = (pad_state & BUTTON_R1) ? TV_LLEN_DISP : 1;
|
ClearScreen(BOT_SCREEN, COLOR_STD_BG);
|
||||||
bool switched = (pad_state & BUTTON_R1);
|
ShowString("%s", max_len ? STR_TEXTEDITOR_CONTROLS_DETAILS : STR_TEXTVIEWER_CONTROLS_DETAILS);
|
||||||
if (pad_state & BUTTON_DOWN) line0_next = line_seek(text, len, ww, line0, step_ud);
|
display_view_instructions = false;
|
||||||
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;
|
// handle user input
|
||||||
else if (switched && (pad_state & BUTTON_X)) {
|
u32 pad_state = InputWait(0);
|
||||||
u64 lnext64 = ShowNumberPrompt(lcurr, STR_CURRENT_LINE_N_ENTER_NEW_LINE_BELOW, lcurr);
|
u32 step_ud = (pad_state & BUTTON_R1) ? TV_NLIN_DISP : 1;
|
||||||
if (lnext64 && (lnext64 != (u64) -1)) line0_next = line_seek(text, len, 0, line0, (int) lnext64 - lcurr);
|
u32 step_lr = (pad_state & BUTTON_R1) ? TV_LLEN_DISP : 1;
|
||||||
ShowString("%s", STR_TEXTVIEWER_CONTROLS_DETAILS);
|
bool switched = (pad_state & BUTTON_R1);
|
||||||
} else if (switched && (pad_state & BUTTON_Y)) {
|
if (pad_state & BUTTON_DOWN) line0_next = line_seek_chars(text, len, ww, line0, step_ud);
|
||||||
ww = ww ? 0 : TV_LLEN_DISP;
|
else if (pad_state & BUTTON_UP) line0_next = line_seek_chars(text, len, ww, line0, -step_ud);
|
||||||
line0_next = line_seek(text, len, ww, line0, 0);
|
else if (pad_state & BUTTON_RIGHT) off_disp_chars += step_lr;
|
||||||
} else if (pad_state & (BUTTON_B|BUTTON_START)) break;
|
else if (pad_state & BUTTON_LEFT) off_disp_chars -= step_lr;
|
||||||
|
else if (max_len && pad_state & BUTTON_A) {
|
||||||
|
cursor = line0;
|
||||||
|
off_disp_chars = 0;
|
||||||
|
uppercase = 0;
|
||||||
|
swkbd = NULL;
|
||||||
|
swkbd_prev = NULL;
|
||||||
|
}
|
||||||
|
else if (switched && pad_state & BUTTON_X) {
|
||||||
|
u64 lnext64 = ShowNumberPrompt(lcurr, STR_CURRENT_LINE_N_ENTER_NEW_LINE_BELOW, lcurr);
|
||||||
|
if (lnext64 && (lnext64 != (u64) -1))
|
||||||
|
line0_next = line_seek_chars(text, len, 0, line_start(text, len, 0, line0), (int) lnext64 - lcurr);
|
||||||
|
ShowString("%s", STR_TEXTVIEWER_CONTROLS_DETAILS);
|
||||||
|
} else if (switched && pad_state & BUTTON_Y) {
|
||||||
|
ww = ww ? 0 : TV_LLEN_DISP;
|
||||||
|
line0_next = line_start(text, len, ww, line0);
|
||||||
|
} else if (pad_state & (BUTTON_B|BUTTON_START)) break;
|
||||||
|
} else { // edit mode
|
||||||
|
char key_pressed = ShowMultiLineKeyboard(swkbd_alphabet, swkbd_special, swkbd_numpad, &swkbd, &swkbd_prev, &uppercase);
|
||||||
|
char key_character = 0;
|
||||||
|
bool switched = HID_ReadState() & BUTTON_R1;
|
||||||
|
if (key_pressed == KEY_ESCAPE) {
|
||||||
|
cursor = NULL;
|
||||||
|
display_view_instructions = true;
|
||||||
|
} else if (key_pressed == KEY_DOWN) {
|
||||||
|
const char* cursor_line_start = line_start(text, len, ww, cursor);
|
||||||
|
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor);
|
||||||
|
cursor = line_seek_chars(text, len, ww, cursor_line_start, switched ? TV_NLIN_DISP : 1);
|
||||||
|
cursor = line_start(text, len, ww, cursor);
|
||||||
|
const char* next_line_start = line_seek_chars(text, len, ww, cursor, 1);
|
||||||
|
for (u32 i = 0; GetNextChar(cursor) < next_line_start && i < cursor_chars_from_line_start; IncChar(&cursor), ++i);
|
||||||
|
} else if (key_pressed == KEY_UP) {
|
||||||
|
const char* cursor_line_start = line_start(text, len, ww, cursor);
|
||||||
|
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor);
|
||||||
|
cursor = line_seek_chars(text, len, ww, cursor_line_start, -(switched ? TV_NLIN_DISP : 1));
|
||||||
|
const char* next_line_start = line_seek_chars(text, len, ww, cursor, 1);
|
||||||
|
for (u32 i = 0; GetNextChar(cursor) < next_line_start && i < cursor_chars_from_line_start; IncChar(&cursor), ++i);
|
||||||
|
} else if (key_pressed == KEY_RIGHT) {
|
||||||
|
if (switched) {
|
||||||
|
const char* cursor_line_start = line_start(text, len, ww, cursor);
|
||||||
|
const char* next_line_start = line_seek_chars(text, len, ww, cursor_line_start, 1);
|
||||||
|
if (next_line_start == text + len && (!ww || chars_between_pointers(cursor_line_start, next_line_start) != TV_LLEN_DISP)) IncChar(&next_line_start);
|
||||||
|
while (GetNextChar(cursor) < next_line_start && !is_newline(cursor)) IncChar(&cursor);
|
||||||
|
}
|
||||||
|
else if (cursor < text + len) IncChar(&cursor);
|
||||||
|
} else if (key_pressed == KEY_LEFT) {
|
||||||
|
if (switched) {
|
||||||
|
const char* cursor_line_start = line_start(text, len, ww, cursor);
|
||||||
|
while (cursor > cursor_line_start) DecChar(&cursor);
|
||||||
|
}
|
||||||
|
else if (cursor > text) DecChar(&cursor);
|
||||||
|
} else if (key_pressed == KEY_BKSPC) {
|
||||||
|
if (cursor > text) {
|
||||||
|
u32 size = GetPrevCharSize(cursor);
|
||||||
|
memmove((char *) cursor - size, cursor, text + len - cursor + 1);
|
||||||
|
len -= size;
|
||||||
|
cursor -= size;
|
||||||
|
}
|
||||||
|
} else if (key_pressed == KEY_UNICODE) {
|
||||||
|
if (cursor >= text + 4 && cursor <= text + len) {
|
||||||
|
u16 codepoint = 0;
|
||||||
|
for (const char *c = cursor - 4; c < cursor; c++) {
|
||||||
|
if ((*c >= '0' && *c <= '9') || (*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f')) {
|
||||||
|
codepoint <<= 4;
|
||||||
|
codepoint |= *c - (*c <= '9' ? '0' : ((*c <= 'F' ? 'A' : 'a') - 10));
|
||||||
|
} else {
|
||||||
|
codepoint = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codepoint != 0) {
|
||||||
|
char character[5] = {0};
|
||||||
|
u16 input[2] = {codepoint, 0};
|
||||||
|
utf16_to_utf8((u8*) character, input, 4, 1);
|
||||||
|
|
||||||
|
u32 char_size = GetCharSize(character);
|
||||||
|
memmove((char *) cursor - 4 + char_size, cursor, text + len - cursor + 1);
|
||||||
|
memcpy((char *) cursor - 4, character, char_size);
|
||||||
|
cursor -= 4 - char_size;
|
||||||
|
len -= 4 - char_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key_pressed == KEY_ENTER) key_character = crlf ? '\r' : '\n';
|
||||||
|
else if (key_pressed < 0x80) key_character = key_pressed;
|
||||||
|
|
||||||
|
if (key_character && len + (key_character == '\r' ? 1 : 0) < max_len) {
|
||||||
|
if (uppercase == 1) {
|
||||||
|
uppercase = 0;
|
||||||
|
}
|
||||||
|
memmove((char *) cursor + 1, cursor, text + len++ - cursor + 1);
|
||||||
|
*((char *) cursor++) = key_character;
|
||||||
|
if (key_character == '\r') {
|
||||||
|
memmove((char *) cursor + 1, cursor, text + len++ - cursor + 1);
|
||||||
|
*((char *) cursor++) = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor && !ww) {
|
||||||
|
const char* cursor_line_start = line_start(text, len, ww, cursor);
|
||||||
|
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor);
|
||||||
|
if (cursor_chars_from_line_start < off_disp_chars + strlen(al_str)) off_disp_chars = cursor_chars_from_line_start - strlen(al_str);
|
||||||
|
if (cursor_chars_from_line_start >= off_disp_chars + TV_LLEN_DISP - strlen(ar_str)) off_disp_chars = cursor_chars_from_line_start + strlen(ar_str) - TV_LLEN_DISP + 1;
|
||||||
|
}
|
||||||
|
while (cursor && cursor < line0_next) line0_next = line_seek_chars(text, len, ww, line0_next, -1);
|
||||||
|
while (cursor && line0_next < line_seek_chars(text, len, ww, GetNextChar(cursor), -TV_NLIN_DISP)) line0_next = line_seek_chars(text, len, ww, line0_next, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find last allowed lines (ww and nonww)
|
||||||
|
const char* llast_nww = line_seek_chars(text, len, 0, text + len + 1, -TV_NLIN_DISP);
|
||||||
|
const char* llast_ww = line_seek_chars(text, len, TV_LLEN_DISP, text + len + 1, -TV_NLIN_DISP);
|
||||||
|
|
||||||
// check for problems, apply changes
|
// check for problems, apply changes
|
||||||
if (!ww && (line0_next > llast_nww)) line0_next = llast_nww;
|
if (!ww && (line0_next > llast_nww)) line0_next = llast_nww;
|
||||||
else if (ww && (line0_next > llast_ww)) line0_next = llast_ww;
|
else if (ww && (line0_next > llast_ww)) line0_next = llast_ww;
|
||||||
if (line0_next < line0) { // fix line number for decrease
|
if (line0_next < line0) { // fix line number for decrease
|
||||||
do if (*(--line0) == '\n') lcurr--;
|
do {
|
||||||
|
DecChar(&line0);
|
||||||
|
if (is_newline(line0)) lcurr--;
|
||||||
|
}
|
||||||
while (line0 > line0_next);
|
while (line0 > line0_next);
|
||||||
} else { // fix line number for increase / same
|
} else { // fix line number for increase / same
|
||||||
for (; line0_next > line0; line0++)
|
for (; line0_next > line0; IncChar(&line0)) if (is_newline(line0)) lcurr++;
|
||||||
if (*line0 == '\n') lcurr++;
|
|
||||||
}
|
}
|
||||||
if (off_disp + TV_LLEN_DISP > llen_max) off_disp = llen_max - TV_LLEN_DISP;
|
|
||||||
if ((off_disp < 0) || ww) off_disp = 0;
|
// find maximum line length
|
||||||
|
u32 llen_max = 0;
|
||||||
|
for (const char* ptr = text; ptr < text + len; ptr = line_seek_chars(text, len, 0, ptr, 1)) {
|
||||||
|
u32 llen = line_len_chars(text, len, 0, ptr, NULL) + 1;
|
||||||
|
if (llen > llen_max) llen_max = llen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off_disp_chars + TV_LLEN_DISP > llen_max) off_disp_chars = llen_max - TV_LLEN_DISP;
|
||||||
|
if (off_disp_chars < 0 || ww) off_disp_chars = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for user edits
|
||||||
|
if (text_cpy) {
|
||||||
|
if (save_path) {
|
||||||
|
bool diffs = false;
|
||||||
|
if (len != text_cpy_len) diffs = true;
|
||||||
|
else for (u32 i = 0; i < len; ++i) if (text[i] != text_cpy[i]) { diffs = true; break; }
|
||||||
|
if (diffs && ShowPrompt(true, "%s", STR_TEXT_EDITS_SAVE_CHANGES) && !FileSetData(save_path, text, len, 0, true))
|
||||||
|
ShowPrompt(false, "%s", STR_FAILED_WRITING_TO_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(text_cpy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear screens
|
// clear screens
|
||||||
@ -1774,7 +2002,7 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script) {
|
|||||||
// (misses safety checks for wider compatibility)
|
// (misses safety checks for wider compatibility)
|
||||||
bool MemToCViewer(const char* text, u32 len, const char* title) {
|
bool MemToCViewer(const char* text, u32 len, const char* title) {
|
||||||
const u32 max_captions = 24; // we assume this is enough
|
const u32 max_captions = 24; // we assume this is enough
|
||||||
char* captions[max_captions];
|
const char* captions[max_captions];
|
||||||
u32 lineno[max_captions];
|
u32 lineno[max_captions];
|
||||||
u32 ww = TV_LLEN_DISP;
|
u32 ww = TV_LLEN_DISP;
|
||||||
|
|
||||||
@ -1786,13 +2014,13 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
|
|||||||
|
|
||||||
// clear screens / view start of readme on top
|
// clear screens / view start of readme on top
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
MemTextView(text, len, (char*) text, 0, 1, ww, 0, false);
|
MemTextView(text, len, text, 0, 1, ww, 0, false, NULL);
|
||||||
|
|
||||||
// parse text for markdown captions
|
// parse text for markdown captions
|
||||||
u32 n_captions = 0;
|
u32 n_captions = 0;
|
||||||
char* ptr = (char*) text;
|
const char* ptr = text;
|
||||||
for (u32 lno = 1;; lno++) {
|
for (u32 lno = 1;; lno++) {
|
||||||
char* ptr_next = line_seek(text, len, 0, ptr, 1);
|
const char* ptr_next = line_seek_chars(text, len, 0, ptr, 1);
|
||||||
if (ptr == ptr_next) break;
|
if (ptr == ptr_next) break;
|
||||||
if (*ptr == '#') {
|
if (*ptr == '#') {
|
||||||
captions[n_captions] = ptr;
|
captions[n_captions] = ptr;
|
||||||
@ -1812,7 +2040,7 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
|
|||||||
y0 += 2 * (FONT_HEIGHT_EXT + (2*TV_VPAD));
|
y0 += 2 * (FONT_HEIGHT_EXT + (2*TV_VPAD));
|
||||||
for (u32 i = 0; (i < n_captions) && (y0 < SCREEN_HEIGHT); i++) {
|
for (u32 i = 0; (i < n_captions) && (y0 < SCREEN_HEIGHT); i++) {
|
||||||
u32 text_color = ((int) i == cursor) ? COLOR_TVRUN : COLOR_TVTEXT;
|
u32 text_color = ((int) i == cursor) ? COLOR_TVRUN : COLOR_TVTEXT;
|
||||||
char* caption = captions[i];
|
const char* caption = captions[i];
|
||||||
u32 len = 0;
|
u32 len = 0;
|
||||||
u32 lvl = 0;
|
u32 lvl = 0;
|
||||||
for (; *caption == '#'; caption++, lvl++);
|
for (; *caption == '#'; caption++, lvl++);
|
||||||
@ -1826,16 +2054,16 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
|
|||||||
// handle user input
|
// handle user input
|
||||||
u32 pad_state = InputWait(0);
|
u32 pad_state = InputWait(0);
|
||||||
if ((cursor >= 0) && (pad_state & BUTTON_A)) {
|
if ((cursor >= 0) && (pad_state & BUTTON_A)) {
|
||||||
if (!MemTextViewer(text, len, lineno[cursor], false)) return false;
|
if (!MemTextViewer(text, len, lineno[cursor], false, 0, NULL)) return false;
|
||||||
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false);
|
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL);
|
||||||
} else if (pad_state & BUTTON_B) {
|
} else if (pad_state & BUTTON_B) {
|
||||||
break;
|
break;
|
||||||
} else if (pad_state & BUTTON_UP) {
|
} else if (pad_state & BUTTON_UP) {
|
||||||
cursor = (cursor <= 0) ? ((int) n_captions - 1) : cursor - 1;
|
cursor = (cursor <= 0) ? ((int) n_captions - 1) : cursor - 1;
|
||||||
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false);
|
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL);
|
||||||
} else if (pad_state & BUTTON_DOWN) {
|
} else if (pad_state & BUTTON_DOWN) {
|
||||||
if (++cursor >= (int) n_captions) cursor = 0;
|
if (++cursor >= (int) n_captions) cursor = 0;
|
||||||
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false);
|
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1848,18 +2076,20 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
|
|||||||
bool FileTextViewer(const char* path, bool as_script) {
|
bool FileTextViewer(const char* path, bool as_script) {
|
||||||
// load text file (completely into memory)
|
// load text file (completely into memory)
|
||||||
// text file needs to fit inside the STD_BUFFER_SIZE
|
// text file needs to fit inside the STD_BUFFER_SIZE
|
||||||
u32 flen, len;
|
size_t fileSize = FileGetSize(path);
|
||||||
|
if (fileSize >= STD_BUFFER_SIZE) {
|
||||||
|
ShowPrompt(false, STR_ERROR_TEXT_FILE_TOO_BIG, fileSize, STD_BUFFER_SIZE - 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
char* text = malloc(STD_BUFFER_SIZE);
|
char* text = malloc(STD_BUFFER_SIZE);
|
||||||
if (!text) return false;
|
if (!text) return false;
|
||||||
|
|
||||||
flen = FileGetData(path, text, STD_BUFFER_SIZE - 1, 0);
|
u32 flen = FileGetData(path, text, STD_BUFFER_SIZE - 1, 0);
|
||||||
|
|
||||||
text[flen] = '\0';
|
text[flen] = '\0';
|
||||||
len = (ptrdiff_t)memchr(text, '\0', flen + 1) - (ptrdiff_t)text;
|
|
||||||
|
|
||||||
// let MemTextViewer take over
|
// let MemTextViewer take over
|
||||||
bool result = MemTextViewer(text, len, 1, as_script);
|
bool result = MemTextViewer(text, flen, 1, as_script, STD_BUFFER_SIZE - 1, path);
|
||||||
|
|
||||||
free(text);
|
free(text);
|
||||||
return result;
|
return result;
|
||||||
@ -1968,11 +2198,11 @@ bool ExecuteGM9Script(const char* path_script) {
|
|||||||
}
|
}
|
||||||
if (show_preview) {
|
if (show_preview) {
|
||||||
if (lno <= (TV_NLIN_DISP/2)) {
|
if (lno <= (TV_NLIN_DISP/2)) {
|
||||||
MemTextView(script, script_size, script, 0, 1, 0, lno, true);
|
MemTextView(script, script_size, script, 0, 1, 0, lno, true, NULL);
|
||||||
} else {
|
} else {
|
||||||
char* ptr_view = line_seek(script, script_size, 0, ptr, -(TV_NLIN_DISP/2));
|
const char* ptr_view = line_seek_chars(script, script_size, 0, ptr, -(TV_NLIN_DISP/2));
|
||||||
u32 lno_view = lno - (TV_NLIN_DISP/2);
|
u32 lno_view = lno - (TV_NLIN_DISP/2);
|
||||||
MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true);
|
MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
bool for_handler(char* path, const char* dir, const char* pattern, bool recursive);
|
bool for_handler(char* path, const char* dir, const char* pattern, bool recursive);
|
||||||
|
|
||||||
bool ValidateText(const char* text, u32 size);
|
bool ValidateText(const char* text, u32 size);
|
||||||
bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script);
|
bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max_len, const char* save_path);
|
||||||
bool MemToCViewer(const char* text, u32 len, const char* title);
|
bool MemToCViewer(const char* text, u32 len, const char* title);
|
||||||
bool FileTextViewer(const char* path, bool as_script);
|
bool FileTextViewer(const char* path, bool as_script);
|
||||||
bool ExecuteGM9Script(const char* path_script);
|
bool ExecuteGM9Script(const char* path_script);
|
||||||
|
|||||||
@ -191,7 +191,7 @@
|
|||||||
"CALCULATE_SHA256": "Calculate SHA-256",
|
"CALCULATE_SHA256": "Calculate SHA-256",
|
||||||
"CALCULATE_SHA1": "Calculate SHA-1",
|
"CALCULATE_SHA1": "Calculate SHA-1",
|
||||||
"SHOW_FILE_INFO": "Show file info",
|
"SHOW_FILE_INFO": "Show file info",
|
||||||
"SHOW_IN_TEXTVIEWER": "Show in Textviewer",
|
"SHOW_IN_TEXTVIEWER": "Show in Text Editor",
|
||||||
"CALCULATE_CMAC": "Calculate CMAC",
|
"CALCULATE_CMAC": "Calculate CMAC",
|
||||||
"COPY_TO_OUT": "Copy to %s",
|
"COPY_TO_OUT": "Copy to %s",
|
||||||
"DUMP_TO_OUT": "Dump to %s",
|
"DUMP_TO_OUT": "Dump to %s",
|
||||||
@ -778,7 +778,7 @@
|
|||||||
"SCRIPTERR_APPLY_IPS_FAILD": "apply IPS failed",
|
"SCRIPTERR_APPLY_IPS_FAILD": "apply IPS failed",
|
||||||
"SCRIPTERR_APPLY_BPS_FAILED": "apply BPS failed",
|
"SCRIPTERR_APPLY_BPS_FAILED": "apply BPS failed",
|
||||||
"SCRIPTERR_APPLY_BPM_FAILED": "apply BPM failed",
|
"SCRIPTERR_APPLY_BPM_FAILED": "apply BPM failed",
|
||||||
"SCRIPTERR_TEXTVIEWER_FAILED": "textviewer failed",
|
"SCRIPTERR_TEXTVIEWER_FAILED": "text editor failed",
|
||||||
"SCRIPTERR_BAD_DUMPSIZE": "bad dumpsize",
|
"SCRIPTERR_BAD_DUMPSIZE": "bad dumpsize",
|
||||||
"SCRIPTERR_CART_INIT_FAIL": "cart init fail",
|
"SCRIPTERR_CART_INIT_FAIL": "cart init fail",
|
||||||
"SCRIPTERR_CART_DUMP_FAILED": "cart dump failed",
|
"SCRIPTERR_CART_DUMP_FAILED": "cart dump failed",
|
||||||
@ -792,7 +792,11 @@
|
|||||||
"SCRIPTERR_UNCLOSED_CONDITIONAL": "unclosed conditional",
|
"SCRIPTERR_UNCLOSED_CONDITIONAL": "unclosed conditional",
|
||||||
"SCRIPTERR_ERROR_MESSAGE_FAIL": "error message fail",
|
"SCRIPTERR_ERROR_MESSAGE_FAIL": "error message fail",
|
||||||
"ERROR_INVALID_TEXT_DATA": "Error: Invalid text data",
|
"ERROR_INVALID_TEXT_DATA": "Error: Invalid text data",
|
||||||
|
"ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.",
|
||||||
"TEXTVIEWER_CONTROLS_DETAILS": "Textviewer Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nB - Exit\n",
|
"TEXTVIEWER_CONTROLS_DETAILS": "Textviewer Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nB - Exit\n",
|
||||||
|
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
|
||||||
|
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nY - Caps / Capslock\nX - Delete char\nA - Insert newline\nB - Enter view mode\n",
|
||||||
|
"TEXT_EDITS_SAVE_CHANGES": "You made text edits.\nWrite changes to file?",
|
||||||
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Current line: %i\nEnter new line below.",
|
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Current line: %i\nEnter new line below.",
|
||||||
"PREVIEW_DISABLED": "(preview disabled)",
|
"PREVIEW_DISABLED": "(preview disabled)",
|
||||||
"PATH_LINE_N_ERR_LINE": "%s\nline %lu: %s\n%s",
|
"PATH_LINE_N_ERR_LINE": "%s\nline %lu: %s\n%s",
|
||||||
|
|||||||
@ -312,7 +312,7 @@ verify S:/firm1.bin
|
|||||||
# applybpm 0:/example/patch.bpm 0:/data/originalfolder 0:/game/moddedfolder
|
# applybpm 0:/example/patch.bpm 0:/data/originalfolder 0:/game/moddedfolder
|
||||||
|
|
||||||
# 'textview' COMMAND
|
# 'textview' COMMAND
|
||||||
# This will show a text file on screen, in a dedicated text viewer. Size restrictions apply (max 1MiB)
|
# This will show a text file on screen, in a dedicated text editor. Size restrictions apply (max 1MiB)
|
||||||
# textview 0:/sometext.txt
|
# textview 0:/sometext.txt
|
||||||
|
|
||||||
# 'boot' COMMAND
|
# 'boot' COMMAND
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user