/*
 * tngp/ui.c
 *
 * Copyright (C) 2022 bzt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * @brief User Interface routines
 *
 */

#include "tngp.h"

int ui_lineh, ui_btnh, ui_wt, ui_wb, ui_wl, ui_wr, ui_dt, ui_db, ui_dl, ui_dr, ui_bl, ui_br, ui_ol, ui_or, ui_sldb;
int ui_vbw, ui_vbs, ui_hbh, ui_hbs, ui_slots, ui_chatw, ui_chath;
extern char ws[0x110000];

/**
 * Initialize UI
 */
void ui_init()
{
    world_sprite_t *spr;
    int i;

    /* get window border size */
    ui_wt = ui_wb = ui_wl = ui_wr = 0;
    spr = spr_get(world.uispr[E_WINTL].cat, world.uispr[E_WINTL].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINTBG].cat, world.uispr[E_WINTBG].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINNL].cat, world.uispr[E_WINNL].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINNBG].cat, world.uispr[E_WINNBG].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINNR].cat, world.uispr[E_WINNR].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINIL].cat, world.uispr[E_WINIL].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINIBG].cat, world.uispr[E_WINIBG].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINIR].cat, world.uispr[E_WINIR].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINTR].cat, world.uispr[E_WINTR].idx, 0);
    if(spr && spr->h > ui_wt) ui_wt = spr->h;
    spr = spr_get(world.uispr[E_WINML].cat, world.uispr[E_WINML].idx, 0);
    if(spr && spr->w > ui_wl) ui_wl = spr->w;
    spr = spr_get(world.uispr[E_WINMR].cat, world.uispr[E_WINMR].idx, 0);
    if(spr && spr->w > ui_wr) ui_wr = spr->w;
    spr = spr_get(world.uispr[E_WINBL].cat, world.uispr[E_WINBL].idx, 0);
    if(spr && spr->h > ui_wb) ui_wb = spr->h;
    spr = spr_get(world.uispr[E_WINBBG].cat, world.uispr[E_WINBBG].idx, 0);
    if(spr && spr->h > ui_wb) ui_wb = spr->h;
    spr = spr_get(world.uispr[E_WINBR].cat, world.uispr[E_WINBR].idx, 0);
    if(spr && spr->h > ui_wb) ui_wb = spr->h;
    /* get dialog border size */
    ui_dt = ui_db = ui_dl = ui_dr = 0;
    spr = spr_get(world.uispr[E_DLGTL].cat, world.uispr[E_DLGTL].idx, 0);
    if(spr && spr->h > ui_dt) ui_dt = spr->h;
    spr = spr_get(world.uispr[E_DLGTBG].cat, world.uispr[E_DLGTBG].idx, 0);
    if(spr && spr->h > ui_dt) ui_dt = spr->h;
    spr = spr_get(world.uispr[E_DLGNL].cat, world.uispr[E_DLGNL].idx, 0);
    if(spr && spr->h > ui_dt) ui_dt = spr->h;
    spr = spr_get(world.uispr[E_DLGNBG].cat, world.uispr[E_DLGNBG].idx, 0);
    if(spr && spr->h > ui_dt) ui_dt = spr->h;
    spr = spr_get(world.uispr[E_DLGNR].cat, world.uispr[E_DLGNR].idx, 0);
    if(spr && spr->h > ui_dt) ui_dt = spr->h;
    spr = spr_get(world.uispr[E_DLGTR].cat, world.uispr[E_DLGTR].idx, 0);
    if(spr && spr->h > ui_dt) ui_dt = spr->h;
    spr = spr_get(world.uispr[E_DLGML].cat, world.uispr[E_DLGML].idx, 0);
    if(spr && spr->w > ui_dl) ui_dl = spr->w;
    spr = spr_get(world.uispr[E_DLGMR].cat, world.uispr[E_DLGMR].idx, 0);
    if(spr && spr->w > ui_dr) ui_dr = spr->w;
    spr = spr_get(world.uispr[E_DLGBL].cat, world.uispr[E_DLGBL].idx, 0);
    if(spr && spr->h > ui_db) ui_db = spr->h;
    spr = spr_get(world.uispr[E_DLGBBG].cat, world.uispr[E_DLGBBG].idx, 0);
    if(spr && spr->h > ui_db) ui_db = spr->h;
    spr = spr_get(world.uispr[E_DLGBR].cat, world.uispr[E_DLGBR].idx, 0);
    if(spr && spr->h > ui_db) ui_db = spr->h;
    /* calculate line height */
    ui_lineh = world.fonts[FNT_TXT].size;
    if(world.fonts[FNT_SEL].size > ui_lineh) ui_lineh = world.fonts[FNT_SEL].size;
    for(i = E_CHK0; i <= E_SLDR; i++) {
        spr = spr_get(world.uispr[i].cat, world.uispr[i].idx, 0);
        if(spr && spr->h > ui_lineh) ui_lineh = spr->h;
    }
    /* calculate button height */
    ui_btnh = world.fonts[FNT_BTN].size; ui_bl = ui_br = 0;
    for(i = E_BTNNL; i <= E_BTNIR; i++) {
        spr = spr_get(world.uispr[i].cat, world.uispr[i].idx, 0);
        if(spr) {
            if(spr->h > ui_btnh) ui_btnh = spr->h;
            if(((i - E_BTNNL) % 3) == 0 && spr->w > ui_bl) ui_bl = spr->w;
            if(((i - E_BTNNL) % 3) == 2 && spr->w > ui_br) ui_br = spr->w;
        }
    }
    /* option boxes */
    ui_ol = 0;
    spr = spr_get(world.uispr[E_OPTPREVPRESS].cat, world.uispr[E_OPTPREVPRESS].idx, 0);
    if(spr && spr->w > ui_ol) ui_ol = spr->w;
    spr = spr_get(world.uispr[E_OPTPREV].cat, world.uispr[E_OPTPREV].idx, 0);
    if(spr && spr->w > ui_ol) ui_ol = spr->w;
    ui_or = 0;
    spr = spr_get(world.uispr[E_OPTNEXTPRESS].cat, world.uispr[E_OPTNEXTPRESS].idx, 0);
    if(spr && spr->w > ui_or) ui_or = spr->w;
    spr = spr_get(world.uispr[E_OPTNEXT].cat, world.uispr[E_OPTNEXT].idx, 0);
    if(spr && spr->w > ui_or) ui_or = spr->w;
    /* slider */
    ui_sldb = 0;
    spr = spr_get(world.uispr[E_SLDBTN].cat, world.uispr[E_SLDBTN].idx, 0);
    if(spr) ui_sldb = spr->w;
    /* vertical scrollbars */
    ui_vbw = ui_vbs = 0;
    spr = spr_get(world.uispr[E_SCRU].cat, world.uispr[E_SCRU].idx, 0);
    if(spr) {
        if(spr->w > ui_vbw) ui_vbw = spr->w;
        if(spr->h > ui_vbs) ui_vbs = spr->h;
    }
    spr = spr_get(world.uispr[E_SCRD].cat, world.uispr[E_SCRD].idx, 0);
    if(spr) {
        if(spr->w > ui_vbw) ui_vbw = spr->w;
        if(spr->h > ui_vbs) ui_vbs = spr->h;
    }
    spr = spr_get(world.uispr[E_SCRVBG].cat, world.uispr[E_SCRVBG].idx, 0);
    if(spr && spr->w > ui_vbw) ui_vbw = spr->w;
    /* horizontal scrollbars */
    ui_hbh = ui_hbs = 0;
    spr = spr_get(world.uispr[E_SCRL].cat, world.uispr[E_SCRL].idx, 0);
    if(spr) {
        if(spr->w > ui_hbs) ui_hbs = spr->w;
        if(spr->h > ui_hbh) ui_hbh = spr->h;
    }
    spr = spr_get(world.uispr[E_SCRR].cat, world.uispr[E_SCRR].idx, 0);
    if(spr) {
        if(spr->w > ui_hbs) ui_hbs = spr->w;
        if(spr->h > ui_hbh) ui_hbh = spr->h;
    }
    spr = spr_get(world.uispr[E_SCRHBG].cat, world.uispr[E_SCRHBG].idx, 0);
    if(spr && spr->w > ui_hbh) ui_hbh = spr->h;
    /* scrollbar button */
    spr = spr_get(world.uispr[E_SCRBTN].cat, world.uispr[E_SCRBTN].idx, 0);
    if(spr) {
        if(spr->w > ui_vbw) ui_vbw = spr->w;
        if(spr->h > ui_hbh) ui_hbh = spr->h;
    }
    /* item slot size */
    ui_slots = 0;
    spr = spr_get(world.invslot.cat, world.invslot.idx, 0);
    if(spr) {
        if(spr->w > ui_slots) ui_slots = spr->w;
        if(spr->h > ui_slots) ui_slots = spr->h;
    }
    spr = spr_get(world.invslots.cat, world.invslots.idx, 0);
    if(spr) {
        if(spr->w > ui_slots) ui_slots = spr->w;
        if(spr->h > ui_slots) ui_slots = spr->h;
    }
    /* chat window */
    ui_chatw = 0; ui_chath = world.fonts[FNT_TXT].size;
    spr = spr_get(world.uispr[E_INPL].cat, world.uispr[E_INPL].idx, 0);
    if(spr) {
        if(spr->w > ui_chatw) ui_chatw = spr->w;
        if(spr->h > ui_chath) ui_chath = spr->h;
    }
    spr = spr_get(world.uispr[E_INPBG].cat, world.uispr[E_INPBG].idx, 0);
    if(spr && spr->h > ui_chath) ui_chath = spr->h;
    spr = spr_get(world.uispr[E_INPR].cat, world.uispr[E_INPR].idx, 0);
    if(spr) {
        if(spr->w > ui_chatw) ui_chatw = spr->w;
        if(spr->h > ui_chath) ui_chath = spr->h;
    }
}

