/*
 User Extensions

  LispBox uLisp Extension - Version 1.0 - June 2024
  Hartmut Grawe - github.com/ersatzmoco - June 2024

  edited by hasn0life for Lilygo T-Deck - Jan 2025

  updated by picolisper for LilyGo T-Deck and
  PicoCalc				- April 2025

*/


// Utility functions
uint8_t
dec_to_bcd(uint8_t n)
{
	uint8_t bcd = 0;
	uint8_t	tens = n / 10;

	bcd = tens << 4;
	tens *= 10;
	bcd += (n - tens) & 0x0f;
	return bcd;
}


uint8_t
bcd_to_dec(uint8_t n)
{
	return ((n>>4) * 10) + (n&0x0f);
}

/*
 * STANDARD DEFINITIONS
 *
 * These definitions should be the same on every platform.
 */
object *
fn_platform(object *args, object *env)
{
	(void) args;
	(void) env;

#if defined(PLATFORM_PICOCALC)
	const char platformName[] = ":picocalc";
#elif defined(TDECK_PERI_POWERON) /* first t-deck define */
	const char platformName[] = ":t-deck";
#elif defined(ARDUINO_TEENSY41)
	const char platformName[] = ":teensy41";
#else
	const char platformName[] = ":unknown";
#endif
	return internlong((char *)platformName);
}

object *
fn_bcd_to_dec(object *args, object *env)
{
	(void) env;

	object *arg = car(args);

	int n = checkinteger(arg);
	if ((n < 0) || (n > 153)) {
		error2("number not in the range [0...#x99].");
	}

	uint8_t result = bcd_to_dec(static_cast<uint8_t>(n));
	return number(static_cast<int>(result));
}

object *
fn_dec_to_bcd(object *args, object *env)
{
	(void) env;

	object *arg = car(args);

	int n = checkinteger(arg);
	if ((n < 0) || (n > 99)) {
		error2("number not in the range [0...99].");
	}

	uint8_t result = dec_to_bcd(static_cast<uint8_t>(n));
	return number(static_cast<int>(result));
}

object *
fn_now(object *args, object *env)
{
	(void) env;
	static unsigned long Offset;
	unsigned long now = millis()/1000;
	int nargs = listlength(args);

	// Set time
	if (nargs == 3) {
		Offset = (unsigned long)((checkinteger(first(args))*60		\
					+ checkinteger(second(args)))*60	\
				+ checkinteger(third(args)) - now);
	} else if (nargs > 0) {
		error2(PSTR("wrong number of arguments"));
	}

	// Return time
	unsigned long secs = Offset + now;
	object *seconds = number(secs%60);
	object *minutes = number((secs/60)%60);
	object *hours = number((secs/3600)%24);

	return cons(hours, cons(minutes, cons(seconds, NULL)));
}


void
hyperprint(object *form, int lm, pfun_t pfun)
{
	if (atom(form)) {
		if (isbuiltin(form, NOTHING)) {
			printsymbol(form, pfun);
		} else {
			printobject(form, pfun);
		}
	} else if (quoted(form)) {
		pfun('\'');
		hyperprint(car(cdr(form)), lm + 1, pfun);
	} else {
		lm = lm + PPINDENT;
		bool fits = (subwidth(form, PPWIDTH - lm - PPINDENT) >= 0);
		int special = 0, extra = 0; bool separate = true;
		object *arg = car(form);

		if (symbolp(arg) && builtinp(arg->name)) {
			uint8_t minmax = getminmax(builtin(arg->name));
			if (minmax == 0327 || minmax == 0313) {
				special = 2; // defun, setq, setf, defvar
			} else if (minmax == 0317 || minmax == 0017 ||
				   minmax == 0117 || minmax == 0123) {
				special = 1;
			}
		}

		while (form != NULL) {
			if (atom(form)) {
				pfstring(PSTR(" . "), pfun);
				printobject(form, pfun);
				pfun(')');
				return;
			} else if (separate) {
				pfun('(');
				separate = false;
			} else if (special) {
				pfun(' ');
				special--;
			} else if (fits) {
				pfun(' ');
			} else {
				pln(pfun);
				indent(lm, ' ', pfun);
			}

			hyperprint(car(form), lm+extra, pfun);
			form = cdr(form);
		}
		pfun(')');
	}
}

