// text output and refresh #include "pch.h" WINDControl wind; ROLLControl roll; static const char *logcol[] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }; // --------------------------------------------------------------------------- int con_wraproll(int roll, int value) { roll += value; if(roll >= CON_LINES) return roll - CON_LINES; else if(roll < 0) return roll + CON_LINES; else return roll; } void con_set_disa_cur(uint32_t addr) { con.disa_cursor = addr & ~3; con.text = con.disa_cursor - (wind.disa_h - 1) / 2 * 4; con.update |= CON_UPDATE_DISA; } void con_recalc_wnds() { wind.regs_y = 0; if(wind.visible && CON_UPDATE_REGS) wind.data_y = wind.regs_y + wind.regs_h; else wind.data_y = wind.regs_y; if(wind.visible && CON_UPDATE_DATA) wind.disa_y = wind.data_y + wind.data_h; else wind.disa_y = wind.data_y; if(wind.visible && CON_UPDATE_DISA) wind.roll_y = wind.disa_y + wind.disa_h; else wind.roll_y = wind.disa_y; wind.roll_h = CON_HEIGHT - wind.roll_y - 2; // - statusline - editline wind.stat_h = wind.edit_h = 1; wind.edit_y = wind.roll_y + wind.roll_h; wind.stat_y = wind.edit_y + 1; } void con_blt_region(int regY, int regH) { COORD pos = { 0, (SHORT)regY }; COORD sz = { CON_WIDTH, CON_HEIGHT }; SMALL_RECT rgn = { 0, (SHORT)regY, CON_WIDTH-1, (SHORT)(regY + regH - 1) }; BOOL success = WriteConsoleOutputA(con.output, *con.buf, sz, pos, &rgn); } void con_nextline() { con.X = 0; con.Y++; if(con.Y >= CON_HEIGHT) con.Y = CON_HEIGHT - 1; } void con_printchar(char ch) { con.buf[con.Y][con.X].Attributes = con.attr; con.buf[con.Y][con.X].Char.AsciiChar = ch; con.X++; if(con.X >= CON_WIDTH) con_nextline(); } void con_printline(const char *text) { if(!text) return; while(*text) { if(*text == 1) { con.attr = (con.attr & 0xfff0) | text[1]; text += 2; } else if(*text == 2) { con.attr &= 0xff0f; con.attr |= (text[1] & 0xf) << 4; text += 2; } else if(*text == '\n') { con_nextline(); text++; } else if(*text == '\t') { int numToRound = con.X + 1; int multiple = 4; int untilX = ((((numToRound)+(multiple)-1) / (multiple)) * (multiple)); int numspaces = untilX - con.X; while (numspaces--) { con_printchar(' '); } text++; } else con_printchar(*text++); } } void con_gotoxy(int X, int Y) { con.X = X; con.Y = Y; } void con_print_at(int X, int Y, const char *text) { con_gotoxy(X, Y); con_printline(text); } void con_status(const char *txt) { sprintf_s (roll.statusline, sizeof(roll.statusline), " %s\n", txt); con_update(CON_UPDATE_STAT); } void con_cursorxy(int x, int y) { COORD cr = { (SHORT)x, (SHORT)y }; SetConsoleCursorPosition(con.output, cr); } void con_fill_line(int y, int charCode) { for(int i = 0; i < CON_WIDTH; i++) { con.buf[y][i].Attributes = con.attr; con.buf[y][i].Char.AsciiChar = (char)charCode; } } void con_clear_line(int y, uint16_t attr) { for(int i = 0; i < CON_WIDTH; i++) { con.buf[y][i].Attributes = attr; con.buf[y][i].Char.AsciiChar = ' '; } } void con_printf_at(int x, int y, const char *txt, ...) { va_list varg; char buf[0x200] = { 0, }; va_start(varg, txt); vsprintf_s (buf, sizeof(buf), txt, varg); con_print_at(x, y, buf); va_end(varg); } void con_set_autoscroll(bool value) { roll.autoscroll = value; if(value) con_status("Ready. Press PgUp to look behind."); else con_status("Scroll Mode - Press PgUp, PgDown, Up, Down to scroll."); } // generate HTML text static char * string_to_HTML_string(char *txt, char *html, size_t htmlSize) { char *ptr = html; size_t len = strlen(txt); const char * logcurcol = logcol[(int)ConColor::NORM]; ptr += sprintf_s (ptr, htmlSize - (ptr - html), "%s", logcurcol); for(size_t n=0; n"); logcurcol = logcol[txt[n+1]]; ptr += sprintf_s(ptr, htmlSize - (ptr - html), "%s", logcurcol); n+=2; } else if(c == 2) { n+=2; } else if(c == '<') { ptr += sprintf_s(ptr, htmlSize - (ptr - html), "<"); n++; } else if(c == '>') { ptr += sprintf_s (ptr, htmlSize - (ptr - html), ">"); n++; } else { *ptr++ = c; n++; } } ptr += sprintf_s (ptr, htmlSize - (ptr - html), ""); ptr += sprintf_s (ptr, htmlSize - (ptr - html), "\n"); *ptr++ = 0; return html; } static void log_console_output(char *txt) { static int nwrites = 10; if(con.log) { if(!con.logf) { con.logf = nullptr; fopen_s (&con.logf, con.logfile, "w"); if (con.logf) { fprintf(con.logf, "\n"); fprintf(con.logf, "\n"); fprintf(con.logf, "\n"); fprintf(con.logf, "
\n");
            }
        }

        if(con.logf)
        {
            fprintf(con.logf, "%s", txt);
        }

        if(!nwrites--)
        {
            nwrites = 10;
            if (con.logf)
                fflush(con.logf);
        }
    }
}