/**
 * Calculate the current frame index
 */
int ui_anim(int type, int nframe, int currframe)
{
    int ret = 0;

    if(nframe < 2) return 0;
    if(currframe < 0) currframe = main_tick / 100;
    switch(type) {
        case 0:
            ret = currframe % (nframe + 8);
            if(ret >= nframe) ret = nframe - 1;
        break;
        case 1:
            ret = currframe % (2 * nframe - 2);
            if(ret >= nframe) ret = 2 * nframe - ret - 2;
        break;
        case 2:
            ret = currframe % nframe;
        break;
    }
    return ret;
}

/**
 * Recalculate size (sw, sh) to fit into (mw, mh) keeping aspect ratio
 */
void ui_fit(int mw, int mh, int sw, int sh, int *w, int *h)
{
    if(!w || !h) return;
    if(mw < 1 || mh < 1 || sw < 1 || sh < 1) { *w = *h = 0; return; }
    *w = mw; *h = sh * mw / sw;
    if(*h > mh) { *h = mh; *w = sw * mh / sh; }
    if(*w < 1) *w = 1;
    if(*h < 1) *h = 1;
}

/**
 * Parse string and cut to fit into width
 */
char *ui_fitstr(char *str, int w, int fnt, int *nl, char *user, int *attrs)
{
    ssfn_buf_t buf;
    ssfn_t *font;
    int n = 1, l;
    char *s, *d, *ret, *o, *last = NULL;

    if(!str || !*str || w < 16) return NULL;
    s = o = world_parsestr(str, user, attrs);
    if(!s) return NULL;
    font = font_get(fnt);
    memset(&buf, 0, sizeof(ssfn_buf_t));
    buf.w = w; buf.h = world.fonts[fnt].size;
    ret = d = (char*)main_alloc(2 * strlen(s) + 1);
    while(*s) {
        if(*s == ' ') { last = d; }
        l = ssfn_render(font, &buf, s);
        if(l == SSFN_ERR_NOGLYPH) l = 1;
        if(l < 0) break;
        if(buf.x > w) {
            n++;
            buf.x = 0;
            if(last) {
                *last++ = '\n';
                s -= d - last;
                d = last;
                last = NULL;
                continue;
            } else {
                *d++ = '\n';
            }
        }
        memcpy(d, s, l);
        d += l;
        s += l;
    }
    free(o);
    if(nl) *nl = n;
    return ret;
}

/**
 * Set one pixel, recolorizing grayscale pixels using a palette
 */