object *
fn_sym_def(object *args, object *env)
{
	(void) env;

	object *obj = first(args);
	pfun_t pfun = pstreamfun(cdr(args));
#if defined(gfxsupport)
	if (pfun == gfxwrite) ppwidth = GFXPPWIDTH;
#endif
	object *pair = findvalue(obj, env);
	object *var = car(pair);
	object *val = cdr(pair);
	pln(pfun);

	if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) {
		hyperprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun);
	} else {
		hyperprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun);
	}

	pln(pfun);
	ppwidth = PPWIDTH;
	return bsymbol(NOTHING);
}


>>>>>>> 5392bac (moving to project folder)
object *
fn_listlibrary2(object *args, object *env)
{
	(void) args, (void) env;
	object *lst = nil;

	GlobalStringIndex = 0;
	object *line = read(glibrary);
	while (line != NULL) {
		builtin_t bname = builtin(first(line)->name);
		if (bname == DEFUN || bname == DEFVAR) {
			lst = cons(second(line), lst);
		}
		line = read(glibrary);
	}

	return lst;
}


object *
fn_lambdap(object *arg, object *env)
{
	(void) env;

	if (consp(arg)) {
		arg = car(arg);
	}

	if (builtin(arg->name) == LAMBDA) {
		return tee;
	}

	return nil;
}


object *
fn_searchstr(object *args, object *env)
{
	(void) env;

	int startpos = 0;
	object *pattern = first(args);
	object *target = second(args);
	args = cddr(args);

	if (pattern == NULL) {
		return number(0);
	} else if (target == NULL) {
		return nil;
	}

	if (args != NULL) {
		startpos = checkinteger(car(args));
	}

	if (stringp(pattern) && stringp(target)) {
		int l = stringlength(target);
		int m = stringlength(pattern);

		if (startpos > l) {
			error2(indexrange);
		}

		for (int i = startpos; i <= l-m; i++) {
			int j = 0;
			while (j < m && nthchar(target, i+j) == nthchar(pattern, j)) {
				j++;
			}

			if (j == m) {
				return number(i);
			}
		}
		return nil;
	} else {
		error2("arguments are not both lists or strings");
	}

	return nil;
}


object *
fn_searchn(object *args, object *env)
{
	(void) env;

	int matches = 0;
	int last_index = 0;
	object *pattern = first(args);
	object *target = second(args);

	if (cddr(args) != NULL) {
		object *num = third(args);

		if (integerp(num)) {
			matches = num->integer;
		}
	}

	if (pattern == NULL) {
		return number(0);
	} else if (target == NULL) {
		return nil;
	} else if (listp(pattern) && listp(target)) {
		int l = listlength(target);
		int m = listlength(pattern);

		for (int i = 0; i <= l-m; i++) {
			object *target1 = target;
			while (pattern != NULL && eq(car(target1), car(pattern))) {
				pattern = cdr(pattern);
				target1 = cdr(target1);
			}

			if (pattern == NULL){
				last_index = i;

				if (matches-- == 0) {
					return number(i);
				}
			}
			pattern = first(args); target = cdr(target);
		}

		if (last_index > 0) {
			return number(last_index);
		}
		return nil;
	} else if (stringp(pattern) && stringp(target)) {
		int l = stringlength(target);
		int m = stringlength(pattern);

		for (int i = 0; i <= l-m; i++) {
			int j = 0;
			while (j < m && nthchar(target, i+j) == nthchar(pattern, j)) {
				j++;
			}

			if (j == m) {
				last_index = i;
				if(matches-- == 0){
					return number(i);
				}
			}
		}

		if (last_index > 0) {
			return number(last_index);
		}

		return nil;
	} else {
		error2(PSTR("arguments are not both lists or strings"));
	}

	return nil;
}


// SD card standard library.

#if defined(sdcardsupport)
object *
fn_sd_rename(object *args, object *env)
{
	(void) env;
	char buffer1[BUFFERSIZE];
	char buffer2[BUFFERSIZE];

	object *pathFrom = car(args);
	if (!stringp(pathFrom)) {
		error2("filenames must be strings.");
	}

	object *pathTo = car(cdr(args));
	if (!stringp(pathTo)) {
		error2("filenames must be strings.");
	}

	if (!SD.rename((const char *)MakeFilename(pathFrom, buffer1),
		       (const char *)MakeFilename(pathTo, buffer2))) {
		return nil;
	}

	return tee;
}

object *
fn_sd_remove(object *args, object *env)
{
	(void) env;
	char buffer[BUFFERSIZE];

	object *arg = car(args);
	if (!SD.remove(MakeFilename(arg, buffer))) {
		return nil;
	}

	return tee;
}

