#include <stdio.h>
#include <string.h>

#ifdef __UNIXSDL__
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#endif

#include "../c_init.h"
#include "../c_intrf.h"
#include "../cpu/regs.h"
#include "../gblhdr.h"
#include "../ui.h"
#include "../ver.h"
#include "../zmovie.h"
#include "../zpath.h"
#include "../zstate.h"
#include "../ztimec.h"
#include "c_gui.h"
#include "c_guiwindp.h"
#include "cfg.h"
#include "gui.h"
#include "guicombo.h"
#include "guifuncs.h"
#include "guikeys.h"
#include "guitools.h"
#include "guiwindp.h"
#include "input.h"

#if defined __UNIXSDL__ && defined __OPENGL__
#include "../unix/gl_draw.h"
#endif

char CMovieExt = 'v';
char GUICheatTextZ1[16] = "_";
char GUICheatTextZ2[23] = "_";
char GUIChoseSaveText2[] = "-";
char GUIChoseSlotTextX[] = "-";
char GUIComboTextH[21];
char GUILoadTextA[38];
u1 GUIFreshInputSelect = 1;
u1 NetplayUDPConfig = 1;
u1 GUILoadPos;
u1 GUIStatesText5 = 0;
u1 GUIWincoladd;
u1* const GUIInputRefP[] = { &pl1contrl, &pl2contrl, &pl3contrl, &pl4contrl, &pl5contrl };
u4 GUIIStA[3];
u4 GUILStA[3];
u4 GUILStB[3];
u4 GUIVStA[3];
u4 GUIcurrentinputcursloc;
u4 GUIcurrentinputviewloc;
u4 GUIcurrentvideocursloc;
u4 GUIcurrentvideoviewloc;

static s4 cloadnleft;
static s4 cloadnposb;
static u4 CSStartEntry;
static u4 GUIWincol;

static void drawshadow2(u4 const p1, s4 const p2, s4 const p3)
{
    s4 y = GUIwinposy[p1];
    if (y > 223)
        return;
    if (y < 0)
        y = 0;
    s4 ebx = y + p3 + 9;
    if (ebx < 0)
        return;
    if (ebx > 223)
        ebx = 223;
    ebx = ebx - y + 1;

    s4 x = GUIwinposx[p1];
    s4 ecx = x;
    if (x > 255)
        return;
    if (x < -3)
        x = -3;
    ecx += p2;
    if (ecx < 0)
        return;
    if (ecx > 255)
        ecx = 255;
    ecx = ecx - x + 1;

    u1* const edi = vidbuffer + (y + 3) * 288 + (x + 3) + 16;
    GUIDrawShadow2(edi, ecx, ebx);
}

static void GUIRect(s4 const x1, s4 const x2, s4 y, u4 h, u1 const colour)
{
    do
        GUIHLine(x1, x2, y++, colour);
    while (--h != 0);
}

static s4 DrawTitleBar(s4 const x1, s4 const x2, s4 ebx)
{
    GUIHLine(x1, x2, ebx++, 46 + 157 + 6 - GUIWincoladd);

    {
        u4 edx = 42 + 157 + 4 + 4 - GUIWincoladd;
        u4 n = 8;
        do
            GUIHLine(x1, x2, ebx++, edx--);
        while (--n != 0);
    }

    GUIHLine(x1, x2, ebx++, 38 + 157 + 4 - GUIWincoladd);

    GUIRect(x1, x1, ebx - 10, 9, 44 + 157 + 4 - GUIWincoladd);
    GUIRect(x2, x2, ebx - 9, 9, 40 + 157 + 4 - GUIWincoladd);

    return ebx;
}

static void GUIDrawTArea(u4 const id, u4* const peax, u4* const pebx) // win #id
{
    u4 const edx = GUIWincol + 1;
    s4 const eax = GUIwinposx[id];
    s4 const ebx = GUIwinposy[id] + 10;
    s4 const ecx = eax + GUIwinsizex[id];
    GUIRect(eax, ecx, ebx, 12, edx);
    GUIHLine(eax + 1, ecx, ebx + 12, edx + 3);
    *peax = eax; // set eax to minX
    *pebx = ebx; // set ebx to minY
}

static void GUIDrawWindowBox(u4 const p1, char const* const p2)
{
    switch (cwindrawn) {
    case 0:
        GUIWincoladd = 0;
        GUIWincol = 148;
        break;
    case 1:
        GUIWincoladd = 4;
        GUIWincol = 148 + 5;
        break;
    default:
        GUIWincoladd = 4;
        GUIWincol = 148 + 10;
        break;
    }

    drawshadow2(p1, GUIwinsizex[p1], GUIwinsizey[p1]);

    {
        s4 const eax = GUIwinposx[p1];
        s4 ebx = GUIwinposy[p1];
        s4 const ecx = eax + GUIwinsizex[p1];
        ebx = DrawTitleBar(eax, ecx, ebx);

        u4 const esi = GUIwinsizey[p1] - 1;
        GUIRect(eax, ecx, ebx, esi, GUIWincol + 2);

        GUIHLine(eax, ecx, ebx + esi, GUIWincol);
    }

    {
        s4 const eax = GUIwinposx[p1];
        s4 const ebx = GUIwinposy[p1] + 10;
        u4 const esi = GUIwinsizey[p1] - 1;
        GUIRect(eax, eax, ebx, esi, GUIWincol + 3);
    }

    {
        s4 const eax = GUIwinposx[p1] + GUIwinsizex[p1];
        s4 const ebx = GUIwinposy[p1] + 10;
        u4 const esi = GUIwinsizey[p1];
        GUIRect(eax, eax, ebx, esi, GUIWincol + 1);
    }

    {
        s4 const ebx = GUIwinposy[p1] + 3;
        s4 const edx = GUIwinposx[p1] + 3;
        GUIOuttextwin(edx, ebx, p2, 184);
    }

    {
        s4 const ebx = GUIwinposy[p1] + 2;
        s4 const edx = GUIwinposx[p1] + 2;
        GUIOuttextwin(edx, ebx, p2, GUIWincoladd == 0 ? 220 : 214);
    }

    {
        s4 const eax = GUIwinposx[p1] + GUIwinsizex[p1] - 10;
        s4 const ebx = GUIwinposy[p1];
        static u1 const GUIIconDataClose[] = {
            /**/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            /**/ 0, 216, 216, 216, 216, 216, 216, 216, 0, 0,
            /**/ 214, 212, 202, 212, 212, 212, 202, 212, 210, 0,
            /**/ 214, 212, 212, 200, 212, 200, 212, 212, 210, 202,
            /**/ 214, 212, 212, 212, 198, 212, 212, 212, 210, 202,
            /**/ 214, 212, 212, 196, 212, 196, 212, 212, 210, 200,
            /**/ 214, 212, 194, 212, 212, 212, 194, 212, 210, 200,
            /**/ 0, 208, 208, 208, 208, 208, 208, 208, 198, 198,
            /**/ 0, 0, 198, 198, 198, 198, 198, 198, 198, 0,
            /**/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        };
        GUIoutputiconwin(eax, ebx, GUIIconDataClose);
    }
}

static void DrawTabOn(u4 const* const p1, u4* const peax, u4 ebx, u4* const pebp) // p1 = array, ebp = offset, eax = minX, ebx = minY
{
    u4 eax = *peax;
    u4 ebp = *pebp;

    char const* esi = (char const*)p1; // XXX ugly cast
    u4 ecx = 8 + eax;
    while (esi[ebp] == '\0')
        ++ebp;
    while (esi[ebp] != '\0')
        ++esi, ecx += 6;

    u4 const edx = GUIWincol;
    GUIHLine(eax + 1, ecx, ebx++, edx + 4);

    GUIRect(eax + 1, ecx, ebx, 12, edx + 2);
    GUIRect(eax, eax, ebx, 11, edx + 3);

    char const* const label = (char const*)p1 + ebp; // XXX ugly cast
    GUIOuttextwin(eax + 6, ebx + 4, label, GUIWincol);
    GUIOuttextwin(eax + 5, ebx + 3, label, GUIWincoladd == 0 ? 163 : 164);

    eax = ecx + 1; // restore and set Xoff for drawing step
    GUIRect(eax, eax, ebx, 12, edx + 3);

    {
        char const* const esi = (char const*)p1; // XXX ugly cast
        while (esi[ebp] != '\0')
            ++ebp;
    }

    *peax = eax + 1;
    *pebp = ebp;
}

static void DrawTabOff(u4 const* const p1, u4* const peax, u4 ebx, u4* const pebp) // p1 = array, ebp = offset, eax = minX, ebx = minY
{
    u4 eax = *peax;
    u4 ebp = *pebp;

    char const* esi = (char const*)p1; // XXX ugly cast
    u4 ecx = 8 + eax;
    while (esi[ebp] == '\0')
        ++ebp;
    while (esi[ebp] != '\0')
        ++esi, ecx += 6;

    u4 const edx = GUIWincol;
    ++ebx;
    GUIHLine(eax + 1, ecx, ebx++, edx + 3);

    GUIRect(eax, eax, ebx, 10, edx + 2);

    char const* const label = (char const*)p1 + ebp; // XXX ugly cast
    u1 const colour = GUIWincoladd == 0 ? 202 : 196;
    GUIOuttextwin(eax + 6, ebx + 4, label, colour);
    GUIOuttextwin(eax + 5, ebx + 3, label, colour + 15);

    eax = ecx + 1;
    GUIRect(eax, eax, ebx, 10, edx);

    {
        char const* const esi = (char const*)p1;
        while (esi[ebp] != '\0')
            ++ebp;
    }

    *peax = eax + 1;
    *pebp = ebp;
}

static void GUIDrawTabs(u4 const* const p1, u4* const peax, u4 const ebx) // tabs/label array
{
    u4 ecx = p1[1]; // total #
    if (ecx == 0)
        return;
    u4 esi = p1[0]; // active tab
    u4 ebp = 8; // set array offset at top of labels
    do {
        if (--esi == 0) // check if tab is the current one
        {
            DrawTabOn(p1, peax, ebx, &ebp); // draws tab, updates eax, ebx & ebp for next tab...
        } else {
            DrawTabOff(p1, peax, ebx, &ebp); // ... and autosizes the tab for its label
        }
    } while (--ecx != 0);
}

/* XXX NOTE: Macro is defective:
 * - if p2 has the form a + b, then 2 * b must be added to p4
 * - if p3 has the form a + b, then 2 * b must be added to p5
 */
static void DrawGUIWinBox(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, u4 const p6)
{
    s4 const eax = GUIwinposx[p1] + p2;
    s4 const ebx = GUIwinposy[p1] + p3;
    s4 const ecx = eax + p4 - p2 + 1;
    u4 const esi = p5 - p3 + 1;
    GUIRect(eax, ecx, ebx, esi, p6);
}

static void DrawGUIButton(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, char const* const p6, u4 const p7, u4 const p8, u4 const p9)
{
    u1 const colour = GUIWincoladd == 0 ? 217 : 211;
    bool const held = GUICBHold == p7;
    DrawGUIWinBox(p1, p2, p3, p4, p3, colour + (held ? -18 : -5));
    DrawGUIWinBox(p1, p2, p3, p2, p5, colour + (held ? -16 : -8));
    DrawGUIWinBox(p1, p2 + 1, p3 + 1, p4, p5, colour + (held ? -14 : -11));
    DrawGUIWinBox(p1, p4 + 1, p3 + 1, p4, p5, colour + (held ? -12 : -14));
    DrawGUIWinBox(p1, p2, p5, p4 - 1, p5, colour + (held ? -10 : -17));
    if (!held) {
        GUIOuttextwin2(p1, p2 + 5 + p8, p3 + 4 + p9, p6, colour - 15);
        GUIOuttextwin2(p1, p2 + 4 + p8, p3 + 3 + p9, p6, colour);
    } else {
        GUIOuttextwin2(p1, p2 + 6 + p8, p3 + 5 + p9, p6, colour - 18);
        GUIOuttextwin2(p1, p2 + 5 + p8, p3 + 4 + p9, p6, colour - 3);
    }
}

static void GUIDisplayTextY(u4 const p1, u4 const p2, u4 const p3, char const* const p4) // Yellow Text&Shadow
{
    GUIOuttextwin2(p1, p2, p3, p4, GUIWincol);
    GUIOuttextwin2(p1, p2 - 1, p3 - 1, p4, GUIWincoladd == 0 ? 163 : 164);
}

static void GUIDisplayText(u4 const p1, u4 const p2, u4 const p3, char const* const p4) // Text&Shadow
{
    u1 const colour = GUIWincoladd == 0 ? 202 : 196;
    GUIOuttextwin2(p1, p2, p3, p4, colour);
    GUIOuttextwin2(p1, p2 - 1, p3 - 1, p4, colour + 15);
}

static void GUIDisplayBBox(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, u4 const p6) // Black Box W/Border
{
    GUIWincol = cwindrawn == 0 ? 148 : cwindrawn == 1 ? 148 + 5
                                                      : 148 + 10;
    DrawGUIWinBox(p1, p2, p3, p4, p5, p6);
    DrawGUIWinBox(p1, p2, p3 - 3 + 2, p4, p3 - 1, GUIWincol);
    DrawGUIWinBox(p1, p2 - 1, p3, p2 - 2, p5, GUIWincol + 1);
    DrawGUIWinBox(p1, p2, p5 + 1, p4, p5 + 1, GUIWincol + 4);
    DrawGUIWinBox(p1, p4 + 2, p3, p4 + 1, p5, GUIWincol + 3);
}

static void GUIDisplayTextG(u4 const p1, u4 const p2, u4 const p3, char const* const p4) // Green Text&Shadow
{
    GUIOuttextwin2(p1, p2, p3, p4, 223);
    GUIOuttextwin2(p1, p2 - 1, p3 - 1, p4, GUIWincoladd == 0 ? 221 : 222);
}

static void GUIOuttextwin2d(u4 const p1, u4 const p2, u4 const p3, char const* const p4, u4 const p5, char** const p6, u4 const p7) // Boxed, green text, limited to 5th param
{
    char const* ecx = p4; // Move pointer to text into ecx
    while (*ecx != '\0')
        ++ecx; // Check for null in string
    u4 eax = ecx - p4; // Subtract pointer from \0 pointer gives us string length
    if (eax > p5)
        eax = p5; // Restrict to length to display

    GUIDisplayTextG(p1, p2, p3, ecx - eax);
    if (GUIInputBox == p7 + 1 && p6[p7] == p4) {
        static u1 GUIBlinkCursorLoop = 0;
        if (++GUIBlinkCursorLoop == 60)
            GUIBlinkCursorLoop = 0;
        if (GUIBlinkCursorLoop < 30) {
            GUIDisplayTextG(p1, eax * 6 /* 6 pixels */ + p2, p3, "_");
        }
    }
}

static void GUIDisplayBBoxS(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, u4 const p6) // Black Box W/Border
{
    // Minus right side
    GUIWincol = cwindrawn == 0 ? 148 : cwindrawn == 1 ? 148 + 5
                                                      : 148 + 10;
    DrawGUIWinBox(p1, p2, p3, p4, p5, p6);
    DrawGUIWinBox(p1, p2, p3 - 3 + 2, p4, p3 - 1, GUIWincol);
    DrawGUIWinBox(p1, p2 - 1, p3, p2 - 2, p5, GUIWincol + 1);
    DrawGUIWinBox(p1, p2, p5 + 1, p4, p5 + 1, GUIWincol + 4);
}

static void DrawGUIWinBox2(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, s4 const ebx)
{
    s4 const eax = GUIwinposx[p1] + p2;
    s4 const ecx = GUIwinposx[p1] + p3 + 1;
    u1 const edx = GUIWincoladd == 0 ? p5 : p5 + 1;
    GUIRect(eax, ecx, ebx + GUIwinposy[p1], p4, edx);
}

static void GUIDisplayTextu(u4 const p1, u4 const p2, u4 const p3, char const* const p4, u4 const p5) // Text&Shadow With Underline
{
    u1 const colour = GUIWincoladd == 0 ? 202 : 196;
    GUIOuttextwin2u(p1, p2, p3, p4, colour, p5);
    GUIOuttextwin2(p1, p2 - 1, p3 - 1, p4, colour + 15);
}

static u1 const GUIIconDataCheckBoxUC[] = {
    /**/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    /**/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    /**/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    /**/ 0, 220, 219, 218, 217, 216, 215, 0, 0, 0,
    /**/ 0, 219, 218, 217, 216, 215, 214, 202, 0, 0,
    /**/ 0, 218, 217, 216, 215, 214, 213, 202, 0, 0,
    /**/ 0, 217, 216, 215, 214, 213, 212, 202, 0, 0,
    /**/ 0, 216, 215, 214, 213, 212, 211, 202, 0, 0,
    /**/ 0, 215, 214, 213, 212, 211, 210, 202, 0, 0,
    /**/ 0, 0, 202, 202, 202, 202, 202, 202, 0, 0
};

static u1 const GUIIconDataCheckBoxC[] = {
    /**/ 0, 0, 0, 0, 0, 0, 0, 0, 165, 0,
    /**/ 0, 0, 0, 0, 0, 0, 0, 165, 0, 0,
    /**/ 0, 0, 0, 0, 0, 0, 165, 0, 0, 0,
    /**/ 0, 220, 219, 218, 217, 165, 215, 0, 0, 0,
    /**/ 0, 165, 165, 217, 165, 165, 214, 202, 0, 0,
    /**/ 0, 218, 165, 216, 165, 214, 213, 202, 0, 0,
    /**/ 0, 217, 165, 165, 165, 213, 212, 202, 0, 0,
    /**/ 0, 216, 215, 165, 213, 212, 211, 202, 0, 0,
    /**/ 0, 215, 214, 165, 212, 211, 210, 202, 0, 0,
    /**/ 0, 0, 202, 202, 202, 202, 202, 202, 0, 0
};

static void GUIDisplayCheckboxTn(u4 const p1, u4 const p2, u4 const p3, u1 const* const p4, u4 const p5, char const* const p6) // Variable Checkbox (Text)
{
    u1 const* const icon = *p4 == p5 ? GUIIconDataCheckBoxC : GUIIconDataCheckBoxUC;
    GUIDisplayIconWin(p1, p2, p3, icon);
    GUIDisplayText(p1, p2 + 15, p3 + 5, p6);
}

static void GUIDisplayCheckbox(u4 const p1, u4 const p2, u4 const p3, u1 const* const p4, char const* const p5) // Toggled Checkbox (Text)
{
    u1 const* const icon = *p4 != 0 ? GUIIconDataCheckBoxC : GUIIconDataCheckBoxUC;
    GUIDisplayIconWin(p1, p2, p3, icon);
    GUIDisplayText(p1, p2 + 15, p3 + 5, p5);
}

static void GUIDisplayCheckboxu(u4 const p1, u4 const p2, u4 const p3, u1 const* const p4, char const* const p5, u4 const p6) // Toggled Checkbox (Text Underline)
{
    u1 const* const icon = *p4 != 0 ? GUIIconDataCheckBoxC : GUIIconDataCheckBoxUC;
    GUIDisplayIconWin(p1, p2, p3, icon);
    GUIDisplayTextu(p1, p2 + 15, p3 + 5, p5, p6);
}

static void GUIDisplayCheckboxun(u4 const p1, u4 const p2, u4 const p3, u1 const* const p4, u4 const p5, char const* const p6, u4 const p7) // Set Var. Checkbox (Text Underline)
{
    u1 const* const icon = *p4 == p5 ? GUIIconDataCheckBoxC : GUIIconDataCheckBoxUC;
    GUIDisplayIconWin(p1, p2, p3, icon);
    GUIDisplayTextu(p1, p2 + 15, p3 + 5, p6, p7);
}

static void GUIDisplayButtonHole(u4 const p1, u4 const p2, u4 const p3, u1 const* const p4, u4 const p5)
{
    static u1 const GUIIconDataButtonFill[] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 207, 209, 207, 0, 0, 0, 0,
        0, 0, 207, 211, 212, 211, 207, 0, 0, 0,
        0, 207, 211, 214, 216, 214, 211, 207, 0, 0,
        0, 207, 212, 216, 217, 216, 212, 207, 0, 0,
        0, 207, 211, 214, 216, 214, 211, 207, 0, 0,
        0, 0, 207, 211, 212, 211, 207, 0, 0, 0,
        0, 0, 0, 207, 209, 207, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };

    static u1 const GUIIconDataButtonHole[] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 207, 205, 207, 0, 0, 0, 0,
        0, 0, 207, 203, 202, 203, 207, 0, 0, 0,
        0, 207, 203, 200, 198, 200, 203, 207, 0, 0,
        0, 207, 202, 198, 197, 198, 202, 207, 0, 0,
        0, 207, 203, 200, 198, 200, 203, 207, 0, 0,
        0, 0, 207, 203, 202, 203, 207, 0, 0, 0,
        0, 0, 0, 207, 205, 207, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };

    u1 const* const icon = *p4 == p5 ? GUIIconDataButtonFill : GUIIconDataButtonHole;
    GUIDisplayIconWin(p1, p2, p3, icon);
}

