--- /dev/null
+\r
+\r
+/*! \file console.h\r
+ \brief 3ds stdio support.\r
+\r
+<div class="fileHeader">\r
+Provides stdio integration for printing to the 3DS screen as well as debug print\r
+functionality provided by stderr.\r
+\r
+General usage is to initialize the console by:\r
+consoleDemoInit()\r
+or to customize the console usage by:\r
+consoleInit()\r
+\r
+*/\r
+\r
+#ifndef CONSOLE_H\r
+#define CONSOLE_H\r
+\r
+#include <3ds/types.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+typedef bool(* ConsolePrint)(void* con, char c);\r
+\r
+//! a font struct for the console.\r
+typedef struct ConsoleFont\r
+{\r
+ u8* gfx; //!< A pointer to the font graphics\r
+ u16 asciiOffset; //!< Offset to the first valid character in the font table\r
+ u16 numChars; //!< Number of characters in the font graphics\r
+\r
+}ConsoleFont;\r
+\r
+/** \brief console structure used to store the state of a console render context.\r
+\r
+Default values from consoleGetDefault();\r
+<div class="fixedFont"><pre>\r
+PrintConsole defaultConsole =\r
+{\r
+ //Font:\r
+ {\r
+ (u8*)default_font_bin, //font gfx\r
+ 0, //first ascii character in the set\r
+ 128, //number of characters in the font set\r
+ },\r
+ 0,0, //cursorX cursorY\r
+ 0,0, //prevcursorX prevcursorY\r
+ 40, //console width\r
+ 30, //console height\r
+ 0, //window x\r
+ 0, //window y\r
+ 32, //window width\r
+ 24, //window height\r
+ 3, //tab size\r
+ 0, //font character offset\r
+ 0, //print callback\r
+ false //console initialized\r
+};\r
+</pre></div>\r
+*/\r
+typedef struct PrintConsole\r
+{\r
+ ConsoleFont font; //!< font of the console.\r
+\r
+ u16 *frameBuffer; //!< framebuffer address.\r
+\r
+ int cursorX; /*!< Current X location of the cursor (as a tile offset by default) */\r
+ int cursorY; /*!< Current Y location of the cursor (as a tile offset by default) */\r
+\r
+ int prevCursorX; /*!< Internal state */\r
+ int prevCursorY; /*!< Internal state */\r
+\r
+ int consoleWidth; /*!< Width of the console hardware layer in characters */\r
+ int consoleHeight; /*!< Height of the console hardware layer in characters */\r
+\r
+ int windowX; /*!< Window X location in characters (not implemented) */\r
+ int windowY; /*!< Window Y location in characters (not implemented) */\r
+ int windowWidth; /*!< Window width in characters (not implemented) */\r
+ int windowHeight; /*!< Window height in characters (not implemented) */\r
+\r
+ int tabSize; /*!< Size of a tab*/\r
+ int fg; /*!< foreground color*/\r
+ int bg; /*!< background color*/\r
+ int flags; /*!< reverse/bright flags*/\r
+\r
+ ConsolePrint PrintChar; /*!< callback for printing a character. Should return true if it has handled rendering the graphics\r
+ (else the print engine will attempt to render via tiles) */\r
+\r
+ bool consoleInitialised; /*!< True if the console is initialized */\r
+}PrintConsole;\r
+\r
+#define CONSOLE_COLOR_BRIGHT (1<<0)\r
+#define CONSOLE_COLOR_REVERSE (1<<1)\r
+\r
+/*! \brief Loads the font into the console\r
+ \param console pointer to the console to update, if NULL it will update the current console\r
+ \param font the font to load\r
+*/\r
+void consoleSetFont(PrintConsole* console, ConsoleFont* font);\r
+\r
+/*! \brief Sets the print window\r
+ \param console console to set, if NULL it will set the current console window\r
+ \param x x location of the window\r
+ \param y y location of the window\r
+ \param width width of the window\r
+ \param height height of the window\r
+*/\r
+void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height);\r
+\r
+/*! \brief Gets a pointer to the console with the default values\r
+ this should only be used when using a single console or without changing the console that is returned, other wise use consoleInit()\r
+ \return A pointer to the console with the default values\r
+*/\r
+PrintConsole* consoleGetDefault(void);\r
+\r
+/*! \brief Make the specified console the render target\r
+ \param console A pointer to the console struct (must have been initialized with consoleInit(PrintConsole* console)\r
+ \return a pointer to the previous console\r
+*/\r
+PrintConsole *consoleSelect(PrintConsole* console);\r
+\r
+/*! \brief Initialise the console.\r
+ \param console A pointer to the console data to initialze (if it's NULL, the default console will be used)\r
+ \return A pointer to the current console.\r
+*/\r
+PrintConsole* consoleInit(PrintConsole* console);\r
+\r
+//! Clears the screan by using iprintf("\x1b[2J");\r
+void consoleClear(void);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif\r
--- /dev/null
+#include <stdio.h>\r
+#include <string.h>\r
+#include <sys/iosupport.h>\r
+#include <3ds/gfx.h>\r
+#include <3ds/console.h>\r
+\r
+#include "default_font_bin.h"\r
+\r
+//set up the palette for color printing\r
+static u16 colorTable[] = {\r
+ RGB565( 0, 0, 0), // normal black\r
+ RGB565(17, 0, 0), // normal red\r
+ RGB565( 0,15, 0), // normal green\r
+ RGB565(17,34, 0), // normal yellow\r
+ RGB565( 0, 0,17), // normal blue\r
+ RGB565(17, 0,17), // normal magenta\r
+ RGB565( 0,34,17), // normal cyan\r
+ RGB565(17,34,17), // normal white\r
+ RGB565( 0, 0, 0), // bright black\r
+ RGB565(25, 0, 0), // bright red\r
+ RGB565( 0,52, 0), // bright green\r
+ RGB565(25,52, 0), // bright yellow\r
+ RGB565( 4,18,31), // bright blue\r
+ RGB565(25, 0,25), // bright magenta\r
+ RGB565( 0,52,25), // bright cyan\r
+ RGB565(28,57,28) // bright white\r
+};\r
+\r
+PrintConsole defaultConsole =\r
+{\r
+ //Font:\r
+ {\r
+ (u8*)default_font_bin, //font gfx\r
+ 0, //first ascii character in the set\r
+ 128 //number of characters in the font set\r
+ },\r
+ (u16*)NULL,\r
+ 0,0, //cursorX cursorY\r
+ 0,0, //prevcursorX prevcursorY\r
+ 40, //console width\r
+ 30, //console height\r
+ 0, //window x\r
+ 0, //window y\r
+ 40, //window width\r
+ 30, //window height\r
+ 3, //tab size\r
+ 7, // foreground color\r
+ 0, // background color\r
+ CONSOLE_COLOR_BRIGHT, // flags\r
+ 0, //print callback\r
+ false //console initialized\r
+};\r
+\r
+PrintConsole currentCopy;\r
+\r
+PrintConsole* currentConsole = ¤tCopy;\r
+\r
+PrintConsole* consoleGetDefault(void){return &defaultConsole;}\r
+\r
+void consolePrintChar(char c);\r
+void consoleDrawChar(int c);\r
+\r
+//---------------------------------------------------------------------------------\r
+static void consoleCls(char mode) {\r
+//---------------------------------------------------------------------------------\r
+\r
+ int i = 0;\r
+ int colTemp,rowTemp;\r
+\r
+ switch (mode)\r
+ {\r
+ case '[':\r
+ case '0':\r
+ {\r
+ colTemp = currentConsole->cursorX ;\r
+ rowTemp = currentConsole->cursorY ;\r
+\r
+ while(i++ < ((currentConsole->windowHeight * currentConsole->windowWidth) - (rowTemp * currentConsole->consoleWidth + colTemp)))\r
+ consolePrintChar(' ');\r
+\r
+ currentConsole->cursorX = colTemp;\r
+ currentConsole->cursorY = rowTemp;\r
+ break;\r
+ }\r
+ case '1':\r
+ {\r
+ colTemp = currentConsole->cursorX ;\r
+ rowTemp = currentConsole->cursorY ;\r
+\r
+ currentConsole->cursorY = 0;\r
+ currentConsole->cursorX = 0;\r
+\r
+ while (i++ < (rowTemp * currentConsole->windowWidth + colTemp))\r
+ consolePrintChar(' ');\r
+\r
+ currentConsole->cursorX = colTemp;\r
+ currentConsole->cursorY = rowTemp;\r
+ break;\r
+ }\r
+ case '2':\r
+ {\r
+ currentConsole->cursorY = 0;\r
+ currentConsole->cursorX = 0;\r
+\r
+ while(i++ < currentConsole->windowHeight * currentConsole->windowWidth)\r
+ consolePrintChar(' ');\r
+\r
+ currentConsole->cursorY = 0;\r
+ currentConsole->cursorX = 0;\r
+ break;\r
+ }\r
+ }\r
+}\r
+//---------------------------------------------------------------------------------\r
+static void consoleClearLine(char mode) {\r
+//---------------------------------------------------------------------------------\r
+\r
+ int i = 0;\r
+ int colTemp;\r
+\r
+ switch (mode)\r
+ {\r
+ case '[':\r
+ case '0':\r
+ {\r
+ colTemp = currentConsole->cursorX ;\r
+\r
+ while(i++ < (currentConsole->windowWidth - colTemp)) {\r
+ consolePrintChar(' ');\r
+ }\r
+\r
+ currentConsole->cursorX = colTemp;\r
+\r
+ break;\r
+ }\r
+ case '1':\r
+ {\r
+ colTemp = currentConsole->cursorX ;\r
+\r
+ currentConsole->cursorX = 0;\r
+\r
+ while(i++ < ((currentConsole->windowWidth - colTemp)-2)) {\r
+ consolePrintChar(' ');\r
+ }\r
+\r
+ currentConsole->cursorX = colTemp;\r
+\r
+ break;\r
+ }\r
+ case '2':\r
+ {\r
+ colTemp = currentConsole->cursorX ;\r
+\r
+ currentConsole->cursorX = 0;\r
+\r
+ while(i++ < currentConsole->windowWidth) {\r
+ consolePrintChar(' ');\r
+ }\r
+\r
+ currentConsole->cursorX = colTemp;\r
+\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ colTemp = currentConsole->cursorX ;\r
+\r
+ while(i++ < (currentConsole->windowWidth - colTemp)) {\r
+ consolePrintChar(' ');\r
+ }\r
+\r
+ currentConsole->cursorX = colTemp;\r
+\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------------\r
+ssize_t con_write(struct _reent *r,int fd,const char *ptr, size_t len) {\r
+//---------------------------------------------------------------------------------\r
+\r
+ char chr;\r
+\r
+ int i, count = 0;\r
+ char *tmp = (char*)ptr;\r
+\r
+ if(!tmp || len<=0) return -1;\r
+\r
+ i = 0;\r
+\r
+ while(i<len) {\r
+\r
+ chr = *(tmp++);\r
+ i++; count++;\r
+\r
+ if ( chr == 0x1b && *tmp == '[' ) {\r
+ bool escaping = true;\r
+ char *escapeseq = tmp;\r
+ int escapelen = 0;\r
+\r
+ do {\r
+ chr = *(tmp++);\r
+ i++; count++; escapelen++;\r
+ int parameter, consumed, assigned;\r
+ bool scanning;\r
+\r
+ switch (chr) {\r
+ //---------------------------------------\r
+ // Cursor directional movement\r
+ //---------------------------------------\r
+ case 'A':\r
+ assigned = sscanf(escapeseq,"[%dA", ¶meter);\r
+ if (assigned==0) parameter = 1;\r
+ currentConsole->cursorY = (currentConsole->cursorY - parameter) < 0 ? 0 : currentConsole->cursorY - parameter;\r
+ escaping = false;\r
+ break;\r
+ case 'B':\r
+ sscanf(escapeseq,"[%dB", ¶meter);\r
+ currentConsole->cursorY = (currentConsole->cursorY + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + parameter;\r
+ escaping = false;\r
+ break;\r
+ case 'C':\r
+ sscanf(escapeseq,"[%dC", ¶meter);\r
+ currentConsole->cursorX = (currentConsole->cursorX + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + parameter;\r
+ escaping = false;\r
+ break;\r
+ case 'D':\r
+ sscanf(escapeseq,"[%dD", ¶meter);\r
+ currentConsole->cursorX = (currentConsole->cursorX - parameter) < 0 ? 0 : currentConsole->cursorX - parameter;\r
+ escaping = false;\r
+ break;\r
+ //---------------------------------------\r
+ // Cursor position movement\r
+ //---------------------------------------\r
+ case 'H':\r
+ case 'f':\r
+ sscanf(escapeseq,"[%d;%df", ¤tConsole->cursorY , ¤tConsole->cursorX );\r
+ escaping = false;\r
+ break;\r
+ //---------------------------------------\r
+ // Screen clear\r
+ //---------------------------------------\r
+ case 'J':\r
+ consoleCls(escapeseq[escapelen-2]);\r
+ escaping = false;\r
+ break;\r
+ //---------------------------------------\r
+ // Line clear\r
+ //---------------------------------------\r
+ case 'K':\r
+ consoleClearLine(escapeseq[escapelen-2]);\r
+ escaping = false;\r
+ break;\r
+ //---------------------------------------\r
+ // Save cursor position\r
+ //---------------------------------------\r
+ case 's':\r
+ currentConsole->prevCursorX = currentConsole->cursorX ;\r
+ currentConsole->prevCursorY = currentConsole->cursorY ;\r
+ escaping = false;\r
+ break;\r
+ //---------------------------------------\r
+ // Load cursor position\r
+ //---------------------------------------\r
+ case 'u':\r
+ currentConsole->cursorX = currentConsole->prevCursorX ;\r
+ currentConsole->cursorY = currentConsole->prevCursorY ;\r
+ escaping = false;\r
+ break;\r
+ //---------------------------------------\r
+ // Color scan codes\r
+ //---------------------------------------\r
+ case 'm':\r
+ escapeseq++;\r
+ scanning = true;\r
+\r
+// do while doesn't work at -O2\r
+// do {\r
+ sscanf(escapeseq,"%d;%n", ¶meter, &consumed);\r
+ escapeseq += consumed;\r
+\r
+ if (parameter == 0 ) {\r
+ currentConsole->flags |= CONSOLE_COLOR_BRIGHT;\r
+ currentConsole->flags &= ~CONSOLE_COLOR_REVERSE;\r
+ currentConsole->bg = 0;\r
+ currentConsole->fg = 7;\r
+ } else if (parameter == 7) { // reverse video\r
+ currentConsole->flags |= CONSOLE_COLOR_REVERSE;\r
+ } else if (parameter == 2) { // half bright\r
+ currentConsole->flags &= ~CONSOLE_COLOR_BRIGHT;\r
+ } else if (parameter >= 30 && parameter <= 37) { // writing color\r
+ currentConsole->fg = parameter - 30;\r
+ } else if (parameter >= 40 && parameter <= 47) { // screen color\r
+ currentConsole->bg = parameter - 40;\r
+ }\r
+ if(escapeseq >= tmp) scanning = false;\r
+// } while(scanning);\r
+\r
+ escaping = false;\r
+ break;\r
+ }\r
+ } while (escaping);\r
+ continue;\r
+ }\r
+\r
+ consolePrintChar(chr);\r
+ }\r
+\r
+ return count;\r
+}\r
+\r
+static const devoptab_t dotab_stdout = {\r
+ "con",\r
+ 0,\r
+ NULL,\r
+ NULL,\r
+ con_write,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+static const devoptab_t dotab_null = {\r
+ "null",\r
+ 0,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL\r
+};\r
+\r
+//---------------------------------------------------------------------------------\r
+PrintConsole* consoleInit(PrintConsole* console) {\r
+//---------------------------------------------------------------------------------\r
+\r
+ static bool firstConsoleInit = true;\r
+\r
+ if(firstConsoleInit) {\r
+ devoptab_list[STD_OUT] = &dotab_stdout;\r
+ devoptab_list[STD_ERR] = &dotab_stdout;\r
+\r
+ setvbuf(stdout, NULL , _IONBF, 0);\r
+ setvbuf(stderr, NULL , _IONBF, 0);\r
+\r
+ firstConsoleInit = false;\r
+ }\r
+\r
+ if(console) {\r
+ currentConsole = console;\r
+ } else {\r
+ console = currentConsole;\r
+ }\r
+\r
+ *currentConsole = defaultConsole;\r
+\r
+ console->consoleInitialised = 1;\r
+\r
+ gfxSetScreenFormat(GFX_BOTTOM,GSP_RGB565_OES);\r
+ gfxSetDoubleBuffering(GFX_BOTTOM,false);\r
+ console->frameBuffer = (u16*)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL);\r
+\r
+\r
+ consoleCls('2');\r
+\r
+ return currentConsole;\r
+\r
+}\r
+//---------------------------------------------------------------------------------\r
+PrintConsole *consoleSelect(PrintConsole* console){\r
+//---------------------------------------------------------------------------------\r
+ PrintConsole *tmp = currentConsole;\r
+ currentConsole = console;\r
+ return tmp;\r
+}\r
+\r
+//---------------------------------------------------------------------------------\r
+void consoleSetFont(PrintConsole* console, ConsoleFont* font){\r
+//---------------------------------------------------------------------------------\r
+\r
+ if(!console) console = currentConsole;\r
+\r
+ console->font = *font;\r
+\r
+}\r
+\r
+//---------------------------------------------------------------------------------\r
+static void newRow() {\r
+//---------------------------------------------------------------------------------\r
+\r
+\r
+ currentConsole->cursorY ++;\r
+\r
+ if(currentConsole->cursorY >= currentConsole->windowHeight) {\r
+ currentConsole->cursorY --;\r
+ u16 *dst = ¤tConsole->frameBuffer[(currentConsole->windowX * 8 * 240) + (239 - (currentConsole->windowY * 8))];\r
+ u16 *src = dst - 8;\r
+\r
+ int i,j;\r
+\r
+ for (i=0; i<currentConsole->windowWidth*8; i++) {\r
+ u32 *from=(u32*)src;\r
+ u32 *to = (u32*)dst;\r
+ for (j=0; j<((currentConsole->windowHeight*8)-8)/2;j++) *(to--) = *(from--);\r
+\r
+ dst += 240;\r
+ src += 240;\r
+ }\r
+\r
+ consoleClearLine('2');\r
+ }\r
+}\r
+//---------------------------------------------------------------------------------\r
+void consoleDrawChar(int c) {\r
+//---------------------------------------------------------------------------------\r
+ c -= currentConsole->font.asciiOffset;\r
+ if ( c < 0 || c > currentConsole->font.numChars ) return;\r
+\r
+ u8 *fontdata = currentConsole->font.gfx + (8 * c);\r
+\r
+ int writingColor = currentConsole->fg;\r
+ int screenColor = currentConsole->bg;\r
+\r
+ if (currentConsole->flags & CONSOLE_COLOR_BRIGHT) {\r
+ writingColor |= 8;\r
+ screenColor |=8;\r
+ }\r
+\r
+ if (currentConsole->flags & CONSOLE_COLOR_REVERSE) {\r
+ int tmp = writingColor;\r
+ writingColor = screenColor;\r
+ screenColor = tmp;\r
+ }\r
+\r
+ u16 bg = colorTable[screenColor];\r
+ u16 fg = colorTable[writingColor];\r
+\r
+ u8 b1 = *(fontdata++);\r
+ u8 b2 = *(fontdata++);\r
+ u8 b3 = *(fontdata++);\r
+ u8 b4 = *(fontdata++);\r
+ u8 b5 = *(fontdata++);\r
+ u8 b6 = *(fontdata++);\r
+ u8 b7 = *(fontdata++);\r
+ u8 b8 = *(fontdata++);\r
+\r
+ u8 mask = 0x80;\r
+\r
+\r
+ int i;\r
+\r
+ int x = (currentConsole->cursorX + currentConsole->windowX) * 8;\r
+ int y = ((currentConsole->cursorY + currentConsole->windowY) *8 );\r
+\r
+ u16 *screen = ¤tConsole->frameBuffer[(x * 240) + (239 - (y + 7))];\r
+\r
+ for (i=0;i<8;i++) {\r
+ if (b8 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b7 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b6 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b5 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b4 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b3 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b2 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ if (b1 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; }\r
+ mask >>= 1;\r
+ screen += 240 - 8;\r
+ }\r
+\r
+}\r
+\r
+//---------------------------------------------------------------------------------\r
+void consolePrintChar(char c) {\r
+//---------------------------------------------------------------------------------\r
+ if (c==0) return;\r
+\r
+ if(currentConsole->PrintChar)\r
+ if(currentConsole->PrintChar(currentConsole, c))\r
+ return;\r
+\r
+ if(currentConsole->cursorX >= currentConsole->windowWidth) {\r
+ currentConsole->cursorX = 0;\r
+\r
+ newRow();\r
+ }\r
+\r
+ switch(c) {\r
+ /*\r
+ The only special characters we will handle are tab (\t), carriage return (\r), line feed (\n)\r
+ and backspace (\b).\r
+ Carriage return & line feed will function the same: go to next line and put cursor at the beginning.\r
+ For everything else, use VT sequences.\r
+\r
+ Reason: VT sequences are more specific to the task of cursor placement.\r
+ The special escape sequences \b \f & \v are archaic and non-portable.\r
+ */\r
+ case 8:\r
+ currentConsole->cursorX--;\r
+\r
+ if(currentConsole->cursorX < 0) {\r
+ if(currentConsole->cursorY > 0) {\r
+ currentConsole->cursorX = currentConsole->windowX - 1;\r
+ currentConsole->cursorY--;\r
+ } else {\r
+ currentConsole->cursorX = 0;\r
+ }\r
+ }\r
+\r
+ consoleDrawChar(' ');\r
+ break;\r
+\r
+ case 9:\r
+ currentConsole->cursorX += currentConsole->tabSize - ((currentConsole->cursorX)%(currentConsole->tabSize));\r
+ break;\r
+ case 10:\r
+ newRow();\r
+ case 13:\r
+ currentConsole->cursorX = 0;\r
+ break;\r
+ default:\r
+ consoleDrawChar(c);\r
+ ++currentConsole->cursorX ;\r
+ break;\r
+ }\r
+}\r
+\r
+//---------------------------------------------------------------------------------\r
+void consoleClear(void) {\r
+//---------------------------------------------------------------------------------\r
+ iprintf("\x1b[2J");\r
+}\r
+\r
+//---------------------------------------------------------------------------------\r
+void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height){\r
+//---------------------------------------------------------------------------------\r
+\r
+ if(!console) console = currentConsole;\r
+\r
+ console->windowWidth = width;\r
+ console->windowHeight = height;\r
+ console->windowX = x;\r
+ console->windowY = y;\r
+\r
+ console->cursorX = 0;\r
+ console->cursorY = 0;\r
+\r
+}\r
+\r
+\r