object *
fn_sd_existsp(object *args, object *env)
{
	(void) env;

	char buffer[BUFFERSIZE];
	object *arg = car(args);
	if (!SD.exists(MakeFilename(arg, buffer))) {
		return nil;
	}

	return tee;
}

/*
   (sd-make-dir path)
   Create a directory on the SD card.
   This will also create any intermediate directories that don’t already exists;
   e.g. SD.mkdir("a/b/c") will create a, b, and c.
 */
object *
fn_sd_mkdir(object *args, object *env)
{
	(void) env;

	char	 buffer[BUFFERSIZE];
	object	*arg = car(args);

	if (SD.mkdir(MakeFilename(arg, buffer))) {
		return tee;
	}

	return nil;
}


/*
   (sd-remove-dir path)
   Remove a directory from the SD card. The directory must be empty.
 */
object *
fn_sd_rmdir(object *args, object *env)
{
	(void) env;

	char	 buffer[BUFFERSIZE];
	object	*arg = car(args);

	if (SD.rmdir(MakeFilename(arg, buffer))) {
			return tee;
	}

	return nil;
}


/*
 * (sd-list)
 */
object *
fn_sd_list(object *args, object *env)
{
	(void) env;

		SDBegin();
	char *sd_path_buf = NULL;
	File root;
	object *result = cons(NULL, NULL);
	object *ptr = result;

	if (args != NULL) {
		object *arg1 = checkstring(first(args));
		int len = stringlength(arg1) + 2; //make it longer for the initial slash and the null terminator

		sd_path_buf = (char*)malloc(len);
		if (sd_path_buf != NULL) {
			cstring(arg1, &sd_path_buf[1], len-1);
			sd_path_buf[0] = '/';  //really weird way to add a slash at the front...
			root = SD.open(sd_path_buf);
		}
	} else{
		root = SD.open("/");
	}

	while (true) {
		File entry =  root.openNextFile();
		if (!entry) {
			break; // no more files
		}
		object *filename = lispstring((char*)entry.name());

		if (entry.isDirectory()) {
			cdr(ptr) = cons(filename, NULL);
		} else{
			cdr(ptr) = cons(cons(filename, number(entry.size())), NULL);
		}

		ptr = cdr(ptr);
		entry.close();
	}

	if (sd_path_buf != NULL) {
		free(sd_path_buf);
	}

	root.close();
	return cdr(result);
}
#endif


/*
 * PICOCALC-SPECIFIC FUNCTIONS
 *
 * Only works on the PicoCalc...
 */
#if defined(PLATFORM_PICOCALC)
char
getkey()
{
	PCKeyboard::KeyEvent	kevt;
	char			keypress;

	do {
		kevt = pc_kbd.keyEvent();

		if (kevt.state == PCKeyboard::StatePress) {
			if (kevt.key == 6) {
				continue;
			}

			keypress = kevt.key;
			break;
		}
	} while (pc_kbd.keyCount() == 0);


	do {
		kevt = pc_kbd.keyEvent();
	} while (kevt.state != PCKeyboard::StateRelease);

	switch (keypress) {
		case 177:
			keypress = 27;
			break;
		default:
			// do nothing
			break;
	}

	return keypress;
}

object *
fn_get_key(object *args, object *env)
{
	(void) args;
	(void) env;

	return character(getkey());
}
#endif


#if defined(TDECK_PERI_POWERON)
#define touchscreen

#if defined(touchscreen)
#include "TouchDrvGT911.hpp"
TouchDrvGT911 touch;
#endif

#define TDECK_TOUCH_INT     16

#define TDECK_TRACKBALL_UP 3
#define TDECK_TRACKBALL_DOWN 15
#define TDECK_TRACKBALL_LEFT 1
#define TDECK_TRACKBALL_RIGHT 2
#define PLATFORM_TDECK

volatile int ball_val = 0;

// Touchscreen
void
initTouch()
{
#if defined (touchscreen)
	pinMode(TDECK_TOUCH_INT, INPUT);
	touch.setPins(-1, TDECK_TOUCH_INT);\
		//keyboard already initialized the I2C?
		if (!touch.begin(Wire1, GT911_SLAVE_ADDRESS_L)) {
			while (1) {
				Serial.println("Failed to find GT911 - check your wiring!");
				delay(1000);
			}
		}
	// Set touch max xy
	touch.setMaxCoordinates(320, 240);
	// Set swap xy
	touch.setSwapXY(true);
	// Set mirror xy
	touch.setMirrorXY(false, true);
#endif
}