void con_add_roller_line(const char *txt, ConColor col)
{
    char line[0x1000], *ptr = (char *)txt;
    char html[0x1000] = { 0, };

    sprintf_s(line, sizeof(line), "\x1%c%s", col, txt);
    ptr = line;

    // roll console "roller" 1 line up
    con.reportLock.Lock();
    roll.rollpos = con_wraproll(roll.rollpos, 1);
    strncpy_s(roll.data[roll.rollpos], sizeof(roll.data[roll.rollpos]), ptr, CON_LINELEN-1);
    log_console_output(string_to_HTML_string(ptr, html, sizeof(html)));
    con.reportLock.Unlock();
    con_update(CON_UPDATE_MSGS);
}

void con_change_focus(FOCUSWND newfocus)
{
    FOCUSWND oldfocus = wind.focus;

    if(oldfocus == newfocus)
        return; // we dont need any repaint

    wind.focus = newfocus;
    
    switch(oldfocus)
    {           // switch focus from?
        case WREGS:     con.update |= (CON_UPDATE_REGS); break;
        case WDATA:     con.update |= (CON_UPDATE_DATA); break;
        case WDISA:     con.update |= (CON_UPDATE_DISA); break;
        case WCONSOLE:  con.update |= (CON_UPDATE_MSGS); break;
    }

    switch(newfocus)
    {           // switch focus to?
        case WREGS:     con.update |= (CON_UPDATE_REGS); break;
        case WDATA:     con.update |= (CON_UPDATE_DATA); break;
        case WDISA:     con.update |= (CON_UPDATE_DISA); break;
        case WCONSOLE:  con.update |= (CON_UPDATE_MSGS); break;
    }
}

static void con_update_scroll_window()
{
    int i, y, back, line;

    // cleanup window and skip header line
    memset(con.buf[wind.roll_y + 1], 0, (sizeof(CHAR_INFO) * (wind.roll_h - 1) * CON_WIDTH));
    con_attr(0, 3);
    con_fill_line(wind.roll_y, 0xc4);
    if(wind.focus == WCONSOLE) con_printf_at(0, wind.roll_y, "\x1%c\x1f", ConColor::WHITE);
    con_attr(0, 3);
    con_print_at(2, wind.roll_y, "F4");
    con_print_at(6, wind.roll_y, " console output");
    con_attr(7, 0);

    // printing coord in buffer (skip header)
    y = wind.roll_y + 1;

    // shift backward
    back = wind.roll_h - 1;

    // where to get buffer line
    line = (roll.autoscroll) ? (roll.rollpos - back) : (roll.viewpos - back);
    line += 1;
    con.reportLock.Lock();
    for(i=1; i= 0) con_print_at(0, y, roll.data[line]);
        line++;
        y++;
    }
    con.reportLock.Unlock();
}

void con_fullscreen(bool full)
{
    for(int i=0; iregs.pc);
    }
    if(con.update == 0) return;

    // registres
    if(con.update & CON_UPDATE_REGS)
    {
        con_update_registers();
        con_blt_region(wind.regs_y, wind.regs_h);
    }

    // data dump window
    if(con.update & CON_UPDATE_DATA)
    {
        con_update_dump_window();
        con_blt_region(wind.data_y, wind.data_h);
    }

    // disassembler window
    if(con.update & CON_UPDATE_DISA)
    {
        con_update_disa_window();
        con_blt_region(wind.disa_y, wind.disa_h);
    }

    // message history
    if(con.update & CON_UPDATE_MSGS)
    {
        con_update_scroll_window();
        con_blt_region(wind.roll_y, wind.roll_h);
    }

    // editline window
    if(con.update & CON_UPDATE_EDIT)
    {
        memset(&con.buf[wind.edit_y][0], 0, sizeof(CHAR_INFO) * 80 );
        con_attr(7, 0);
        con_print_at(0, wind.edit_y, "> ");
        con_printline(roll.editline);
        con_blt_region(wind.edit_y, 1);
        con_cursorxy(roll.editpos + 2, wind.edit_y);
    }

    // statusline window
    if(con.update & CON_UPDATE_STAT)
    {
        int i;

        con_attr(0, 3);
        for(i=0; i