void ui_set(uint32_t *d, uint32_t *s, uint32_t *p)
{
    register uint8_t *a = (uint8_t*)d, *b = (uint8_t*)s;
    uint32_t color;

    if(p) {
        b = (uint8_t*)&color;
        switch(*s & 0xffffff) {
            case 0xf0f0f0: color = (*s & 0xff000000) | (p[0] & 0xffffff); break;
            case 0xd0d0d0: color = (*s & 0xff000000) | (p[1] & 0xffffff); break;
            case 0xb0b0b0: color = (*s & 0xff000000) | (p[2] & 0xffffff); break;
            case 0x909090: color = (*s & 0xff000000) | (p[3] & 0xffffff); break;
            case 0x707070: color = (*s & 0xff000000) | (p[4] & 0xffffff); break;
            case 0x505050: color = (*s & 0xff000000) | (p[5] & 0xffffff); break;
            case 0x303030: color = (*s & 0xff000000) | (p[6] & 0xffffff); break;
            case 0x101010: color = (*s & 0xff000000) | (p[7] & 0xffffff); break;
            default: color = *s; break;
        }
    }

    if(!a[3])
        *d = *((uint32_t*)b);
    else {
        if(b[3] > a[3]) a[3] = b[3];
        a[2] = (b[2]*b[3] + (256 - b[3])*a[2]) >> 8;
        a[1] = (b[1]*b[3] + (256 - b[3])*a[1]) >> 8;
        a[0] = (b[0]*b[3] + (256 - b[3])*a[0]) >> 8;
    }
}

/**
 * Blit with alpha blending
 */
void ui_blit(uint32_t *dst, int dw, int dh, int dp, int x, int y, uint32_t *src, int sx, int sy, int sw, int sh, int sp, int g)
{
    int i, j, c;
    uint8_t *s = (uint8_t*)src, *d = (uint8_t*)dst, *a, *b;

    if(sw < 1 || sh < 1 || dw < 1 || dh < 1 || !src || !dst) return;

    s += sp * sy + sx * 4; d += dp * y + x * 4;
    for(j = 0; j < dh; j++, d += dp) {
        for(i = 0, a = d; i < dw; i++, a += 4) {
            b = s + (j % sh) * sp + (i % sw) * 4;
            if(g & 1) {
                c = (b[0] + b[1] + b[2]) / 3 * (b[3] >> 1);
                a[2] = (c + (256 - (b[3] >> 1))*a[2]) >> 8;
                a[1] = (c + (256 - (b[3] >> 1))*a[1]) >> 8;
                a[0] = (c + (256 - (b[3] >> 1))*a[0]) >> 8;
            } else {
                a[2] = (b[2]*b[3] + (256 - b[3])*a[2]) >> 8;
                a[1] = (b[1]*b[3] + (256 - b[3])*a[1]) >> 8;
                a[0] = (b[0]*b[3] + (256 - b[3])*a[0]) >> 8;
            }
            if(g & 2) a[3] = b[3];
        }
    }
}

/**
 * Make a window out of sprites
 * w, h: requested dimensions (may not be)
 * tabs: trname array of tab texts
 * active: active tab
 * title: if tabs is NULL, then a string as a single active tab
 * border: returned border sizes
 * tabx: returned tab x positions
 * am: align width as multiple of am
 * aa: align width as multiple, with aa additional space
 */