void
ISR_trackball_up()
{
	ball_val = 218;
}

void
ISR_trackball_down()
{
	ball_val = 217;
}

void
ISR_trackball_left()
{
	ball_val = 216;
}

void
ISR_trackball_right ()
{
	ball_val = 215;
}

void
inittrackball()
{
	pinMode(TDECK_TRACKBALL_UP, INPUT_PULLUP);
	pinMode(TDECK_TRACKBALL_DOWN, INPUT_PULLUP);
	pinMode(TDECK_TRACKBALL_LEFT, INPUT_PULLUP);
	pinMode(TDECK_TRACKBALL_RIGHT, INPUT_PULLUP);
	attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_UP), ISR_trackball_up, FALLING);
	attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_DOWN), ISR_trackball_down, FALLING);
	attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_LEFT), ISR_trackball_left, FALLING);
	attachInterrupt(digitalPinToInterrupt(TDECK_TRACKBALL_RIGHT), ISR_trackball_right, FALLING);
}

object *
fn_get_touch_points(object *args, object *env)
{
#if defined(touchscreen)
	int16_t x[5], y[5];
	uint8_t touched = 0;
	object *result = nil;
	do {
		touched = touch.getPoint(x, y, touch.getSupportTouchPoint());
		if (touched > 0) {
			//start from the end of the list so we dont have to reverse it
			for (int i = touched; i > 0; --i) {
				result = cons(cons(number(x[i-1]), number(y[i-1])), result);
			}
		}
	} while(touch.isPressed());
	return result;

#else
	return nil;
#endif
}

bool
isScreenTouched()
{
	bool received_touch = false;

	// Clear any previous readings since it buffers those.
	do {
		int16_t x[5], y[5];
		uint8_t touched = touch.getPoint(x, y, touch.getSupportTouchPoint());
	} while(touch.isPressed());

	// touch.ispressed() will trigger like 5 times if you press it once so
	// we have to loop through it and get the touchpoints
	do {
		int16_t x[5], y[5];
		uint8_t touched = touch.getPoint(x, y, touch.getSupportTouchPoint());
		if (touched > 0) {
			received_touch = true;
		}
	} while(touch.isPressed());

	return received_touch;
}


// T-Deck extras
char
touchKeyModEditor(char temp)
{
#if defined (touchscreen)
	/* t-deck / blackberry keyboard missing symbols
	   missing mapped	alt symbol
	   `       k       '
	   ~       p       @
	   %       $
	   ^       a       *
	   &       q       #
	   =       o       +
	   <       t       (
	   >       y       )
	   \       u       _
	   |       g       /

	   [       alt-t   (
	   ]       alt-y   )

	   {       n/a
	   }       n/a
	   tab  	space

	   while holding the touch screen
	   c --- quit editor and return to REPL
	   n --- discard current text buffer (i.e. new file)
	   backspace --- delete line starting at cursor position
	   trackball left --- move cursor to start of line
	   trackball right --- move cursor to end of line
	   ^ --- move cursor to beginning of buffer
	   trackball up / trackball down --- move one page up or down

	   Fn-h --- help menu
	   Fn-( --- toggle bracket matching on/off
	   Fn-) --- check if bracket under the cursor has a matching bracket
	            in the buffer. If so, they are temporarily highlighted.
		    (Use when continuous bracket matching is off.)
	   Fn-b --- bind contents of the text buffer to a symbol of your choice and quit editor
	   Fn-d --- delete a file on the SD card
	   Fn-s --- save text buffer to SD card
	   Fn-l --- load text from SD card into buffer, discarding the present one
	   Fn-i --- show directory of SD card

	 */

	if (isScreenTouched()) {
		if (temp == 'k')      return '`';
		else if (temp == 'p') return '~';
		else if (temp == '$') return '%';
		else if (temp == 'a') return '^';
		else if (temp == 'q') return '&';
		else if (temp == 'o') return '=';
		else if (temp == 't') return '<';
		else if (temp == 'y') return '>';
		else if (temp == 'u') return '\\';
		else if (temp == 'g') return '|';
		else if (temp == '(') return '[';
		else if (temp == ')') return ']';
		else if (temp == ' ') return '\t';

		else if  (temp == 'c') return (char)17; //quit
		else if (temp == 'n') return (char)24; //new
		else if (temp == 8) return (char)12; //delete line
		else if (temp == '*') return (char)94; //beginning
		else if (temp == 'h') return (char)16;  //help
		else if (temp == 's') return (char)203; //save
		else if (temp == 'l') return (char)204; //load
		else if (temp == 'd') return (char)202; //delete
		else if (temp == 'b') return (char)198; //bind
		else if (temp == 'i') return (char)205; //show dir
		else if (temp == '1') return (char)194; //toggle bracket
		else if (temp == '2') return (char)195; //highlight

	}
#else
	if (temp == '@') temp = '~';
	if (temp == '_') temp = '\\';
#endif
	return temp;
}