static void GUIDisplayButtonHoleTu(u4 const p1, u4 const p2, u4 const p3, u1 const* const p4, u4 const p5, char const* const p6, u4 const p7)
{
    GUIDisplayButtonHole(p1, p2, p3, p4, p5);
    GUIDisplayTextu(p1, p2 + 15, p3 + 3, p6, p7);
}

static void GUIDrawSlider(u4 const p1, u4 const p2, u4 const p3, u4 const p4, void const* const p5, u1 (*p7)(void const*), char const* (*p8)(void const*)) // win #id, minX, width, Ypos, var, text, proc1, proc2
{
    s4 const eax = GUIwinposx[p1] + p2;
    s4 const ebx = GUIwinposy[p1] + p4;
    s4 const ecx = eax + p3;
    u4 const edx = 215 - GUIWincoladd;
    GUIHLine(eax, ecx, ebx, edx);
    GUIHLine(eax + 1, ecx + 1, ebx + 1, edx - 13);
    u4 const x = p2 - 4 + p7(p5); // proc1 == alters var correctly and puts result in al
    static u1 const GUIIconDataSlideBar[] = {
        0, 0, 0, 0, 216, 0, 0, 0, 0, 0,
        0, 0, 0, 212, 216, 220, 0, 0, 0, 0,
        0, 0, 0, 212, 216, 220, 202, 0, 0, 0,
        0, 0, 212, 212, 216, 218, 220, 0, 0, 0,
        0, 0, 212, 214, 216, 218, 220, 202, 0, 0,
        0, 0, 212, 214, 216, 218, 220, 202, 0, 0,
        0, 0, 0, 212, 216, 220, 202, 202, 0, 0,
        0, 0, 0, 212, 216, 220, 202, 0, 0, 0,
        0, 0, 0, 0, 216, 202, 202, 0, 0, 0,
        0, 0, 0, 0, 0, 202, 0, 0, 0, 0
    };
    GUIDisplayIconWin(p1, x, p4 - 4, GUIIconDataSlideBar);
    char const* const esi = p8(p5); // proc2 == alters text correctly and puts pointer in esi
    GUIDisplayTextG(p1, p2 + p3 + 6, p4 - 1, esi); // Display Value (Green)
}

static void GUIOuttextwin2load(u4 const p1, u4 const p2, u4 const p3, char* const* const eax)
{
    char const* const name = *eax;
    ++cloadnposb;
    GUIOuttextwin2l(p1, p2, p3, name, 223);
    GUIOuttextwin2l(p1, p2 - 1, p3 - 1, name, GUIWincoladd == 0 ? 221 : 222);
    --cloadnleft;
}

static void GUIOuttextwinloadfile(u4 const p1, u4 const p2, u4 const p3)
{
    if (cloadnleft & 0x80000000)
        return;
    if ((u4)cloadnposb >= (u4)GUIfileentries)
        return;
    char* const* const eax = &selected_names[cloadnposb];
    GUIOuttextwin2load(p1, p2, p3, eax);
}

static void GUIOuttextwinloaddir(u4 const p1, u4 const p2, u4 const p3)
{
    if (cloadnleft & 0x80000000)
        return;
    if ((u4)cloadnposb >= (u4)GUIdirentries)
        return;
    char* const* const eax = &d_names[cloadnposb + 2];
    GUIOuttextwin2load(p1, p2, p3, eax);
}