SDL_Texture *ui_window(int w, int h, int *tabs, int active, char *title, SDL_Rect *border, int *tabx, int am, int aa)
{
    world_sprite_t *spr, *spr2, *spr3;
    SDL_Texture *ret;
    int i, j = 0, p, W, H, x, a, b, tw[16] = { 0 }, tb[16] = { 0 };
    uint32_t *dst;
    ssfn_t *font;
    ssfn_buf_t buf = { 0 };
    char *str;

    if(w < 1 || h < 1) return NULL;
    if(tabs) {
        /* multiple tabs with translated text indeces */
        for(i = 0; i < 16 && tabs[i]; i++) {
            if(i == active) {
                spr = spr_get(world.uispr[E_WINNL].cat, world.uispr[E_WINNL].idx, 0); if(spr) j += spr->w;
                spr = spr_get(world.uispr[E_WINNR].cat, world.uispr[E_WINNR].idx, 0); if(spr) j += spr->w;
                font = font_get(FNT_TABN);
            } else {
                spr = spr_get(world.uispr[E_WINIL].cat, world.uispr[E_WINIL].idx, 0); if(spr) j += spr->w;
                spr = spr_get(world.uispr[E_WINIR].cat, world.uispr[E_WINIR].idx, 0); if(spr) j += spr->w;
                font = font_get(FNT_TABI);
            }
            if(world.text && world.text[tabs[i]] && ssfn_bbox(font, world.text[tabs[i]], &tw[i], &a, &x, &tb[i]) == SSFN_OK)
                j += tw[i];
        }
        a = j;
        spr = spr_get(world.uispr[E_WINTL].cat, world.uispr[E_WINTL].idx, 0); if(spr) a += spr->w;
        spr = spr_get(world.uispr[E_WINTL].cat, world.uispr[E_WINTR].idx, 0); if(spr) a += spr->w;
        if(a > w + ui_wl + ui_wr) w = a - ui_wl - ui_wr;
    } else
    if(title) {
        /* single active tab with a string */
        spr = spr_get(world.uispr[E_WINNL].cat, world.uispr[E_WINNL].idx, 0); if(spr) j += spr->w;
        spr = spr_get(world.uispr[E_WINNR].cat, world.uispr[E_WINNR].idx, 0); if(spr) j += spr->w;
        font = font_get(FNT_TABN);
        if(ssfn_bbox(font, title, &tw[0], &a, &x, &tb[0]) == SSFN_OK)
            j += tw[0];
        a = j;
        spr = spr_get(world.uispr[E_WINTL].cat, world.uispr[E_WINTL].idx, 0); if(spr) a += spr->w;
        spr = spr_get(world.uispr[E_WINTL].cat, world.uispr[E_WINTR].idx, 0); if(spr) a += spr->w;
        if(a > w + ui_wl + ui_wr) w = a - ui_wl - ui_wr;
    }
    /* align width */
    if(am > 0)
        w = ui_wl + ((w - ui_wl - ui_wr - aa + am - 1) / am) * am + aa + ui_wr;
    W = ui_wl + w + ui_wr; H = ui_wt + h + ui_wb;
    if(border) { border->x = ui_wl; border->y = ui_wt; border->w = W; border->h = H; }
    ret = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, W, H);
    if(ret) {
        SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
        SDL_LockTexture(ret, NULL, (void**)&dst, &p);
        x = 0;
        spr = spr_get(world.uispr[E_WINTL].cat, world.uispr[E_WINTL].idx, 0);
        if(spr) { spr_blit(dst, W, H, p, x, ui_wt - spr->h, spr->w, spr->h, spr, 0, NULL); x += spr->w; }
        spr = spr_get(world.uispr[E_WINTBG].cat, world.uispr[E_WINTBG].idx, 0);
        if(spr) { a = (W - j) / 2 - x; spr_blit(dst, W, H, p, x, ui_wt - spr->h, a, spr->h, spr, 0, NULL); x += a; }
        if(tabs) {
            buf.w = W; buf.h = H; buf.p = p; buf.ptr = (uint8_t*)dst;
            for(i = 0; i < 16 && tabs[i]; i++) {
                if(i == active) {
                    spr = spr_get(world.uispr[E_WINNL].cat, world.uispr[E_WINNL].idx, 0);
                    spr2 = spr_get(world.uispr[E_WINNBG].cat, world.uispr[E_WINNBG].idx, 0);
                    spr3 = spr_get(world.uispr[E_WINNR].cat, world.uispr[E_WINNR].idx, 0);
                    font = font_get(FNT_TABN);
                    buf.fg = world.colors[COLOR_TABN];
                } else {
                    spr = spr_get(world.uispr[E_WINIL].cat, world.uispr[E_WINIL].idx, 0);
                    spr2 = spr_get(world.uispr[E_WINIBG].cat, world.uispr[E_WINIBG].idx, 0);
                    spr3 = spr_get(world.uispr[E_WINIR].cat, world.uispr[E_WINIR].idx, 0);
                    font = font_get(FNT_TABI);
                    buf.fg = world.colors[COLOR_TABI];
                }
                if(tabx) tabx[i] = x;
                if(spr) { spr_blit(dst, W, H, p, x, ui_wt - spr->h, spr->w, spr->h, spr, 0, NULL); x += spr->w; }
                if(tw[i] > 0) {
                    if(spr2) spr_blit(dst, W, H, p, x, ui_wt - spr2->h, tw[i], spr2->h, spr2, 0, NULL);
                    buf.x = x; buf.y = tb[i] + world.padtop[i == active ? PAD_TABN : PAD_TABI];
                    str = world.text[tabs[i]];
                    if(font) font->style |= SSFN_STYLE_A;
                    while(*str && ((b = ssfn_render(font, &buf, str)) > 0 || b == SSFN_ERR_NOGLYPH)) str += (b < 1 ? 1 : b);
                    x += tw[i];
                }
                if(spr3) { spr_blit(dst, W, H, p, x, ui_wt - spr3->h, spr3->w, spr3->h, spr3, 0, NULL); x += spr3->w; }
            }
            if(tabx) tabx[i] = x;
        } else
        if(title) {
            buf.w = W; buf.h = H; buf.p = p; buf.ptr = (uint8_t*)dst;
            spr = spr_get(world.uispr[E_WINNL].cat, world.uispr[E_WINNL].idx, 0);
            spr2 = spr_get(world.uispr[E_WINNBG].cat, world.uispr[E_WINNBG].idx, 0);
            spr3 = spr_get(world.uispr[E_WINNR].cat, world.uispr[E_WINNR].idx, 0);
            font = font_get(FNT_TABN);
            buf.fg = world.colors[COLOR_TABN];
            if(tabx) tabx[0] = x;
            if(spr) { spr_blit(dst, W, H, p, x, ui_wt - spr->h, spr->w, spr->h, spr, 0, NULL); x += spr->w; }
            if(tw[0] > 0) {
                if(spr2) spr_blit(dst, W, H, p, x, ui_wt - spr2->h, tw[0], spr2->h, spr2, 0, NULL);
                buf.x = x; buf.y = tb[0] + world.padtop[PAD_TABN];
                str = title;
                if(font) font->style |= SSFN_STYLE_A;
                while(*str && ((b = ssfn_render(font, &buf, str)) > 0 || b == SSFN_ERR_NOGLYPH)) str += (b < 1 ? 1 : b);
                x += tw[0];
            }
            if(spr3) { spr_blit(dst, W, H, p, x, ui_wt - spr3->h, spr3->w, spr3->h, spr3, 0, NULL); x += spr3->w; }
            if(tabx) tabx[1] = x;
        }
        spr = spr_get(world.uispr[E_WINTBG].cat, world.uispr[E_WINTBG].idx, 0);
        spr2 = spr_get(world.uispr[E_WINTR].cat, world.uispr[E_WINTR].idx, 0);
        if(spr) { a = W - x - (spr2 ? spr2->w : 0); spr_blit(dst, W, H, p, x, ui_wt - spr->h, a, spr->h, spr, 0, NULL); x += a; }
        if(spr2) { spr_blit(dst, W, H, p, x, ui_wt - spr2->h, spr2->w, spr2->h, spr2, 0, NULL); x += spr2->w; }

        spr = spr_get(world.uispr[E_WINML].cat, world.uispr[E_WINML].idx, 0);
        if(spr) spr_blit(dst, W, H, p, 0, ui_wt, spr->w, H - ui_wt - ui_wb, spr, 0, NULL);
        spr = spr_get(world.uispr[E_WINBG].cat, world.uispr[E_WINBG].idx, 0);
        if(spr) spr_blit(dst, W, H, p, ui_wl, ui_wt, W - ui_wl - ui_wr, H - ui_wt - ui_wb, spr, 0, NULL);
        spr = spr_get(world.uispr[E_WINMR].cat, world.uispr[E_WINMR].idx, 0);
        if(spr) spr_blit(dst, W, H, p, W - spr->w, ui_wt, spr->w, H - ui_wt - ui_wb, spr, 0, NULL);

        spr = spr_get(world.uispr[E_WINBL].cat, world.uispr[E_WINBL].idx, 0);
        spr2 = spr_get(world.uispr[E_WINBBG].cat, world.uispr[E_WINBBG].idx, 0);
        spr3 = spr_get(world.uispr[E_WINBR].cat, world.uispr[E_WINBR].idx, 0);
        if(spr) spr_blit(dst, W, H, p, 0, H - ui_wb, spr->w, spr->h, spr, 0, NULL);
        if(spr2) spr_blit(dst, W, H, p, spr ? spr->w : 0, H - ui_wb, W - (spr ? spr->w : 0) - (spr3 ? spr3->w : 0), spr2->h, spr2, 0, NULL);
        if(spr3) spr_blit(dst, W, H, p, W - spr3->w, H - ui_wb, spr3->w, spr3->h, spr3, 0, NULL);

        SDL_UnlockTexture(ret);
    }
    return ret;
}

/**
 * Make a dialog window out of sprites
 */