object *
fn_KeyboardGetKey(object *args, object *env)
{
	(void) env, (void) args;

	Wire1.requestFrom(0x55, 1);
	if (Wire1.available()) {
		char temp = Wire1.read();
		if ((temp != 0) && (temp !=255)){
			temp = touchKeyModEditor(temp);
			//Serial.println((int)temp);
			return number(temp);
		}
	}

	if (ball_val != 0) {
		int temp = ball_val;
		ball_val = 0;
		if (isScreenTouched()) {
			// ((or 1 210) (se:linestart))
			// ((or 5 213) (se:lineend))
			// (211 (se:prevpage))
			// (214 (se:nextpage))
			switch(temp){
				case 218: temp = 211; break; //up
				case 217: temp = 214; break; //down
				case 216: temp = 210; break; //left
				case 215: temp = 213; break; //right
			}
		}
		return number(temp);
	}
	return nil;
}


/*
   (keyboard-flush)
   Discard missing key up/down events.
 */
object *
fn_KeyboardFlush(object *args, object *env)
{
	(void) args, (void) env;
	return nil;
}
#endif



/*
 * SYMBOL NAMES
 *
 * Define symbol names as const char[] PROGMEM here.
 */
const char stringlambdap[] PROGMEM		= "lambdap";
const char stringnow[] PROGMEM			= "now";
const char string_sym_def[] PROGMEM		= "symdef";
const char stringbcd_to_dec[] PROGMEM		= "bcd-to-dec";
const char stringdec_to_bcd[] PROGMEM		= "dec-to-bcd";
const char stringlist_library2[] PROGMEM	= "list-library2";
const char stringplatform[] PROGMEM		= "platform";
const char stringSearchStr[] PROGMEM		= "search-str";
const char stringsearchn[] PROGMEM		= "searchn";

#if defined(sdcardsupport)
const char stringsd_rename[] PROGMEM		= "sd-rename";
const char stringsd_remove[] PROGMEM		= "sd-remove";
const char stringsd_existsp[] PROGMEM		= "sd-exists-p";
const char stringsd_mkdir[] PROGMEM		= "sd-make-dir";
const char stringsd_rmdir[] PROGMEM		= "sd-remove-dir";
const char stringsd_dir[] PROGMEM		= "sd-list";
#endif

#if defined(PLATFORM_PICOCALC)
const char string_get_key[] PROGMEM		= "get-key";

#elif defined(TDECK_PERI_POWERON)
const char string_gettouchpoints[] PROGMEM = "get-touch-points";
const char stringKeyboardGetKey[] PROGMEM = "keyboard-get-key";
const char stringKeyboardFlush[] PROGMEM = "keyboard-flush";
#endif


/*
 * DOCUMENTATION STRINGS
 *
 * Define documentation strings as const char[] PROGMEM here.
 */

const char doclambdap[] PROGMEM		= "(lambdap x)"
"Returns t if the form passed in is a lambda.";

const char docnow[] PROGMEM = "(now [hh mm ss])\n"
"Sets the current time, or with no arguments returns the current time\n"
"as a list of three integers (hh mm ss).";

const char doc_sym_def[] PROGMEM = "(symbol-def symbol [str])\n"
"Prints the definition of a symbol (variable or function) defined in\n"
"ulisp using the pretty printer."
"If str is specified it prints to the specified stream.\n"
"It returns no value.";

const char doc_bcd_to_dec[] PROGMEM = "(bcd-to-dec n)\n"
"Convert the BCD-encoded number n to decimal.";

const char doc_dec_to_bcd[] PROGMEM = "(dec-to-bcd n)\n"
"BCD encode n.";

const char doc_list_library2[] PROGMEM = "(list-library2)\n"
"Return a list of all symbols in the current Lisp library.";

