diff --git a/port/include/console.h b/port/include/console.h new file mode 100644 index 000000000..29476f895 --- /dev/null +++ b/port/include/console.h @@ -0,0 +1,14 @@ +#ifndef _IN_CONSOLE_H +#define _IN_CONSOLE_H + +#include +#include "types.h" + +void conInit(void); +Gfx *conRender(Gfx *gdl); +void conTick(void); +void conPrint(s32 showmsg, const char *s); +void conPrintf(s32 showmsg, const char *fmt, ...); +void conPrintLn(s32 showmsg, const char *s); + +#endif diff --git a/port/src/console.c b/port/src/console.c new file mode 100644 index 000000000..c4c79261e --- /dev/null +++ b/port/src/console.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include "platform.h" +#include "types.h" +#include "constants.h" +#include "data.h" +#include "bss.h" +#include "config.h" +#include "video.h" +#include "input.h" +#include "console.h" +#include "system.h" +#include "utils.h" +#include "net/net.h" +#include "game/hudmsg.h" +#include "game/game_1531a0.h" +#include "lib/vi.h" + +#define CON_ROWS 80 +#define CON_COLS 56 +#define CON_VISROWS 10 +#define CON_MSGROWS 4 +#define CON_MSGTIMER 3.f + +static char conBuf[CON_ROWS][CON_COLS + 1]; +static char conInput[CON_COLS + 1]; +static char *conVisRows[CON_VISROWS]; +static s32 conPrintRow = 0; +static s32 conPrintCol = 0; +static s32 conMsgRows = 0; +static f32 conMsgTimer = 0.f; +static s32 conInputCol = 0; +static u32 conTextColour = 0x00ff00ff; +static s32 conOpen = 0; +static s32 conButton = 0; + +void conInit(void) +{ + memset(conBuf, 0, sizeof(conBuf)); + memset(conInput, 0, sizeof(conInput)); + memset(conVisRows, 0, sizeof(conVisRows)); + conPrintRow = conPrintCol = 0; + conOpen = 0; + conMsgRows = 0; + conMsgTimer = 0.0f; + conInputCol = 0; +} + +void conPrint(s32 showmsg, const char *str) +{ + if (!str || !*str) { + return; + } + + const s32 oldRow = conPrintRow; + + for (const char *s = str; *s; ++s) { + char ch = *s; + switch (ch) { + case '\n': + if (conPrintCol) { + conPrintCol = 0; + conPrintRow = (conPrintRow + 1) % CON_ROWS; + } + break; + case '\r': + break; + case '\t': + ch = ' '; + /* fallthrough */ + default: + conBuf[conPrintRow][conPrintCol++] = ch; + if (conPrintCol == CON_COLS) { + conPrintCol = 0; + conPrintRow = (conPrintRow + 1) % CON_ROWS; + } + break; + } + conBuf[conPrintRow][conPrintCol] = '\0'; + } + + if (conPrintRow != oldRow) { + // scroll time + for (s32 i = 0; i < ARRAYCOUNT(conVisRows); ++i) { + s32 row = conPrintRow - i - 1; + if (row < 0) { + row = CON_ROWS + row; + } + conVisRows[i] = &conBuf[row][0]; + } + if (showmsg) { + if (conMsgRows < CON_MSGROWS) { + ++conMsgRows; + } + conMsgTimer = sysGetSeconds() + CON_MSGTIMER; + } + } +} + +void conPrintLn(s32 showmsg, const char *s) +{ + char tmp[4096]; + snprintf(tmp, sizeof(tmp), "%s\n", s); + conPrint(showmsg, tmp); +} + +void conPrintf(s32 showmsg, const char *fmt, ...) +{ + char tmp[4096]; + tmp[sizeof(tmp) - 1] = '\0'; + tmp[0] = '\0'; + + va_list args; + va_start(args, fmt); + vsnprintf(tmp, sizeof(tmp), fmt, args); + va_end(args); + + conPrint(showmsg, tmp); +} + +static inline Gfx *conRenderMsgs(Gfx *gdl) +{ + if (conMsgRows) { + s32 x, y; + const u32 c = (conTextColour & 0xffffff00) | 0xa0; + for (s32 i = 0; i < conMsgRows; ++i) { + char *s = conVisRows[i]; + if (s) { + x = 4; + y = 4 + 8 * (conMsgRows - i - 1); + gdl = textRenderProjected(gdl, &x, &y, s, g_CharsHandelGothicXs, g_FontHandelGothicXs, c, viGetWidth(), viGetHeight(), 0, 0); + } + } + const f32 t = sysGetSeconds(); + if (conMsgTimer <= t) { + conMsgTimer = t + CON_MSGTIMER; + --conMsgRows; + } + } else { + conMsgTimer = 0; + } + + return gdl; +} + +Gfx *conRender(Gfx *gdl) +{ + gdl = text0f153628(gdl); + + if (!conOpen) { + gSPExtraGeometryModeEXT(gdl++, G_ASPECT_MODE_EXT, G_ASPECT_LEFT_EXT); + gdl = conRenderMsgs(gdl); + } else { + s32 x, y; + gSPExtraGeometryModeEXT(gdl++, G_ASPECT_MODE_EXT, G_ASPECT_CENTER_EXT); + gdl = hudmsgRenderBox(gdl, 16, 0, SCREEN_WIDTH_LO - 16, 4 + 8 * (CON_VISROWS + 1), 1.f, conTextColour, 0.9f); + for (s32 i = 0; i < CON_VISROWS; ++i) { + char *s = conVisRows[i]; + if (s) { + x = 18; + y = 4 + 8 * (CON_VISROWS - i - 1); + gdl = textRenderProjected(gdl, &x, &y, s, g_CharsHandelGothicXs, g_FontHandelGothicXs, conTextColour, viGetWidth(), viGetHeight(), 0, 0); + } + } + char tmp[CON_COLS + 3]; + snprintf(tmp, sizeof(tmp), "> %s", conInput); + x = 18; + y = 4 + 8 * CON_VISROWS; + gdl = textRenderProjected(gdl, &x, &y, tmp, g_CharsHandelGothicXs, g_FontHandelGothicXs, conTextColour, viGetWidth(), viGetHeight(), 0, 0); + } + + gSPClearExtraGeometryModeEXT(gdl++, G_ASPECT_MODE_EXT); + + gdl = text0f153780(gdl); + + return gdl; +} + +void conTick(void) +{ + const s32 button = inputKeyPressed(VK_GRAVE); + if (button && !conButton) { + conOpen = !conOpen; + if (conOpen) { + inputStartTextInput(); + } else { + inputStopTextInput(); + } + } + + conButton = button; + + if (conOpen) { + char chr = inputGetLastTextChar(); + if (chr && isprint(chr)) { + if (conInputCol < CON_COLS) { + conInput[conInputCol++] = chr; + conInput[conInputCol] = '\0'; + } + } + const s32 key = inputGetLastKey(); + if (key == VK_BACKSPACE) { + if (conInputCol) { + conInput[--conInputCol] = '\0'; + } else { + conInput[0] = '\0'; + } + } else if (key == VK_RETURN) { + if (conInput[0] && conInputCol) { + if (g_NetMode) { + netChat(conInput); + } + } + conInput[0] = '\0'; + conInputCol = 0; + } + inputClearLastTextChar(); + inputClearLastKey(); + } +} diff --git a/port/src/main.c b/port/src/main.c index 263ef1c37..8573ce68e 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -16,6 +16,7 @@ #include "config.h" #include "mod.h" #include "system.h" +#include "console.h" #include "utils.h" #include "net/net.h" @@ -40,8 +41,6 @@ s32 g_TickRateDiv = 1; s32 g_SkipIntro = false; -extern s32 g_StageNum; - s32 bootGetMemSize(void) { return (s32)g_OsMemSize; @@ -84,6 +83,7 @@ static void gameInit(void) static void cleanup(void) { sysLogPrintf(LOG_NOTE, "shutdown"); + netDisconnect(); inputSaveBinds(); configSave(CONFIG_PATH); crashShutdown(); @@ -98,6 +98,7 @@ int main(int argc, const char **argv) crashInit(); } + conInit(); sysInit(); fsInit(); configInit(); diff --git a/port/src/pdmain.c b/port/src/pdmain.c index c568e3ac3..1376df5a6 100644 --- a/port/src/pdmain.c +++ b/port/src/pdmain.c @@ -72,6 +72,7 @@ #include "lib/str.h" #include "data.h" #include "types.h" +#include "console.h" #include "net/net.h" #include "net/netmsg.h" @@ -559,6 +560,8 @@ void mainTick(void) gdl = profileRender(gdl); } + gdl = conRender(gdl); + gDPFullSync(gdl++); gSPEndDisplayList(gdl++); } diff --git a/port/src/pdsched.c b/port/src/pdsched.c index 9880db4f9..19f32cbd1 100644 --- a/port/src/pdsched.c +++ b/port/src/pdsched.c @@ -24,6 +24,7 @@ #include "video.h" #include "audio.h" #include "input.h" +#include "console.h" #include "mixer.h" #include "net/net.h" @@ -291,6 +292,7 @@ void schedEndFrame(OSSched *sc) } inputUpdate(); + conTick(); joyStartReadData(&g_PiMesgQueue); joyReadData(); diff --git a/port/src/system.c b/port/src/system.c index e8433cf82..ba09986ab 100644 --- a/port/src/system.c +++ b/port/src/system.c @@ -8,6 +8,7 @@ #include #include #include "platform.h" +#include "console.h" #include "system.h" #define LOG_FNAME "pd.log" @@ -96,6 +97,12 @@ u64 sysGetMicroseconds(void) return ((u64)tv.tv_sec * USEC_IN_SEC + (u64)tv.tv_usec) - startTick; } +float sysGetSeconds(void) +{ + u64 t = sysGetMicroseconds(); + return (f32)t / 1000000.f; +} + void sysLogPrintf(s32 level, const char *fmt, ...) { static const char *prefix[3] = { @@ -117,9 +124,11 @@ void sysLogPrintf(s32 level, const char *fmt, ...) } } - FILE *fout = (level == LOG_NOTE) ? stdout : stderr; - fprintf(fout, "%s%s\n", prefix[level], logmsg); + FILE *fout = ((level & 0x0f) == LOG_NOTE) ? stdout : stderr; + fprintf(fout, "%s%s\n", prefix[level & 0x0f], logmsg); fflush(fout); + + conPrintLn((level & LOGFLAG_SHOWMSG) != 0, logmsg); } void sysFatalError(const char *fmt, ...)