SDL_Texture *ui_dlgwin(int w, int h, char *str, SDL_Rect *border)
{
    world_sprite_t *spr, *spr2, *spr3;
    SDL_Texture *ret;
    int j = 0, p, W, H, x, a, b, d;
    uint32_t *dst;
    ssfn_t *font;
    ssfn_buf_t buf = { 0 };

    if(w < 1 || h < 1) return NULL;
    if(str) {
        spr = spr_get(world.uispr[E_DLGNL].cat, world.uispr[E_DLGNL].idx, 0); if(spr) j += spr->w;
        spr = spr_get(world.uispr[E_DLGNR].cat, world.uispr[E_DLGNR].idx, 0); if(spr) j += spr->w;
        font = font_get(FNT_DLGT);
        if(*str && ssfn_bbox(font, str, &b, &a, &x, &d) == SSFN_OK)
            j += b;
        spr = spr_get(world.uispr[E_DLGTL].cat, world.uispr[E_DLGTL].idx, 0); if(spr) j += spr->w;
        spr = spr_get(world.uispr[E_DLGTR].cat, world.uispr[E_DLGTR].idx, 0); if(spr) j += spr->w;
        if(j > w + ui_dl + ui_dr) w = j - ui_dl - ui_dr;
    }
    W = ui_dl + w + ui_dr; H = ui_dt + h + ui_db;
    if(border) { border->x = ui_dl; border->y = ui_dt; border->w = W; border->h = H; }
    ret = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, W, H);
    if(ret) {
        SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
        SDL_LockTexture(ret, NULL, (void**)&dst, &p);
        x = 0;
        spr = spr_get(world.uispr[E_DLGTL].cat, world.uispr[E_DLGTL].idx, 0);
        if(spr) { spr_blit(dst, W, H, p, x, ui_dt - spr->h, spr->w, spr->h, spr, 0, NULL); x += spr->w; }
        if(str) {
            buf.w = W; buf.h = H; buf.p = p; buf.ptr = (uint8_t*)dst;
            spr = spr_get(world.uispr[E_DLGNL].cat, world.uispr[E_DLGNL].idx, 0);
            spr2 = spr_get(world.uispr[E_DLGNBG].cat, world.uispr[E_DLGNBG].idx, 0);
            spr3 = spr_get(world.uispr[E_DLGNR].cat, world.uispr[E_DLGNR].idx, 0);
            font = font_get(FNT_DLGT);
            buf.fg = world.colors[COLOR_DLGT];
            if(spr) { spr_blit(dst, W, H, p, x, ui_dt - spr->h, spr->w, spr->h, spr, 0, NULL); x += spr->w; }
            if(b > 0) {
                if(spr2) spr_blit(dst, W, H, p, x, ui_dt - spr2->h, b, spr2->h, spr2, 0, NULL);
                buf.x = x; buf.y = d + world.padtop[PAD_DLGT];
                if(font) font->style |= SSFN_STYLE_A;
                while(*str && ((a = ssfn_render(font, &buf, str)) > 0 || a == SSFN_ERR_NOGLYPH)) str += (a < 1 ? 1 : a);
                x += b;
            }
            if(spr3) { spr_blit(dst, W, H, p, x, ui_dt - spr3->h, spr3->w, spr3->h, spr3, 0, NULL); x += spr3->w; }
        }
        spr = spr_get(world.uispr[E_DLGTBG].cat, world.uispr[E_DLGTBG].idx, 0);
        spr2 = spr_get(world.uispr[E_DLGTR].cat, world.uispr[E_DLGTR].idx, 0);
        if(spr) { a = W - x - (spr2 ? spr2->w : 0); spr_blit(dst, W, H, p, x, ui_dt - spr->h, a, spr->h, spr, 0, NULL); x += a; }
        if(spr2) { spr_blit(dst, W, H, p, x, ui_dt - spr2->h, spr2->w, spr2->h, spr2, 0, NULL); x += spr2->w; }

        spr = spr_get(world.uispr[E_DLGML].cat, world.uispr[E_DLGML].idx, 0);
        if(spr) spr_blit(dst, W, H, p, 0, ui_dt, spr->w, H - ui_dt - ui_db, spr, 0, NULL);
        spr = spr_get(world.uispr[E_DLGBG].cat, world.uispr[E_DLGBG].idx, 0);
        if(spr) spr_blit(dst, W, H, p, ui_dl, ui_dt, W - ui_dl - ui_dr, H - ui_dt - ui_db, spr, 0, NULL);
        spr = spr_get(world.uispr[E_DLGMR].cat, world.uispr[E_DLGMR].idx, 0);
        if(spr) spr_blit(dst, W, H, p, W - spr->w, ui_dt, spr->w, H - ui_dt - ui_db, spr, 0, NULL);

        spr = spr_get(world.uispr[E_DLGBL].cat, world.uispr[E_DLGBL].idx, 0);
        spr2 = spr_get(world.uispr[E_DLGBBG].cat, world.uispr[E_DLGBBG].idx, 0);
        spr3 = spr_get(world.uispr[E_DLGBR].cat, world.uispr[E_DLGBR].idx, 0);
        if(spr) spr_blit(dst, W, H, p, 0, H - ui_db, spr->w, spr->h, spr, 0, NULL);
        if(spr2) spr_blit(dst, W, H, p, spr ? spr->w : 0, H - ui_db, W - (spr ? spr->w : 0) - (spr3 ? spr3->w : 0), spr2->h, spr2, 0, NULL);
        if(spr3) spr_blit(dst, W, H, p, W - spr3->w, H - ui_db, spr3->w, spr3->h, spr3, 0, NULL);

        SDL_UnlockTexture(ret);
    }
    return ret;
}

/**
 * Draw a box with a key
 */
void ui_keybox(uint32_t *dst, int p, int x, int y, int w, int h, char *key, int sel, int pressed)
{
    int i, j, p1, p2, p3;
    uint32_t c = sel == 2 ? 0xff002000 : (sel ? 0xff200000 : (pressed == -1 ? 0xff0f0f0f : 0xff040404)), P, code;

    x += 4; y += 4; w -= 8; h -= 8;
    if(w < 1 || h < 1 || !key) return;
    code = ssfn_utf8(&key);
    P = p/4; p1 = y * P + x; p2 = (y + h - 1) * P + x;
    for(i=0; i < w; i++) {
        if(i && i < w - 1) dst[p1 + i - P] = dst[p2 + i + P] = c;
        dst[p1 + i] = dst[p2 + i] = c;
    }
    p1 += P;
    for(j=1, p3 = p1; j+1 < h; j++, p3 += P) {
        dst[p3 - 1] = dst[p3 + w] =  dst[p3] = dst[p3 + w - 1] = c;
    }
    for(j=1; j+1 < h; j++, p1 += P)
        for(i=1; i + 1 < w; i++)
            dst[p1 + i] = c;
    if(sel != 2 && pressed != -1) {
        ssfn_dst.fg = 0xff010101;
        ssfn_dst.x = x + (w - (int)ws[code]) / 2 + 1;
        ssfn_dst.y = y + h / 2 - 7;
        ssfn_putc(code);
    }
    ssfn_dst.fg = sel ? 0xffffffff : (pressed != -1 ? 0xffa0a0a0 : 0xff040404);
    ssfn_dst.x = x + (w - (int)ws[code]) / 2;
    ssfn_dst.y = y + h / 2 - 8 + sel * (pressed == 1 ? 1 : 0);
    ssfn_putc(code);
}