const char doc_platform[] PROGMEM = "(platform)\n"
"Returns a keyword with the current platform. Supports :picocalc,\n"
":t-deck, and :teensy41. Otherwise, returns :unknown.";

const char docSearchStr[] PROGMEM = "(search pattern target [startpos])\n"
"Returns the index of the first occurrence of pattern in target, or nil if it's not found\n"
"starting from startpos";

const char docsearchn[] PROGMEM = "(searchn pattern target [n])\n"
"Returns the index of the nth occurrence of pattern in target,\n"
"which can be lists or strings, or nil if it's not found.\n"
"if the pattern occured more than once but less than n times, it returns the last occuring index";


// SD card doc strings
#if defined(sdcardsupport)
const char docsd_rename[] PROGMEM	= "(sd-rename from to)\n"
"Renames the file named by 'from' to 'to.'";

const char docsd_remove[] PROGMEM	= "(sd-remove file)\n"
"Removes the named file from the filesystem. Returns t if the file\n"
"was remove successfully.";

const char docsd_existsp[] PROGMEM	= "(sd-exists-p file)\n"
"Returns t if the named file exists.";

const char docsd_dir[] PROGMEM = "(sd-list [directory])\n"
"Returns a list of filenames in the root or named directory.";

const char docsd_mkdir[] PROGMEM = "(sd-make-dir directory)\n"
"Create a directory with the specified name. Returns t if \n"
"successful, otherwise nil.";

const char docsd_rmdir[] PROGMEM = "(sd-remove-dir directory)\n"
"Remove the named directory. Returns t if successful, otherwise nil.";
#endif


// PicoCalc-specific doc strings
#if defined(PLATFORM_PICOCALC)
const char doc_get_key[] PROGMEM = "(get-key)\n"
"Waits for a keypress event, then returns the key.";

// T-Deck-specific doc strings
#elif defined(TDECK_PERI_POWERON)
const char doc_gettouchpoints[] PROGMEM = "(get-touch-points)\n"
"Returns all the points being touched on the screen in a list of x,y pairs or an empty list";

const char docKeyboardGetKey[] PROGMEM = "(keyboard-get-key [pressed])\n"
"Get key last recognized - default: when released, if [pressed] is t: when pressed).";
const char docKeyboardFlush[] PROGMEM = "(keyboard-flush)\n"
"Discard missing key up/down events.";

// End of platform-specific doc strings.
#endif


// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {
	{ stringlambdap, fn_lambdap, 0211, doclambdap },
	{ stringnow, fn_now, 0203, docnow },
	{ string_sym_def, fn_sym_def, 0212, doc_sym_def },
	{ stringbcd_to_dec, fn_bcd_to_dec, 0211, doc_bcd_to_dec },
	{ stringdec_to_bcd, fn_dec_to_bcd, 0211, doc_dec_to_bcd },
	{ stringlist_library2, fn_listlibrary2, 0200, doc_list_library2 },
	{ stringplatform, fn_platform, 0200, doc_platform },
	{ stringSearchStr, fn_searchstr, 0224, docSearchStr },
	{ stringsearchn, fn_searchn, 0223, docsearchn },

#if defined(sdcardsupport)
	{ stringsd_rename, fn_sd_rename, 0222, docsd_rename },
	{ stringsd_remove, fn_sd_remove, 0211, docsd_remove },
	{ stringsd_existsp, fn_sd_existsp, 0211, docsd_existsp },
	{ stringsd_dir, fn_sd_list, 0201, docsd_dir },
	{ stringsd_mkdir, fn_sd_mkdir, 0211, docsd_mkdir },
	{ stringsd_rmdir, fn_sd_rmdir, 0211, docsd_rmdir },
#endif

#if defined(PLATFORM_PICOCALC)
	{ string_get_key, fn_get_key, 0200, doc_get_key },

#elif defined(TDECK_PERI_POWERON)
	{ string_gettouchpoints, fn_get_touch_points, 0200, doc_gettouchpoints },
	{ stringKeyboardGetKey, fn_KeyboardGetKey, 0201, docKeyboardGetKey },
	{ stringKeyboardFlush, fn_KeyboardFlush, 0200, docKeyboardFlush },

#endif
};

// Table cross-reference functions

tbl_entry_t *tables[] = {lookup_table, lookup_table2};
const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) };

const tbl_entry_t *
table(int n)
{
	return tables[n];
}

unsigned int
tablesize(int n)
{
	return tablesizes[n];
}