static void DrawSlideBar(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, u4 const p6, u4 const p7, u4* const p8, u4 const p9, u4 const p10)
{
    DrawSlideBarWin(p1, p2, p3 + 8, p4, p5, p6, p7 - 16, p8);
    if ((GUICHold & 0xFF) == p9)
        GUIWincoladd += 3;
    static u1 const GUIIconDataUpArrow[] = {
        201, 209, 209, 209, 209, 209, 209, 200, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        207, 205, 201, 202, 203, 202, 205, 203, 0, 0,
        207, 200, 205, 202, 203, 205, 201, 203, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        199, 201, 201, 201, 201, 201, 201, 198, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    GUIDisplayIconWin(p1, p2, p3, GUIIconDataUpArrow);
    if ((GUICHold & 0xFF) == p9)
        GUIWincoladd -= 3;

    if ((GUICHold & 0xFF) == p10)
        GUIWincoladd += 3;
    static u1 const GUIIconDataDownArrow[] = {
        201, 209, 209, 209, 209, 209, 209, 200, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        207, 200, 205, 202, 203, 205, 201, 203, 0, 0,
        207, 205, 201, 202, 203, 202, 205, 203, 0, 0,
        207, 205, 205, 202, 203, 205, 205, 203, 0, 0,
        199, 201, 201, 201, 201, 201, 201, 198, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    GUIDisplayIconWin(p1, p2, p3 + p7 - 8, GUIIconDataDownArrow);
    if ((GUICHold & 0xFF) == p10)
        GUIWincoladd -= 3;
}

void DisplayGUILoad(void)
{
    GUIDrawWindowBox(1, "LOAD GAME");

    char const* const GUILoadText3 = "LONG FILENAME";
    GUIDisplayText(1, 21, 166, GUILoadText3);
    GUIDisplayText(1, 21, 174, "SNES HEADER NAME");
    GUIDisplayText(1, 6, 16, "FILENAME");
    GUIDisplayText(1, 161, 16, "DIRECTORY");
    GUIDisplayText(1, 146, 172, "FORCE");

    u4 ecx = 0;
    char const* esi = ZRomPath;
    while (esi[ecx] != '\0')
        ++ecx;
    if (ecx > 39)
        esi += ecx - 39;
    GUIDisplayText(1, 6, 138, esi);

    cloadmaxlen = 39;
    u1 const colour = GUIWincoladd == 0 ? 202 : 196;
    if (GUIcurrentfilewin != 0) {
        char const* const eax = d_names[GUIcurrentdircursloc + 2];
        GUIOuttextwin2l(1, 6, 158, eax, colour);
        GUIOuttextwin2l(1, 5, 157, eax, colour + 15);
    } else if (GUIfileentries != 0) {
        s4 const eax = GUIcurrentcursloc;
        if ((u4)eax < (u4)GUIfileentries) {
            char const* const name = selected_names[eax];
            GUIOuttextwin2l(1, 6, 158, name, colour);
            GUIOuttextwin2l(1, 5, 157, name, colour + 15);
        }
    }

    DrawGUIButton(1, 186, 165, 228, 176, "LOAD", 1, 0, 0);

    // The Three Boxes
    GUIDisplayBBoxS(1, 5, 25, 144, 134, 167); // 126 = 6 * 21,  112 = 7 * 16
    GUIDisplayBBoxS(1, 160, 25, 228, 134, 167); // 78 =  6 * 13
    GUIDisplayBBox(1, 5, 145, 228, 152, 167); // 126 = 6 * 21,  112 = 7 * 16

    {
        u1 const ebx = GUILoadPos; // Flash Code?
        if (GUILDFlash & 8) {
            GUILoadTextA[ebx] = '\0';
        } else {
            GUILoadTextA[ebx] = '_';
            GUILoadTextA[ebx + 1] = '\0';
        }
    }

    // Check if it's in the Files box
    s4 const ebx = GUIcurrentfilewin == 0 ? GUIcurrentcursloc - GUIcurrentviewloc : GUIcurrentdircursloc - GUIcurrentdirviewloc;

    // Draw 2 more boxes?
    s4 const y = 27 + ebx * 7;
    if (GUIcurrentfilewin == 0) {
        DrawGUIWinBox2(1, 5, 144, 7, 224, y);
    } else {
        DrawGUIWinBox2(1, 160, 228, 7, 224, y);
    }

    GUIDisplayTextG(1, 8, 148, GUILoadTextA);

    if (GUIfileentries == 0)
        GUIcurrentfilewin = 1;

    cloadnleft = GUIfileentries - GUIcurrentviewloc;
    cloadnposb = GUIcurrentviewloc;
    cloadmaxlen = 23;

    // Text/Shadow for Filename Box
    for (u4 i = 0; i != 15; ++i) {
        GUIOuttextwinloadfile(1, 8, 29 + 7 * i);
    }

    cloadnleft = GUIdirentries - GUIcurrentdirviewloc;
    cloadnposb = GUIcurrentdirviewloc;
    cloadmaxlen = 11;

    // Text/Shadow for DIR Box
    for (u4 i = 0; i != 15; ++i) {
        GUIOuttextwinloaddir(1, 164, 29 + 7 * i);
    }

    GUILoadTextA[GUILoadPos] = '\0';

    GUIDisplayButtonHole(1, 9, 163, &GUIloadfntype, 0); // Radio Buttons
    GUIDisplayButtonHole(1, 9, 171, &GUIloadfntype, 1);

    GUIDisplayCheckboxTn(1, 10, 187, &showallext, 1, "SHOW ALL EXTENSIONS"); // Checkboxes
    GUIDisplayCheckboxTn(1, 144, 177, &ForceROMTiming, 1, "NTSC");
    GUIDisplayCheckboxTn(1, 144, 187, &ForceROMTiming, 2, "PAL");
    GUIDisplayCheckboxTn(1, 184, 177, &ForceHiLoROM, 1, "LOROM");
    GUIDisplayCheckboxTn(1, 184, 187, &ForceHiLoROM, 2, "HIROM");

    // Slidebar for Files
    // win#,X,Y start, %4-List Loc, %5-List size, %6-Screen size, %7-Bar Size
    DrawSlideBar(1, 146, 25, GUIcurrentviewloc, GUIfileentries, 15, 110, GUILStA, 1, 2);

    // Slidebar for DIR
    DrawSlideBar(1, 230, 25, GUIcurrentdirviewloc, GUIdirentries, 15, 110, GUILStB, 3, 4);
}

void DisplayGUIReset(void)
{
    GUIDrawWindowBox(12, "RESET GAME");

    // Red Box around buttons
    u1 const dl = GUIWincoladd == 0 ? 225 : 224;
    u4 const x = GUICResetPos == 0 ? 19 : 79;
    DrawGUIWinBox(12, x, 29, x + 38, 42, dl);

    DrawGUIButton(12, 20, 30, 56, 41, "YES", 2, 0, 0);
    DrawGUIButton(12, 80, 30, 116, 41, "NO", 3, 0, 0);

    GUIDisplayTextY(12, 6, 16, "RESET: ARE YOU SURE ?");
}

void DisplayGUIStates(void)
{
    GUIDrawWindowBox(14, "STATE CONFIRM");

    // Red Box around buttons
    u1 const dl = GUIWincoladd == 0 ? 225 : 224;
    u4 const x = GUICStatePos == 0 ? 19 : 79;
    DrawGUIWinBox(12, x, 29, x + 38, 42, dl);

    DrawGUIButton(14, 20, 30, 56, 41, "YES", 10, 0, 0);
    DrawGUIButton(14, 80, 30, 116, 41, "NO", 11, 0, 0);

    // Determine Load or Save box
    char const* const msg = GUIStatesText5 == 1 ? "OKAY TO LOAD STATE?" : "OKAY TO SAVE STATE?";
    GUIDisplayTextY(14, 6, 16, msg);
}

void DisplayGUIChoseSave(void)
{
    GUIDrawWindowBox(2, "STATE SELECT");

    u1 const ah = current_zst % 10;
    u1 const al = current_zst / 10 + '0';

    GUIDisplayTextY(2, 6, 16, "SELECT SAVE SLOT:");
    u4 x = 0;
    u4 y = 0;
    for (u4 i = 0; i != 10; x += 20, ++i) {
        if (i == 5) {
            x = 0;
            y = 15;
        }
        GUIChoseSaveText2[0] = '0' + i;
        GUIDisplayText(2, 21 + x, 31 + y, GUIChoseSaveText2);
        GUIDisplayButtonHole(2, 10 + x, 28 + y, &ah, i);
    }
    GUIDisplayTextY(2, 6, 61, "SLOT LEVEL:");

    GUIChoseSaveText2[0] = ah;
    GUIChoseSlotTextX[0] = al;

    GUIDisplayBBox(2, 72, 59, 90, 66, 167); // Save Slot Frameskip +/- Box
    GUIDisplayTextG(2, 83, 61, GUIChoseSlotTextX);
    DrawGUIButton(2, 94, 59, 102, 67, "+", 80, -2, -1);
    DrawGUIButton(2, 105, 59, 113, 67, "-", 81, -2, -1);
}

static void PrintKey(u4 const id, u4 const x, u4 const y, u4 const key)
{
    char GUIGameDisplayKy[4];
    sprintf(GUIGameDisplayKy, "%.3s", ScanCodeListing + key * 3);
    GUIDisplayTextG(id, x, y, GUIGameDisplayKy);
}

static void DGOptnsBorderBox(u4 const p1, u4 const p2, u4 const p3)
{
    GUIWincol = cwindrawn == 0 ? 148 : cwindrawn == 1 ? 148 + 5
                                                      : 148 + 10;
    DrawGUIWinBox(p1, p2 + 1, p3, p2 + 20, p3, GUIWincol);
    DrawGUIWinBox(p1, p2, p3 + 1, p2 - 1, p3 + 7, GUIWincol + 1);
    DrawGUIWinBox(p1, p2 + 1, p3 + 8, p2 + 20, p3 + 8, GUIWincol + 4);
    DrawGUIWinBox(p1, p2 + 22, p3 + 1, p2 + 21, p3 + 7, GUIWincol + 3);
}

static void DDrawBox(u4 const p1, s4 const p2, s4 const p3, u4 const* const p4)
{
    s4 const eax = GUIwinposx[p1] + p2 + 1;
    s4 const ebx = GUIwinposy[p1] + p3 + 1;
    GUIRect(eax, eax + 20, ebx, 7, 167);
    PrintKey(p1, p2 + 4, p3 + 3, *p4);
    DGOptnsBorderBox(p1, p2, p3);
}

#define GUIInputDispAll(p1)                                 \
    do {                                                    \
        DDrawBox(3, 44, 101, &p1##upk); /* Up         */    \
        DDrawBox(3, 44, 111, &p1##downk); /* Down       */  \
        DDrawBox(3, 44, 121, &p1##leftk); /* Left       */  \
        DDrawBox(3, 44, 131, &p1##rightk); /* Right      */ \
        DDrawBox(3, 44, 141, &p1##startk); /* Start      */ \
        DDrawBox(3, 44, 151, &p1##selk); /* Select     */   \
        DDrawBox(3, 84, 101, &p1##Ak); /* A          */     \
        DDrawBox(3, 84, 111, &p1##Bk); /* B          */     \
        DDrawBox(3, 84, 121, &p1##Xk); /* X          */     \
        DDrawBox(3, 84, 131, &p1##Yk); /* Y          */     \
        DDrawBox(3, 84, 141, &p1##Lk); /* L          */     \
        DDrawBox(3, 84, 151, &p1##Rk); /* R          */     \
                                                            \
        DDrawBox(3, 124, 101, &p1##Xtk); /* X Turbo    */   \
        DDrawBox(3, 124, 111, &p1##Ytk); /* Y Turbo    */   \
        DDrawBox(3, 124, 121, &p1##Ltk); /* L Turbo    */   \
        DDrawBox(3, 164, 101, &p1##Atk); /* A Turbo    */   \
        DDrawBox(3, 164, 111, &p1##Btk); /* B Turbo    */   \
        DDrawBox(3, 164, 121, &p1##Rtk); /* R Turbo    */   \
                                                            \
        DDrawBox(3, 124, 141, &p1##ULk); /* Up-Left    */   \
        DDrawBox(3, 124, 151, &p1##DLk); /* Down-Left  */   \
        DDrawBox(3, 164, 141, &p1##URk); /* Up-Right   */   \
        DDrawBox(3, 164, 151, &p1##DRk); /* Down-Right */   \
    } while (0)

void DisplayGUIInput(void)
{
    GUIDrawWindowBox(3, "INPUT DEVICE");
    cplayernum = GUIInputTabs[0] - 1;

    {
        u4 eax;
        u4 ebx;
        GUIDrawTArea(3, &eax, &ebx);
        GUIDrawTabs(GUIInputTabs, &eax, ebx);
    }

    GUIDisplayTextY(3, 6, 26, "DEVICE:");
    u1 const ebx = *GUIInputRefP[cplayernum];
    if (GUIFreshInputSelect != 0) {
        GUIFreshInputSelect = 0;
        GUIJT_viewable = 5;
        GUIJT_entries = NumInputDevices;
        GUIJT_offset = ebx;
        GUIJT_currentviewloc = (s4*)&GUIcurrentinputviewloc; // XXX ugly cast
        GUIJT_currentcursloc = (s4*)&GUIcurrentinputcursloc; // XXX ugly cast
        GUIGenericJumpTo();
    }
    GUIDisplayTextY(3, 6 + 54, 83, GUIInputNames[ebx]); // CDV
    GUIDisplayTextY(3, 6, 83, "CURRENT:");

    GUIDisplayTextY(3, 6, 94, "KEYS:");
    GUIDisplayText(3, 6, 104, "    UP");
    GUIDisplayText(3, 6, 114, "  DOWN");
    GUIDisplayText(3, 6, 124, "  LEFT");
    GUIDisplayText(3, 6, 134, " RIGHT");
    GUIDisplayText(3, 6, 144, " START");
    GUIDisplayText(3, 6, 154, "SELECT");

    GUIDisplayText(3, 76, 104, "A");
    GUIDisplayText(3, 76, 114, "B");
    GUIDisplayText(3, 76, 124, "X");
    GUIDisplayText(3, 76, 134, "Y");
    GUIDisplayText(3, 76, 144, "L");
    GUIDisplayText(3, 76, 154, "R");

    GUIDisplayTextY(3, 116, 94, "TURBO:");
    GUIDisplayText(3, 156, 104, "A");
    GUIDisplayText(3, 156, 114, "B");
    GUIDisplayText(3, 156, 124, "R");
    GUIDisplayText(3, 116, 104, "X");
    GUIDisplayText(3, 116, 114, "Y");
    GUIDisplayText(3, 116, 124, "L");

    GUIDisplayTextY(3, 113, 134, "DIAGONALS:");
    GUIDisplayText(3, 113, 144, "UL");
    GUIDisplayText(3, 153, 144, "UR");
    GUIDisplayText(3, 113, 154, "DL");
    GUIDisplayText(3, 153, 154, "DR");

    GUIDisplayCheckboxu(3, 5, 160, &GameSpecificInput, "GAME SPECIFIC", 0);
    GUIDisplayCheckboxu(3, 5, 170, &AllowUDLR, "ALLOW U+D/L+R", 0);
    GUIDisplayCheckboxu(3, 105, 160, &SNESRumble, "P1 Rumble", 3);
    GUIDisplayCheckboxu(3, 105, 170, &Turbo30hz, "TURBO AT 30HZ", 0);
    GUIDisplayCheckboxu(3, 5, 180, &pl12s34, "USE PL3/4 AS PL1/2", 0);

    DrawGUIButton(3, 123, 34, 153, 45, "SET", 14, 0, 0); // Buttons
    DrawGUIButton(3, 123, 50, 177, 61, "SET KEYS", 40, 0, 0);

    GUIDisplayBBoxS(3, 5, 34, 107, 77, 167); // Main Box
    u4 const eax = GUIcurrentinputcursloc - GUIcurrentinputviewloc;
    DrawGUIWinBox2(3, 5, 107, 7, 224, 36 + eax * 8);

    // Text&Shadow inside Main Box
    char const(*name)[17] = GUIInputNames + GUIcurrentinputviewloc;
    for (u4 i = 0; i != 5; ++i) {
        GUIDisplayTextG(3, 11, 38 + 8 * i, *name++);
    }

    DrawSlideBar(3, 109, 34, GUIcurrentinputviewloc, NumInputDevices, 5, 44, GUIIStA, 9, 10);

    // Hotkey Boxes
    switch (cplayernum) {
    case 0:
        GUIInputDispAll(pl1);
        break;
    case 1:
        GUIInputDispAll(pl2);
        break;
    case 2:
        GUIInputDispAll(pl3);
        break;
    case 3:
        GUIInputDispAll(pl4);
        break;
    case 4:
        GUIInputDispAll(pl5);
        break;
    }
}

void DisplayGUIOption(void)
{
    GUIDrawWindowBox(4, "OPTIONS");

    {
        u4 eax;
        u4 ebx;
        GUIDrawTArea(4, &eax, &ebx);
        GUIDrawTabs(GUIOptionTabs, &eax, ebx);
    }

    if (GUIOptionTabs[0] == 1) { // Basic
        GUIDisplayTextY(4, 11, 26, "SYSTEM:");
        GUIDisplayCheckboxu(4, 11, 41, &Show224Lines, "SHOW 224 LINES", 9);

        GUIDisplayTextY(4, 11, 66, "GFX ENGINES:");
        GUIDisplayCheckboxu(4, 11, 71, &newengen, "USE NEW GFX ENG", 4);
        if (newengen == 0) {
            GUIDisplayCheckboxu(4, 11, 81, &bgfixer, "USE ALT OLD GFX ENG", 4);
        }

        GUIDisplayTextY(4, 11, 106, "ROM:");
        GUIDisplayCheckboxu(4, 11, 111, &AutoPatch, "ENABLE IPS AUTO-PATCHING", 7);
        GUIDisplayCheckboxu(4, 11, 121, &DisplayInfo, "SHOW ROM INFO ON LOAD", 5);
        GUIDisplayCheckboxu(4, 11, 131, &RomInfo, "LOG ROM INFO", 2);

#ifdef __WIN32__
        GUIDisplayTextY(4, 11, 156, "WINDOWS SPECIFIC:");
        GUIDisplayCheckboxu(4, 11, 161, &PauseFocusChange, "PAUSE EMU IN BACKGROUND", 13);
        GUIDisplayCheckboxu(4, 11, 171, &HighPriority, "INCREASE EMU PRIORITY", 13);
#endif
        GUIDisplayCheckboxu(4, 11, 181, &DisableScreenSaver, "DISABLE POWER MANAGEMENT", 0);
    }

    if (GUIOptionTabs[0] == 2) {
        GUIDisplayTextY(4, 11, 26, "OVERLAYS:");
        GUIDisplayCheckboxu(4, 11, 31, &FPSAtStart, "SHOW FPS CNTR ON EMU LOAD", 5);
        GUIDisplayCheckboxu(4, 11, 41, &CPUAtStart, "SHOW CPU PCNT ON EMU LOAD", 7);
        GUIDisplayCheckboxu(4, 11, 51, &TimerEnable, "SHOW CLOCK", 5);
        if (TimerEnable == 1) {
            GUIDisplayCheckboxu(4, 89, 51, &TwelveHourClock, "12 HOUR MODE", 3);
            GUIDisplayCheckboxu(4, 11, 61, &ClockBox, "SHOW CLOCK BOX", 13);
        }

        GUIDisplayTextY(4, 11, 86, "MESSAGES:");
        GUIDisplayCheckboxu(4, 11, 91, &SmallMsgText, "USE SMALL MESSAGE TEXT", 4);
        GUIDisplayCheckboxu(4, 11, 101, &GUIEnableTransp, "USE TRANSPARENT TEXT", 4);

        GUIDisplayTextY(4, 11, 126, "SCREENSHOT FORMAT:");
        GUIDisplayButtonHoleTu(4, 11, 131, &ScreenShotFormat, 0, "BMP", 0);
#ifndef NO_PNG
        GUIDisplayButtonHoleTu(4, 11, 141, &ScreenShotFormat, 1, "PNG", 0);
#endif
    }
}

static u1 glscslidSet(void const* const p1) // slider variable
{
    return *(u1 const*)p1;
}

static char const* glscslidText(void const* const p1) // slider var, text
{
    static char GUIVideoTextB2z[] = "---%";
    sprintf(GUIVideoTextB2z, "%3d", *(u1 const*)p1);
    return GUIVideoTextB2z;
}

static u1 NTSCslidSet(void const* const p1) // slider variable
{
    return *(s1 const*)p1 + 100;
}

static char const* NTSCslidText(void const* const p1) // slider var, text
{
    static char GUIVideoTextCD3[] = "----%";
    sprintf(GUIVideoTextCD3, "%4d", *(s1 const*)p1);
    return GUIVideoTextCD3;
}

void DisplayGUIVideo(void)
{
    // Check features
    if (newgfx16b == 0) {
        En2xSaI = 0;
        hqFilter = 0;
    }

    if (En2xSaI != 0) {
        hqFilter = 0;
        scanlines = 0;
        antienab = 0;
    }

    if (hqFilter != 0) {
        En2xSaI = 0;
        scanlines = 0;
        antienab = 0;
    }

    GUIDrawWindowBox(5, "VIDEO CONFIG");

    if (GUINTVID[cvidmode] == 0) { // not NTSC
        NTSCFilter = 0;
        GUIVntscTab[0] = 0;
        if ((GUIVideoTabs[0] & 0xFF) == 0)
            GUIVideoTabs[0] = (GUIVideoTabs[0] & 0xFFFFFF00) | 1;
    }

    {
        u4 eax;
        u4 ebx;
        GUIDrawTArea(5, &eax, &ebx);
        GUIDrawTabs(GUIVideoTabs, &eax, ebx);
        if (NTSCFilter != 0)
            GUIDrawTabs(GUIVntscTab, &eax, ebx);
    }

    if (GUIVideoTabs[0] == 1) // Video Modes List/Options Tab
    {
        DrawGUIButton(5, 128, 30, 164, 41, "SET", 4, 0, 0); // Mode Set Button

        GUIDisplayTextY(5, 130, 50, "LEGEND:");
        GUIDisplayText(5, 130, 58, "D = ALLOW FILTERS");
        GUIDisplayText(5, 130, 66, "S = STRETCH");
        GUIDisplayText(5, 130, 74, "R = KEEP 8:7 RATIO");
        GUIDisplayText(5, 130, 82, "W = WINDOWED");
        GUIDisplayText(5, 130, 90, "F = FULLSCREEN");
#ifdef __OPENGL__
        GUIDisplayText(5, 130, 98, "O = USES OPENGL");
#endif

        DrawGUIButton(5, 180, 115, 216, 126, "SET", 12, 0, 0); // Custom Set Button

        GUIDisplayText(5, 130, 120, "CUSTOM:");
        GUIDisplayText(5, 180, 135, "X");
        GUIDisplayBBox(5, 130, 130, 170, 140, 167);
        GUIDisplayBBox(5, 191, 130, 231, 140, 167);

        GetCustomXY();

        GUIOuttextwin2d(5, 138, 133, GUICustomX, 4, GUICustomResTextPtr, 0);
        GUIOuttextwin2d(5, 199, 133, GUICustomY, 4, GUICustomResTextPtr, 1);

        GUIDisplayBBoxS(5, 5, 26, 115, 189, 167); // Video Modes Box
        DrawSlideBar(5, 117, 26, GUIcurrentvideoviewloc, NumVideoModes, 20, 164, GUIVStA, 5, 6);

        u4 const ebx = (GUIcurrentvideocursloc - GUIcurrentvideoviewloc) * 8 + 28; // Box
        DrawGUIWinBox2(5, 5, 115, 7, 224, ebx);

        char const(*name)[18] = GUIVideoModeNames + GUIcurrentvideoviewloc;
        u4 const n = NumVideoModes < 20 ? NumVideoModes : 20;
        for (u4 i = 0; i != n; ++i) {
            GUIDisplayTextG(5, 11, 30 + 8 * i, *name++);
        }

        GUIDisplayTextY(5, 7, 194, "CURRENT:");
        GUIDisplayTextY(5, 91, 194, GUIVideoModeNames[cvidmode]); // (5,61,194) // Mode Value
    }

    // Filters tab
    if (GUIVideoTabs[0] == 2) {
        // Video Filters
        {
            {
                char const* const GUIVideoTextB1 = "VIDEO FILTERS:"; // Filters.Exclusive
                // Bilinear
                if (GUIBIFIL[cvidmode] != 0) {
                    GUIDisplayTextY(5, 13, 30, GUIVideoTextB1);
                    GUIDisplayCheckboxu(5, 18, 35, &BilinearFilter, "BILINEAR FILTER", 1);
                } else {
                    // Interpolations
#ifdef __WIN32__
                    if (GUIDSIZE[cvidmode] != 0)
#else
                    if (GUII2VID[cvidmode] != 0)
#endif
                    {
                        GUIDisplayTextY(5, 13, 30, GUIVideoTextB1);
                        GUIDisplayCheckboxu(5, 18, 35, &antienab, "INTERPOLATION", 0); // -y
                    }
                }

                // NTSC filter
                if (GUINTVID[cvidmode] != 0)
                    GUIDisplayCheckboxu(5, 128, 35, &NTSCFilter, "NTSC FILTER", 0);

                // Hq*x
                if (GUIHQ2X[cvidmode] != 0) {
                    GUIDisplayCheckboxu(5, 128, 55, &hqFilter, "HQ FILTER", 1);
                    if (hqFilter != 0) {
                        GUIDisplayButtonHoleTu(5, 128, 68, &hqFilterlevel, 2, "2X", 1);
                        goto hq_x;
                    }
                } else {
                hq_x:;
                    if (GUIHQ3X[cvidmode] != 0)
                        GUIDisplayButtonHoleTu(5, 158, 68, &hqFilterlevel, 3, "3X", 0);
                    if (GUIHQ4X[cvidmode] != 0)
                        GUIDisplayButtonHoleTu(5, 188, 68, &hqFilterlevel, 4, "4X", 0);
                }
            }

            char const* const GUIVideoTextB2 = "SCANLINES:"; // Filters.Scanlines
            // GL Scanlines
            if (GUIBIFIL[cvidmode] != 0) {
                GUIDisplayTextY(5, 13, 80, GUIVideoTextB2); // Scanlines text
                GUIDrawSlider(5, 23, 100, 90, &sl_intensity, glscslidSet, glscslidText);
            } else {
                // Scanlines
                if (GUIDSIZE[cvidmode] != 0) {
                    GUIDisplayTextY(5, 13, 80, GUIVideoTextB2); // Scanlines text
                    GUIDisplayButtonHoleTu(5, 18, 87, &scanlines, 0, "NONE", 1); // None
                    GUIDisplayButtonHoleTu(5, 168, 87, &scanlines, 1, "FULL", 0); // Full
                }
                if (GUIDSIZE[cvidmode] != 0) {
                    GUIDisplayButtonHoleTu(5, 68, 87, &scanlines, 2, "25%", 0); // 25%
                    GUIDisplayButtonHoleTu(5, 118, 87, &scanlines, 3, "50%", 0); // 50%
                }
            }
        }

        GUIDisplayTextY(5, 13, 110, "MISC FILTERS:"); // Filters.Other
        GUIDisplayCheckboxu(5, 18, 115, &GrayscaleMode, "GRAYSCALE MODE", 0); // -v8

        // Hires Mode7
        if (GUIM7VID[cvidmode] != 0 && newengen != 0) {
            GUIDisplayCheckboxu(5, 128, 115, &Mode7HiRes16b, "HI-RES MODE 7", 0);
        }

        // Monitor Refresh
        // VSync
#if !defined __UNIXSDL__ || defined __OPENGL__
#ifdef __UNIXSDL__
        if (allow_glvsync == 1 && GUIBIFIL[cvidmode] != 0)
#endif
        {
            GUIDisplayTextY(5, 13, 140, "MONITOR SYNC:"); // Video.Sync
            GUIDisplayCheckboxu(5, 18, 145, &vsyncon, "VSYNC", 0); // -w
        }
#endif

        // Triple Buffering
#ifndef __UNIXSDL__
        char const* const GUIVideoTextB4b = "TRIPLE BUFFERING"; // -3
#endif
#ifdef __WIN32__
        if (GUIWFVID[cvidmode] != 0) {
            GUIDisplayCheckboxu(5, 128, 145, &TripleBufferWin, GUIVideoTextB4b, 0);
        }
#endif

        char const* const GUIVideoTextB5 = "DISPLAY OPTIONS:"; // Video.Display
        // Keep 4:3 Ratio
        if (GUIKEEP43[cvidmode] != 0 && Keep43Check()) {
            GUIDisplayTextY(5, 13, 170, GUIVideoTextB5);
            GUIDisplayCheckboxu(5, 18, 175, &Keep4_3Ratio, "USE 4:3 RATIO", 8);
        }
    }

    char const* const GUIVideoTextCD1 = "RESET"; // NTSC buttons + counter
    char const* const GUIVideoTextCD2 = "RESET ALL";

    if (GUIVntscTab[0] == 1) // NTSC Tab
    {
        GUIDisplayCheckboxu(5, 5, 25, &NTSCBlend, "BLEND FRAMES", 0); // NTSC Tab
        GUIDisplayCheckboxu(5, 135, 25, &NTSCRef, "REFRESH", 0);

        DrawGUIButton(5, 8, 166, 67, 177, "COMPOSITE", 81, 0, 0);
        DrawGUIButton(5, 72, 166, 119, 177, "S-VIDEO", 82, 0, 0);
        DrawGUIButton(5, 124, 166, 147, 177, "RGB", 83, 0, 0);
        DrawGUIButton(5, 152, 166, 217, 177, "MONOCHROME", 84, 0, 0);
        DrawGUIButton(5, 102, 186, 137, 197, GUIVideoTextCD1, 37, 0, 0);
        DrawGUIButton(5, 148, 186, 207, 197, GUIVideoTextCD2, 39, 0, 0);

        GUIDisplayTextY(5, 7, 46, "HUE:");
        GUIDisplayTextY(5, 7, 66, "SATURATION:");
        GUIDisplayTextY(5, 7, 86, "CONTRAST:");
        GUIDisplayTextY(5, 7, 106, "BRIGHTNESS:");
        GUIDisplayTextY(5, 7, 126, "SHARPNESS:");
        GUIDisplayTextY(5, 7, 156, "PRESETS:"); // NTSC Presets

        GUIDrawSlider(5, 8, 200, 56, &NTSCHue, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 76, &NTSCSat, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 96, &NTSCCont, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 116, &NTSCBright, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 136, &NTSCSharp, NTSCslidSet, NTSCslidText);
    }

    if (GUIVntscTab[0] == 2) // Advanced NTSC Options Tab
    {
        DrawGUIButton(5, 102, 186, 137, 197, GUIVideoTextCD1, 38, 0, 0);
        DrawGUIButton(5, 148, 186, 207, 197, GUIVideoTextCD2, 39, 0, 0);

        GUIDisplayTextY(5, 7, 36, "GAMMA:"); // NTSC Adv Tab
        GUIDisplayTextY(5, 7, 56, "RESOLUTION:");
        GUIDisplayTextY(5, 7, 76, "ARTIFACTS:");
        GUIDisplayTextY(5, 7, 96, "FRINGING:");
        GUIDisplayTextY(5, 7, 116, "BLEED:");
        GUIDisplayTextY(5, 7, 136, "HUE WARPING:");

        GUIDrawSlider(5, 8, 200, 46, &NTSCGamma, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 66, &NTSCRes, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 86, &NTSCArt, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 106, &NTSCFringe, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 126, &NTSCBleed, NTSCslidSet, NTSCslidText);
        GUIDrawSlider(5, 8, 200, 146, &NTSCWarp, NTSCslidSet, NTSCslidText);
    }
}

void DisplayGUISound(void)
{
    GUIDrawWindowBox(6, "SOUND CONFIG");

    GUIDisplayTextY(6, 6, 16, "SOUND SWITCHES:");
    GUIDisplayCheckboxu(6, 11, 21, &SPCDisable, "DISABLE SPC EMULATION", 0);
    if (SPCDisable == 0) {
        GUIDisplayCheckboxu(6, 11, 31, &soundon, "ENABLE SOUND", 0);
        if (soundon == 1) {
            GUIDisplayCheckboxu(6, 11, 41, &StereoSound, "ENABLE STEREO SOUND", 7);
            if (StereoSound == 1) {
                GUIDisplayCheckboxu(6, 11, 51, &RevStereo, "REVERSE STEREO CHANNELS", 2);
                GUIDisplayCheckboxu(6, 11, 61, &Surround, "SIMULATE SURROUND SOUND", 2);
            }
#ifdef __WIN32__
            GUIDisplayCheckboxu(6, 11, 71, &PrimaryBuffer, "USE PRIMARY BUFFER", 4);
#endif
        }
    }

    char const* const GUISoundTextF = "NONE";

    GUIDisplayTextY(6, 6, 152, "INTERPOLATION:");
    GUIDisplayButtonHoleTu(6, 11, 157, &SoundInterpType, 0, GUISoundTextF, 0);
    GUIDisplayButtonHoleTu(6, 11, 167, &SoundInterpType, 1, "GAUSSIAN", 0);
    GUIDisplayButtonHoleTu(6, 11, 177, &SoundInterpType, 2, "CUBIC SPLINE", 0);
    GUIDisplayButtonHoleTu(6, 11, 187, &SoundInterpType, 3, "8-POINT", 0);

    GUIDisplayTextY(6, 106, 152, "LOWPASS:");
    GUIDisplayButtonHoleTu(6, 111, 157, &LowPassFilterType, 0, GUISoundTextF, 1);
    GUIDisplayButtonHoleTu(6, 111, 167, &LowPassFilterType, 1, "SIMPLE", 1);
    GUIDisplayButtonHoleTu(6, 111, 177, &LowPassFilterType, 2, "DYNAMIC", 1);

    GUIDisplayTextY(6, 6, 93, "SAMPLING RATE:");
    {
        GUIDisplayBBox(6, 15, 101, 69, 109, 167); // Sampling Rate Box
        static char const GUISoundTextB1[][8] = {
            " 8000HZ",
            "11025HZ",
            "22050HZ",
            "44100HZ",
            "16000HZ",
            "32000HZ",
            "48000HZ"
        };
        GUIDisplayTextG(6, 23, 104, GUISoundTextB1[SoundQuality]);
    }

    GUIDisplayTextY(6, 6, 116, "VOLUME LEVEL:");
    GUIDrawSlider(6, 15, 100, 131, &MusicRelVol, glscslidSet, glscslidText);
}

static char const* DisplayGUICheatConv(u1 const* const c)
{
    char const* const tgl = c[-28] & 0x80 ? "SRC" : c[0] & 0x04 ? "OFF"
        : c[0] & 0x80                                           ? "RPL"
                                                                : "ON ";
    char const* const desc = (char const*)(c + 8);

    static char GUICheatTextZ3[6 + 1 + 2 + 1 + 2 + 1 + 3 + 1 + 20 + 1];
    sprintf(GUICheatTextZ3, "%06X %02X %02X %s %.20s", c[4] << 16 | c[3] << 8 | c[2], c[1], c[5], tgl, desc);
    return GUICheatTextZ3;
}

void DisplayGUICheat(void)
{
    GUIDrawWindowBox(7, "CHEAT");

    GUIDisplayText(7, 6, 13, "ADDRESS CV PV TGL DESCRIPTION"); // Top
    GUIDisplayText(7, 6, 132, "ENTER CODE:"); // Text by input boxes
    GUIDisplayText(7, 6, 143, "DESCRIPTION:");
    GUIDisplayText(7, 11, 154, "VALID CODES: GAME GENIE, PAR, AND GF"); // Info for User
    GUIDisplayText(7, 11, 164, "NOTE: YOU MAY HAVE TO RESET THE GAME");
    GUIDisplayText(7, 11, 172, "AFTER ENTERING THE CODE. REMEMBER TO");
    GUIDisplayText(7, 11, 180, "INSERT THE \"-\" FOR GAME GENIE CODES.");

    DrawGUIButton(7, 5, 113, 47, 124, "REMOVE", 5, 0, 0); // Draw Buttons
    DrawGUIButton(7, 52, 113, 94, 124, "TOGGLE", 6, 0, 0);
    DrawGUIButton(7, 99, 113, 141, 124, "SAVE", 7, 0, 0);
    DrawGUIButton(7, 146, 113, 188, 124, "LOAD", 8, 0, 0);
    DrawGUIButton(7, 193, 113, 235, 124, "FIX", 33, 0, 0);
    DrawGUIButton(7, 212, 134, 236, 145, "ADD", 9, 0, 0);

    GUIDisplayBBoxS(7, 5, 20, 229, 108, 167); // Draw Cheat Box

    if (GUIcurrentcheatwin == 0) // Red Highlight for Cheats box
    {
        u4 const ebx = 22 + (GUIcurrentcheatcursloc - GUIcurrentcheatviewloc) * 7;
        DrawGUIWinBox2(7, 5, 229, 7, 224, ebx);
    }

    u1 const* ccheatnpos = cheatdata + GUIcurrentcheatviewloc * 28; // Green Text
    u4 ccheatnleft = NumCheats - GUIcurrentcheatviewloc - 1;
    for (u4 i = 0; i != 12; ++i) {
        if (!(ccheatnleft & 0x80000000)) {
            u4 const p1 = 12;
            u4 const p2 = 24 + 7 * i;
            char const* const GUICheatTextZ3 = DisplayGUICheatConv(ccheatnpos);
            GUIDisplayTextG(7, p1, p2, GUICheatTextZ3);
            ccheatnpos += 28;
            --ccheatnleft;
        }
    }

    DrawSlideBar(7, 231, 20, GUIcurrentcheatviewloc, NumCheats, 12, 89, GUICStA, 7, 8);

    { // Code Box
        u1 const dl = GUIcurrentcheatwin != 1 ? 167 : GUIWincoladd == 0 ? 226
                                                                        : 227;
        GUIDisplayBBox(7, 82, 129, 172, 136, dl);
    }

    { // Description Box
        u1 const dl = GUIcurrentcheatwin != 2 ? 167 : GUIWincoladd == 0 ? 226
                                                                        : 227;
        GUIDisplayBBox(7, 82, 140, 196, 147, dl);
    }

    GUIDisplayTextG(7, 84, 132, GUICheatTextZ1); // Green Text&Shadow
    GUIDisplayTextG(7, 84, 143, GUICheatTextZ2);

    // Code for movement of cursor
    u1 const eax = GUICheatPosA;
    GUICheatTextZ1[eax] = '\0';
    u1 const ebx = GUICheatPosB;
    GUICheatTextZ2[ebx] = '\0';
    if (!(GUICCFlash & 8)) {
        switch (GUIcurrentcheatwin) {
        case 1:
            GUICheatTextZ1[eax] = '_';
            break;
        case 2:
            GUICheatTextZ2[ebx] = '_';
            break;
        }
    }

    char const* const GUICheatTextE1 = "AUTO-LOAD .CHT FILE AT GAME LOAD";
    if (GUIcurrentcheatwin == 0) { // Draw underline only if you don't have an input box selected
        GUIDisplayCheckboxu(7, 11, 186, &AutoLoadCht, GUICheatTextE1, 0);
    } else {
        GUIDisplayCheckbox(7, 11, 186, &AutoLoadCht, GUICheatTextE1);
    }
}

static void DrawWindowSearch(void)
{
    GUIDrawWindowBox(13, "CHEAT SEARCH");
}

static u4 FindChtSrcRes(u4 edi) // Calculate search results
{
    ++edi;
    u1 const* eax = vidbuffer + 129600 + 65536 * 2;
    u4 ecx = 16384;
    u4 esi = 0;
    u4 ebx = 0;
    do {
        u1 dl = *eax++;
        u1 dh = 8;
        do {
            if (dl & 1) {
                ++ebx;
                if (--edi == 0)
                    CSStartEntry = esi;
            }
            ++esi;
            dl >>= 1;
        } while (--dh != 0);
    } while (--ecx != 0);
    return ebx;
}

static void DisplayChtSrcResNoSearch(void)
{
    DrawGUIButton(13, 10, 140, 60, 152, "RESTART", 51, 0, 1);
    DrawGUIButton(13, 70, 140, 110, 152, "VIEW", 52, 0, 1);

    // Call and display # of results
    char GUICSrcTextG1[11];
    convertnum(GUICSrcTextG1, FindChtSrcRes(0));
    GUIDisplayText(13, 12, 125, "# OF RESULTS:");
    GUIDisplayText(13, 97, 125, GUICSrcTextG1);
    GUIcurrentchtsrcviewloc = 0;
    GUIcurrentchtsrccursloc = 0;
}

static void DisplayChtSrcRes(void) // Buttons (Restart/View/Search)
{
    DrawGUIButton(13, 120, 140, 170, 152, "SEARCH", 53, 0, 1);
    DisplayChtSrcResNoSearch();
}

static void CheatSearchingComp(void) // Comparative search
{
    GUIDisplayTextY(13, 6, 16, "SELECT COMPARISON:");
    GUIDisplayButtonHoleTu(13, 11, 33, &CheatCompareValue, 0, "NEW VALUE IS > OLD VALUE", 0);
    GUIDisplayButtonHoleTu(13, 11, 43, &CheatCompareValue, 1, "NEW VALUE IS < OLD VALUE", 1);
    GUIDisplayButtonHoleTu(13, 11, 53, &CheatCompareValue, 2, "NEW VALUE IS = OLD VALUE", 2);
    GUIDisplayButtonHoleTu(13, 11, 63, &CheatCompareValue, 3, "NEW VALUE IS != OLD VALUE", 5);
    DisplayChtSrcRes();
}

static void CSRemoveFlash(char* const str)
{
    char* const i = strchr(str, '_');
    if (i)
        *i = '\0';
}

static void CSAddFlash(char* i)
{
    for (; *i != '\0'; ++i) {
        if (*i == '_')
            return;
    }
    i[0] = '_';
    i[1] = '\0';
}

static void CheatSearching(void) // Exact Value Search
{
    if (CheatSrcSearchType == 1) {
        CheatSearchingComp();
        return;
    }

    GUIDisplayText(13, 5, 20, "ENTER VALUE:"); // Make Yellow
    GUIDisplayText(13, 5, 65, "MAX VALUE:");

    GUIDisplayBBox(13, 10, 40, 80, 47, 167); // Input Box

    if (!(GUICCFlash & 8))
        CSRemoveFlash(CSInputDisplay); // Flash Cursor Code?

    u1 const col_shadow = CSOverValue == 1 ? 202 /* Alt Color */ : 223 /* Green Shadow */;
    u1 const col_text = CSOverValue == 1 ? 207 : // Alt Color
        GUIWincoladd == 0 ? 221
                          : // Green Text
        222;
    GUIOuttextwin2(13, 13, 42, CSInputDisplay, col_shadow);
    GUIOuttextwin2(13, 12, 41, CSInputDisplay, col_text);

    CSAddFlash(CSInputDisplay); // More flash?

    char GUICSrcTextG1[11];
    u4 const eax = SrcMask[CheatSrcByteSize]; // Find Max Size
    char* const esi = GUICSrcTextG1;
    if (CheatSrcByteBase != 1) {
        convertnum(esi, eax);
    } else {
        converthex(esi, eax, CheatSrcByteSize + 1);
    }
    GUIDisplayText(13, 71, 65, GUICSrcTextG1); // Max Size Text
    DisplayChtSrcRes();
}

static void Incheatmode(void) // Return and Re-search Window
{
    GUIwinsizex[13] = 180;
    GUIwinsizey[13] = 150;
    DrawWindowSearch();

    if (CheatSearchStatus != 1) {
        CheatSearching();
    } else {
        GUIDisplayText(13, 5, 20, "NOW RETURN TO YOUR GAME");
        GUIDisplayText(13, 5, 30, "AND COME BACK WHEN");
        GUIDisplayText(13, 5, 40, "THE NEXT SEARCH");
        GUIDisplayText(13, 5, 50, "SHOULD BE PROCESSED");
        DisplayChtSrcResNoSearch();
    }
}

static void Cheatmodeview(void) // View ResultsWindow
{
    static char GUICSrcTextE[] = "ADDR   VALUE     PVALUE";
    GUICSrcTextE[12] = CheatSrcByteSize == 3 || CheatSrcByteBase != 0 ? ' ' : '\0';

    GUIwinsizex[13] = 185;
    GUIwinsizey[13] = 150;
    DrawWindowSearch();

    GUIDisplayText(13, 10, 12, GUICSrcTextE); // Text

    GUIDisplayBBoxS(13, 5, 20, 171, 108, 167); // Box

    NumCheatSrc = FindChtSrcRes(GUIcurrentchtsrcviewloc);

    // Display Window Contents
    u4 ccheatnleft = NumCheatSrc - GUIcurrentchtsrcviewloc;
    if (ccheatnleft > 12)
        ccheatnleft = 12;

    u4 CheatSearchXPos = 10;
    u4 CheatSearchYPos = 24;
    u4 CSCurEntry = CSStartEntry;

    if (ccheatnleft != 0) {
        u4 curentryleft = GUIcurrentchtsrccursloc - GUIcurrentchtsrcviewloc;
        DrawGUIWinBox2(13, 5, 171, 7, 224, 22 + curentryleft * 7);
        do {
            if (curentryleft-- == 0)
                curentryval = CSCurEntry;

            char GUICSrcTextG1[11];
            converthex(GUICSrcTextG1, CSCurEntry + 0x7E0000, 3);
            GUIDisplayTextG(13, CheatSearchXPos, CheatSearchYPos, GUICSrcTextG1);

            char* const esi = GUICSrcTextG1;
            u4 const eax = *(u4 const*)(wramdata + CSCurEntry); // XXX ugly cast
            if (CheatSrcByteBase != 0) {
                converthex(esi, eax, CheatSrcByteSize + 1);
            } else {
                convertnum(esi, eax & SrcMask[CheatSrcByteSize]);
            }
            CheatSearchXPos += 42;
            GUIDisplayTextG(13, CheatSearchXPos, CheatSearchYPos, GUICSrcTextG1);

            CheatSearchXPos += 60;
            if (GUICSrcTextE[12] != '\0') {
                char* const esi = GUICSrcTextG1;
                u4 const eax = *(u4 const*)(vidbuffer + 129600 + CSCurEntry); // XXX ugly cast
                if (CheatSrcByteBase != 0) {
                    converthex(esi, eax, CheatSrcByteSize + 1);
                } else {
                    convertnum(esi, eax & SrcMask[CheatSrcByteSize]);
                }
                GUIDisplayTextG(13, CheatSearchXPos, CheatSearchYPos, GUICSrcTextG1);
            }
            CheatSearchXPos -= 102;
            CheatSearchYPos += 7;

            do // Search for next entry
            {
                ++CSCurEntry;
            } while (!(vidbuffer[129600 + 65536 * 2 + (CSCurEntry >> 3)] & (1 << (CSCurEntry & 7))));
        } while (--ccheatnleft != 0);
    }
    // win#,X,Y start, %4-List Loc, %5-List size, %6-Screen size, %7-Bar Size
    DrawSlideBar(13, 173, 20, GUIcurrentchtsrcviewloc, NumCheatSrc, 12, 89, GUICSStA, 11, 12);
    DrawGUIButton(13, 70, 140, 130, 152, "RETURN", 54, 0, 1);
    DrawGUIButton(13, 140, 140, 180, 152, "ADD", 55, 0, 1);
}

static void Cheatmodeadd(void) // Add Window
{
    GUIwinsizex[13] = 170;
    GUIwinsizey[13] = 165;
    DrawWindowSearch();

    GUIDisplayText(13, 5, 20, "ENTER NEW VALUE:"); // Text
    GUIDisplayText(13, 5, 45, "ENTER CHEAT DESCRIPTION:");
    GUIDisplayText(13, 5, 70, "PAR CODE EQUIVALENT:");

    GUIDisplayCheckbox(13, 8, 139, &CheatUpperByteOnly, "USE ONLY UPPER BYTE"); // Checkbox

    GUIDisplayBBox(13, 10, 30, 80, 37, 167); // Boxes
    GUIDisplayBBox(13, 10, 55, 126, 62, 167);
    GUIDisplayBBox(13, 10, 80, 80, 120, 167);

    DrawGUIButton(13, 60, 155, 120, 167, "RETURN", 56, 0, 1); // Buttons
    DrawGUIButton(13, 130, 155, 160, 167, "ADD", 57, 0, 1);

    GUIDisplayText(13, 5, 130, "MAX VALUE:"); // Max Value Text
    char GUICSrcTextG1[11];
    u4 const eax = SrcMask[CheatSrcByteSize];
    char* const esi = GUICSrcTextG1;
    if (CheatSrcByteBase != 1) { // dec
        convertnum(esi, eax);
    } else { // hex
        converthex(esi, eax, CheatSrcByteSize + 1);
    }
    GUIDisplayText(13, 71, 130, GUICSrcTextG1);

    // Cheat Input
    if (CurCStextpos != 0 || !(GUICCFlash & 8))
        CSRemoveFlash(CSInputDisplay);
    u1 const col_shadow = CSOverValue == 1 ? 202 : 223;
    u1 const col_text = CSOverValue == 1 ? 207 : GUIWincoladd == 0 ? 221
                                                                   : 222;
    GUIOuttextwin2(13, 13, 32, CSInputDisplay, col_shadow);
    GUIOuttextwin2(13, 12, 31, CSInputDisplay, col_text);
    CSAddFlash(CSInputDisplay);

    // Cheat Desc. Input
    if (CurCStextpos == 1 && !(GUICCFlash & 8))
        CSAddFlash(CSDescDisplay);
    GUIDisplayTextG(13, 13, 57, CSDescDisplay);
    CSRemoveFlash(CSDescDisplay);

    if (CSOverValue != 1 && CSInputDisplay[0] != '_') {
        u4 CheatSearchYPos = 83; // PAR Code?
        curaddrvalcs = curentryval;
        u4 const eax = CSCurValue;
        curvaluecs = eax;
        u1 ecx = CheatSrcByteSize + 1;
        if (CheatUpperByteOnly != 0) {
            ecx = 1;
            while (curvaluecs > 0xFF) {
                curvaluecs >>= 8;
                ++curaddrvalcs;
            }
        }
        do { // Max Value Display?
            converthex(GUICSrcTextG1, curaddrvalcs + 0x7E0000, 3);
            converthex(GUICSrcTextG1 + 6, curvaluecs, 1);
            curvaluecs >>= 8;
            GUIDisplayTextG(13, 13, CheatSearchYPos + 1, GUICSrcTextG1);
            CheatSearchYPos += 10;
            ++curaddrvalcs;
        } while (--ecx != 0);
    }
}

void DisplayGUISearch(void)
{
    switch (CheatWinMode) // Determine which CS window we're on
    {
    case 1:
        Incheatmode();
        return;
    case 2:
        Cheatmodeview();
        return;
    case 3:
        Cheatmodeadd();
        return;
    }

    // Opening Screen
    GUIwinsizex[13] = 170;
    GUIwinsizey[13] = 150;
    DrawWindowSearch();

    // Radio Buttons
    GUIDisplayTextY(13, 6, 16, "SELECT SIZE AND FORMAT:");
    GUIDisplayButtonHoleTu(13, 11, 28, &CheatSrcByteSize, 0, "1 BYTE  [0..255]", 0);
    GUIDisplayButtonHoleTu(13, 11, 38, &CheatSrcByteSize, 1, "2 BYTES [0..65535]", 0);
    GUIDisplayButtonHoleTu(13, 11, 48, &CheatSrcByteSize, 2, "3 BYTES [0..16777215]", 0);
    GUIDisplayButtonHoleTu(13, 11, 58, &CheatSrcByteSize, 3, "4 BYTES [0..4294967295]", 0);
    GUIDisplayButtonHoleTu(13, 11, 73, &CheatSrcByteBase, 0, "DEC (BASE 10)", 0);
    GUIDisplayButtonHoleTu(13, 11, 83, &CheatSrcByteBase, 1, "HEX (BASE 16)", 0);

    GUIDisplayTextY(13, 6, 101, "SELECT SEARCH TYPE:");
    GUIDisplayButtonHoleTu(13, 11, 113, &CheatSrcSearchType, 0, "EXACT VALUE SEARCH", 0);
    GUIDisplayButtonHoleTu(13, 11, 123, &CheatSrcSearchType, 1, "COMPARATIVE SEARCH", 0);

    DrawGUIButton(13, 95, 140, 140, 152, "START", 50, 0, 1);
}

enum {
    NETPLAY_IDLE = 0,
    NETPLAY_WAITING = 1,
    NETPLAY_CONNECTING = 2,
    NETPLAY_CONNECTED = 3,
    NETPLAY_JOINING = 4,
    NETPLAY_HANDSHAKING = 5
};

typedef struct {
    u4 magic;
    u4 session;
    u4 seq;
    u4 joy;
    u4 crc;
} NetplayInputPacket;

static char NetplayStatusLine[64] = "IDLE";
static char NetplayLastEvent[64] = "";
#ifdef __UNIXSDL__
static int NetplayClientSocket = -1;
static int NetplayServerSocket = -1;
#endif
static u1 NetplaySessionState = NETPLAY_IDLE;
#ifdef __UNIXSDL__
static uint16_t const NetplayDefaultPort = 7845;
#endif
static u1 NetplayHostRole = 0;
static u4 NetplayLocalSeq = 0;
static u4 NetplayRemoteSeq = 0;
static u1 NetplayRemoteSeqValid = 0;
static u4 NetplayRemoteJoy = 0x00008000;
static u4 NetplaySessionToken = 0;
char NetplayHostName[32] = "127.0.0.1";
char* GUINetplayTextPtr[1] = { NetplayHostName };
#ifdef __UNIXSDL__
static u1 NetplayPendingRemoteValid = 0;
static u1 NetplayHandshakePending = 0;
static NetplayInputPacket NetplayPendingRemote;
static u4 const NetplayMagic = 0x4E455450; // "NETP"
#endif

#define NETPLAY_INPUT_DELAY 3
#define NETPLAY_FRAME_MS 17

#ifdef __UNIXSDL__
static char NetplayExternalIP[24] = "";
static u4 NetplayInputQueue[NETPLAY_INPUT_DELAY];
static int NetplayInputQueuePos = 0;
static int NetplayInputQueueFilled = 0;
static volatile int NetplayStunActive = 0;
static volatile int NetplayJoinActive = 0;
static volatile int NetplayJoinCancel = 0;
static int NetplayJoinFd = -1;
static int NetplayJoinResult = 0;
static int NetplayJoinIsUDP = 0;
static char NetplayJoinHostCopy[32] = "";
static uint64_t NetplayHandshakeDeadline = 0;

static void NetplaySetNonBlocking(int const fd)
{
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0)
        return;
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

static void NetplaySetTCPOptions(int const fd)
{
    int const one = 1;
    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
#ifdef TCP_KEEPIDLE
    int const idle = 5;
    setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
#endif
#ifdef TCP_KEEPINTVL
    int const intvl = 2;
    setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl));
#endif
#ifdef TCP_KEEPCNT
    int const cnt = 3;
    setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));
#endif
}

static int NetplayWaitFD(int const fd, int const want_write, int timeout_ms)
{
    if (timeout_ms < 0)
        timeout_ms = 0;
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(fd, &fds);
    struct timeval timeout;
    timeout.tv_sec = timeout_ms / 1000;
    timeout.tv_usec = (timeout_ms % 1000) * 1000;
    if (want_write != 0)
        return select(fd + 1, NULL, &fds, NULL, &timeout);
    return select(fd + 1, &fds, NULL, NULL, &timeout);
}

static uint64_t NetplayNowMS(void)
{
    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    return (uint64_t)now.tv_sec * 1000 + (uint64_t)now.tv_nsec / 1000000;
}

static u4 NetplayGenerateSessionToken(void)
{
    u4 token = 0;
    FILE* fp = fopen("/dev/urandom", "rb");

    if (fp != NULL) {
        IGNORE_RESULT(fread(&token, sizeof(token), 1, fp));
        fclose(fp);
    }
    if (token == 0)
        token = (u4)NetplayNowMS() ^ (u4)(uintptr_t)&token ^ (u4)getpid();
    return token != 0 ? token : 1;
}

static int NetplayWaitFDDeadline(int const fd, int const want_write, uint64_t deadline)
{
    uint64_t now = NetplayNowMS();
    uint64_t remaining;

    if (now >= deadline)
        return 0;
    remaining = deadline - now;
    if (remaining > INT_MAX)
        remaining = INT_MAX;
    return NetplayWaitFD(fd, want_write, (int)remaining);
}

static void NetplayPacketHton(NetplayInputPacket* const p)
{
    p->magic = htonl(p->magic);
    p->session = htonl(p->session);
    p->seq = htonl(p->seq);
    p->joy = htonl(p->joy);
    p->crc = htonl(p->crc);
}

static void NetplayPacketNtoh(NetplayInputPacket* const p)
{
    p->magic = ntohl(p->magic);
    p->session = ntohl(p->session);
    p->seq = ntohl(p->seq);
    p->joy = ntohl(p->joy);
    p->crc = ntohl(p->crc);
}

static int NetplaySendExact(int const fd, void const* const data, size_t size, int timeout_ms)
{
    size_t sent = 0;
    char const* ptr = (char const*)data;
    uint64_t const deadline = NetplayNowMS() + (uint64_t)(timeout_ms > 0 ? timeout_ms : 0);
    while (sent < size) {
        if (NetplayWaitFDDeadline(fd, 1, deadline) <= 0)
            return 0;
        int flags = 0;
#ifdef MSG_NOSIGNAL
        flags = MSG_NOSIGNAL;
#endif
        ssize_t n = send(fd, ptr + sent, size - sent, flags);
        if (n > 0) {
            sent += (size_t)n;
            continue;
        }
        if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
            continue;
        return 0;
    }
    return 1;
}

static int NetplayRecvExact(int const fd, void* const data, size_t size, int timeout_ms)
{
    size_t recvd = 0;
    char* ptr = (char*)data;
    uint64_t const deadline = NetplayNowMS() + (uint64_t)(timeout_ms > 0 ? timeout_ms : 0);
    while (recvd < size) {
        if (NetplayWaitFDDeadline(fd, 0, deadline) <= 0)
            return 0;
        ssize_t n = recv(fd, ptr + recvd, size - recvd, 0);
        if (n > 0) {
            recvd += (size_t)n;
            continue;
        }
        if (n == 0)
            return 0;
        if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
            continue;
        return 0;
    }
    return 1;
}

static int NetplaySendPacket(int const fd, NetplayInputPacket const* const packet, int timeout_ms)
{
    NetplayInputPacket wire = *packet;
    NetplayPacketHton(&wire);
    if (NetplayUDPConfig == 0)
        return NetplaySendExact(fd, &wire, sizeof(wire), timeout_ms);
    if (NetplayWaitFD(fd, 1, timeout_ms) <= 0)
        return 0;
    int flags = 0;
#ifdef MSG_NOSIGNAL
    flags = MSG_NOSIGNAL;
#endif
    ssize_t n = send(fd, &wire, sizeof(wire), flags);
    return n == (ssize_t)sizeof(wire);
}

static int NetplaySendPacketTo(int const fd, NetplayInputPacket const* const packet, struct sockaddr const* const address, socklen_t address_len)
{
    NetplayInputPacket wire = *packet;
    NetplayPacketHton(&wire);
    int flags = 0;
#ifdef MSG_NOSIGNAL
    flags = MSG_NOSIGNAL;
#endif
    ssize_t n = sendto(fd, &wire, sizeof(wire), flags, address, address_len);
    return n == (ssize_t)sizeof(wire);
}

static int NetplayIsHandshakePacket(NetplayInputPacket const* const packet, u4 const session)
{
    return packet->magic == NetplayMagic && packet->session == session && packet->seq == 0 && packet->joy == 0x00008000u && packet->crc == 0;
}

static int NetplayRecvPacket(int const fd, NetplayInputPacket* const packet, int timeout_ms)
{
    if (NetplayUDPConfig == 0) {
        if (!NetplayRecvExact(fd, packet, sizeof(*packet), timeout_ms))
            return 0;
        NetplayPacketNtoh(packet);
        return 1;
    }
    uint64_t const deadline = NetplayNowMS() + (uint64_t)(timeout_ms > 0 ? timeout_ms : 0);
    for (;;) {
        if (NetplayWaitFDDeadline(fd, 0, deadline) <= 0)
            return 0;
        NetplayInputPacket tmp;
        ssize_t n = recv(fd, &tmp, sizeof(tmp), 0);
        if (n == (ssize_t)sizeof(tmp)) {
            NetplayPacketNtoh(&tmp);
            if (tmp.magic == NetplayMagic) {
                *packet = tmp;
                return 1;
            }
            continue;
        }
        if (n == 0)
            return 0;
        if (n < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
            continue;
        return 0;
    }
}

static void NetplayFetchExternalIP(void)
{
    // RFC 5389 STUN Binding Request — no attributes, fixed transaction ID
    static u1 const req[20] = {
        0x00,
        0x01, // Binding Request
        0x00,
        0x00, // Attributes length: 0
        0x21,
        0x12,
        0xA4,
        0x42, // Magic cookie
        0x6E,
        0x65,
        0x74,
        0x70, // Transaction ID
        0x6C,
        0x61,
        0x79,
        0x5A,
        0x53,
        0x4E,
        0x45,
        0x53,
    };

    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    struct addrinfo* res = NULL;
    if (getaddrinfo("stun.l.google.com", "19302", &hints, &res) != 0 || res == NULL)
        return;

    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) {
        freeaddrinfo(res);
        return;
    }

    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    sendto(fd, req, sizeof(req), 0, res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);

    u1 resp[256];
    ssize_t n = recv(fd, resp, sizeof(resp), 0);
    close(fd);
    if (n < 20)
        return;

    uint16_t msg_len;
    memcpy(&msg_len, resp + 2, 2);
    msg_len = ntohs(msg_len);

    int pos = 20;
    while (pos + 4 <= (int)n && pos < 20 + (int)msg_len) {
        uint16_t attr_type, attr_len;
        memcpy(&attr_type, resp + pos, 2);
        memcpy(&attr_len, resp + pos + 2, 2);
        attr_type = ntohs(attr_type);
        attr_len = ntohs(attr_len);

        if (attr_type == 0x0020 && attr_len >= 8) { // XOR-MAPPED-ADDRESS
            u4 xaddr;
            memcpy(&xaddr, resp + pos + 8, 4);
            u4 const ip = ntohl(xaddr) ^ 0x2112A442u;
            snprintf(NetplayExternalIP, sizeof(NetplayExternalIP), "%u.%u.%u.%u",
                (ip >> 24) & 0xFFu, (ip >> 16) & 0xFFu,
                (ip >> 8) & 0xFFu, ip & 0xFFu);
            return;
        }
        if (attr_type == 0x0001 && attr_len >= 8) { // MAPPED-ADDRESS fallback
            u4 addr;
            memcpy(&addr, resp + pos + 8, 4);
            u4 const ip = ntohl(addr);
            snprintf(NetplayExternalIP, sizeof(NetplayExternalIP), "%u.%u.%u.%u",
                (ip >> 24) & 0xFFu, (ip >> 16) & 0xFFu,
                (ip >> 8) & 0xFFu, ip & 0xFFu);
            return;
        }
        pos += 4 + (int)((attr_len + 3u) & ~3u);
    }
}

static void* NetplayStunThreadFunc(void* arg)
{
    (void)arg;
    NetplayFetchExternalIP();
    NetplayStunActive = 0;
    return NULL;
}

static void NetplayStartStunLookup(void)
{
    NetplayExternalIP[0] = '\0';
    NetplayStunActive = 1;
    pthread_t t;
    if (pthread_create(&t, NULL, NetplayStunThreadFunc, NULL) == 0)
        pthread_detach(t);
    else
        NetplayStunActive = 0;
}

static u4 NetplayStateHash(void)
{
    if (wramdata == NULL)
        return 0;
    u4 h = 2166136261u;
    for (int i = 0; i < 256; i++) {
        h ^= (u4)wramdata[i];
        h *= 16777619u;
    }
    return h;
}

static void* NetplayJoinThreadFunc(void* arg)
{
    (void)arg;
    int const type = NetplayJoinIsUDP != 0 ? SOCK_DGRAM : SOCK_STREAM;

    struct addrinfo hints, *res = NULL;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = type;
    char portstr[8];
    snprintf(portstr, sizeof(portstr), "%u", (unsigned)NetplayDefaultPort);

    if (getaddrinfo(NetplayJoinHostCopy, portstr, &hints, &res) != 0 || res == NULL || NetplayJoinCancel != 0) {
        if (res)
            freeaddrinfo(res);
        NetplayJoinResult = 0;
        NetplayJoinActive = 0;
        return NULL;
    }

    int fd = socket(AF_INET, type, 0);
    if (fd < 0 || NetplayJoinCancel != 0) {
        if (fd >= 0)
            close(fd);
        freeaddrinfo(res);
        NetplayJoinResult = 0;
        NetplayJoinActive = 0;
        return NULL;
    }

    if (type == SOCK_STREAM) {
        int rc = connect(fd, res->ai_addr, res->ai_addrlen);
        freeaddrinfo(res);
        if (rc != 0 || NetplayJoinCancel != 0) {
            close(fd);
            NetplayJoinResult = 0;
            NetplayJoinActive = 0;
            return NULL;
        }
    } else {
        connect(fd, res->ai_addr, res->ai_addrlen);
        freeaddrinfo(res);
        if (NetplayJoinCancel != 0) {
            close(fd);
            NetplayJoinResult = 0;
            NetplayJoinActive = 0;
            return NULL;
        }
    }

    NetplayInputPacket hello;
    NetplayInputPacket challenge;
    memset(&hello, 0, sizeof(hello));
    hello.magic = NetplayMagic;
    hello.joy = 0x00008000u;
    if (type == SOCK_DGRAM) {
        if (!NetplaySendPacket(fd, &hello, 1000)) {
            close(fd);
            NetplayJoinResult = 0;
            NetplayJoinActive = 0;
            return NULL;
        }
    }
    if (!NetplayRecvPacket(fd, &challenge, 1000) || challenge.magic != NetplayMagic || challenge.session == 0 || challenge.seq != 0 || challenge.joy != 0x00008000u || challenge.crc != 0) {
        close(fd);
        NetplayJoinResult = 0;
        NetplayJoinActive = 0;
        return NULL;
    }
    NetplaySessionToken = challenge.session;
    hello.session = NetplaySessionToken;
    if (!NetplaySendPacket(fd, &hello, 1000)) {
        close(fd);
        NetplayJoinResult = 0;
        NetplayJoinActive = 0;
        return NULL;
    }

    if (NetplayJoinCancel != 0) {
        close(fd);
        NetplayJoinResult = 0;
        NetplayJoinActive = 0;
        return NULL;
    }

    NetplayJoinFd = fd;
    NetplayJoinResult = 1;
    NetplayJoinActive = 0;
    return NULL;
}
#endif

void NetplayDisconnectSession(void)
{
#ifdef __UNIXSDL__
    if (NetplayClientSocket >= 0) {
        close(NetplayClientSocket);
        NetplayClientSocket = -1;
    }
    if (NetplayServerSocket >= 0) {
        close(NetplayServerSocket);
        NetplayServerSocket = -1;
    }
    NetplayInputQueuePos = 0;
    NetplayInputQueueFilled = 0;
    NetplayExternalIP[0] = '\0';
    NetplayLastEvent[0] = '\0';
    NetplayJoinCancel = 1;
    if (NetplayJoinFd >= 0) {
        close(NetplayJoinFd);
        NetplayJoinFd = -1;
    }
#endif
    NetplaySessionState = NETPLAY_IDLE;
    NetplayHostRole = 0;
    NetplayLocalSeq = 0;
    NetplayRemoteSeq = 0;
    NetplayRemoteSeqValid = 0;
    NetplayRemoteJoy = 0x00008000;
    NetplaySessionToken = 0;
#ifdef __UNIXSDL__
    NetplayPendingRemoteValid = 0;
    NetplayHandshakePending = 0;
#endif
}

void NetplayHostSession(void)
{
#ifndef __UNIXSDL__
    strcpy(NetplayStatusLine, "UNSUPPORTED ON THIS PORT");
    NetplaySessionState = NETPLAY_IDLE;
#else
    int const type = NetplayUDPConfig != 0 ? SOCK_DGRAM : SOCK_STREAM;
    int fd = socket(AF_INET, type, 0);
    if (fd < 0) {
        strcpy(NetplayLastEvent, "SERVER SOCKET FAILED");
        return;
    }

    int reuse = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(NetplayDefaultPort);

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
        close(fd);
        strcpy(NetplayLastEvent, "BIND FAILED");
        return;
    }

    NetplayDisconnectSession();
    NetplayHostRole = 1;
    NetplaySessionToken = NetplayGenerateSessionToken();
    NetplaySetNonBlocking(fd);
    if (type == SOCK_STREAM) {
        if (listen(fd, 1) != 0) {
            close(fd);
            strcpy(NetplayLastEvent, "LISTEN FAILED");
            return;
        }
        NetplayServerSocket = fd;
        NetplaySessionState = NETPLAY_WAITING;
        NetplayStartStunLookup();
    } else {
        NetplayClientSocket = fd;
        NetplaySessionState = NETPLAY_WAITING;
        NetplayStartStunLookup();
    }
#endif
}

void NetplayJoinSession(void)
{
#ifndef __UNIXSDL__
    strcpy(NetplayLastEvent, "UNSUPPORTED ON THIS PORT");
    NetplaySessionState = NETPLAY_IDLE;
#else
    if (NetplayJoinActive != 0)
        return;
    NetplayDisconnectSession();
    NetplayHostRole = 0;
    NetplayJoinIsUDP = NetplayUDPConfig != 0 ? 1 : 0;
    NetplayJoinFd = -1;
    NetplayJoinResult = 0;
    NetplayJoinCancel = 0;
    memcpy(NetplayJoinHostCopy, NetplayHostName, sizeof(NetplayJoinHostCopy));
    NetplayJoinActive = 1;
    pthread_t t;
    if (pthread_create(&t, NULL, NetplayJoinThreadFunc, NULL) == 0) {
        pthread_detach(t);
        NetplaySessionState = NETPLAY_JOINING;
    } else {
        NetplayJoinActive = 0;
        strcpy(NetplayLastEvent, "THREAD FAILED");
    }
#endif
}

static void NetplayAdvanceState(int timeout_ms)
{
#ifdef __UNIXSDL__
    if (NetplaySessionState == NETPLAY_WAITING && NetplayUDPConfig == 0 && NetplayServerSocket >= 0) {
        if (NetplayWaitFD(NetplayServerSocket, 0, timeout_ms) > 0) {
            int fd = accept(NetplayServerSocket, NULL, NULL);
            if (fd >= 0) {
                NetplayInputPacket challenge;
                memset(&challenge, 0, sizeof(challenge));
                challenge.magic = NetplayMagic;
                challenge.session = NetplaySessionToken;
                challenge.joy = 0x00008000u;
                if (NetplaySendPacket(fd, &challenge, 1000)) {
                    NetplayClientSocket = fd;
                    NetplaySetNonBlocking(fd);
                    NetplaySetTCPOptions(fd);
                    NetplayHandshakeDeadline = NetplayNowMS() + 1000;
                    NetplaySessionState = NETPLAY_HANDSHAKING;
                    strcpy(NetplayLastEvent, "TCP CLIENT HANDSHAKING");
                } else {
                    close(fd);
                }
            }
        }
    }

    if (NetplaySessionState == NETPLAY_HANDSHAKING && NetplayClientSocket >= 0) {
        if (NetplayNowMS() >= NetplayHandshakeDeadline) {
            close(NetplayClientSocket);
            NetplayClientSocket = -1;
            NetplaySessionState = NETPLAY_WAITING;
            strcpy(NetplayLastEvent, "TCP HANDSHAKE TIMEOUT");
        } else if (NetplayWaitFD(NetplayClientSocket, 0, timeout_ms) > 0) {
            NetplayInputPacket handshake;
            if (NetplayRecvPacket(NetplayClientSocket, &handshake, 1000) && NetplayIsHandshakePacket(&handshake, NetplaySessionToken)) {
                NetplayPendingRemote = handshake;
                NetplayPendingRemoteValid = 1;
                NetplayHandshakePending = 1;
                NetplayInputQueuePos = 0;
                NetplayInputQueueFilled = 0;
                close(NetplayServerSocket);
                NetplayServerSocket = -1;
                NetplaySessionState = NETPLAY_CONNECTED;
                strcpy(NetplayLastEvent, "TCP CLIENT CONNECTED");
            } else {
                close(NetplayClientSocket);
                NetplayClientSocket = -1;
                NetplaySessionState = NETPLAY_WAITING;
                strcpy(NetplayLastEvent, "TCP HANDSHAKE FAILED");
            }
        }
    }

    if (NetplaySessionState == NETPLAY_WAITING && NetplayUDPConfig != 0 && NetplayClientSocket >= 0 && NetplayHostRole != 0) {
        if (NetplayWaitFD(NetplayClientSocket, 0, timeout_ms) > 0) {
            struct sockaddr_in peer;
            socklen_t peer_len = sizeof(peer);
            NetplayInputPacket packet;
            ssize_t n = recvfrom(NetplayClientSocket, &packet, sizeof(packet), 0, (struct sockaddr*)&peer, &peer_len);
            if (n == (ssize_t)sizeof(packet)) {
                NetplayPacketNtoh(&packet);
                if (packet.magic == NetplayMagic && packet.session == 0 && packet.seq == 0 && packet.joy == 0x00008000u && packet.crc == 0) {
                    NetplayInputPacket challenge;
                    memset(&challenge, 0, sizeof(challenge));
                    challenge.magic = NetplayMagic;
                    challenge.session = NetplaySessionToken;
                    challenge.joy = 0x00008000u;
                    NetplaySendPacketTo(NetplayClientSocket, &challenge, (struct sockaddr*)&peer, peer_len);
                } else if (NetplayIsHandshakePacket(&packet, NetplaySessionToken) && connect(NetplayClientSocket, (struct sockaddr*)&peer, peer_len) == 0) {
                    NetplayPendingRemote = packet;
                    NetplayPendingRemoteValid = 1;
                    NetplayHandshakePending = 1;
                    NetplayInputQueuePos = 0;
                    NetplayInputQueueFilled = 0;
                    NetplaySessionState = NETPLAY_CONNECTED;
                    strcpy(NetplayLastEvent, "UDP PEER CONNECTED");
                }
            }
        }
    }

    if (NetplaySessionState == NETPLAY_CONNECTING && NetplayClientSocket >= 0) {
        if (NetplayWaitFD(NetplayClientSocket, 1, timeout_ms) > 0) {
            int so_error = 0;
            socklen_t len = sizeof(so_error);
            if (getsockopt(NetplayClientSocket, SOL_SOCKET, SO_ERROR, &so_error, &len) == 0 && so_error == 0) {
                NetplaySetTCPOptions(NetplayClientSocket);
                NetplayInputQueuePos = 0;
                NetplayInputQueueFilled = 0;
                NetplaySessionState = NETPLAY_CONNECTED;
                strcpy(NetplayLastEvent, "TCP CONNECTED");
            } else if (so_error != 0) {
                NetplayDisconnectSession();
                strcpy(NetplayLastEvent, "CONNECT FAILED");
            }
        }
    }

    if (NetplaySessionState == NETPLAY_JOINING && NetplayJoinActive == 0) {
        if (NetplayJoinResult != 0 && NetplayJoinFd >= 0) {
            int const fd = NetplayJoinFd;
            NetplayJoinFd = -1;
            NetplaySetNonBlocking(fd);
            if (NetplayJoinIsUDP == 0)
                NetplaySetTCPOptions(fd);
            NetplayClientSocket = fd;
            NetplayInputQueuePos = 0;
            NetplayInputQueueFilled = 0;
            NetplaySessionState = NETPLAY_CONNECTED;
            strcpy(NetplayLastEvent, NetplayJoinIsUDP != 0 ? "UDP CONNECTED" : "TCP CONNECTED");
        } else {
            NetplaySessionState = NETPLAY_IDLE;
            strcpy(NetplayLastEvent, "CONNECT FAILED");
        }
    }
#else
    (void)timeout_ms;
#endif
}

void NetplaySyncInputs(unsigned int* joy_a, unsigned int* joy_b)
{
#ifdef __UNIXSDL__
    NetplayAdvanceState(0);
    if (NetplaySessionState != NETPLAY_CONNECTED || NetplayClientSocket < 0)
        return;

    u4 const raw = NetplayHostRole != 0 ? (u4)*joy_a : (u4)*joy_b;

    // Delay local input by NETPLAY_INPUT_DELAY frames so the peer has time to
    // receive it before the frame executes — absorbs up to ~50ms of jitter.
    u4 delayed;
    if (NetplayInputQueueFilled < NETPLAY_INPUT_DELAY) {
        delayed = 0x00008000u; // neutral during warmup
        NetplayInputQueueFilled++;
    } else {
        delayed = NetplayInputQueue[NetplayInputQueuePos];
    }
    NetplayInputQueue[NetplayInputQueuePos] = raw;
    NetplayInputQueuePos = (NetplayInputQueuePos + 1) % NETPLAY_INPUT_DELAY;

    if (NetplayHostRole != 0)
        *joy_a = delayed;
    else
        *joy_b = delayed;

    u4 const local_crc = NetplayStateHash();

    NetplayInputPacket local;
    local.magic = NetplayMagic;
    local.session = NetplaySessionToken;
    local.seq = NetplayLocalSeq++;
    local.joy = delayed;
    local.crc = local_crc;

    int const timeout = NETPLAY_INPUT_DELAY * NETPLAY_FRAME_MS;

    NetplayInputPacket remote;
    int ok = 0;
    if (NetplayHostRole != 0) {
        if (NetplayHandshakePending != 0) {
            NetplayInputPacket handshake;
            if (NetplayPendingRemoteValid != 0) {
                handshake = NetplayPendingRemote;
                NetplayPendingRemoteValid = 0;
                ok = 1;
            } else {
                ok = NetplayRecvPacket(NetplayClientSocket, &handshake, timeout);
            }
            if (ok == 0 || !NetplayIsHandshakePacket(&handshake, NetplaySessionToken)) {
                NetplayDisconnectSession();
                strcpy(NetplayLastEvent, "INVALID HANDSHAKE");
                return;
            }
            NetplayHandshakePending = 0;
        }
        ok = NetplayRecvPacket(NetplayClientSocket, &remote, timeout);
        if (ok != 0)
            ok = NetplaySendPacket(NetplayClientSocket, &local, timeout);
    } else {
        ok = NetplaySendPacket(NetplayClientSocket, &local, timeout);
        if (ok != 0)
            ok = NetplayRecvPacket(NetplayClientSocket, &remote, timeout);
    }

    if (ok == 0) {
        NetplayDisconnectSession();
        strcpy(NetplayLastEvent, "CONNECTION LOST");
        return;
    }

    if (remote.magic != NetplayMagic || remote.session != NetplaySessionToken || (NetplayRemoteSeqValid != 0 && remote.seq != NetplayRemoteSeq + 1)) {
        NetplayDisconnectSession();
        strcpy(NetplayLastEvent, "INVALID PEER PACKET");
        return;
    }

    if (local_crc != 0 && remote.crc != 0 && remote.crc != local_crc && NetplayLocalSeq > NETPLAY_INPUT_DELAY + 2)
        strcpy(NetplayLastEvent, "DESYNC DETECTED");

    NetplayRemoteSeq = remote.seq;
    NetplayRemoteSeqValid = 1;
    NetplayRemoteJoy = remote.joy;
    if (NetplayHostRole != 0)
        *joy_b = NetplayRemoteJoy;
    else
        *joy_a = NetplayRemoteJoy;
#else
    (void)joy_a;
    (void)joy_b;
#endif
}

static void NetplayUpdateStatus(void)
{
#ifdef __UNIXSDL__
    switch ((int)NetplaySessionState) {
    case NETPLAY_IDLE:
        snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "%s",
            NetplayLastEvent[0] != '\0' ? NetplayLastEvent : "IDLE");
        return;
    case NETPLAY_WAITING:
        if (NetplayStunActive == 0 && NetplayExternalIP[0] != '\0')
            snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "%s - EXT: %s",
                NetplayUDPConfig != 0 ? "WAITING UDP" : "WAITING TCP",
                NetplayExternalIP);
        else
            snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "%s",
                NetplayUDPConfig != 0 ? "WAITING FOR UDP PEER" : "WAITING FOR TCP CLIENT");
        return;
    case NETPLAY_CONNECTING:
        snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "CONNECTING (TCP)");
        return;
    case NETPLAY_HANDSHAKING:
        snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "HANDSHAKING (TCP)");
        return;
    case NETPLAY_JOINING:
        snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "RESOLVING %s...", NetplayJoinHostCopy);
        return;
    case NETPLAY_CONNECTED:
        snprintf(NetplayStatusLine, sizeof(NetplayStatusLine), "%s",
            NetplayLastEvent[0] != '\0' ? NetplayLastEvent : "CONNECTED");
        return;
    }
#endif
}

void DisplayNetOptns(void)
{
    NetplayAdvanceState(0);
    NetplayUpdateStatus();

    GUIwinsizex[8] = 220;
    GUIwinsizey[8] = 96;
    GUIDrawWindowBox(8, "NETPLAY");

    GUIDisplayTextY(8, 6, 16, "STATUS:");
    GUIDisplayTextG(8, 50, 16, NetplayStatusLine);
    GUIDisplayText(8, 6, 28, "HOST:");
    GUIDisplayBBox(8, 38, 24, 214, 34, 167);
    GUIOuttextwin2d(8, 40, 28, NetplayHostName, 26, GUINetplayTextPtr, 0);
    GUIDisplayText(8, 6, 46, NetplayHostRole != 0 ? "ROLE: HOST (P1)" : "ROLE: CLIENT (P2)");
    GUIDisplayText(8, 6, 56, NetplayUDPConfig != 0 ? "PORT: 7845 (UDP LOCKSTEP)" : "PORT: 7845 (TCP LOCKSTEP)");
    GUIDisplayCheckbox(8, 8, 66, &NetplayUDPConfig, "USE UDP");

    DrawGUIButton(8, 8, 74, 56, 85, "HOST", 85, 0, 0);
    DrawGUIButton(8, 66, 74, 114, 85, "JOIN", 86, 0, 0);
    DrawGUIButton(8, 124, 74, 212, 85, "DISCONNECT", 87, 0, 0);
}

void DisplayGameOptns(void)
{
    GUIDrawWindowBox(9, "MISC KEYS");

    GUIDisplayTextY(9, 6, 16, "BG DISABLES:");
    GUIDisplayText(9, 9, 25, "BG1");
    GUIDisplayText(9, 9 + 45, 25, "BG2");
    GUIDisplayText(9, 9 + 90, 25, "BG3");
    GUIDisplayText(9, 9 + 135, 25, "BG4");
    GUIDisplayText(9, 9 + 180, 25, "OBJ");

    GUIDisplayTextY(9, 6, 34, "SOUND KEYS:");
    GUIDisplayText(9, 9, 43, "CH1");
    GUIDisplayText(9, 9 + 45, 43, "CH2");
    GUIDisplayText(9, 9 + 45 * 2, 43, "CH3");
    GUIDisplayText(9, 9 + 45 * 3, 43, "CH4");
    GUIDisplayText(9, 9 + 45 * 4, 43, "+VOL");
    GUIDisplayText(9, 9, 52, "CH5");
    GUIDisplayText(9, 9 + 45, 52, "CH6");
    GUIDisplayText(9, 9 + 45 * 2, 52, "CH7");
    GUIDisplayText(9, 9 + 45 * 3, 52, "CH8");
    GUIDisplayText(9, 9 + 45 * 4, 52, "-VOL");

    GUIDisplayTextY(9, 6, 61, "QUICK KEYS:");
    GUIDisplayText(9, 9, 72, "LOAD");
    GUIDisplayText(9, 9 + 52, 72, "RESET");
    GUIDisplayText(9, 9 + 109, 72, "EXIT");
    GUIDisplayText(9, 9 + 160, 72, "CLOCK");
    GUIDisplayText(9, 9, 82, "CHAT");
    GUIDisplayText(9, 9 + 52, 82, "SNAPSHOT");
    GUIDisplayText(9, 137, 82, "SAVE SPC");

    GUIDisplayTextY(9, 6, 93, "MISC TOGGLES:");
    GUIDisplayText(9, 9, 102, "USE PL12/34");
    GUIDisplayText(9, 9, 112, "PANIC KEY");
    GUIDisplayText(9, 9, 122, "DISPLAY FPS");
    GUIDisplayText(9, 9, 132, "BATT POWER");
    GUIDisplayText(9, 9, 142, "CPU USAGE");

    GUIDisplayTextY(9, 119, 93, "GFX TOGGLES:");
    GUIDisplayText(9, 122, 102, "NEW GFX ENG");
    GUIDisplayText(9, 122, 112, "BG WINDOW");
    GUIDisplayText(9, 122, 122, "OFFSET MODE");
    GUIDisplayText(9, 122, 132, "+ GAMMA");
    GUIDisplayText(9, 122, 142, "- GAMMA");

    // Draw black boxes
    DDrawBox(9, 26, 22, &KeyBGDisble0);
    DDrawBox(9, 71, 22, &KeyBGDisble1);
    DDrawBox(9, 116, 22, &KeyBGDisble2);
    DDrawBox(9, 161, 22, &KeyBGDisble3);
    DDrawBox(9, 206, 22, &KeySprDisble);

    DDrawBox(9, 26, 40, &KeyDisableSC0);
    DDrawBox(9, 71, 40, &KeyDisableSC1);
    DDrawBox(9, 116, 40, &KeyDisableSC2);
    DDrawBox(9, 161, 40, &KeyDisableSC3);
    DDrawBox(9, 213, 40, &KeyVolUp);
    DDrawBox(9, 26, 49, &KeyDisableSC4);
    DDrawBox(9, 71, 49, &KeyDisableSC5);
    DDrawBox(9, 116, 49, &KeyDisableSC6);
    DDrawBox(9, 161, 49, &KeyDisableSC7);
    DDrawBox(9, 213, 49, &KeyVolDown);

    DDrawBox(9, 32, 69, &KeyQuickLoad);
    DDrawBox(9, 90, 69, &KeyQuickRst);
    DDrawBox(9, 141, 69, &KeyQuickExit);
    DDrawBox(9, 199, 69, &KeyQuickClock);
    DDrawBox(9, 32, 79, &KeyQuickChat);
    DDrawBox(9, 109, 79, &KeyQuickSnapShot);
    DDrawBox(9, 185, 79, &KeyQuickSaveSPC);

    DDrawBox(9, 77, 99, &KeyUsePlayer1234);
    DDrawBox(9, 77, 109, &KeyResetAll);
    DDrawBox(9, 77, 119, &KeyDisplayFPS);
    DDrawBox(9, 77, 129, &KeyDisplayBatt);
    DDrawBox(9, 77, 139, &KeyDisplayCPU);

    DDrawBox(9, 190, 99, &KeyNewGfxSwt);
    DDrawBox(9, 190, 109, &KeyWinDisble);
    DDrawBox(9, 190, 119, &KeyOffsetMSw);
    DDrawBox(9, 190, 129, &KeyIncreaseGamma);
    DDrawBox(9, 190, 139, &KeyDecreaseGamma);
}

static u1 GUICslidSet(void const* const p1) // slider var
{
    return 2 + *(u1 const*)p1 * 4;
}

static char const* GUICslidText(void const* p1) // slider var, text
{
    static char GUIGUIOptnsTextD2[12];
    sprintf(GUIGUIOptnsTextD2, "%2u", *(u1 const*)p1);
    return GUIGUIOptnsTextD2;
}

void DisplayGUIOptns(void)
{
#ifdef __WIN32__ // If Windows, extend window down
    GUIwinsizey[10] = 192;
#endif
    GUIDrawWindowBox(10, "GUI OPTIONS");

    // Setup Colors
    u1 TRVal2;
    u1 TGVal2;
    u1 TBVal2;
    switch (CurPalSelect) {
    default:
        TRVal2 = GUIRAdd;
        TGVal2 = GUIGAdd;
        TBVal2 = GUIBAdd;
        break;
    case 1:
        TRVal2 = GUITRAdd;
        TGVal2 = GUITGAdd;
        TBVal2 = GUITBAdd;
        break;
    case 2:
        TRVal2 = GUIWRAdd;
        TGVal2 = GUIWGAdd;
        TBVal2 = GUIWBAdd;
        break;
    }

    GUIDrawSlider(10, 25, 127, 124, &TRVal2, GUICslidSet, GUICslidText);
    GUIDrawSlider(10, 25, 127, 136, &TGVal2, GUICslidSet, GUICslidText);
    GUIDrawSlider(10, 25, 127, 148, &TBVal2, GUICslidSet, GUICslidText);

    GUIDisplayTextY(10, 6, 16, "GUI SWITCHES:");

    // Checkboxes
    GUIDisplayCheckboxu(10, 12, 23, &GUIRClick, "RCLICK OPENS GUI", 1);
    GUIDisplayCheckboxu(10, 12, 33, &lhguimouse, "SWAP L/R MBUTTONS", 6);
    GUIDisplayCheckboxu(10, 12, 43, &mouseshad, "SHOW MOUSE SHADOW", 0);
    GUIDisplayCheckboxu(10, 12, 53, &mousewrap, "MICE WRAP GUI WIN", 0);
#ifdef __WIN32__
    GUIDisplayCheckboxu(10, 12, 63, &TrapMouseCursor, "TRAP MOUSE CURSOR", 3);
    GUIDisplayCheckboxu(10, 12, 73, &MouseWheel, "WHEEL MICE SCROLL", 1);
#endif

    GUIDisplayCheckboxu(10, 129, 23, &esctomenu, "ESC TO GAME MENU", 7);
    GUIDisplayCheckboxu(10, 129, 33, &JoyPad1Move, "CTRL GUI W/GPAD1", 6);
    GUIDisplayCheckboxu(10, 129, 43, &FilteredGUI, "FILTERED GUI", 0);
    GUIDisplayCheckboxu(10, 129, 53, &newfont, "USE CUSTOM FONT", 12);
    GUIDisplayCheckboxu(10, 129, 63, &savewinpos, "SAVE GUI WIN POS", 9);

    GUIDisplayTextY(10, 6, 91, "BG EFFECTS:");
    GUIDisplayButtonHoleTu(10, 72, 88, &GUIEffect, 0, "NONE", 3);
    GUIDisplayButtonHoleTu(10, 122, 88, &GUIEffect, 1, "SNOW", 1);
    GUIDisplayButtonHoleTu(10, 182, 88, &GUIEffect, 4, "BURNING", 2);
    GUIDisplayButtonHoleTu(10, 72, 98, &GUIEffect, 5, "SMOKE", 3);
    GUIDisplayButtonHoleTu(10, 122, 98, &GUIEffect, 2, "WATER A", 6);
    GUIDisplayButtonHoleTu(10, 182, 98, &GUIEffect, 3, "WATER B", 6);

    GUIDisplayTextY(10, 6, 111, "COLOR:");
    GUIDisplayText(10, 60, 111, "BACK");
    GUIDisplayText(10, 100, 111, "TITLE");
    GUIDisplayText(10, 145, 111, "WIN");

#ifdef __WIN32__
    GUIDisplayTextY(10, 6, 161, "MAIN WINDOW OPTIONS:");
    GUIDisplayCheckboxu(10, 12, 168, &AlwaysOnTop, "EMU ALWAYS ON TOP", 14);
    GUIDisplayCheckboxu(10, 12, 178, &SaveMainWindowPos, "SAVE MAIN WINDOW POSITION", 2);
    GUIDisplayCheckboxu(10, 12, 188, &AllowMultipleInst, "ALLOW MULTIPLE INSTANCES OF EMU", 1);
#endif

    GUIDisplayText(10, 16, 123, "R");
    GUIDisplayText(10, 16, 135, "G");
    GUIDisplayText(10, 16, 147, "B");
    // Radio Buttons
    GUIDisplayButtonHole(10, 48, 108, &CurPalSelect, 0);
    GUIDisplayButtonHole(10, 88, 108, &CurPalSelect, 1);
    GUIDisplayButtonHole(10, 133, 108, &CurPalSelect, 2);
}

void DisplayGUIAbout(void)
{
    static char const about_version[] = "ZSNES V" ZVER "  " __DATE__;
    char compiled_for[64];
    char compiled_with[96] = "";
    char compiled_with_next[96] = "";
    size_t library_count = 0;

    while (VERSION_LIBRARIES[library_count])
        library_count++;

    snprintf(compiled_for, sizeof(compiled_for), "Compiled for %s (%s)%c",
        VERSION_PLATFORM, VERSION_ARCH, library_count ? ',' : '.');

    if (library_count) {
        strcpy(compiled_with, "with ");
        for (size_t i = 0; i < library_count; i++) {
            if (i)
                strcat(compiled_with, i + 1 == library_count ? " and " : ", ");
            strcat(compiled_with, VERSION_LIBRARIES[i]);
        }
        strcat(compiled_with, ".");

        if (strlen(compiled_with) > 34) {
            char* split = compiled_with + 34;
            while (split != compiled_with && *split != ' ')
                split--;
            strcpy(compiled_with_next, split + 1);
            *split = '\0';
        }
    }

    if (GUIwinposx[11] + GUIwinsizex[11] > 255)
        GUIwinposx[11] = 255 - GUIwinsizex[11];

    GUIDrawWindowBox(11, "ABOUT");
    if (EEgg != 1) {
        GUIDisplayText(11, 6, 16, about_version);
        DrawGUIButton(11, 70, 28, 165, 38, "GITHUB PROJECT", 65, 0, 0);
        GUIDisplayText(11, 6, 56, compiled_for);
        if (library_count)
            GUIDisplayText(11, 6, 66, compiled_with);
        if (compiled_with_next[0])
            GUIDisplayText(11, 6, 76, compiled_with_next);
        GUIDisplayText(11, 15, 121, "ZSNES is released under");
        GUIDisplayText(11, 15, 131, "the GPL2 license. See the");
        GUIDisplayText(11, 15, 141, "contents of the `COPYING`");
        GUIDisplayText(11, 15, 151, "file for more information.");
    } else { // Playground
        GUIDisplayText(11, 42, 36, "HIDDEN MESSAGE!");
        GUIDisplayText(11, 30, 96, "PRESS 'E' TO RETURN");
        GUIDisplayText(11, 39, 106, "TO THE ABOUT BOX");
    }
}

void DisplayGUIMovies(void)
{
    GUIDrawWindowBox(15, "MOVIE OPTIONS"); // Display Window

    u4 eax;
    u4 ebx;
    GUIDrawTArea(15, &eax, &ebx);
    if (MovieProcessing < 4 || 6 < MovieProcessing)
        GUIDrawTabs(GUIMovieTabs, &eax, ebx);
    if (MovieProcessing < 1 || 3 < MovieProcessing)
        GUIDrawTabs(GUIDumpingTab, &eax, ebx);

    if (RawDumpInProgress) {
        GUIMovieTabs[0] = 0;
        GUIDumpingTab[0] = 1;
    }

    if (MovieRecordWinVal != 0) {
        GUIDisplayText(15, 9, 26, "WARNING: THIS MOVIE"); // Overwrite Message Box
        GUIDisplayText(15, 9, 36, "FILE ALREADY EXISTS");
        GUIDisplayText(15, 9, 51, "OKAY TO OVERWRITE ?");

        DrawGUIButton(15, 17, 65, 59, 76, "YES", 19, 0, 0); // Yes/No Buttons
        DrawGUIButton(15, 70, 65, 112, 76, "NO", 20, 0, 0);
    } else {
        // Main Window
        GUIDisplayTextY(15, 8, 31, "SELECT MOVIE:"); // Slot text
        GUIDisplayText(15, 20, 42, "0");
        GUIDisplayText(15, 40, 42, "1");
        GUIDisplayText(15, 60, 42, "2");
        GUIDisplayText(15, 80, 42, "3");
        GUIDisplayText(15, 100, 42, "4");
        GUIDisplayText(15, 120, 42, "5");
        GUIDisplayText(15, 140, 42, "6");
        GUIDisplayText(15, 160, 42, "7");
        GUIDisplayText(15, 180, 42, "8");
        GUIDisplayText(15, 200, 42, "9");

        // Display Radio buttons
        GUIDisplayButtonHole(15, 8, 39, (u1 const*)&CMovieExt, 'v'); // XXX ugly cast
        GUIDisplayButtonHole(15, 28, 39, (u1 const*)&CMovieExt, '1'); // XXX ugly cast
        GUIDisplayButtonHole(15, 48, 39, (u1 const*)&CMovieExt, '2'); // XXX ugly cast
        GUIDisplayButtonHole(15, 68, 39, (u1 const*)&CMovieExt, '3'); // XXX ugly cast
        GUIDisplayButtonHole(15, 88, 39, (u1 const*)&CMovieExt, '4'); // XXX ugly cast
        GUIDisplayButtonHole(15, 108, 39, (u1 const*)&CMovieExt, '5'); // XXX ugly cast
        GUIDisplayButtonHole(15, 128, 39, (u1 const*)&CMovieExt, '6'); // XXX ugly cast
        GUIDisplayButtonHole(15, 148, 39, (u1 const*)&CMovieExt, '7'); // XXX ugly cast
        GUIDisplayButtonHole(15, 168, 39, (u1 const*)&CMovieExt, '8'); // XXX ugly cast
        GUIDisplayButtonHole(15, 188, 39, (u1 const*)&CMovieExt, '9'); // XXX ugly cast

        // Determine and Display Status
        char const* status;
        switch (MovieProcessing) {
        default:
            status = "INACTIVE        ";
            break;
        case 1:
            status = "PLAYING         ";
            break;
        case 2:
            status = "RECORDING       ";
            break;
        case 3:
            status = "OLD PLAYING     ";
            break;
        case 4:
            status = "DUMPING ENDING  ";
            break;
        case 5:
            status = "DUMPING         ";
            break;
        case 6:
            status = "DUMPING OLD     ";
            break;
        }
        static char GUIMovieTextZ[] = "STATUS:                  ";
        strcpy(GUIMovieTextZ + 8, status);
        GUIDisplayTextY(15, 6, 192, GUIMovieTextZ);

        if (GUIMovieTabs[0] == 1) {
            GUIDisplayTextY(15, 8, 56, "RECORD FROM:"); // "Start From" Section
            GUIDisplayTextY(15, 8, 100, "CHAPTERS:"); // Chapters

            GUIDisplayButtonHoleTu(15, 8, 64, &MovieStartMethod, 0, "NOW", 0);
            GUIDisplayButtonHoleTu(15, 43, 64, &MovieStartMethod, 1, "POWER", 0);
            GUIDisplayButtonHoleTu(15, 89, 64, &MovieStartMethod, 2, "RESET", 0);
            GUIDisplayButtonHoleTu(15, 135, 64, &MovieStartMethod, 3, "POWER+SRAM CLEAR", 6);

            DrawGUIButton(15, 7, 80, 49, 91, "PLAY", 16, 0, 0); // Draw Buttons
            DrawGUIButton(15, 55, 80, 97, 91, "RECORD", 17, 0, 0);
            DrawGUIButton(15, 103, 80, 145, 91, "STOP", 18, 0, 0);
            DrawGUIButton(15, 151, 80, 193, 91, "APPEND", 32, 0, 0);
            DrawGUIButton(15, 7, 108, 50, 119, "INSERT", 29, 0, 0);
            DrawGUIButton(15, 85, 108, 138, 119, "PREVIOUS", 30, 0, 0);
            DrawGUIButton(15, 173, 108, 203, 119, "NEXT", 31, 0, 0);

            DDrawBox(15, 57, 109, &KeyInsrtChap); // Chapter Keyboard Shortcut Boxes
            DDrawBox(15, 145, 109, &KeyPrevChap);
            DDrawBox(15, 210, 109, &KeyNextChap);

            GUIDisplayTextY(15, 8, 125, "ON MOVIE STATE LOAD:"); // Movie State Load

            GUIDisplayButtonHoleTu(15, 8, 133, &MZTForceRTR, 0, "DO NOT SWITCH MODES", 14);
            GUIDisplayButtonHoleTu(15, 8, 143, &MZTForceRTR, 1, "SWITCH TO RECORD", 12);
            GUIDisplayButtonHoleTu(15, 8, 153, &MZTForceRTR, 2, "SWITCH TO PLAYBACK", 14);

            DDrawBox(15, 134, 123, &KeyRTRCycle); // MZT Load Shortcut Box

            GUIDisplayCheckboxu(15, 8, 163, &MovieDisplayFrame, "DISPLAY FRAME COUNTER", 0); // Checkbox
        }

        if (GUIDumpingTab[0] == 1) {
            GUIDisplayTextY(15, 8, 56, "VIDEO OPTIONS:"); // Video Section
            GUIDisplayButtonHoleTu(15, 8, 64, &MovieVideoMode, 0, "NO VIDEO DUMP", 1);
            GUIDisplayButtonHoleTu(15, 8, 74, &MovieVideoMode, 1, "RAW VIDEO", 2);
            GUIDisplayButtonHoleTu(15, 8, 84, &MovieVideoMode, 2, "FFV1", 0);
            GUIDisplayButtonHoleTu(15, 8, 94, &MovieVideoMode, 3, "X264 LOSSLESS", 9);
            GUIDisplayButtonHoleTu(15, 8, 104, &MovieVideoMode, 4, "XVID LOSSLESS", 0);
            GUIDisplayButtonHoleTu(15, 8, 114, &MovieVideoMode, 5, "CUSTOM", 0);

            GUIDisplayTextY(15, 129, 56, "AUDIO OPTIONS:"); // Audio Section
            GUIDisplayTextY(15, 162, 171, "DUMPING:");

            if (MovieVideoMode != 5) {
                GUIDisplayCheckboxu(15, 130, 62, &MovieAudio, "DUMP AUDIO", 5);
                if (MovieAudio == 1)
                    goto mux;
            } else {
            mux:
                GUIDisplayCheckboxu(15, 130, 72, &MovieAudioCompress, "COMPRESS AUDIO", 2);
                if ((s1)MovieVideoMode >= 2 && MovieVideoMode != 5) // XXX ugly cast
                {
                    GUIDisplayCheckboxu(15, 130, 82, &MovieVideoAudio, "MERGE WITH VIDEO", 11);
                }
            }

            DrawGUIButton(15, 165, 178, 200, 189, "START", 34, 0, 0);
            DrawGUIButton(15, 206, 178, 235, 189, "STOP", 35, 0, 0);

            GUIDisplayTextY(15, 8, 127, "DUMPING LENGTH:"); // Video Section
            GUIDisplayButtonHoleTu(15, 8, 135, &MovieForcedLengthEnabled, 0, "ZMV LENGTH", 0);
            GUIDisplayButtonHoleTu(15, 8, 145, &MovieForcedLengthEnabled, 1, "DUMP # OF FRAMES", 11);
            GUIDisplayButtonHoleTu(15, 8, 155, &MovieForcedLengthEnabled, 2, "UNTIL STOP", 0);

            GUIDisplayBBox(15, 136, 144, 205, 154, 167);

            GetMovieForcedLength();

            GUIOuttextwin2d(15, 139, 148, GUIMovieForcedText, 10, GUIMovieTextPtr, 0);
        }
    }
}

static void DrawBorderedBox(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5)
{
    // draw borders
    DrawGUIWinBox(p1, p2, p3 - 1, p4, p3, GUIWincol);
    DrawGUIWinBox(p1, p2 - 1, p3, p2 - 2, p5, GUIWincol + 1);
    DrawGUIWinBox(p1, p2, p5, p4, p5 + 1, GUIWincol + 4);
    DrawGUIWinBox(p1, p4, p3, p4 + 1, p5, GUIWincol + 3);
    DrawGUIWinBox(p1, p2, p3, p4, p5, 167);
}

static void DrawBorderedBoxB(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, char const* const p7) // Special function for combo displays
{
    DrawBorderedBox(p1, p2, p3, p4, p5);
    GUIDisplayTextG(p1, p2 + 5, p3 + 2, p7);
}

static void DrawBorderedBoxB2(u4 const p1, u4 const p2, u4 const p3, u4 const p4, u4 const p5, char const* const p7) // Special function for combo displays
{
    DrawBorderedBox(p1, p2, p3, p4, p5);
    GUIDisplayTextG(p1, p2 + 2, p3 + 2, p7);
}

// Key types: Up, Down, Left, Right, A, B, X, Y, L, R (Press/Relase/P+R)
// Frame delays: 1 frame, 2, 3, 4, 5, 1 sec., 2, 3, 4, 5
void DisplayGUICombo(void)
{
    if (GUIccomblcursloc != GUIccombcursloc) {
        GUIccomblcursloc = GUIccombcursloc;
        // copy contents into temporary variables
        ComboData const* const esi = &(GUIComboGameSpec == 0 ? CombinDataGlob : CombinDataLocl)[GUIccombcursloc];
        memcpy(GUIComboTextH, esi->name, sizeof(esi->name));
        memcpy(GUIComboData, esi->combo, sizeof(esi->combo));
        GUIComboKey = (GUIComboKey & 0xFFFF0000) | (esi->key);
        GUIComboPNum = esi->player;
        GUIComboLHorz = esi->ff;
        // determine length of combo data
        u1 const* eax = GUIComboData;
        u4 ecx = 0;
        while (*eax++ != 0)
            ++ecx;
        GUINumCombo = ecx;
    }

    // copy into data if description equal
    {
        ComboData* const edi = &(GUIComboGameSpec == 0 ? CombinDataGlob : CombinDataLocl)[GUIccombcursloc];
        if (strncmp(edi->name, GUIComboTextH, 20) == 0) {
            ComboClip();
            memcpy(edi->name, GUIComboTextH, sizeof(edi->name));
            memcpy(edi->combo, GUIComboData, sizeof(edi->combo));
            edi->key = GUIComboKey;
            edi->player = GUIComboPNum;
            edi->ff = GUIComboLHorz;
        }
    }

    GUIDrawWindowBox(16, "KEY COMBINATION EDITOR");

    DrawBorderedBox(16, 10, 20, 190, 80);
    DrawBorderedBox(16, 37, 91, 157, 99);
    DrawBorderedBox(16, 10, 91, 32, 99);
    DrawBorderedBox(16, 10, 110, 220, 146);

    // win#,X,Y start,List Loc,List size,# Lines,Bar Size(Y),UpArrowResource#,DownArrowRes#
    DrawSlideBar(16, 192, 20, GUIccombviewloc, NumCombo, 8, 61, GUICSStC, 13, 14);

    // Draw control boxes
    DrawBorderedBoxB(16, 75, 150, 85, 157, "\xFB");
    DrawBorderedBoxB(16, 89, 150, 99, 157, "\xFC");
    DrawBorderedBoxB(16, 103, 150, 113, 157, "\xFD");
    DrawBorderedBoxB(16, 117, 150, 127, 157, "\xFE");
    DrawBorderedBoxB(16, 131, 150, 141, 157, "A");
    DrawBorderedBoxB(16, 145, 150, 155, 157, "B");
    DrawBorderedBoxB(16, 159, 150, 169, 157, "X");
    DrawBorderedBoxB(16, 173, 150, 183, 157, "Y");
    DrawBorderedBoxB(16, 187, 150, 197, 157, "L");
    DrawBorderedBoxB(16, 201, 150, 211, 157, "R");
    DrawBorderedBoxB2(16, 215, 150, 227, 157, "ST");
    DrawBorderedBoxB2(16, 231, 150, 243, 157, "SL");

    DrawBorderedBoxB(16, 75, 160, 85, 167, "\xFB");
    DrawBorderedBoxB(16, 89, 160, 99, 167, "\xFC");
    DrawBorderedBoxB(16, 103, 160, 113, 167, "\xFD");
    DrawBorderedBoxB(16, 117, 160, 127, 167, "\xFE");
    DrawBorderedBoxB(16, 131, 160, 141, 167, "A");
    DrawBorderedBoxB(16, 145, 160, 155, 167, "B");
    DrawBorderedBoxB(16, 159, 160, 169, 167, "X");
    DrawBorderedBoxB(16, 173, 160, 183, 167, "Y");
    DrawBorderedBoxB(16, 187, 160, 197, 167, "L");
    DrawBorderedBoxB(16, 201, 160, 211, 167, "R");
    DrawBorderedBoxB2(16, 215, 160, 227, 167, "ST");
    DrawBorderedBoxB2(16, 231, 160, 243, 167, "SL");

    DrawBorderedBoxB(16, 75, 170, 85, 177, "\xFB");
    DrawBorderedBoxB(16, 89, 170, 99, 177, "\xFC");
    DrawBorderedBoxB(16, 103, 170, 113, 177, "\xFD");
    DrawBorderedBoxB(16, 117, 170, 127, 177, "\xFE");
    DrawBorderedBoxB(16, 131, 170, 141, 177, "A");
    DrawBorderedBoxB(16, 145, 170, 155, 177, "B");
    DrawBorderedBoxB(16, 159, 170, 169, 177, "X");
    DrawBorderedBoxB(16, 173, 170, 183, 177, "Y");
    DrawBorderedBoxB(16, 187, 170, 197, 177, "L");
    DrawBorderedBoxB(16, 201, 170, 211, 177, "R");
    DrawBorderedBoxB2(16, 215, 170, 227, 177, "ST");
    DrawBorderedBoxB2(16, 231, 170, 243, 177, "SL");

    DrawBorderedBoxB(16, 10, 189, 20, 196, "1");
    DrawBorderedBoxB(16, 24, 189, 34, 196, "2");
    DrawBorderedBoxB(16, 38, 189, 48, 196, "3");
    DrawBorderedBoxB(16, 52, 189, 62, 196, "4");
    DrawBorderedBoxB(16, 66, 189, 76, 196, "5");
    DrawBorderedBoxB(16, 80, 189, 90, 196, "9");

    DrawBorderedBoxB(16, 107, 189, 117, 196, "\xFA");
    DrawBorderedBoxB(16, 121, 189, 131, 196, "1");
    DrawBorderedBoxB(16, 135, 189, 145, 196, "2");
    DrawBorderedBoxB(16, 149, 189, 159, 196, "3");
    DrawBorderedBoxB(16, 163, 189, 173, 196, "4");
    DrawBorderedBoxB(16, 177, 189, 187, 196, "5");

    DrawBorderedBoxB(16, 204, 189, 218, 196, "\xFF");

    GUIDisplayText(16, 10, 13, "DESCRIPTION"); // Text
    GUIDisplayText(16, 138, 13, "KEY P# LH");
    GUIDisplayText(16, 38, 84, "DESCRIPTION:");
    GUIDisplayText(16, 10, 84, "KEY:");
    GUIDisplayText(16, 10, 103, "COMBINATION KEYS:");
    GUIDisplayText(16, 10, 152, "PRESS+REL");
    GUIDisplayText(16, 10, 162, "PRESS ONLY");
    GUIDisplayText(16, 10, 172, "REL ONLY");
    GUIDisplayText(16, 10, 182, "FRAME DELAY");
    GUIDisplayText(16, 114, 182, "SECOND DELAY");
    GUIDisplayText(16, 204, 182, "DEL");
    GUIDisplayText(16, 145, 85, "P#  1  2  3  4  5");

    char const* const GUIComboTextA = "CLEAR";
    char const* const GUIComboTextB = "ADD";
    char const* const GUIComboTextC = "REPLACE";
    char const* const GUIComboTextD = "DELETE";

    // XXX twice? see below
    DrawGUIButton(16, 202, 20, 246, 31, GUIComboTextA, 60, -1, 0); // Buttons
    DrawGUIButton(16, 202, 35, 246, 46, GUIComboTextB, 61, -1, 0);
    DrawGUIButton(16, 202, 50, 246, 61, GUIComboTextC, 62, -1, 0);
    DrawGUIButton(16, 202, 65, 246, 76, GUIComboTextD, 63, -1, 0);

    // Calculate Text Cursor Position / Draw Box Text
    {
        u4 const eax = strlen(GUIComboTextH);
        GUIComboPos = eax;
        if (!(GUICCFlash & 8)) {
            GUIComboTextH[eax] = '_';
            GUIComboTextH[eax + 1] = '\0';
        }
        GUIDisplayTextG(16, 39, 94, GUIComboTextH);
        GUIComboTextH[GUIComboPos] = '\0';
    }

    // Display Current Combo Key
    PrintKey(16, 14, 94, GUIComboKey);

    // Buttons
    DrawGUIButton(16, 202, 20, 246, 31, GUIComboTextA, 60, -1, 0);
    DrawGUIButton(16, 202, 35, 246, 46, GUIComboTextB, 61, -1, 0);
    DrawGUIButton(16, 202, 50, 246, 61, GUIComboTextC, 62, -1, 0);
    DrawGUIButton(16, 202, 65, 246, 76, GUIComboTextD, 63, -1, 0);

    // Radio Switches
    GUIDisplayButtonHole(16, 158, 82, &GUIComboPNum, 0);
    GUIDisplayButtonHole(16, 176, 82, &GUIComboPNum, 1);
    GUIDisplayButtonHole(16, 194, 82, &GUIComboPNum, 2);
    GUIDisplayButtonHole(16, 212, 82, &GUIComboPNum, 3);
    GUIDisplayButtonHole(16, 230, 82, &GUIComboPNum, 4);
    // Checkboxes
    GUIDisplayCheckbox(16, 163, 88, &GUIComboLHorz, "\xFE = LAST \xFD/\xFE");
    GUIDisplayCheckbox(16, 163, 96, &GUIComboGameSpec, "GAME SPECFIC");

    // Draw Combination Keys (Each 15x11 -> 210x36)
    u1 eax = GUINumCombo;
    if (eax != 0) {
        u4 ebx = 0;
        u4 ecx = 11;
        u4 edx = 112;
        do {
            u1 al = GUIComboData[ebx++] - 1;
            u1 const* esi;
            if (al < 12) {
                static u1 const GUIIconDataComboPressRelease[] = {
                    0, 166, 0, 0, 166, 0, 0, 0, 0, 0,
                    0, 166, 0, 166, 166, 166, 0, 0, 0, 0,
                    0, 166, 0, 0, 166, 0, 0, 0, 0, 0,
                    166, 166, 166, 0, 166, 0, 0, 0, 0, 0,
                    0, 166, 0, 0, 166, 0, 0, 0, 0, 0,
                    0, 234, 234, 234, 234, 0, 0, 0, 0, 0,
                    234, 234, 234, 234, 234, 234, 0, 0, 0, 0,
                    234, 234, 234, 234, 234, 234, 0, 0, 0, 0,
                    235, 234, 234, 234, 234, 235, 0, 0, 0, 0,
                    0, 235, 235, 235, 235, 0, 0, 0, 0, 0
                };
                esi = GUIIconDataComboPressRelease;
            } else if ((al -= 12) < 12) {
                static u1 const GUIIconDataComboPress[] = {
                    0, 0, 166, 0, 0, 0, 0, 0, 0, 0,
                    0, 0, 166, 0, 0, 0, 0, 0, 0, 0,
                    0, 0, 166, 0, 0, 0, 0, 0, 0, 0,
                    0, 166, 166, 166, 0, 0, 0, 0, 0, 0,
                    0, 0, 166, 0, 0, 0, 0, 0, 0, 0,
                    0, 236, 236, 236, 236, 0, 0, 0, 0, 0,
                    236, 236, 236, 236, 236, 236, 0, 0, 0, 0,
                    236, 236, 236, 236, 236, 236, 0, 0, 0, 0,
                    237, 236, 236, 236, 236, 237, 0, 0, 0, 0,
                    0, 237, 237, 237, 237, 0, 0, 0, 0, 0
                };
                esi = GUIIconDataComboPress;
            } else if ((al -= 12) < 12) {
                static u1 const GUIIconDataComboRelease[] = {
                    0, 0, 0, 166, 0, 0, 0, 0, 0, 0,
                    0, 0, 166, 166, 166, 0, 0, 0, 0, 0,
                    0, 0, 0, 166, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 166, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 166, 0, 0, 0, 0, 0, 0,
                    0, 238, 238, 238, 238, 0, 0, 0, 0, 0,
                    238, 238, 238, 238, 238, 238, 0, 0, 0, 0,
                    238, 238, 238, 238, 238, 238, 0, 0, 0, 0,
                    239, 238, 238, 238, 238, 239, 0, 0, 0, 0,
                    0, 239, 239, 239, 239, 0, 0, 0, 0, 0
                };
                esi = GUIIconDataComboRelease;
            } else {
                static u1 const GUIIconDataComboFrame[] = {
                    0, 0, 0, 0, 58, 0, 0, 0, 0, 0,
                    0, 0, 0, 0, 56, 46, 0, 0, 0, 0,
                    0, 0, 0, 0, 54, 44, 0, 0, 0, 0,
                    0, 0, 0, 52, 0, 42, 0, 0, 0, 0,
                    0, 0, 0, 50, 40, 0, 0, 0, 0, 0,
                    0, 0, 0, 48, 38, 0, 0, 0, 0, 0,
                    0, 0, 0, 46, 36, 0, 0, 0, 0, 0,
                    0, 0, 44, 0, 34, 0, 0, 0, 0, 0,
                    0, 0, 42, 32, 0, 0, 0, 0, 0, 0,
                    0, 0, 40, 32, 0, 0, 0, 0, 0, 0
                };
                esi = GUIIconDataComboFrame;
                if (al == 17) {
                    al = 20;
                } else if (al >= 18) {
                    al -= 7;
                    static u1 const GUIIconDataComboSecond[] = {
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                        0, 0, 51, 45, 0, 0, 0, 0, 0, 0,
                        0, 0, 51, 45, 0, 0, 0, 0, 0, 0,
                        0, 54, 52, 48, 46, 0, 0, 0, 0, 0,
                        53, 46, 50, 50, 48, 45, 0, 0, 0, 0,
                        52, 50, 45, 48, 46, 44, 0, 0, 0, 0,
                        51, 50, 50, 46, 50, 43, 0, 0, 0, 0,
                        50, 50, 50, 50, 50, 42, 0, 0, 0, 0,
                        0, 48, 46, 44, 42, 0, 0, 0, 0, 0,
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                    };
                    esi = GUIIconDataComboSecond;
                    if (al == 11)
                        al = 250 - 37;
                }
            }
            if (al > 11)
                al += 37;
            switch (al) {
            case 0:
                al = '\xFB';
                break;
            case 1:
                al = '\xFC';
                break;
            case 2:
                al = '\xFD';
                break;
            case 3:
                al = '\xFE';
                break;
            case 4:
                al = 'A';
                break;
            case 5:
                al = 'B';
                break;
            case 6:
                al = 'X';
                break;
            case 7:
                al = 'Y';
                break;
            case 8:
                al = 'L';
                break;
            case 9:
                al = 'R';
                break;
            case 10:
                al = 'T';
                break;
            case 11:
                al = 'E';
                break;
            }
            static char GUIComboText3[] = " ";
            GUIComboText3[0] = al;

            GUIDisplayTextG(16, ecx + 8, edx + 5, GUIComboText3);

            GUIDisplayIconWin(16, ecx, edx, esi);
            ecx += 15;
            if (ecx == 11 + 15 * 14) {
                ecx -= 15 * 14;
                edx += 11;
            }
        } while (--eax != 0);
    }

    // Display Bordered Box
    if (NumCombo != 0) {
        DrawGUIWinBox2(16, 10, 190, 7, 224, 23 + (GUIccombcursloc - GUIccombviewloc) * 7);
    }

    // Display Scroll Lines
    // Copy Description to GUIScrolBufA, Others to GUIScrolBufB
    s4 ebx = NumCombo - GUIccombviewloc;
    if (ebx > 0) {
        if (ebx > 8)
            ebx = 8;
        ComboData const* edi = (GUIComboGameSpec == 0 ? CombinDataGlob : CombinDataLocl) + GUIccombviewloc;
        u4 ecx = 12;
        u4 eax = 25;
        do {
            static char GUIScrolBufB[9];
            sprintf(GUIScrolBufB, "%.3s %c  %c", ScanCodeListing + edi->key * 3, '1' + edi->player, edi->ff != 0 ? 'Y' : 'N');
            static char GUIScrolBufA[21];
            memcpy(GUIScrolBufA, edi->name, sizeof(edi->name));

            GUIDisplayTextG(16, ecx, eax, GUIScrolBufA);
            GUIDisplayTextG(16, ecx + 128, eax, GUIScrolBufB);
        } while (eax += 7, ++edi, --ebx != 0);
    }
}

static bool EEMode(void)
{
    return GetTime() <= 360;
}

void DisplayGUIAddOns(void)
{
    if (EEMode()) { // You know it!
        GUIDrawWindowBox(17, "TOASTER OPTIONS");

        GUIDisplayText(17, 20, 15, "SLOT 1:");
        GUIDisplayText(17, 112, 15, "SLOT 2:");

        char const* const GUIAddOnText3Alt = "OFF";
        char const* const GUIAddOnText4Alt = "WHITE BREAD";
        GUIDisplayButtonHoleTu(17, 9, 23, &device1, 0, GUIAddOnText3Alt, 0);
        GUIDisplayButtonHoleTu(17, 9, 33, &device1, 1, GUIAddOnText4Alt, 0);

        GUIDisplayButtonHoleTu(17, 100, 23, &device2, 0, GUIAddOnText3Alt, 1);
        GUIDisplayButtonHoleTu(17, 100, 33, &device2, 1, GUIAddOnText4Alt, 1);
        GUIDisplayButtonHoleTu(17, 100, 43, &device2, 2, "RYE BREAD", 0);
        GUIDisplayButtonHoleTu(17, 100, 53, &device2, 3, "WHEAT BREAD", 0);
        GUIDisplayButtonHoleTu(17, 100, 63, &device2, 4, "ENGLISH MUFFIN", 0);

        GUIDisplayText(17, 12, 78, "POP TART");
        GUIDisplayText(17, 103, 78, "WAFFLE");

        char const* const GUIAddOnTextDAlt = "QUICK TOAST";
        GUIDisplayCheckboxu(17, 9, 83, &mouse1lh, GUIAddOnTextDAlt, 0);
        GUIDisplayCheckboxu(17, 100, 83, &mouse2lh, GUIAddOnTextDAlt, 1);

        GUIDisplayTextY(17, 12, 100, "COFFEE?");
        GUIDisplayText(17, 12, 110, "CREAM");
        GUIDisplayText(17, 103, 110, "SUGAR");
    } else { // Regular
        GUIDrawWindowBox(17, "DEVICES SELECTOR");

        GUIDisplayTextY(17, 20, 15, "PORT 1:");
        GUIDisplayTextY(17, 112, 15, "PORT 2:");

        char const* const GUIAddOnText3 = "GAMEPAD";
        char const* const GUIAddOnText4 = "MOUSE";
        GUIDisplayButtonHoleTu(17, 9, 23, &device1, 0, GUIAddOnText3, 0);
        GUIDisplayButtonHoleTu(17, 9, 33, &device1, 1, GUIAddOnText4, 0);

        GUIDisplayButtonHoleTu(17, 100, 23, &device2, 0, GUIAddOnText3, 1);
        GUIDisplayButtonHoleTu(17, 100, 33, &device2, 1, GUIAddOnText4, 1);
        GUIDisplayButtonHoleTu(17, 100, 43, &device2, 2, "SUPER SCOPE", 0);
        GUIDisplayButtonHoleTu(17, 100, 53, &device2, 3, "1 JUSTIFIER", 0);
        GUIDisplayButtonHoleTu(17, 100, 63, &device2, 4, "2 JUSTIFIERS", 0);

        GUIDisplayText(17, 12, 78, "CYCLE P1:");
        GUIDisplayText(17, 103, 78, "CYCLE P2:");

        char const* const GUIAddOnTextD = "LEFT HANDED";
        GUIDisplayCheckboxu(17, 9, 83, &mouse1lh, GUIAddOnTextD, 0);
        GUIDisplayCheckboxu(17, 100, 83, &mouse2lh, GUIAddOnTextD, 1);

        GUIDisplayTextY(17, 12, 100, "SUPER SCOPE KEYS:");
        GUIDisplayText(17, 12, 110, "AUTO-FIRE");
        GUIDisplayText(17, 103, 110, "SS PAUSE");
    }

    DDrawBox(17, 73, 75, &KeyExtraEnab1);
    DDrawBox(17, 164, 75, &KeyExtraEnab2);

    DDrawBox(17, 73, 107, &SSAutoFire);
    DDrawBox(17, 164, 107, &SSPause);
}

void DisplayGUIChipConfig(void)
{
    GUIDrawWindowBox(18, "CONFIGURE CHIPS");

    if (EEMode()) { // You know it!
        GUIDisplayText(18, 10, 38, "EXTRAS");
        GUIDisplayCheckbox(18, 9, 43, &nssdip1, "SALT");
        GUIDisplayCheckbox(18, 59, 43, &nssdip2, "GARLIC");
        GUIDisplayCheckbox(18, 109, 43, &nssdip3, "PEPPER");
        GUIDisplayCheckbox(18, 9, 53, &nssdip4, "CHEESE");
        GUIDisplayCheckbox(18, 59, 53, &nssdip5, "BUTTER");
        GUIDisplayCheckbox(18, 109, 53, &nssdip6, "CATSUP");
    } else { // Regular Shadow
        GUIDisplayTextY(18, 10, 38, "SUPER SYSTEM:");
        GUIDisplayCheckboxu(18, 9, 43, &nssdip1, "DIP 1", 4); // Checkboxes
        GUIDisplayCheckboxu(18, 59, 43, &nssdip2, "DIP 2", 4);
        GUIDisplayCheckboxu(18, 109, 43, &nssdip3, "DIP 3", 4);
        GUIDisplayCheckboxu(18, 9, 53, &nssdip4, "DIP 4", 4);
        GUIDisplayCheckboxu(18, 59, 53, &nssdip5, "DIP 5", 4);
        GUIDisplayCheckboxu(18, 109, 53, &nssdip6, "DIP 6", 4);
    }
}

void DisplayGUIPaths(void)
{
    GUIDrawWindowBox(19, "SETUP PATHS");
    u4 eax;
    u4 ebx;
    GUIDrawTArea(19, &eax, &ebx);
    GUIDrawTabs(GUIPathTabs, &eax, ebx);

    if (GUIPathTabs[0] == 1) { // General
        GUIDisplayText(19, 8, 31, "SAVES:"); // Text
        GUIDisplayText(19, 8, 66, "SAVESTATES:");
        GUIDisplayText(19, 8, 101, "MOVIES:");
        GUIDisplayText(19, 8, 136, "IPS:");
        GUIDisplayText(19, 8, 171, "RELATIVE PATH BASE:");

        GUIDisplayBBox(19, 8, 41, 236, 51, 167); // Input boxes
        GUIDisplayBBox(19, 8, 76, 236, 86, 167);
        GUIDisplayBBox(19, 8, 111, 236, 121, 167);
        GUIDisplayBBox(19, 8, 146, 236, 156, 167);
        // Green Text
        GUIOuttextwin2d(19, 10, 45, SRAMPath, 37, GUIPathsTab1Ptr, 0);
        GUIOuttextwin2d(19, 10, 80, SStatePath, 37, GUIPathsTab1Ptr, 1);
        GUIOuttextwin2d(19, 10, 115, MoviePath, 37, GUIPathsTab1Ptr, 2);
        GUIOuttextwin2d(19, 10, 150, IPSPath, 37, GUIPathsTab1Ptr, 3);

        // Display Radio buttons
        char const* const GUIPathsTextA5A = "CONFIG DIR";
        char const* const GUIPathsTextA5B = "ROM DIR";
        GUIDisplayText(19, 23, 181, GUIPathsTextA5A);
        GUIDisplayText(19, 103, 181, GUIPathsTextA5B);

        GUIDisplayButtonHole(19, 8, 178, &RelPathBase, 0);
        GUIDisplayButtonHole(19, 88, 178, &RelPathBase, 1);

        if (GUIInputBox == 0) {
            GUIDisplayButtonHoleTu(19, 8, 178, &RelPathBase, 0, GUIPathsTextA5A, 0);
            GUIDisplayButtonHoleTu(19, 88, 178, &RelPathBase, 1, GUIPathsTextA5B, 0);
        }
    }

    if (GUIPathTabs[0] == 2) { // More paths
        GUIDisplayText(19, 8, 31, "SNAPSHOTS:");
        GUIDisplayText(19, 8, 66, "SPCS:");
        GUIDisplayText(19, 8, 101, "CHEATS:");
        GUIDisplayText(19, 8, 136, "COMBOS:");
        GUIDisplayText(19, 8, 171, "GAME SPECIFIC INPUT:");

        GUIDisplayBBox(19, 8, 41, 236, 51, 167);
        GUIDisplayBBox(19, 8, 76, 236, 86, 167);
        GUIDisplayBBox(19, 8, 111, 236, 121, 167);
        GUIDisplayBBox(19, 8, 146, 236, 156, 167);
        GUIDisplayBBox(19, 8, 181, 236, 191, 167);

        GUIOuttextwin2d(19, 10, 45, SnapPath, 37, GUIPathsTab2Ptr, 0);
        GUIOuttextwin2d(19, 10, 80, SPCPath, 37, GUIPathsTab2Ptr, 1);
        GUIOuttextwin2d(19, 10, 115, CHTPath, 37, GUIPathsTab2Ptr, 2);
        GUIOuttextwin2d(19, 10, 150, ComboPath, 37, GUIPathsTab2Ptr, 3);
        GUIOuttextwin2d(19, 10, 185, INPPath, 37, GUIPathsTab2Ptr, 4);
    }

    if (GUIPathTabs[0] == 3) { // bc
        GUIDisplayText(19, 8, 31, "BS-X:");
        GUIDisplayText(19, 8, 66, "SUFAMI TURBO:");
        GUIDisplayText(19, 8, 101, "SD GUNDAM G-NEXT:");
        GUIDisplayText(19, 8, 136, "SAME GAME:");

        GUIDisplayBBox(19, 8, 41, 236, 51, 167);
        GUIDisplayBBox(19, 8, 76, 236, 86, 167);
        GUIDisplayBBox(19, 8, 111, 236, 121, 167);
        GUIDisplayBBox(19, 8, 146, 236, 156, 167);

        GUIOuttextwin2d(19, 10, 45, BSXPath, 37, GUIPathsTab3Ptr, 0);
        GUIOuttextwin2d(19, 10, 80, STPath, 37, GUIPathsTab3Ptr, 1);
        GUIOuttextwin2d(19, 10, 115, GNextPath, 37, GUIPathsTab3Ptr, 2);
        GUIOuttextwin2d(19, 10, 150, SGPath, 37, GUIPathsTab3Ptr, 3);
    }
}

void DisplayGUISave(void)
{
    GUIDrawWindowBox(20, "SAVE OPTIONS");

    GUIDisplayText(20, 8, 19, "# OF REWIND STATES"); // Text
    GUIDisplayText(20, 8, 31, "1/5 SECONDS PER REWIND");
    GUIDisplayText(20, 9, 159, "SAVE");
    GUIDisplayText(20, 9 + 57, 159, "LOAD");
    GUIDisplayText(20, 9 + 114, 159, "PICK");
    GUIDisplayText(20, 9, 168, "REWIND");

    GUIDisplayTextY(20, 6, 123, "STATE SHORTCUTS:");
    GUIDisplayText(20, 9, 132, "ST0");
    GUIDisplayText(20, 9 + 45, 132, "ST1");
    GUIDisplayText(20, 9 + 45 * 2, 132, "ST2");
    GUIDisplayText(20, 9 + 45 * 3, 132, "ST3");
    GUIDisplayText(20, 9, 141, "ST4");
    GUIDisplayText(20, 9 + 45, 141, "ST5");
    GUIDisplayText(20, 9 + 45 * 2, 141, "ST6");
    GUIDisplayText(20, 9 + 45 * 3, 141, "ST7");
    GUIDisplayText(20, 9, 150, "ST8");
    GUIDisplayText(20, 9 + 45, 150, "ST9");
    GUIDisplayText(20, 9 + 45 * 2, 150, "ST+");
    GUIDisplayText(20, 9 + 45 * 3, 150, "ST-");

    GUIDisplayCheckboxu(20, 11, 38, &nosaveSRAM, "DO NOT SAVE SRAM", 0);
    if (nosaveSRAM == 0) {
        GUIDisplayCheckboxu(20, 11, 48, &SRAMSave5Sec, "SRAM CHECK+SAVE", 5); // Checkboxes
    }
    GUIDisplayCheckboxu(20, 11, 58, &SRAMState, "LOAD SAVESTATE W/SRAM", 0);
    GUIDisplayCheckboxu(20, 11, 68, &LatestSave, "START AT LATEST SAVE", 0);
    GUIDisplayCheckboxu(20, 11, 78, &AutoIncSaveSlot, "AUTO INCREMENT SAVE SLOT", 5);
    GUIDisplayCheckboxu(20, 11, 88, &AutoState, "AUTO STATE SAVE/LOAD", 0);
    GUIDisplayCheckboxu(20, 11, 98, &PauseLoad, "PAUSE AFTER LOADING STATE", 0);
    GUIDisplayCheckboxu(20, 11, 108, &PauseRewind, "PAUSE AFTER REWIND", 12);

    char GUISaveTextZ3[12];

    GUIDisplayBBox(20, 150, 17, 165, 24, 167); // Rewind States Box
    sprintf(GUISaveTextZ3, "%02u", RewindStates);
    GUIDisplayTextG(20, 154, 19, GUISaveTextZ3);

    GUIDisplayBBox(20, 150, 29, 165, 36, 167); // Second/Rewind Box
    sprintf(GUISaveTextZ3, "%02u", RewindFrames);
    GUIDisplayTextG(20, 154, 31, GUISaveTextZ3);

    DDrawBox(20, 26, 129, &KeyStateSlc0); // Boxes for State section
    DDrawBox(20, 71, 129, &KeyStateSlc1);
    DDrawBox(20, 116, 129, &KeyStateSlc2);
    DDrawBox(20, 161, 129, &KeyStateSlc3);
    DDrawBox(20, 26, 138, &KeyStateSlc4);
    DDrawBox(20, 71, 138, &KeyStateSlc5);
    DDrawBox(20, 116, 138, &KeyStateSlc6);
    DDrawBox(20, 161, 138, &KeyStateSlc7);
    DDrawBox(20, 26, 147, &KeyStateSlc8);
    DDrawBox(20, 71, 147, &KeyStateSlc9);
    DDrawBox(20, 116, 147, &KeyIncStateSlot);
    DDrawBox(20, 161, 147, &KeyDecStateSlot);
    DDrawBox(20, 32, 156, &KeySaveState);
    DDrawBox(20, 89, 156, &KeyLoadState);
    DDrawBox(20, 146, 156, &KeyStateSelct);
    DDrawBox(20, 45, 165, &KeyRewind);

    DrawGUIButton(20, 173, 17, 181, 25, "+", 70, -2, -1); // + Rewind States
    DrawGUIButton(20, 184, 17, 192, 25, "-", 71, -2, -1); // - Rewind States
    DrawGUIButton(20, 173, 29, 181, 37, "+", 72, -2, -1); // + Second/Rewind
    DrawGUIButton(20, 184, 29, 192, 37, "-", 73, -2, -1); // - Second/Rewind
}

static u1 SpdslidSet(void const* const p1) // slider var
{
    return *(u1 const*)p1 * 2;
}

static char const* SpdslidText(void const* const p1) // slider var, text
{
    static char GUISpeedTextD1[12];
    u4 const al = *(u1 const*)p1; // currently emuspeed ranges from 0 to 58
    if (al >= 29) // this will turn it into '/30' to '30x'
    { // ff
        sprintf(GUISpeedTextD1, "%ux", al - 28);
    } else { // slomo
        sprintf(GUISpeedTextD1, "/%u", 30 - al);
    }
    return GUISpeedTextD1;
}

void DisplayGUISpeed(void)
{
    GUIDrawWindowBox(21, "SPEED OPTIONS");

    if (frameskip == 0) {
        GUIDisplayText(21, 6, 15, "MAX FRAME SKIP");
        GUIDisplayText(21, 40, 79, "+ EMU SPEED");
        GUIDisplayText(21, 40, 99, "- EMU SPEED");
        GUIDisplayTextY(21, 8, 164, "EMU SPEED:");
    } else {
        GUIDisplayText(21, 6, 15, "FRAME RATE");
        GUIDisplayText(21, 40, 79, "+ FRAME RATE");
        GUIDisplayText(21, 40, 99, "- FRAME RATE");
    }
    GUIDisplayText(21, 6, 26, "FASTFWD RATIO x");
    GUIDisplayText(21, 6, 37, "SLOWDWN RATIO /");

    GUIDisplayTextY(21, 8, 49, "SHORTCUTS:");
    GUIDisplayText(21, 40, 59, "FAST FORWARD");
    GUIDisplayText(21, 40, 69, "SLOW DOWN");
    GUIDisplayText(21, 40, 89, "RESET SPEED");
    GUIDisplayText(21, 40, 109, "PAUSE GAME");
    GUIDisplayText(21, 40, 119, "INCR FRAME");

    if (frameskip == 0) // Shortcut Boxes
    {
        DDrawBox(21, 10, 76, &KeyEmuSpeedUp);
        DDrawBox(21, 10, 96, &KeyEmuSpeedDown);
    } else {
        DDrawBox(21, 10, 76, &KeyFRateUp);
        DDrawBox(21, 10, 96, &KeyFRateDown);
    }
    DDrawBox(21, 10, 56, &KeyFastFrwrd);
    DDrawBox(21, 10, 66, &KeySlowDown);
    DDrawBox(21, 10, 86, &KeyResetSpeed);
    DDrawBox(21, 10, 106, &EMUPauseKey);
    DDrawBox(21, 10, 116, &INCRFrameKey);

    GUIDisplayCheckboxu(21, 11, 135, &FastFwdToggle, "TOGGLED FFWD/SLWDWN", 0);
    GUIDisplayCheckboxun(21, 11, 145, &frameskip, 0, "AUTO FRAME RATE", 0);

    char GUISpeedTextZ3[12];

    GUIDisplayBBox(21, 96, 24, 114, 31, 167); // FF Ratio Box
    sprintf(GUISpeedTextZ3, "%2u", FFRatio + 2);
    GUIDisplayTextG(21, 101, 26, GUISpeedTextZ3);

    GUIDisplayBBox(21, 96, 35, 114, 42, 167); // SD Ratio Box
    sprintf(GUISpeedTextZ3, "%2u", SDRatio + 2);
    GUIDisplayTextG(21, 101, 37, GUISpeedTextZ3);

    DrawGUIButton(21, 118, 24, 126, 32, "+", 74, -2, -1); // + Rewind States
    DrawGUIButton(21, 129, 24, 137, 32, "-", 75, -2, -1); // - Rewind States
    DrawGUIButton(21, 118, 35, 126, 43, "+", 76, -2, -1); // + Second/Rewind
    DrawGUIButton(21, 129, 35, 137, 43, "-", 77, -2, -1); // - Second/Rewind

    if (frameskip == 0) {
        GUIDrawSlider(21, 7, 116, 175, &EmuSpeed, SpdslidSet, SpdslidText);
    }

    GUIDisplayBBox(21, 96, 13, 114, 20, 167);
    static char GUISpeedTextX[] = "-";
    if (frameskip != 0) // Determine if AutoFR is enabled
    { // Non AFR FrameRate +/- Box
        GUISpeedTextX[0] = '0' + (frameskip - 1);
    } else { // AFR Max Frameskip +/- Box
        GUISpeedTextX[0] = '0' + maxskip;
    }
    GUIDisplayTextG(21, 107, 15, GUISpeedTextX);
    DrawGUIButton(21, 118, 13, 126, 21, "+", 78, -2, -1);
    DrawGUIButton(21, 129, 13, 137, 21, "-", 79, -2, -1);
}