/**
 * Display a number string with extra small glyphs (4x5)
 */
void ui_small(uint32_t *dst, int dw, int dh, int dp, int x, int y, uint32_t c, char *str)
{
    int i, j, k, l, d = 0, p;
    uint32_t n[] = {0x4aaa4,0x4c444,0xc248e,0xc2c2c,0x24ae2,0xe8c2c,0x68eac,0xe2444,0x4a4a4,0xea62c,0x04040,0xc2404};

    if(!dst) return;
    dp /= 4; p = dp * y;
    for(; *str && x < dw; str++, x += 4) {
        if(*str == ' ') continue;
        d = *str == '?' ? 11 : (*str == ':' ? 10 : *str - '0');
        for(k = 1<<19, j = l = 0; j < 5 && y + j < dh; j++, l += dp)
            for(i = 0; i < 4 && x + i < dw; i++, k >>= 1)
                if((n[d] & k)) dst[p + l + x + i] = c;
    }
}

/**
 * Draw text
 */
void ui_text(uint32_t *dst, int dw, int dh, int dp, int x, int y, ssfn_t *font, uint32_t color, char *str)
{
    ssfn_buf_t buf = { 0 };
    int k;

    if(!dst || !str || !*str) return;
    buf.w = dw; buf.h = dh; buf.p = dp; buf.ptr = (uint8_t*)dst; buf.fg = color; buf.x = x; buf.y = y;
    while(*str) {
        if(*str == '\n') {
            buf.x = x; buf.y += font->size;
            str++;
            continue;
        }
        k = ssfn_render(font, &buf, str);
        if(k > 0 || k == SSFN_ERR_NOGLYPH) str += (k < 1 ? 1 : k); else break;
    }
}

/**
 * Draw a line
 */
void ui_line(uint32_t *dst, int dw, int dh, int dp, int x0, int y0, int x1, int y1, uint32_t color)
{
    int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
    int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
    int err = dx+dy, e2;

    dp /= 4;
    for (;;){
        if(y0 < dh && x0 < dw)
            dst[y0 * dp + x0] = color;
        e2 = 2*err;
        if (e2 >= dy) { if (x0 == x1) { break; } err += dy; x0 += sx; }
        if (e2 <= dx) { if (y0 == y1) { break; } err += dx; y0 += sy; }
    }
}

/**
 * Draw a filled rectangle
 */
void ui_tri(uint32_t *dst, int dw, int dh, int dp, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color)
{
    uint8_t *A, *B = (uint8_t*)&color;
    int i, j, t, h, s, xa, ya, xb, yb;
    float a, b;

    if((y0 == y1 && y0 == y2) || (x0 == x1 && x0 == x2)) return;
    if(y0 > y1) { i = x0; x0 = x1; x1 = i; i = y0; y0 = y1; y1 = i; }
    if(y0 > y2) { i = x0; x0 = x2; x2 = i; i = y0; y0 = y2; y2 = i; }
    if(y1 > y2) { i = x1; x1 = x2; x2 = i; i = y1; y1 = y2; y2 = i; }
    t = y2 - y0; dp /= 4;
    for(i = 0; i < t; i++) {
        h = i > y1 - y0 || y1 == y0; s = h ? y2 - y1 : y1 - y0;
        a = (float)i / (float)t; b = ((float)i - (float)(h ? y1 - y0 : 0)) / (float)s;
        xa = (1.0 - a) * x0 + x2 * a; ya = (1.0 - a) * y0 + y2 * a;
        xb = h ? (1.0 - b) * x1 + x2 * b : (1.0 - b) * x0 + x1 * b;
        yb = h ? (1.0 - b) * y1 + y2 * b : (1.0 - b) * y0 + y1 * b;
        if(xa > xb) { j = xa; xa = xb; xb = j; j = ya; ya = yb; yb = j; }
        for(j = xa; j <= xb; j++)
            if(y0 + i < dh && j < dw) {
                A = (uint8_t*)&dst[(y0 + i) * dp + j];
                if(!A[3])
                    *((uint32_t*)A) = color;
                else {
                    if(B[3] > A[3]) A[3] = B[3];
                    A[2] = (B[2]*B[3] + (256 - B[3])*A[2]) >> 8;
                    A[1] = (B[1]*B[3] + (256 - B[3])*A[1]) >> 8;
                    A[0] = (B[0]*B[3] + (256 - B[3])*A[0]) >> 8;
                }
            }
    }
}

/**
 * Draw a filled box
 */
void ui_box(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int h, uint32_t l, uint32_t b, uint32_t d)
{
    int i, j, p, p2;

    if(w < 1 || h < 1 || x < 0 || y < 0 || x + w >= dw || y + h >= dh || !dst) return;

    p = y * dp/4 + x;
    p2 = (y + h - 1) * dp/4 + x;
    for(i=0; i + 1 < w; i++) {
        dst[p + i] = l;
        dst[p2 + i + 1] = d;
    }
    p += dp/4;
    for(j=1; j + 1 < h; j++, p += dp/4) {
        dst[p] = l;
        dst[p + w - 1] = d;
        for(i = 1; i + 1 < w; i++)
            dst[p + i] = b;
    }
}

/**
 * Draw a button
 */
void ui_button(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int type, char *str)
{
    world_sprite_t *spr, *spr2;
    ssfn_t *font;
    int i = type * 3, j = x, k, b = w;

    if(!dst) return;

    spr = spr_get(world.uispr[E_BTNNL + i].cat, world.uispr[E_BTNNL + i].idx, 0);
    if(spr) { spr_blit(dst, dw, dh, dp, x + ui_bl - spr->w, y + (ui_btnh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL); j += ui_bl; b -= ui_bl; }
    spr = spr_get(world.uispr[E_BTNNBG + i].cat, world.uispr[E_BTNNBG + i].idx, 0);
    spr2 = spr_get(world.uispr[E_BTNNR + i].cat, world.uispr[E_BTNNR + i].idx, 0);
    if(spr) { k = b - (spr2 ? ui_br : 0); spr_blit(dst, dw, dh, dp, j, y + (ui_btnh - spr->h) / 2, k, spr->h, spr, 0, NULL); j += k; }
    if(spr2) spr_blit(dst, dw, dh, dp, x + w - ui_br, y + (ui_btnh - spr2->h) / 2, spr2->w, spr2->h, spr2, 0, NULL);
    font = font_get(FNT_BTN);
    if(font) font->style |= SSFN_STYLE_A;
    if(str && *str && ssfn_bbox(font, str, &i, &j, &k, &b) == SSFN_OK)
        ui_text(dst, dw, dh, dp, x + (w - i) / 2, y + (ui_btnh - world.fonts[FNT_BTN].size) / 2 + b + world.padtop[PAD_BTNT] +
            (type == 2 ? 1 : 0), font, world.colors[COLOR_BTNN + type], str);
    if(font) font->style &= ~SSFN_STYLE_A;
}

/**
 * Draw a check box
 */
void ui_chkbox(uint32_t *dst, int dw, int dh, int dp, int x, int y, int checked)
{
    world_sprite_t *spr;
    if(!dst) return;
    spr = spr_get(world.uispr[E_CHK0 + checked].cat, world.uispr[E_CHK0 + checked].idx, 0);
    if(spr) spr_blit(dst, dw, dh, dp, x, y + (ui_lineh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL);
}

/**
 * Draw a number box
 */
void ui_number(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int type, int val)
{
    world_sprite_t *spr, *spr2;
    ssfn_t *font;
    int i, j = x, k, a, b = w, c, d;
    char tmp[8];

    if(!dst) return;
    sprintf(tmp, "%5d", val);
    spr = spr_get(world.uispr[type & 2 ? E_OPTPREVPRESS : E_OPTPREV].cat, world.uispr[type & 2 ? E_OPTPREVPRESS : E_OPTPREV].idx, 0);
    if(spr) { spr_blit(dst, dw, dh, dp, x + ui_ol - spr->w, y + (ui_lineh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL); j += ui_ol; b -= ui_ol; }
    spr = spr_get(world.uispr[E_OPTBG].cat, world.uispr[E_OPTBG].idx, 0);
    spr2 = spr_get(world.uispr[type & 4 ? E_OPTNEXTPRESS : E_OPTNEXT].cat, world.uispr[type & 4 ? E_OPTNEXTPRESS : E_OPTNEXT].idx, 0);
    if(spr) {
        i = spr->h < ui_lineh ? ui_lineh : spr->h;
        k = b - (spr2 ? ui_or : 0); spr_blit(dst, dw, dh, dp, j, y + (ui_lineh - i) / 2, k, i, spr, 0, NULL);
    }
    if(spr2) spr_blit(dst, dw, dh, dp, x + w - ui_or, y + (ui_lineh - spr2->h) / 2, spr2->w, spr2->h, spr2, 0, NULL);
    k = type & 1 ? FNT_TXT : FNT_SEL;
    font = font_get(k);
    if(ssfn_bbox(font, tmp, &a, &c, &d, &b) == SSFN_OK)
        ui_text(dst, dw, dh, dp, x + w - (spr2 ? spr2->w : 0) - a, y + (ui_lineh - world.fonts[k].size) / 2 + b, font,
            world.colors[COLOR_FG + (type & 1)], tmp);
}

/**
 * Draw an option box
 */
void ui_option(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int type, char **opts, int val)
{
    world_sprite_t *spr, *spr2;
    ssfn_t *font;
    int i, j = x, k, a, b = w, c, d;

    if(!dst) return;
    spr = spr_get(world.uispr[type & 2 ? E_OPTPREVPRESS : E_OPTPREV].cat, world.uispr[type & 2 ? E_OPTPREVPRESS : E_OPTPREV].idx, 0);
    if(spr) { spr_blit(dst, dw, dh, dp, x + ui_ol - spr->w, y + (ui_lineh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL); j += ui_ol; b -= ui_ol; }
    spr = spr_get(world.uispr[E_OPTBG].cat, world.uispr[E_OPTBG].idx, 0);
    spr2 = spr_get(world.uispr[type & 4 ? E_OPTNEXTPRESS : E_OPTNEXT].cat, world.uispr[type & 4 ? E_OPTNEXTPRESS : E_OPTNEXT].idx, 0);
    if(spr) {
        i = spr->h < ui_lineh ? ui_lineh : spr->h;
        k = b - (spr2 ? spr2->w : 0); spr_blit(dst, dw, dh, dp, j, y + (ui_lineh - i) / 2, k, i, spr, 0, NULL);
    }
    if(spr2) spr_blit(dst, dw, dh, dp, x + w - spr2->w, y + (ui_lineh - spr2->h) / 2, spr2->w, spr2->h, spr2, 0, NULL);
    if(opts && val >= 0) {
        for(i = 0; i < 64 && opts[i]; i++);
        if(val >= i) return;
        k = type & 1 ? FNT_SEL : FNT_TXT;
        font = font_get(k);
        if(*opts[val] && ssfn_bbox(font, opts[val], &a, &c, &d, &b) == SSFN_OK)
            ui_text(dst, dw, dh, dp, j + world.padtop[PAD_H], y + (ui_lineh - world.fonts[k].size) / 2 + b, font,
                world.colors[COLOR_FG + (type & 1)], opts[val]);
    }
}

/**
 * Draw a slider
 */
void ui_slider(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int val, int max)
{
    world_sprite_t *spr, *spr2;
    int j = x, k, b = w;

    if(!dst) return;

    spr = spr_get(world.uispr[E_SLDL].cat, world.uispr[E_SLDL].idx, 0);
    if(spr) { spr_blit(dst, dw, dh, dp, x, y + (ui_lineh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL); j += spr->w; b -= spr->w; }
    spr = spr_get(world.uispr[E_SLDBG].cat, world.uispr[E_SLDBG].idx, 0);
    spr2 = spr_get(world.uispr[E_SLDR].cat, world.uispr[E_SLDR].idx, 0);
    if(spr) { k = b - (spr2 ? spr2->w : 0); spr_blit(dst, dw, dh, dp, j, y + (ui_lineh - spr->h) / 2, k, spr->h, spr, 0, NULL); j += k; }
    if(spr2) spr_blit(dst, dw, dh, dp, x + w - spr2->w, y + (ui_lineh - spr2->h) / 2, spr2->w, spr2->h, spr2, 0, NULL);
    spr = spr_get(world.uispr[E_SLDBTN].cat, world.uispr[E_SLDBTN].idx, 0);
    if(spr)
        spr_blit(dst, dw, dh, dp, x + (max > 0 ? (w - spr->w) * val / max : 0), y + (ui_lineh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL);
}

/**
 * Draw a text input box
 */
void ui_textinp(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int type, char *str)
{
    world_sprite_t *spr, *spr2 = NULL;
    ssfn_buf_t buf = { 0 };
    ssfn_t *font;
    int i, j = x, k, l, m, b = w;

    if(!dst) return;

    if(type != 4) {
        spr = spr_get(world.menubtn[MENU_T].l.cat, world.menubtn[MENU_T].l.idx, 0);
        if(spr) { spr_blit(dst, dw, dh, dp, x, y + (ui_btnh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL); j += spr->w; b -= spr->w; }
        spr = spr_get(world.menubtn[MENU_T].m.cat, world.menubtn[MENU_T].m.idx, 0);
        spr2 = spr_get(world.menubtn[MENU_T].r.cat, world.menubtn[MENU_T].r.idx, 0);
        if(spr) { k = b - (spr2 ? spr2->w : 0); spr_blit(dst, dw, dh, dp, j, y + (ui_btnh - spr->h) / 2, k, spr->h, spr, 0, NULL); }
        if(spr2) spr_blit(dst, dw, dh, dp, x + w - spr2->w, y + (ui_btnh - spr2->h) / 2, spr2->w, spr2->h, spr2, 0, NULL);
    }
    font = font_get(FNT_TXT + (type & 1));
    if(font && type != 4) font->style |= SSFN_STYLE_A;
    l = world.fonts[FNT_TXT + (type & 1)].size;
    buf.w = dw; buf.h = dh; buf.p = dp; buf.ptr = (uint8_t*)dst; buf.fg = world.colors[type == 4 ? COLOR_CHAT : COLOR_FG + (type & 1)];
    buf.x = j; m = y + ((type == 4 ? ui_chath : ui_btnh) - l) / 2;
    if(ui_input_str && str == ui_input_cur && !*str && (main_tick / 333) & 1) {
        for(y = 0; y < l; y++)
            dst[(m + y) * dp / 4 + buf.x - 1] = buf.fg;
    }
    if(str && *str && ssfn_bbox(font, str, &i, &l, &k, &b) == SSFN_OK) {
        buf.y = m + b;
        while(*str && buf.x < x + w - (spr2 ? spr2->w : 0)) {
            if(type & 2) {
                ssfn_utf8(&str);
                ssfn_render(font, &buf, "*");
                k = 0;
            } else {
                k = ssfn_render(font, &buf, str);
                str += (k < 1 ? 1 : k);
            }
            if(ui_input_str && str == ui_input_cur && (main_tick / 333) & 1) {
                for(y = 0; y < l; y++)
                    dst[(m + y) * dp / 4 + buf.x - 1] = buf.fg;
            }
            if(k < 0 && k != SSFN_ERR_NOGLYPH) break;
        }
    }
    if(font) font->style &= ~SSFN_STYLE_A;
}

/**
 * Draw a vertical scrollbar
 */
void ui_vscr(uint32_t *dst, int dw, int dh, int dp, int x, int y, int h, int val, int max)
{
    world_sprite_t *spr, *spr2;
    int j = y, b = h;

    if(!dst) return;

    spr = spr_get(world.uispr[E_SCRU].cat, world.uispr[E_SCRU].idx, 0);
    if(spr) { y += ui_vbs; spr_blit(dst, dw, dh, dp, x + (ui_vbw - spr->w) / 2, y - spr->h, spr->w, spr->h, spr, 0, NULL); j = y; b -= ui_vbs; }
    spr = spr_get(world.uispr[E_SCRVBG].cat, world.uispr[E_SCRVBG].idx, 0);
    spr2 = spr_get(world.uispr[E_SCRD].cat, world.uispr[E_SCRD].idx, 0);
    if(spr) { b -= (spr2 ? ui_vbs : 0); spr_blit(dst, dw, dh, dp, x + (ui_vbw - spr->w) / 2, j, spr->w, b, spr, 0, NULL); j += b; }
    if(spr2) { spr_blit(dst, dw, dh, dp, x + (ui_vbw - spr2->w) / 2, j, spr2->w, spr2->h, spr2, 0, NULL); }
    spr = spr_get(world.uispr[E_SCRBTN].cat, world.uispr[E_SCRBTN].idx, 0);
    if(spr)
        spr_blit(dst, dw, dh, dp, x + (ui_vbw - spr->w) / 2, y + (max > 0 ? (b - spr->h) * val / max : 0), spr->w, spr->h, spr, 0, NULL);
}

/**
 * Draw a horizontal scrollbar
 */
void ui_hscr(uint32_t *dst, int dw, int dh, int dp, int x, int y, int w, int val, int max)
{
    world_sprite_t *spr, *spr2;
    int j = x, b = w;

    if(!dst) return;

    spr = spr_get(world.uispr[E_SCRL].cat, world.uispr[E_SCRL].idx, 0);
    if(spr) { x += ui_hbs; spr_blit(dst, dw, dh, dp, x - spr->w, y + (ui_hbh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL); j = x; b -= ui_hbs; }
    spr = spr_get(world.uispr[E_SCRHBG].cat, world.uispr[E_SCRHBG].idx, 0);
    spr2 = spr_get(world.uispr[E_SCRR].cat, world.uispr[E_SCRR].idx, 0);
    if(spr) { b -= (spr2 ? ui_hbs : 0); spr_blit(dst, dw, dh, dp, j, y + (ui_hbh - spr->h) / 2, spr->w, b, spr, 0, NULL); j += b; }
    if(spr2) { spr_blit(dst, dw, dh, dp, j, y + (ui_hbh - spr2->h) / 2, spr2->w, spr2->h, spr2, 0, NULL); }
    spr = spr_get(world.uispr[E_SCRBTN].cat, world.uispr[E_SCRBTN].idx, 0);
    if(spr)
        spr_blit(dst, dw, dh, dp, x + (max > 0 ? (b - spr->w) * val / max : 0), y + (ui_hbh - spr->h) / 2, spr->w, spr->h, spr, 0, NULL);
}
