/*
 * Copyright (C) 1997-2008 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
 *
 * http://www.zsnes.com
 * http://sourceforge.net/projects/zsnes
 * https://zsnes.bountysource.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdarg.h>
#include <string.h>

#include "../c_init.h"
#include "../c_intrf.h"
#include "../cpu/c_dsp.h"
#include "../cpu/execute.h"
#include "../cpu/regs.h"
#include "../gblvars.h"
#include "../link.h"
#include "cfg.h"
#include "input.h"
#ifndef lengthof
#define lengthof(x) (sizeof(x) / sizeof *(x))
#endif
#ifndef endof
#define endof(x) ((x) + lengthof(x))
#endif
#include "../ui.h"
#include "../vcache.h"
#include "../video/procvid.h"
#include "../zmovie.h"
#include "../zpath.h"
#include "../zstate.h"
#include "c_gui.h"
#include "c_guiwindp.h"
#include "gui.h"
#include "guicheat.h"
#include "guicombo.h"
#include "guifuncs.h"
#include "guikeys.h"
#include "guimisc.h"
#include "guimouse.h"
#include "guiwindp.h"

#include "../video/ntsc.h"
#include "../video/procvidc.h"

#ifdef __WIN32__
#include "../win/winlink.h"
#endif

u1 GUIcwinpress;
u1 lastmouseholded;

static u1 LastHoldEnable;
static u1 MouseMoveOkay;
static u1 ntscCurVar;
static u1 ntscLastVar[6];
static u1 ntscWhVar;
static u2 mousebuttonstat;
static u4 GUIBlankVar;
static u4 GUINumValue;
static u4 GUIdispmode;
static u4 GUIlastcpos;
static u4 GUIlastdispval;
static u4 GUIlastvpos;
static u4 GUIlastypos;

static char const guipresstext1[] = "ENTER THE KEY";
static char const guipresstext2[] = "OR BUTTON TO USE";

static void GUIProcVideo(void)
{
    GUICBHold = 0;
    newengen = 1;
    guiprevideo();
    u1 const prevvid = cvidmode;
    cvidmode = GUIcurrentvideocursloc;
    initvideo();
    {
        GUISetPal();
        guipostvideo();
        if (GUIkeydelay == 0) {
            videotroub = 0;
            cvidmode = prevvid;
            initvideo();
            GUISetPal();
            vidpastecopyscr();
        }
    }
    GUIkeydelay = 0;
}

static void GUINTSCReset(void)
{
    if (GUICBHold != 38) {
        NTSCBlend = 0;
        NTSCRef = 0;
        NTSCHue = 0;
        NTSCSat = 0;
        NTSCCont = 0;
        NTSCBright = 0;
        NTSCSharp = 0;
    }
    if (GUICBHold != 37) {
        NTSCGamma = 0;
        NTSCRes = 0;
        NTSCArt = 0;
        NTSCFringe = 0;
        NTSCBleed = 0;
        NTSCWarp = 0;
    }
    GUICBHold = 0;
    NTSCFilterInit();
}

static void GUINTSCPreset(void)
{
    switch (GUICBHold) {
    case 81:
        NTSCPresetVar = 0;
        break;
    case 82:
        NTSCPresetVar = 1;
        break;
    case 83:
        NTSCPresetVar = 2;
        break;
    case 84:
        NTSCPresetVar = 3;
        break;
    }
    NTSCFilterInit();
    NTSCPresetVar = 4;
    GUICBHold = 0;
}

static void GUIProcCustomVideo(void)
{
    SetCustomXY();
    GUICBHold = 0;
    GUIInputBox = 0;
#ifdef __WIN32__
    if (cvidmode >= 37)
#else
    if (cvidmode >= 20)
#endif
    {
        changeRes = 1;
        initwinvideo();
        Clear2xSaIBuffer();
    }
}

void SwitchFullScreen(void)
{
    Clear2xSaIBuffer();
    if (GUIWFVID[cvidmode] != 0) {
        cvidmode = PrevWinMode;
    } else {
        cvidmode = PrevFSMode;
    }
    if (!TryToggleFullScreen()) {
        initvideo();
    }
}

static bool GUIClickArea(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4)
{
    return p1 <= eax && eax <= p3 && p2 <= edx && edx <= p4;
}

static void GUIClickCButton(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8))
        *p3 ^= 1;
}

static void GUIClickCButtonC(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        *p3 ^= 1;
        GUIccombviewloc = 0;
        GUIccombcursloc = 0;
        GUIccomblcursloc = 0;
    }
}

#ifdef __WIN32__
static void GUIClickCButtonf(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, void (*const p4)(void))
#else
static void GUIClickCButtonf(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
#define GUIClickCButtonf(eax, edx, p1, p2, p3, p4) GUIClickCButtonf((eax), (edx), (p1), (p2), (p3))
#endif
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        *p3 ^= 1;
#ifdef __WIN32__
        p4();
#endif
    }
}

static void GUIClickCButtonK(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, void (*const p4)(void))
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        *p3 ^= 1;
        p4();
    }
}

static void GUIClickCButtonN(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, void (*const p4)(void))
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
#ifdef __WIN32__
        if (*p3 != 1)
            Keep4_3Ratio = 1;
#endif
        *p3 ^= 1;
#ifdef __WIN32__
        p4();
#endif
    }
}

static void GUIClickCButtonM(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        *p3 ^= 1;
        MultiTap = pl12s34 != 1 && (pl3contrl != 0 || pl4contrl != 0 || pl5contrl != 0);
    }
}

static bool GUIClickCButton5(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, u1 const p4)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        *p3 = *p3 == p4 ? *p3 ^ p4 : p4;
        return true;
    }
    return false;
}

static bool GUIClickCButton6(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, u1 const p4)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        if (*p3 == p4) {
            *p3 = 0;
        } else {
            *p3 = p4;
            memset(vidbufferofsb, 0, 288 * 128 * 4);
        }
        return true;
    }

    return false;
}

static void GUIClickCButtonfr(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        FPSOn = 0;
        *p3 = !*p3;
    }
}

static void GUIClickCButtoncpu(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        CPUOn = 0;
        *p3 = !*p3;
    }
}

static bool GUIClickCButtonL(s4 const eax, s4 const edx, s4 const p1, s4 const p2)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        showallext ^= 1;
        GUIcurrentfilewin = 0;
        GetLoadData();
        return true;
    }
    return false;
}

static void GUIClickCButtonI(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 3, p1 + 6, p2 + 8)) {
        *p3 ^= 1;
        if (GUIBIFIL[cvidmode] != 0) {
#ifdef __WIN32__
            initDirectDraw();
#elif defined __OPENGL__
            initwinvideo();
#endif
        }
        Clear2xSaIBuffer();
    }
}

static bool GUISlidebarPostImpl(s4 const eax, s4 const edx, u4 const p1, s4 const p2, s4 const p3, s4 const p4, u1 const p7, u4 const p8, u4* const p9, u4* const p10, u4 const* const p11, u4* const p12, u4 const p13, s4 const p14, s4 const p15, s4 const p16, s4 const p17, void (*const p23)(s4 eax, s4 edx)) // p1-p13: x1,y1,x2,y2,upjump,downjump,holdpos,scsize,view,cur,listsize, p14-p24: x1,y1,x2,y2,view,curs,num,.scru,.scrd,jumpto,sizeofscreen
{
    if (*p11 == 0)
        return false;

    if (GUIdispmode != 1) {
        if (GUIClickArea(eax, edx, p1, p2 - 7, p3, p2 - 1)) {
            *p12 = p13;
            GUICHold = p7;
            goto scrollup;
        }
        if (GUIClickArea(eax, edx, p1, p4 + 1, p3, p4 + 7)) {
            *p12 = p13;
            GUICHold = p7 + 1;
            goto scrolldown;
        }
    } else if (GUIClickArea(eax, edx, p1, p2, p3, p4)) // slidebar
    {
        *p12 = p13;
        // displacement = (GUIdispmode * pixeldisp. / (listsize-scsize))
        s8 const edxeax = (s8)(s4)(*p11 - p8) * (s8)(edx - GUIlastypos);
        u4 const ebx = GUIlastdispval;
        if (ebx != 0 && !(ebx & 0x80000000)) {
            u4 const eax = edxeax / ebx;
            *p9 = GUIlastvpos + eax;
            *p10 = GUIlastcpos + eax;
            if (*p9 & 0x8000000)
                *p9 = 0; // XXX probably should be 0x80000000
            if (*p10 & 0x8000000)
                *p10 = 0; // XXX probably should be 0x80000000
            u4 const eax_ = *p11;
            if (*p10 >= eax_ - 1)
                *p10 = eax_ - 1;
            if (*p9 >= eax_ - p8)
                *p9 = eax_ - p8;
        }
        return true;
    }

    if (p14 <= eax && eax <= p16) {
        if (edx == p15) { // Scroll Up
            *p10 = *p9;
        scrollup:
            if (GUIScrolTim1 != 0)
                goto donescrol;
            if (*p10 != 0) {
                --*p10;
                if (*p9 != 0)
                    --*p9;
            }
        } else if (edx == p17) { // Scroll Down
            if (*p11 > p8)
                *p10 = *p9 + p8 - 1;
        scrolldown:
            if (GUIScrolTim1 != 0)
                goto donescrol;
            if (*p11 - 1 > *p10) {
                ++*p10;
                if (*p11 <= p8)
                    goto donescrol;
                u4 const ebx = *p11 - p8;
                if (++*p9 >= ebx)
                    *p9 = ebx;
            }
        } else {
            return false;
        }

        GUIScrolTim1 = 1;
        if (GUIScrolTim2 >= 4)
            ++GUIScrolTim1;
        if (GUIScrolTim2 != 0) {
            ++GUIScrolTim1;
            --GUIScrolTim2;
        }

    donescrol:
        p23(eax, edx);
        return true;
    }

    return false;
}

static bool GUISlidebarImpl(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u4 const* const p5, u4 const p6, u4* const p7, u4* const p8, u4 const* const p9, u4 const p10) // x1,y1,x2,y2,GUI?StA,ScrnSize,ViewLoc,CursLoc,Entries,win#
{
    GUIdispmode = 0;

    if (GUIClickArea(eax, edx, p1, p2, p3, p4)) {
        if (p5[1] > (u4)(edx - p2)) {
            *p7 -= p6;
            *p8 -= p6;
            if (*p7 & 0x8000000) // XXX probably should be 0x80000000
            {
                *p7 = 0;
                *p8 = 0;
            }
        } else if (p5[2] < (u4)(edx - p2)) {
            *p7 += p6;
            *p8 += p6;
            u4 const ebx = *p9 - 1;
            if (*p8 >= ebx) {
                *p8 = ebx;
                *p7 = ebx - p6 + 1;
            }
            u4 const ebx_ = *p9 - p6;
            if (*p7 >= ebx_)
                *p7 = ebx_;
        } else {
            GUIlastypos = edx;
            GUIdispmode = 1;
            GUIHoldYlim = GUIwinposy[p10] + p2;
            GUIHoldYlimR = GUIwinposy[p10] + p4;
            GUIHoldXlimL = GUIwinposx[p10] + p1;
            GUIHoldXlimR = GUIwinposx[p10] + p3;
            GUIlastdispval = *p5;
            GUIlastcpos = *p8;
            GUIlastvpos = *p7;
            GUIHold = 3;
        }
        return true;
    }

    // upper arrow
    if (GUIClickArea(eax, edx, p1, p2 - 7, p3, p2 - 1)) {
        GUIHoldYlim = GUIwinposy[p10] + p2 - 7;
        GUIHoldYlimR = GUIwinposy[p10] + p2 - 1;
        GUIHoldXlimL = GUIwinposx[p10] + p1;
        GUIHoldXlimR = GUIwinposx[p10] + p3;
        GUIHold = 3;
        return true;
    }

    // lower arrow
    if (GUIClickArea(eax, edx, p1, p4 + 1, p3, p4 + 7)) {
        GUIHoldYlim = GUIwinposy[p10] + p4 + 1;
        GUIHoldYlimR = GUIwinposy[p10] + p4 + 7;
        GUIHoldXlimL = GUIwinposx[p10] + p1;
        GUIHoldXlimR = GUIwinposx[p10] + p3;
        GUIHold = 3;
        return true;
    }

    return false;
}

static void GUIPHoldbutton(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u1 const p5)
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4)) {
        GUIHoldXlimL = p1;
        GUIHoldXlimR = p3;
        GUIHoldYlim = p2;
        GUIHoldYlimR = p4;
        GUICBHold2 = p5;
        GUIHold = 4;
    }
}

static void GUIPHoldbutton2(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u1 const p5, u1* const p6, s1 const p7, u1 const p8)
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4) && GUIHold == 0) {
        GUIHoldXlimL = p1;
        GUIHoldXlimR = p3;
        GUIHoldYlim = p2;
        GUIHoldYlimR = p4;
        GUICBHold2 = p5;
        GUIHold = 4;
        if (*p6 != p8)
            *p6 += p7;
    }
}

static void GUITextBoxInputNach(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u4 const p5, u4 const p6, void (*const p7)(void))
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4)) {
        p7();
        GUIInputBox = p5 + 1;
        GUIInputLimit = p6 - 1;
    }
}

static void GUIPButtonHole(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, u1 const p4)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 1, p1 + 7, p2 + 7))
        *p3 = p4;
}

static void GUIPButtonHoleS(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, u1 const p4)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 1, p1 + 7, p2 + 7)) {
        *p3 = p4;
    }
}

static void GUIPButtonHoleLoad(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u1* const p3, u1 const p4)
{
    if (GUIClickArea(eax, edx, p1 + 1, p2 + 1, p1 + 7, p2 + 7)) {
        *p3 = p4;
        GetLoadData();
    }
}

static bool GUIWinControl(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u4* const p5, u4 const* const p6, u4 const* const p7, s4 const p8, u4 const p9, u4* const p10, u4 const p11, u4 const p12, u4 const p13) // x,y,x2,y2,currentwin,vpos,#entries,starty,y/entry,cpos,winval,win#,dclicktick#
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4) && *p7 != 0) {
        *p5 = p11;
        s4 const eax = (u4)(edx - p8) / (u4)p9 + *p6;
        if (eax <= (s4)*p7 - 1)
            *p10 = eax;
        if (GUIHold == 0) {
            if (GUIDClickTL != 0 && GUIDClCWin == p11 && GUIDClCEntry == eax) {
                GUIDClickTL = 0;
                if (p12 == 1) {
                    GUILoadData();
                } else {
                    CheatCodeToggle();
                }
                return true;
            }
            GUIDClickTL = p13;
            GUIDClCWin = p11;
            GUIDClCEntry = eax;
        }
        GUIHoldYlim = GUIwinposy[p12] + p2 - 1;
        GUIHoldYlimR = GUIwinposy[p12] + p4 + 1;
        GUIHoldXlimL = GUIwinposx[p12] + p1;
        GUIHoldXlimR = GUIwinposx[p12] + p3;
        GUIHold = 3;
        return true;
    }
    return false;
}

static bool GUIWinControl2(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u4* const p5, s4 const p6, s4 const* const p7, s4 const p8, s4 const p9, s4* const p10, u4 const p11, u4 const p12, u4 const p13) // Key Combination Editor Exclusive
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4) && *p7 != 0) {
        GUIccomblcursloc = 0xFFFFFFFF;
        *p5 = p11;
        s4 const eax = (edx - p8) / p9 + p6;
        if (eax <= *p7 - 1)
            *p10 = eax;

        if (GUIHold == 0) {
            if (GUIDClickTL != 0 && GUIDClCWin == p11 && GUIDClCEntry == eax) {
                GUIDClickTL = 0;
                u4 const eax = p12;
                if (eax == 1) {
                    GUILoadData();
                } else {
                    CheatCodeToggle();
                }
                return true;
            }
            GUIDClickTL = p13;
            GUIDClCWin = p11;
            GUIDClCEntry = eax;
        }

        GUIHoldYlim = GUIwinposy[p12] + p2 - 1;
        GUIHoldYlimR = GUIwinposy[p12] + p4 + 1;
        GUIHoldXlimL = GUIwinposx[p12] + p1;
        GUIHoldXlimR = GUIwinposx[p12] + p3;
        GUIHold = 3;
        return true;
    }

    return false;
}

static bool DGOptnsProcBox(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u4* const p3, u1* const keycontrolval)
{
    if (GUIClickArea(eax, edx, p1, p2, p1 + 19, p2 + 6)) {
        u4 const ebx = guipresstest();
        if (ebx != 0x01 && ebx != 0x3B) {
            *p3 = ebx;
            if (keycontrolval != 0)
                *keycontrolval = 1;
        } else {
            *p3 = 0;
        }
        return true;
    }

    return false;
}

static bool DGOptnsProcBoxc(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u4* const p5)
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4)) {
        u4 const ebx = guipresstest();
        switch (ebx) {
        case 1: // ESC
            pressed[1] = 2;
            /* FALLTHROUGH */
        case 0x3B: // Cancel
            *p5 = 0;
            break;

        default:
            *p5 = ebx;
            break;
        }
        return true;
    }

    return false;
}

static void GUIPTabClick(s4 const eax, s4 const edx, s4 const p1, s4 const p2, u4 const p3, u4* const p4, ...) // minX, maxX, value, var, vars to zero
{
    if (GUIClickArea(eax, edx, p1 + 1, 11, p2 - 1, 22)) {
        GUIInputBox = 0;
        p4[0] = p3;
        va_list ap;
        va_start(ap, p4);
        for (;;) {
            u4* const p = va_arg(ap, u4*);
            if (!p)
                break;
            *p = 0;
        }
        GUIFreshInputSelect = 1;
    }
}

static void GUIBoxVar(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u4* const p5, u4 const p6)
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4))
        *p5 = p6;
}

static void ComboBoxProc(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4, u1 const p5)
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4) && GUINumCombo != 42) {
        GUIComboData[GUINumCombo++] = p5;
    }
}

static void ComboBoxProcD(s4 const eax, s4 const edx, s4 const p1, s4 const p2, s4 const p3, s4 const p4)
{
    if (GUIClickArea(eax, edx, p1, p2, p3, p4) && GUINumCombo != 0)
        --GUINumCombo;
}

static void DisplayGUIConfirmClick_skipscrol(s4 const eax, s4 const edx)
{
    if (GUIClickCButtonL(eax, edx, 10, 187))
        return;
    if (GUIWinControl(eax, edx, 5, 27, 144, 26 + 15 * 7, &GUIcurrentfilewin, (u4 const*)&GUIcurrentviewloc, (u4 const*)&GUIfileentries, 27, 7, (u4*)&GUIcurrentcursloc, 0, 1, 30))
        return; // XXX ugly casts
    if (GUIWinControl(eax, edx, 160, 27, 228, 26 + 15 * 7, &GUIcurrentfilewin, (u4 const*)&GUIcurrentdirviewloc, (u4 const*)&GUIdirentries, 27, 7, (u4*)&GUIcurrentdircursloc, 1, 1, 30))
        return; // XXX ugly casts
    GUIPHoldbutton(eax, edx, 186, 165, 228, 176, 1);
    GUIPButtonHoleLoad(eax, edx, 9, 163, &GUIloadfntype, 0);
    GUIPButtonHoleLoad(eax, edx, 9, 171, &GUIloadfntype, 1);
    if (GUIClickCButton5(eax, edx, 144, 177, &ForceROMTiming, 1))
        return;
    if (GUIClickCButton5(eax, edx, 144, 187, &ForceROMTiming, 2))
        return;
    if (GUIClickCButton5(eax, edx, 184, 177, &ForceHiLoROM, 1))
        return;
    if (GUIClickCButton5(eax, edx, 184, 187, &ForceHiLoROM, 2))
        return;
}

static void DisplayGUIConfirmClick(s4 const eax, s4 const edx)
{
    // offset 0 = (ysize-(yend-ystart+1)), offset 1 = starty, offset 2 = endy
    // SlideBar Implementation
    if (GUISlidebarImpl(eax, edx, 146, 33, 153, 33 + 93, GUILStA, 15, (u4*)&GUIcurrentviewloc, (u4*)&GUIcurrentcursloc, (u4 const*)&GUIfileentries, 1))
        return; // XXX ugly casts
    if (GUISlidebarImpl(eax, edx, 230, 33, 237, 33 + 93, GUILStB, 15, (u4*)&GUIcurrentdirviewloc, (u4*)&GUIcurrentdircursloc, (u4 const*)&GUIdirentries, 1))
        return; // XXX ugly casts
    GUIScrolTim1 = 0;
    GUIScrolTim2 = 6;
    DisplayGUIConfirmClick_skipscrol(eax, edx);
}

static void DisplayGUIConfirmClick2(s4 const eax, s4 const edx)
{
    if (GUIfileentries > 1) {
        if (GUISlidebarPostImpl(eax, edx, 146, 33, 153, 33 + 93, 1, 15, (u4*)&GUIcurrentviewloc, (u4*)&GUIcurrentcursloc, (u4 const*)&GUIfileentries, &GUIcurrentfilewin, 0, 5, 26, 144, 27 + 15 * 7, DisplayGUIConfirmClick_skipscrol))
            return; // XXX ugly casts
    }
    if (GUIdirentries > 1) {
        if (GUISlidebarPostImpl(eax, edx, 230, 33, 237, 33 + 93, 3, 15, (u4*)&GUIcurrentdirviewloc, (u4*)&GUIcurrentdircursloc, (u4 const*)&GUIdirentries, &GUIcurrentfilewin, 1, 160, 26, 228, 27 + 15 * 7, DisplayGUIConfirmClick_skipscrol))
            return; // XXX ugly casts
    }
    DisplayGUIConfirmClick(eax, edx);
}

static void DisplayGUIChoseSaveClick(s4 const eax, s4 const edx)
{
    GUIPHoldbutton2(eax, edx, 94, 59, 102, 67, 80, (u1*)&GUIChoseSlotTextX[0], 1, '9'); // XXX ugly cast
    GUIPHoldbutton2(eax, edx, 105, 59, 113, 67, 81, (u1*)&GUIChoseSlotTextX[0], -1, '0'); // XXX ugly cast
    GUIPButtonHole(eax, edx, 10, 28, (u1*)&GUIChoseSaveText2[0], 0); // XXX ugly cast
    GUIPButtonHole(eax, edx, 30, 28, (u1*)&GUIChoseSaveText2[0], 1); // XXX ugly cast
    GUIPButtonHole(eax, edx, 50, 28, (u1*)&GUIChoseSaveText2[0], 2); // XXX ugly cast
    GUIPButtonHole(eax, edx, 70, 28, (u1*)&GUIChoseSaveText2[0], 3); // XXX ugly cast
    GUIPButtonHole(eax, edx, 90, 28, (u1*)&GUIChoseSaveText2[0], 4); // XXX ugly cast
    GUIPButtonHole(eax, edx, 10, 43, (u1*)&GUIChoseSaveText2[0], 5); // XXX ugly cast
    GUIPButtonHole(eax, edx, 30, 43, (u1*)&GUIChoseSaveText2[0], 6); // XXX ugly cast
    GUIPButtonHole(eax, edx, 50, 43, (u1*)&GUIChoseSaveText2[0], 7); // XXX ugly cast
    GUIPButtonHole(eax, edx, 70, 43, (u1*)&GUIChoseSaveText2[0], 8); // XXX ugly cast
    GUIPButtonHole(eax, edx, 90, 43, (u1*)&GUIChoseSaveText2[0], 9); // XXX ugly cast
    current_zst = (GUIChoseSlotTextX[0] - '0') * 10 + GUIChoseSaveText2[0];
}

static void DisplayGUIMovieClick(s4 const eax, s4 const edx)
{
    if (MovieProcessing < 4 || 6 < MovieProcessing) {
        GUIPTabClick(eax, edx, 0, 57, 1, GUIMovieTabs, &GUIDumpingTab[0], (u4*)0);
    }

    if (MovieProcessing < 1 || 3 < MovieProcessing) {
        GUIPTabClick(eax, edx, 58, 110, 1, GUIDumpingTab, &GUIMovieTabs[0], (u4*)0);
    }

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

    if (GUIMovieTabs[0] == 1) {
        if (MovieRecordWinVal != 0) // Overwrite Window
        {
            GUIPHoldbutton(eax, edx, 17, 65, 59, 76, 19);
            GUIPHoldbutton(eax, edx, 70, 65, 112, 76, 20);
            return;
        }

        // Main Window
        GUIPHoldbutton(eax, edx, 7, 80, 49, 91, 16); // Buttons
        GUIPHoldbutton(eax, edx, 55, 80, 97, 91, 17);
        GUIPHoldbutton(eax, edx, 103, 80, 145, 91, 18);
        GUIPHoldbutton(eax, edx, 151, 80, 193, 91, 32);
        GUIPHoldbutton(eax, edx, 7, 108, 50, 119, 29);
        GUIPHoldbutton(eax, edx, 85, 108, 138, 119, 30);
        GUIPHoldbutton(eax, edx, 173, 108, 203, 119, 31);

        GUIPButtonHole(eax, edx, 8, 64, &MovieStartMethod, 0); // Start From
        GUIPButtonHole(eax, edx, 43, 64, &MovieStartMethod, 1);
        GUIPButtonHole(eax, edx, 89, 64, &MovieStartMethod, 2);
        GUIPButtonHole(eax, edx, 135, 64, &MovieStartMethod, 3);

        DGOptnsProcBox(eax, edx, 58, 110, &KeyInsrtChap, 0); // Keyboard Shortcut Boxes
        DGOptnsProcBox(eax, edx, 146, 110, &KeyPrevChap, 0);
        DGOptnsProcBox(eax, edx, 210, 110, &KeyNextChap, 0);

        DGOptnsProcBox(eax, edx, 135, 124, &KeyRTRCycle, 0);

        GUIPButtonHole(eax, edx, 8, 133, &MZTForceRTR, 0);
        GUIPButtonHole(eax, edx, 8, 143, &MZTForceRTR, 1);
        GUIPButtonHole(eax, edx, 8, 153, &MZTForceRTR, 2);

        GUIClickCButton(eax, edx, 8, 163, &MovieDisplayFrame); // Checkbox
    }

    if (GUIDumpingTab[0] == 1) {
        GUIPHoldbutton(eax, edx, 165, 178, 200, 189, 34);
        GUIPHoldbutton(eax, edx, 206, 178, 235, 189, 35);

        GUIPButtonHole(eax, edx, 8, 64, &MovieVideoMode, 0); // Movie Options
        GUIPButtonHole(eax, edx, 8, 74, &MovieVideoMode, 1);
        GUIPButtonHole(eax, edx, 8, 84, &MovieVideoMode, 2);
        GUIPButtonHole(eax, edx, 8, 94, &MovieVideoMode, 3);
        GUIPButtonHole(eax, edx, 8, 104, &MovieVideoMode, 4);
        GUIPButtonHole(eax, edx, 8, 114, &MovieVideoMode, 5);

        GUIClickCButton(eax, edx, 130, 62, &MovieAudio);
        GUIClickCButton(eax, edx, 130, 72, &MovieAudioCompress);
        GUIClickCButton(eax, edx, 130, 82, &MovieVideoAudio);

        GUIPButtonHole(eax, edx, 8, 135, &MovieForcedLengthEnabled, 0); // Movie Options
        GUIPButtonHole(eax, edx, 8, 145, &MovieForcedLengthEnabled, 1);
        GUIPButtonHole(eax, edx, 8, 155, &MovieForcedLengthEnabled, 2);

        GUITextBoxInputNach(eax, edx, 136, 144, 205, 154, 0, 11, SetMovieForcedLength);
    }
}

#define GUIInputSetIndKey(p1, keycontrolval)                                            \
    do {                                                                                \
        /* Check if controller is set */                                                \
        /* Was a dword read of a single byte, which picked up whatever three            \
           globals the linker put after it - undefined, and it made the test            \
           depend on the *other* players' flags. It asks whether this                   \
           controller is set, so read the byte. */                                      \
        if (*(keycontrolval) == 0)                                                      \
            return;                                                                     \
                                                                                        \
        DGOptnsProcBox(eax, edx, 45, 102, &p1##upk, (keycontrolval)); /* Up */          \
        DGOptnsProcBox(eax, edx, 45, 112, &p1##downk, (keycontrolval)); /* Down */      \
        DGOptnsProcBox(eax, edx, 45, 122, &p1##leftk, (keycontrolval)); /* Left */      \
        DGOptnsProcBox(eax, edx, 45, 132, &p1##rightk, (keycontrolval)); /* Right */    \
        DGOptnsProcBox(eax, edx, 45, 142, &p1##startk, (keycontrolval)); /* Start */    \
        DGOptnsProcBox(eax, edx, 45, 152, &p1##selk, (keycontrolval)); /* Select */     \
        DGOptnsProcBox(eax, edx, 85, 102, &p1##Ak, (keycontrolval)); /* A */            \
        DGOptnsProcBox(eax, edx, 85, 112, &p1##Bk, (keycontrolval)); /* B */            \
        DGOptnsProcBox(eax, edx, 85, 122, &p1##Xk, (keycontrolval)); /* X */            \
        DGOptnsProcBox(eax, edx, 85, 132, &p1##Yk, (keycontrolval)); /* Y */            \
        DGOptnsProcBox(eax, edx, 85, 142, &p1##Lk, (keycontrolval)); /* L */            \
        DGOptnsProcBox(eax, edx, 85, 152, &p1##Rk, (keycontrolval)); /* R */            \
                                                                                        \
        DGOptnsProcBox(eax, edx, 125, 102, &p1##Xtk, (keycontrolval)); /* X Turbo */    \
        DGOptnsProcBox(eax, edx, 125, 112, &p1##Ytk, (keycontrolval)); /* Y Turbo */    \
        DGOptnsProcBox(eax, edx, 125, 122, &p1##Ltk, (keycontrolval)); /* L Turbo */    \
        DGOptnsProcBox(eax, edx, 165, 102, &p1##Atk, (keycontrolval)); /* A Turbo */    \
        DGOptnsProcBox(eax, edx, 165, 112, &p1##Btk, (keycontrolval)); /* B Turbo */    \
        DGOptnsProcBox(eax, edx, 165, 122, &p1##Rtk, (keycontrolval)); /* R Turbo */    \
                                                                                        \
        DGOptnsProcBox(eax, edx, 125, 142, &p1##ULk, (keycontrolval)); /* Up-Left */    \
        DGOptnsProcBox(eax, edx, 125, 152, &p1##DLk, (keycontrolval)); /* Down-Left */  \
        DGOptnsProcBox(eax, edx, 165, 142, &p1##URk, (keycontrolval)); /* Up-Right */   \
        DGOptnsProcBox(eax, edx, 165, 152, &p1##DRk, (keycontrolval)); /* Down-Right */ \
    } while (0)

static void DisplayGUIInputClick_skipscrol(s4 const eax, s4 const edx)
{
    // x,y,x2,y2,currentwin,vpos,#entries,starty,y/entry,cpos,winval,win#,dclicktick#
    GUIWinControl(eax, edx, 5, 36, 107, 34 + 5 * 8, &GUIBlankVar, &GUIcurrentinputviewloc, &GUINumValue, 35, 8, &GUIcurrentinputcursloc, 4, 3, 0);

    GUIPTabClick(eax, edx, 0, 21, 1, GUIInputTabs, (u4*)0);
    GUIPTabClick(eax, edx, 22, 43, 2, GUIInputTabs, (u4*)0);
    GUIPTabClick(eax, edx, 44, 65, 3, GUIInputTabs, (u4*)0);
    GUIPTabClick(eax, edx, 66, 87, 4, GUIInputTabs, (u4*)0);
    GUIPTabClick(eax, edx, 88, 109, 5, GUIInputTabs, (u4*)0);

    GUIPHoldbutton(eax, edx, 123, 34, 153, 45, 14); // Buttons
    GUIPHoldbutton(eax, edx, 123, 50, 177, 61, 40);

    switch (cplayernum) {
    case 0:
        GUIInputSetIndKey(pl1, &pl1contrl);
        break;
    case 1:
        GUIInputSetIndKey(pl2, &pl2contrl);
        break;
    case 2:
        GUIInputSetIndKey(pl3, &pl3contrl);
        break;
    case 3:
        GUIInputSetIndKey(pl4, &pl4contrl);
        break;
    case 4:
        GUIInputSetIndKey(pl5, &pl5contrl);
        break;
    }

    GUIClickCButton(eax, edx, 5, 160, &GameSpecificInput);
    GUIClickCButton(eax, edx, 105, 160, &SNESRumble);
    GUIClickCButton(eax, edx, 5, 170, &AllowUDLR);
    GUIClickCButton(eax, edx, 105, 170, &Turbo30hz);
    GUIClickCButtonM(eax, edx, 5, 180, &pl12s34);
}

static void DisplayGUIInputClick(s4 const eax, s4 const edx)
{
    // SlideBar Implementation
    GUINumValue = NumInputDevices;
    if (GUISlidebarImpl(eax, edx, 109, 42, 116, 69, GUIIStA, 5, &GUIcurrentinputviewloc, &GUIcurrentinputcursloc, &GUINumValue, 3))
        return;
    DisplayGUIInputClick_skipscrol(eax, edx);
}

static void DisplayGUIInputClick2(s4 const eax, s4 const edx)
{
    GUINumValue = NumInputDevices;
    if (GUISlidebarPostImpl(eax, edx, 109, 42, 116, 69, 9, 5, &GUIcurrentinputviewloc, &GUIcurrentinputcursloc, &GUINumValue, &GUIBlankVar, 1, 5, 35, 107, 35 + 5 * 8, DisplayGUIInputClick_skipscrol))
        return;
    DisplayGUIInputClick(eax, edx);
}

static void DisplayGUIOptionClick(s4 const eax, s4 const edx)
{
    GUIPTabClick(eax, edx, 0, 39, 1, GUIOptionTabs, (s4*)0);
    GUIPTabClick(eax, edx, 40, 74, 2, GUIOptionTabs, (s4*)0);

    if (GUIOptionTabs[0] == 1) { // Basic
        GUIClickCButton(eax, edx, 11, 41, &Show224Lines);
        GUIClickCButton(eax, edx, 11, 71, &newengen);
        GUIClickCButton(eax, edx, 11, 81, &bgfixer);
        GUIClickCButton(eax, edx, 11, 111, &AutoPatch);
        GUIClickCButton(eax, edx, 11, 121, &DisplayInfo);
        GUIClickCButton(eax, edx, 11, 131, &RomInfo);
#ifdef __WIN32__
        GUIClickCButton(eax, edx, 11, 161, &PauseFocusChange);
        GUIClickCButton(eax, edx, 11, 171, &HighPriority);
        CheckPriority();
#endif
        GUIClickCButton(eax, edx, 11, 181, &DisableScreenSaver);
#ifdef __WIN32__
        CheckScreenSaver();
#endif
    }

    if (GUIOptionTabs[0] == 2) {
        GUIClickCButton(eax, edx, 11, 31, &FPSAtStart);
        GUIClickCButton(eax, edx, 11, 41, &CPUAtStart);
        GUIClickCButton(eax, edx, 11, 51, &TimerEnable);
        GUIClickCButton(eax, edx, 89, 51, &TwelveHourClock);
        GUIClickCButton(eax, edx, 11, 61, &ClockBox);
        GUIClickCButton(eax, edx, 11, 91, &SmallMsgText);
        GUIClickCButton(eax, edx, 11, 101, &GUIEnableTransp);
        GUIPButtonHole(eax, edx, 11, 131, &ScreenShotFormat, 0);
#ifndef NO_PNG
        GUIPButtonHole(eax, edx, 11, 141, &ScreenShotFormat, 1);
#endif
    }
}

static void DisplayGUIVideoClick_notmodestab(s4 const eax, s4 const edx)
{
    if (GUIVideoTabs[0] == 2) // Filters tab
    {
        Clear2xSaIBuffer();

        {
            {
                // Bilinear
                if (GUIBIFIL[cvidmode] != 0) {
                    if (GUIClickArea(eax, edx, 18 + 1, 35 + 3, 18 + 6, 35 + 8))
                        NTSCFilter = 0;
                    GUIClickCButtonI(eax, edx, 18, 35, &BilinearFilter);
                } else {
                    // Interpolations
#ifdef __WIN32__
                    if (GUIDSIZE[cvidmode] != 0)
#else
                    if (GUII2VID[cvidmode] != 0)
#endif
                    {
                        if (GUIClickArea(eax, edx, 18 + 1, 35 + 3, 18 + 6, 35 + 8)) {
                            hqFilter = 0;
                            NTSCFilter = 0;
                            En2xSaI = 0;
                        }
                        GUIClickCButton(eax, edx, 18, 35, &antienab);
                    }
                }

                // NTSC filter
                if (GUINTVID[cvidmode] != 0) {
                    if (GUIClickArea(eax, edx, 128 + 1, 35 + 3, 128 + 6, 35 + 8)) {
                        En2xSaI = 0;
                        hqFilter = 0;
                        scanlines = 0;
                        antienab = 0;
                    }
#ifdef __OPENGL__
                    if (GUIClickArea(eax, edx, 128 + 1, 35 + 3, 128 + 6, 35 + 8))
                        BilinearFilter = 0;
#endif
                    GUIClickCButtonN(eax, edx, 128, 35, &NTSCFilter, NTSCFilterInit);
                }

                // Kreed 2x filters
                if (GUIDSIZE[cvidmode] != 0) {
                    if (GUIClickArea(eax, edx, 18 + 1, 45 + 3, 18 + 6, 45 + 8)) {
                        hqFilter = 0;
                        scanlines = 0;
                        antienab = 0;
                        NTSCFilter = 0;
                    }
                    if (GUIClickArea(eax, edx, 128 + 1, 45 + 3, 128 + 6, 45 + 8)) {
                        hqFilter = 0;
                        scanlines = 0;
                        antienab = 0;
                        NTSCFilter = 0;
                    }
                    if (GUIClickArea(eax, edx, 18 + 1, 55 + 3, 18 + 6, 55 + 8)) {
                        hqFilter = 0;
                        scanlines = 0;
                        antienab = 0;
                        NTSCFilter = 0;
                    }
                    GUIClickCButton6(eax, edx, 18, 45, &En2xSaI, 1);
                    GUIClickCButton6(eax, edx, 128, 45, &En2xSaI, 2);
                    GUIClickCButton6(eax, edx, 18, 55, &En2xSaI, 3);
                }

                u1 const bl = cvidmode; // Hq*x filters
                if (GUIHQ4X[bl] != 0) {
                    GUIPButtonHole(eax, edx, 188, 68, &hqFilterlevel, 4);
                    goto radiobuttonhq3x;
                }
                if (GUIHQ3X[bl] != 0) {
                radiobuttonhq3x:
                    GUIPButtonHole(eax, edx, 158, 68, &hqFilterlevel, 3);
                    goto radiobuttonhq2x;
                }
                if (GUIHQ2X[bl] != 0) {
                radiobuttonhq2x:
                    GUIPButtonHole(eax, edx, 128, 68, &hqFilterlevel, 2);
                    if (GUIClickArea(eax, edx, 128 + 1, 55 + 3, 128 + 6, 55 + 8)) {
                        En2xSaI = 0;
                        scanlines = 0;
                        antienab = 0;
                        NTSCFilter = 0;
                    }
                    GUIClickCButton(eax, edx, 128, 55, &hqFilter);
                }
            }

            GUIClickCButton(eax, edx, 18, 115, &GrayscaleMode); // Grayscale

            // Hires Mode7
            if (GUIM7VID[cvidmode] != 0)
                GUIClickCButton5(eax, edx, 128, 115, &Mode7HiRes16b, 1);

#ifdef __WIN32__
            // Triple buffs/vsyncs
            if (GUIWFVID[cvidmode] != 0) {
                GUIClickCButtonf(eax, edx, 128, 145, &TripleBufferWin, initDirectDraw);
            }
            GUIClickCButtonf(eax, edx, 18, 145, &vsyncon, initDirectDraw);
#endif

#ifdef __OPENGL__
            if (GUIBIFIL[cvidmode] != 0)
                GUIClickCButtonI(eax, edx, 18, 145, &vsyncon);
#endif

            // Keep 4:3 Ratio
            if (GUIKEEP43[cvidmode] != 0)
                GUIClickCButtonK(eax, edx, 18, 175, &Keep4_3Ratio, initwinvideo);

            // GL Scanlines
            if (GUIBIFIL[cvidmode] != 0) {
                // Update mouse location
                s4 const eax = GUImouseposx - GUIwinposx[5];
                s4 const edx = GUImouseposy - GUIwinposy[5];

                if (GUIClickArea(eax, edx, 23, 88, 23 + 100, 92)) {
                    sl_intensity = eax - 23;
                    GUIHold = 8; // Lock mouse to bar when clicked
                    GUIHoldYlim = GUIwinposy[5] + 90;
                    s4 const eax = GUIwinposx[5] + 23;
                    GUIHoldXlimL = eax;
                    GUIHoldXlimR = eax + 100;
                }
            } else {
                // Scanlines
                if (GUIDSIZE[cvidmode] != 0) {
                    if (GUIClickArea(eax, edx, 168 + 1, 87 + 3, 168 + 38, 87 + 8)) {
                        En2xSaI = 0;
                        hqFilter = 0;
                        NTSCFilter = 0;
                    }
                    GUIPButtonHoleS(eax, edx, 18, 87, &scanlines, 0);
                    GUIPButtonHoleS(eax, edx, 168, 87, &scanlines, 1);
                }

                {
                    if (GUIDSIZE[cvidmode] != 0) {
                        if (GUIClickArea(eax, edx, 68 + 1, 87 + 3, 68 + 38, 87 + 8)) {
                            En2xSaI = 0;
                            hqFilter = 0;
                            NTSCFilter = 0;
                        }
                        if (GUIClickArea(eax, edx, 118 + 1, 87 + 3, 118 + 38, 87 + 8)) {
                            En2xSaI = 0;
                            hqFilter = 0;
                            NTSCFilter = 0;
                        }
                        GUIPButtonHoleS(eax, edx, 68, 87, &scanlines, 2);
                        GUIPButtonHoleS(eax, edx, 118, 87, &scanlines, 3);
                    }
                }
            }
        }
    }

    if (GUIVntscTab[0] == 1) // NTSC Tab
    {
        // Update mouse location
        s4 const eax = GUImouseposx - GUIwinposx[5];
        s4 const edx = GUImouseposy - GUIwinposy[5];

        GUIClickCButton(eax, edx, 5, 25, &NTSCBlend); // Checkboxes
        GUIClickCButton(eax, edx, 135, 25, &NTSCRef);

        GUIPHoldbutton(eax, edx, 8, 166, 67, 177, 81);
        GUIPHoldbutton(eax, edx, 72, 166, 119, 177, 82);
        GUIPHoldbutton(eax, edx, 124, 166, 147, 177, 83);
        GUIPHoldbutton(eax, edx, 152, 166, 217, 177, 84);
        GUIPHoldbutton(eax, edx, 102, 186, 137, 197, 37); // button
        GUIPHoldbutton(eax, edx, 148, 186, 207, 197, 39); // button

        // Sliders
        if (8 <= eax && eax <= 8 + 200) // X-Range for click-area
        {
            s1 const al = eax - 108;
            if (54 <= edx && edx <= 58) // Y-Range for click-area
            { // Hue
                NTSCHue = al;
                ntscCurVar = al;
                ntscWhVar = 0;
                GUIHoldYlim = GUIwinposy[5] + 56;
            } else if (74 <= edx && edx <= 78) { // Saturation
                NTSCSat = al;
                ntscCurVar = al;
                ntscWhVar = 1;
                GUIHoldYlim = GUIwinposy[5] + 76;
            } else if (94 <= edx && edx <= 98) { // Contrast
                NTSCCont = al;
                ntscCurVar = al;
                ntscWhVar = 2;
                GUIHoldYlim = GUIwinposy[5] + 96;
            } else if (114 <= edx && edx <= 118) { // Brightness
                NTSCBright = al;
                ntscCurVar = al;
                ntscWhVar = 3;
                GUIHoldYlim = GUIwinposy[5] + 116;
            } else if (134 <= edx && edx <= 138) {
                NTSCSharp = al;
                ntscCurVar = al;
                ntscWhVar = 4;
                GUIHoldYlim = GUIwinposy[5] + 136;
            } else {
                goto nomovebar;
            }

            s4 const eax = GUIwinposx[5] + 8;
            GUIHoldXlimL = eax;
            GUIHoldXlimR = eax + 200;
            GUIHold = 7; // Lock mouse to bar when clicked
        nomovebar:;
        }

        if (NTSCRef != 0) {
            NTSCPresetVar = 4;
            NTSCFilterInit();
        }
    }

    if (GUIVntscTab[0] == 2) // NTSC Tab
    {
        // Update mouse location
        s4 const eax = GUImouseposx - GUIwinposx[5];
        s4 const edx = GUImouseposy - GUIwinposy[5];

        GUIPHoldbutton(eax, edx, 102, 186, 137, 197, 38); // button
        GUIPHoldbutton(eax, edx, 148, 186, 207, 197, 39); // button

        // Sliders
        if (8 <= eax && eax <= 8 + 200) // X-Range for click-area
        {
            s1 const al = eax - 108;
            if (44 <= edx && edx <= 48) // Y-Range for click-area
            { // Gamma
                NTSCGamma = al;
                ntscCurVar = al;
                ntscWhVar = 0;
                GUIHoldYlim = GUIwinposy[5] + 46;
            } else if (64 <= edx && edx <= 68) { // Resolution
                NTSCRes = al;
                ntscCurVar = al;
                ntscWhVar = 1;
                GUIHoldYlim = GUIwinposy[5] + 66;
            } else if (84 <= edx && edx <= 88) { // Artifacts
                NTSCArt = al;
                ntscCurVar = al;
                ntscWhVar = 2;
                GUIHoldYlim = GUIwinposy[5] + 86;
            } else if (104 <= edx && edx <= 108) { // Fringing
                NTSCFringe = al;
                ntscCurVar = al;
                ntscWhVar = 3;
                GUIHoldYlim = GUIwinposy[5] + 106;
            } else if (124 <= edx && edx <= 128) { // Bleed
                NTSCBleed = al;
                ntscCurVar = al;
                ntscWhVar = 4;
                GUIHoldYlim = GUIwinposy[5] + 126;
            } else if (144 <= edx && edx <= 148) { // Hue warping
                NTSCWarp = al;
                ntscCurVar = al;
                ntscWhVar = 5;
                GUIHoldYlim = GUIwinposy[5] + 146;
            } else {
                goto nomovebar2;
            }

            s4 const eax = GUIwinposx[5] + 8;
            GUIHoldXlimL = eax;
            GUIHoldXlimR = eax + 200;
            GUIHold = 7; // Lock mouse to bar when clicked
        nomovebar2:;
        }

        if (NTSCRef != 0) {
            NTSCPresetVar = 4;
            NTSCFilterInit();
        }
    }
}

static void DisplayGUIVideoClick_skipscrol(s4 const eax, s4 const edx)
{
    if (GUIWinControl(eax, edx, 5, 27, 115, 27 + 20 * 8, &GUIBlankVar, &GUIcurrentvideoviewloc, &GUINumValue, 27, 8, &GUIcurrentvideocursloc, 2, 5, 0))
        return;

    GUIPHoldbutton(eax, edx, 130, 31, 166, 41, 4);

    GUIPHoldbutton(eax, edx, 182, 116, 218, 126, 12);

    GUITextBoxInputNach(eax, edx, 130, 130, 178, 140, 0, 5, SetCustomXY);
    GUITextBoxInputNach(eax, edx, 191, 130, 239, 140, 1, 5, SetCustomXY);

    DisplayGUIVideoClick_notmodestab(eax, edx);
}

static void DisplayGUIVideoClick(s4 const eax, s4 const edx)
{
    GUIPTabClick(eax, edx, 0, 39, 1, GUIVideoTabs, GUIVntscTab, (s4*)0);
    GUIPTabClick(eax, edx, 40, 91, 2, GUIVideoTabs, GUIVntscTab, (s4*)0);
    if (NTSCFilter == 1) {
        GUIPTabClick(eax, edx, 92, 125, 1, GUIVntscTab, GUIVideoTabs, (s4*)0);
        GUIPTabClick(eax, edx, 126, 184, 2, GUIVntscTab, GUIVideoTabs, (s4*)0);
    }

    if (GUIVideoTabs[0] == 1) { // SlideBar Implementation
        GUINumValue = NumVideoModes;
        if (GUISlidebarImpl(eax, edx, 117, 33, 124, 182, GUIVStA, 20, &GUIcurrentvideoviewloc, &GUIcurrentvideocursloc, &GUINumValue, 5))
            return;
        DisplayGUIVideoClick_skipscrol(eax, edx);
    } else {
        DisplayGUIVideoClick_notmodestab(eax, edx);
    }
}

static void DisplayGUIVideoClick2(s4 const eax, s4 const edx)
{
    if (GUIVideoTabs[0] == 1) // modes
    {
        GUINumValue = NumVideoModes;
        if (GUISlidebarPostImpl(eax, edx, 117, 33, 124, 182, 5, 20, &GUIcurrentvideoviewloc, &GUIcurrentvideocursloc, &GUINumValue, &GUIBlankVar, 1, 5, 27, 115, 27 + 20 * 8, DisplayGUIVideoClick_skipscrol))
            return;
    }
    DisplayGUIVideoClick(eax, edx);
}

static void DisplayGUISoundClick(void)
{
    s4 const eax = GUImouseposx - GUIwinposx[6];
    s4 const edx = GUImouseposy - GUIwinposy[6];

    GUIClickCButton(eax, edx, 11, 21, &SPCDisable);
    GUIClickCButtonf(eax, edx, 11, 31, &soundon, reInitSound);
    GUIClickCButton(eax, edx, 11, 41, &StereoSound);
    GUIClickCButton(eax, edx, 11, 51, &RevStereo);
    GUIClickCButton(eax, edx, 11, 61, &Surround);
#ifdef __WIN32__
    GUIClickCButton(eax, edx, 11, 71, &PrimaryBuffer);
#endif

    GUIPButtonHole(eax, edx, 11, 157, &SoundInterpType, 0);
    GUIPButtonHole(eax, edx, 11, 167, &SoundInterpType, 1);
    GUIPButtonHole(eax, edx, 11, 177, &SoundInterpType, 2);
    GUIPButtonHole(eax, edx, 11, 187, &SoundInterpType, 3);

    GUIPButtonHole(eax, edx, 111, 157, &LowPassFilterType, 0);
    GUIPButtonHole(eax, edx, 111, 167, &LowPassFilterType, 1);
    GUIPButtonHole(eax, edx, 111, 177, &LowPassFilterType, 2);

    if (GUIClickArea(eax, edx, 15, 101, 69, 109)) {
        static u1 const sampratenext[] = { 1, 4, 5, 6, 2, 3, 0, 0 };
        SoundQuality = sampratenext[SoundQuality];
    }

    if (GUIClickArea(eax, edx, 15, 129, 115, 133)) {
        MusicRelVol = eax - 15;
        GUIHold = 5;
        GUIHoldYlim = GUIwinposy[6] + 131;
        u4 const vol = MusicRelVol * 128 / 100;
        MusicVol = vol < 127 ? vol : 127;
        DSPWriteReg(0x0C, DSPMem[0x0C]);
        DSPWriteReg(0x1C, DSPMem[0x1C]);

        s4 const eax = GUIwinposx[6] + 15;
        GUIHoldXlimL = eax;
        GUIHoldXlimR = eax + 100;
    }
}

static void DisplayGUICheatClick_skipscrol(s4 const eax, s4 const edx)
{
    if (GUIcurrentcheatwin == 0) {
        GUIWinControl(eax, edx, 5, 23, 229, 21 + 12 * 7, &GUIBlankVar, &GUIcurrentcheatviewloc, &NumCheats, 22, 7, &GUIcurrentcheatcursloc, 3, 7, 30);
    }

    GUIPHoldbutton(eax, edx, 5, 113, 47, 124, 5); // Buttons
    GUIPHoldbutton(eax, edx, 52, 113, 94, 124, 6);
    GUIPHoldbutton(eax, edx, 99, 113, 141, 124, 7);
    GUIPHoldbutton(eax, edx, 146, 113, 188, 124, 8);
    GUIPHoldbutton(eax, edx, 212, 134, 236, 145, 9);
    GUIPHoldbutton(eax, edx, 193, 113, 235, 124, 33);
    GUIClickCButton5(eax, edx, 11, 186, &AutoLoadCht, 1); // Checkbox
}

static void DisplayGUICheatClick(s4 const eax, s4 const edx)
{
    GUIBoxVar(eax, edx, 5, 20, 181, 110, &GUIcurrentcheatwin, 0); // Main Box
    GUIBoxVar(eax, edx, 82, 129, 173, 136, &GUIcurrentcheatwin, 1); // Enter Code Box
    GUIBoxVar(eax, edx, 82, 140, 196, 147, &GUIcurrentcheatwin, 2); // Description Box
    // SlideBar Implementation
    if (GUISlidebarImpl(eax, edx, 231, 28, 238, 100, GUICStA, 12, &GUIcurrentcheatviewloc, &GUIcurrentcheatcursloc, &NumCheats, 7))
        return;
    DisplayGUICheatClick_skipscrol(eax, edx);
}

static void DisplayGUICheatClick2(s4 const eax, s4 const edx)
{
    if (GUISlidebarPostImpl(eax, edx, 231, 28, 238, 100, 7, 12, &GUIcurrentcheatviewloc, &GUIcurrentcheatcursloc, &NumCheats, &GUIBlankVar, 1, 5, 22, 229, 22 + 12 * 7, DisplayGUICheatClick_skipscrol))
        return;
    DisplayGUICheatClick(eax, edx);
}

static void DisplayNetOptnsClick(void)
{
    s4 const eax = GUImouseposx - GUIwinposx[8];
    s4 const edx = GUImouseposy - GUIwinposy[8];

    if (GUIClickArea(eax, edx, 38, 24, 214, 34)) {
        GUIInputBox = 1;
        GUIInputLimit = 31;
    }
    GUIClickCButton(eax, edx, 8, 66, &NetplayUDPConfig);
    GUIPHoldbutton(eax, edx, 8, 74, 56, 85, 85);
    GUIPHoldbutton(eax, edx, 66, 74, 114, 85, 86);
    GUIPHoldbutton(eax, edx, 124, 74, 212, 85, 87);
}

static void DisplayGameOptnsClick(s4 const eax, s4 const edx)
{
    // Shortcut Boxes
    DGOptnsProcBox(eax, edx, 27, 23, &KeyBGDisble0, 0);
    DGOptnsProcBox(eax, edx, 27 + 45, 23, &KeyBGDisble1, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 2, 23, &KeyBGDisble2, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 3, 23, &KeyBGDisble3, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 4, 23, &KeySprDisble, 0);
    DGOptnsProcBox(eax, edx, 27, 41, &KeyDisableSC0, 0);
    DGOptnsProcBox(eax, edx, 27 + 45, 41, &KeyDisableSC1, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 2, 41, &KeyDisableSC2, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 3, 41, &KeyDisableSC3, 0);
    DGOptnsProcBox(eax, edx, 34 + 45 * 4, 41, &KeyVolUp, 0);
    DGOptnsProcBox(eax, edx, 27, 50, &KeyDisableSC4, 0);
    DGOptnsProcBox(eax, edx, 27 + 45, 50, &KeyDisableSC5, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 2, 50, &KeyDisableSC6, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 3, 50, &KeyDisableSC7, 0);
    DGOptnsProcBox(eax, edx, 34 + 45 * 4, 50, &KeyVolDown, 0);

    DGOptnsProcBox(eax, edx, 8 + 25, 70, &KeyQuickLoad, 0);
    DGOptnsProcBox(eax, edx, 8 + 57 + 26, 70, &KeyQuickRst, 0);
    DGOptnsProcBox(eax, edx, 8 + 114 + 20, 70, &KeyQuickExit, 0);
    DGOptnsProcBox(eax, edx, 8 + 114 + 78, 70, &KeyQuickClock, 0);
    DGOptnsProcBox(eax, edx, 8 + 25, 80, &KeyQuickChat, 0);
    DGOptnsProcBox(eax, edx, 8 + 57 + 45, 80, &KeyQuickSnapShot, 0);
    DGOptnsProcBox(eax, edx, 8 + 108 + 70, 80, &KeyQuickSaveSPC, 0);

    DGOptnsProcBox(eax, edx, 78, 100, &KeyUsePlayer1234, 0);
    DGOptnsProcBox(eax, edx, 78, 110, &KeyResetAll, 0);
    DGOptnsProcBox(eax, edx, 78, 120, &KeyDisplayFPS, 0);
    DGOptnsProcBox(eax, edx, 78, 130, &KeyDisplayBatt, 0);
    DGOptnsProcBox(eax, edx, 78, 140, &KeyDisplayCPU, 0);

    DGOptnsProcBox(eax, edx, 191, 100, &KeyNewGfxSwt, 0);
    DGOptnsProcBox(eax, edx, 191, 110, &KeyWinDisble, 0);
    DGOptnsProcBox(eax, edx, 191, 120, &KeyOffsetMSw, 0);
    DGOptnsProcBox(eax, edx, 191, 130, &KeyIncreaseGamma, 0);
    DGOptnsProcBox(eax, edx, 191, 140, &KeyDecreaseGamma, 0);
}

static void DisplayGUIOptnsClick(void)
{
    s4 const eax = GUImouseposx - GUIwinposx[10];
    s4 const edx = GUImouseposy - GUIwinposy[10];

    GUIClickCButton(eax, edx, 12, 23, &GUIRClick); // Checkboxes
    GUIClickCButton(eax, edx, 12, 33, &lhguimouse);
    GUIClickCButton(eax, edx, 12, 43, &mouseshad);
    GUIClickCButton(eax, edx, 12, 53, &mousewrap);

    GUIClickCButton(eax, edx, 129, 23, &esctomenu);
    GUIClickCButton(eax, edx, 129, 33, &JoyPad1Move);
    GUIClickCButtonI(eax, edx, 129, 43, &FilteredGUI);
    GUIClickCButton(eax, edx, 129, 53, &newfont);
    GUIClickCButton(eax, edx, 129, 63, &savewinpos);

#ifdef __WIN32__
    GUIClickCButton(eax, edx, 12, 63, &TrapMouseCursor);
    GUIClickCButton(eax, edx, 12, 73, &MouseWheel);
    GUIClickCButton(eax, edx, 12, 168, &AlwaysOnTop);
    GUIClickCButton(eax, edx, 12, 178, &SaveMainWindowPos);
    GUIClickCButton(eax, edx, 12, 188, &AllowMultipleInst);
#endif

    GUIPButtonHole(eax, edx, 72, 88, &GUIEffect, 0); // Radio Buttons
    GUIPButtonHole(eax, edx, 122, 88, &GUIEffect, 1);
    GUIPButtonHole(eax, edx, 182, 88, &GUIEffect, 4);
    GUIPButtonHole(eax, edx, 72, 98, &GUIEffect, 5);
    GUIPButtonHole(eax, edx, 122, 98, &GUIEffect, 2);
    GUIPButtonHole(eax, edx, 182, 98, &GUIEffect, 3);

    GUIPButtonHole(eax, edx, 48, 108, &CurPalSelect, 0);
    GUIPButtonHole(eax, edx, 88, 108, &CurPalSelect, 1);
    GUIPButtonHole(eax, edx, 133, 108, &CurPalSelect, 2);

    // Sliders
    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;
    }

    bool changed = false;
    if (25 <= eax && eax <= 25 + 127) {
        u1 const al = (u4)(eax - 25) / 4;
        if (122 <= edx && edx <= 126) {
            if (*TRVal2 != al) {
                *TRVal2 = al;
                changed = true;
            }
            GUIHold = 2;
            GUIHoldYlim = GUIwinposy[10] + 124;
        } else if (134 <= edx && edx <= 138) {
            if (*TGVal2 != al) {
                *TGVal2 = al;
                changed = true;
            }
            GUIHold = 2;
            GUIHoldYlim = GUIwinposy[10] + 136;
        } else if (146 <= edx && edx <= 150) {
            if (*TBVal2 != al) {
                *TBVal2 = al;
                changed = true;
            }
            GUIHold = 2;
            GUIHoldYlim = GUIwinposy[10] + 148;
        }
    }

    if (changed)
        GUISetPal();

    if (GUIHold == 2) {
        GUIHoldXlimL = GUIwinposx[10] + 25;
        GUIHoldXlimR = GUIwinposx[10] + 25 + 127;
    }
}

static void DisplayGUIAboutClick(s4 const eax, s4 const edx)
{
    if (EEgg != 1) {
        GUIPHoldbutton(eax, edx, 70, 23, 165, 33, 65);
    }
}

static void DisplayGUIComboClick_skipscrol(s4 const eax, s4 const edx)
{
    // x,y,x2,y2,currentwin,vpos,#entries,starty,y/entry,cpos,winval,win#,dclicktick#
    if (GUIWinControl2(eax, edx, 10, 23, 190, 22 + 8 * 7, &GUIBlankVar, GUIccombviewloc, (s4 const*)&NumCombo, 23, 7, (s4*)&GUIccombcursloc, 6, 16, 0))
        return;

    GUIPButtonHole(eax, edx, 158, 82, &GUIComboPNum, 0);
    GUIPButtonHole(eax, edx, 176, 82, &GUIComboPNum, 1);
    GUIPButtonHole(eax, edx, 194, 82, &GUIComboPNum, 2);
    GUIPButtonHole(eax, edx, 212, 82, &GUIComboPNum, 3);
    GUIPButtonHole(eax, edx, 230, 82, &GUIComboPNum, 4);
    GUIClickCButton(eax, edx, 163, 88, &GUIComboLHorz);
    GUIClickCButtonC(eax, edx, 163, 96, &GUIComboGameSpec);
    GUIPHoldbutton(eax, edx, 202, 20, 246, 31, 60);
    GUIPHoldbutton(eax, edx, 202, 35, 246, 46, 61);
    GUIPHoldbutton(eax, edx, 202, 50, 246, 61, 62);
    GUIPHoldbutton(eax, edx, 202, 65, 246, 76, 63);
    if (DGOptnsProcBoxc(eax, edx, 10, 91, 32, 99, &GUIComboKey))
        return;

    ComboBoxProc(eax, edx, 75, 150, 85, 157, 1);
    ComboBoxProc(eax, edx, 89, 150, 99, 157, 2);
    ComboBoxProc(eax, edx, 103, 150, 113, 157, 3);
    ComboBoxProc(eax, edx, 117, 150, 127, 157, 4);
    ComboBoxProc(eax, edx, 131, 150, 141, 157, 5);
    ComboBoxProc(eax, edx, 145, 150, 155, 157, 6);
    ComboBoxProc(eax, edx, 159, 150, 169, 157, 7);
    ComboBoxProc(eax, edx, 173, 150, 183, 157, 8);
    ComboBoxProc(eax, edx, 187, 150, 197, 157, 9);
    ComboBoxProc(eax, edx, 201, 150, 211, 157, 10);
    ComboBoxProc(eax, edx, 215, 150, 227, 157, 11);
    ComboBoxProc(eax, edx, 231, 150, 243, 157, 12);
    ComboBoxProc(eax, edx, 75, 160, 85, 167, 13);
    ComboBoxProc(eax, edx, 89, 160, 99, 167, 14);
    ComboBoxProc(eax, edx, 103, 160, 113, 167, 15);
    ComboBoxProc(eax, edx, 117, 160, 127, 167, 16);
    ComboBoxProc(eax, edx, 131, 160, 141, 167, 17);
    ComboBoxProc(eax, edx, 145, 160, 155, 167, 18);
    ComboBoxProc(eax, edx, 159, 160, 169, 167, 19);
    ComboBoxProc(eax, edx, 173, 160, 183, 167, 20);
    ComboBoxProc(eax, edx, 187, 160, 197, 167, 21);
    ComboBoxProc(eax, edx, 201, 160, 211, 167, 22);
    ComboBoxProc(eax, edx, 215, 160, 227, 167, 23);
    ComboBoxProc(eax, edx, 231, 160, 243, 167, 24);
    ComboBoxProc(eax, edx, 75, 170, 85, 177, 25);
    ComboBoxProc(eax, edx, 89, 170, 99, 177, 26);
    ComboBoxProc(eax, edx, 103, 170, 113, 177, 27);
    ComboBoxProc(eax, edx, 117, 170, 127, 177, 28);
    ComboBoxProc(eax, edx, 131, 170, 141, 177, 29);
    ComboBoxProc(eax, edx, 145, 170, 155, 177, 30);
    ComboBoxProc(eax, edx, 159, 170, 169, 177, 31);
    ComboBoxProc(eax, edx, 173, 170, 183, 177, 32);
    ComboBoxProc(eax, edx, 187, 170, 197, 177, 33);
    ComboBoxProc(eax, edx, 201, 170, 211, 177, 34);
    ComboBoxProc(eax, edx, 215, 170, 227, 177, 35);
    ComboBoxProc(eax, edx, 231, 170, 243, 177, 36);

    ComboBoxProc(eax, edx, 10, 189, 20, 196, 37);
    ComboBoxProc(eax, edx, 24, 189, 34, 196, 38);
    ComboBoxProc(eax, edx, 38, 189, 48, 196, 39);
    ComboBoxProc(eax, edx, 52, 189, 62, 196, 40);
    ComboBoxProc(eax, edx, 66, 189, 76, 196, 41);
    ComboBoxProc(eax, edx, 80, 189, 90, 196, 42);
    ComboBoxProc(eax, edx, 107, 189, 117, 196, 43);
    ComboBoxProc(eax, edx, 121, 189, 131, 196, 44);
    ComboBoxProc(eax, edx, 135, 189, 145, 196, 45);
    ComboBoxProc(eax, edx, 149, 189, 159, 196, 46);
    ComboBoxProc(eax, edx, 163, 189, 173, 196, 47);
    ComboBoxProc(eax, edx, 177, 189, 187, 196, 48);
    ComboBoxProcD(eax, edx, 204, 189, 218, 196);

    NumCombo = GUIComboGameSpec == 0 ? NumComboGlob : NumComboLocl;
}

static void DisplayGUIComboClick(s4 const eax, s4 const edx)
{
    // SlideBar Implementation
    // x1,y1,x2,y2,GUI?StA,ScrnSize,ViewLoc,CursLoc,Entries,win#
    if (GUISlidebarImpl(eax, edx, 192, 28, 199, 72, GUICSStC, 8, &GUIccombviewloc, &GUIccombviewloc, &NumCombo, 16))
        return;
    DisplayGUIComboClick_skipscrol(eax, edx);
}

static void DisplayGUIComboClick2(s4 const eax, s4 const edx)
{
    // x1,y1,x2,y2,upjump,downjump,holdpos,scsize,view,cur,listsize
    // x1,y1,x2,y2,view,curs,num,.scru,.scrd,jumpto,sizeofscreen
    if (GUISlidebarPostImpl(eax, edx, 192, 28, 199, 72, 13, 8, &GUIccombviewloc, &GUIccombcursloc, &NumCombo, &GUIBlankVar, 1, 10, 22, 190, 23 + 8 * 7, DisplayGUIComboClick_skipscrol))
        return;
    DisplayGUIComboClick(eax, edx);
}

static void DisplayGUIResetClick(s4 const eax, s4 const edx)
{
    GUIPHoldbutton(eax, edx, 20, 30, 56, 41, 2);
    GUIPHoldbutton(eax, edx, 80, 30, 116, 41, 3);
}

static void DisplayGUIStatesClick(s4 const eax, s4 const edx)
{
    GUIPHoldbutton(eax, edx, 20, 30, 56, 41, 10);
    GUIPHoldbutton(eax, edx, 80, 30, 116, 41, 11);
}

static void DisplayGUICheatSearchClick_skipscrol(s4 const eax, s4 const edx)
{
    // x,y,x2,y2,currentwin,vpos,#entries,starty,y/entry,cpos,winval,win#,dclicktick#
    if (GUIWinControl(eax, edx, 5, 23, 171, 21 + 12 * 7, &GUIBlankVar, &GUIcurrentchtsrcviewloc, &NumCheatSrc, 22, 7, &GUIcurrentchtsrccursloc, 5, 13, 30))
        return;

    GUIPHoldbutton(eax, edx, 70, 140, 130, 152, 54); // Return/Add Buttons
    GUIPHoldbutton(eax, edx, 140, 140, 180, 152, 55);
}

static void DisplayGUICheatSearchClick_view(s4 const eax, s4 const edx)
{
    // View Cheat
    // x1,y1,x2,y2,GUI?StA,ScrnSize,ViewLoc,CursLoc,Entries,win#
    if (GUISlidebarImpl(eax, edx, 173, 28, 180, 100, GUICSStA, 12, &GUIcurrentchtsrcviewloc, &GUIcurrentchtsrccursloc, &NumCheatSrc, 13))
        return;
    DisplayGUICheatSearchClick_skipscrol(eax, edx);
}

static void DisplayGUICheatSearchClick(s4 const eax, s4 const edx)
{
    switch (CheatWinMode) {
    case 1: // Exact Value
        GUIPHoldbutton(eax, edx, 30, 140, 80, 152, 51);
        if (CheatSearchStatus != 1) { // Return and Re-Search
            if (CheatSrcSearchType == 1) { // Select Comparison
                GUIPButtonHole(eax, edx, 11, 33, &CheatCompareValue, 0);
                GUIPButtonHole(eax, edx, 11, 43, &CheatCompareValue, 1);
                GUIPButtonHole(eax, edx, 11, 53, &CheatCompareValue, 2);
                GUIPButtonHole(eax, edx, 11, 63, &CheatCompareValue, 3);
            }
            // Restart/View/Search Buttons
            GUIPHoldbutton(eax, edx, 120, 140, 170, 152, 53);
        }
        GUIPHoldbutton(eax, edx, 10, 140, 60, 152, 51);
        GUIPHoldbutton(eax, edx, 70, 140, 110, 152, 52);
        break;

    case 2:
        DisplayGUICheatSearchClick_view(eax, edx);
        break;

    case 3: // Add Cheat
        GUIPHoldbutton(eax, edx, 60, 155, 120, 167, 56); // Return/Add Buttons
        GUIPHoldbutton(eax, edx, 130, 155, 160, 167, 57);
        GUIClickCButton(eax, edx, 8, 139, &CheatUpperByteOnly); // Checkbox
        GUIBoxVar(eax, edx, 10, 30, 80, 37, &CurCStextpos, 0); // Input Boxes
        GUIBoxVar(eax, edx, 10, 55, 126, 62, &CurCStextpos, 1);
        break;

    default:
        GUIPHoldbutton(eax, edx, 95, 140, 140, 152, 50); //  Main Menu
        GUIPButtonHole(eax, edx, 11, 28, &CheatSrcByteSize, 0);
        GUIPButtonHole(eax, edx, 11, 38, &CheatSrcByteSize, 1);
        GUIPButtonHole(eax, edx, 11, 48, &CheatSrcByteSize, 2);
        GUIPButtonHole(eax, edx, 11, 58, &CheatSrcByteSize, 3);
        GUIPButtonHole(eax, edx, 11, 73, &CheatSrcByteBase, 0);
        GUIPButtonHole(eax, edx, 11, 83, &CheatSrcByteBase, 1);
        GUIPButtonHole(eax, edx, 11, 113, &CheatSrcSearchType, 0);
        GUIPButtonHole(eax, edx, 11, 123, &CheatSrcSearchType, 1);
        break;
    }
}

static void DisplayGUICheatSearchClick2(s4 const eax, s4 const edx)
{
    if (CheatWinMode == 2) { // Preview Box
        // x1,y1,x2,y2,upjump,downjump,holdpos,scsize,view,cur,listsize
        // x1,y1,x2,y2,view,curs,num,.scru,.scrd,jumpto,sizeofscreen
        if (GUISlidebarPostImpl(eax, edx, 173, 28, 180, 100, 11, 12, &GUIcurrentchtsrcviewloc, &GUIcurrentchtsrccursloc, &NumCheatSrc, &GUIBlankVar, 1, 5, 22, 171, 22 + 12 * 7, DisplayGUICheatSearchClick_skipscrol))
            return;
        DisplayGUICheatSearchClick_view(eax, edx);
    } else {
        DisplayGUICheatSearchClick(eax, edx);
    }
}

static void DisplayGUIAddOnClick(s4 const eax, s4 const edx)
{
    GUIPButtonHole(eax, edx, 9, 23, &device1, 0);
    GUIPButtonHole(eax, edx, 9, 33, &device1, 1);

    GUIPButtonHole(eax, edx, 100, 23, &device2, 0);
    GUIPButtonHole(eax, edx, 100, 33, &device2, 1);
    GUIPButtonHole(eax, edx, 100, 43, &device2, 2);
    GUIPButtonHole(eax, edx, 100, 53, &device2, 3);
    GUIPButtonHole(eax, edx, 100, 63, &device2, 4);

    DGOptnsProcBox(eax, edx, 74, 76, &KeyExtraEnab1, 0);
    DGOptnsProcBox(eax, edx, 165, 76, &KeyExtraEnab2, 0);

    GUIClickCButton(eax, edx, 9, 83, &mouse1lh);
    GUIClickCButton(eax, edx, 100, 83, &mouse2lh);

    DGOptnsProcBox(eax, edx, 74, 108, &SSAutoFire, 0);
    DGOptnsProcBox(eax, edx, 165, 108, &SSPause, 0);
}

static void DisplayGUIChipClick(s4 const eax, s4 const edx)
{
    GUIClickCButton(eax, edx, 9, 43, &nssdip1);
    GUIClickCButton(eax, edx, 59, 43, &nssdip2);
    GUIClickCButton(eax, edx, 109, 43, &nssdip3);
    GUIClickCButton(eax, edx, 9, 53, &nssdip4);
    GUIClickCButton(eax, edx, 59, 53, &nssdip5);
    GUIClickCButton(eax, edx, 109, 53, &nssdip6);
}

#define PATH_LENGTH 1024

static void DisplayGUIPathsClick(s4 const eax, s4 const edx)
{
    GUIPTabClick(eax, edx, 0, 51, 1, GUIPathTabs, (u4*)0);
    GUIPTabClick(eax, edx, 52, 86, 2, GUIPathTabs, (u4*)0);
    GUIPTabClick(eax, edx, 87, 157, 3, GUIPathTabs, (u4*)0);

    if (GUIPathTabs[0] == 1) { // General
        GUITextBoxInputNach(eax, edx, 8, 41, 237, 51, 0, PATH_LENGTH, init_save_paths); // SRAMPath
        GUITextBoxInputNach(eax, edx, 8, 76, 237, 86, 1, PATH_LENGTH, init_save_paths); // SStatePath
        GUITextBoxInputNach(eax, edx, 8, 111, 237, 121, 2, PATH_LENGTH, init_save_paths); // MoviePath
        GUITextBoxInputNach(eax, edx, 8, 146, 237, 156, 3, PATH_LENGTH, init_save_paths); // IPSPath

        GUIPButtonHole(eax, edx, 8, 178, &RelPathBase, 0);
        GUIPButtonHole(eax, edx, 88, 178, &RelPathBase, 1);
    }

    if (GUIPathTabs[0] == 2) { // More paths
        GUITextBoxInputNach(eax, edx, 8, 41, 237, 51, 0, PATH_LENGTH, init_save_paths); // SnapPath
        GUITextBoxInputNach(eax, edx, 8, 76, 237, 86, 1, PATH_LENGTH, init_save_paths); // SPCPath
        GUITextBoxInputNach(eax, edx, 8, 111, 237, 121, 2, PATH_LENGTH, init_save_paths); // CHTPath
        GUITextBoxInputNach(eax, edx, 8, 146, 237, 156, 3, PATH_LENGTH, init_save_paths); // ComboPath
        GUITextBoxInputNach(eax, edx, 8, 181, 237, 191, 4, PATH_LENGTH, init_save_paths); // INPPath
    }

    if (GUIPathTabs[0] == 3) { // BIOS+Carts
        GUITextBoxInputNach(eax, edx, 8, 41, 237, 51, 0, PATH_LENGTH, init_save_paths); // BSXPath
        GUITextBoxInputNach(eax, edx, 8, 76, 237, 86, 1, PATH_LENGTH, init_save_paths); // STPath
        GUITextBoxInputNach(eax, edx, 8, 111, 237, 121, 2, PATH_LENGTH, init_save_paths); // GNextPath
        GUITextBoxInputNach(eax, edx, 8, 146, 237, 156, 3, PATH_LENGTH, init_save_paths); // SGPath
    }
}

static void DisplayGUISaveClick(s4 const eax, s4 const edx)
{
    GUIClickCButton(eax, edx, 11, 38, &nosaveSRAM);
    GUIClickCButton(eax, edx, 11, 48, &SRAMSave5Sec);
    GUIClickCButton(eax, edx, 11, 58, &SRAMState);
    GUIClickCButton(eax, edx, 11, 68, &LatestSave);
    GUIClickCButton(eax, edx, 11, 78, &AutoIncSaveSlot);
    GUIClickCButton(eax, edx, 11, 88, &AutoState);
    GUIClickCButton(eax, edx, 11, 98, &PauseLoad);
    GUIClickCButton(eax, edx, 11, 108, &PauseRewind);

    GUIPHoldbutton2(eax, edx, 173, 17, 181, 24, 70, &RewindStates, 1, 99);
    GUIPHoldbutton2(eax, edx, 184, 17, 192, 24, 71, &RewindStates, -1, 0);
    GUIPHoldbutton2(eax, edx, 173, 29, 181, 36, 72, &RewindFrames, 1, 99);
    GUIPHoldbutton2(eax, edx, 184, 29, 192, 36, 73, &RewindFrames, -1, 1);

    DGOptnsProcBox(eax, edx, 27, 130, &KeyStateSlc0, 0);
    DGOptnsProcBox(eax, edx, 27 + 45, 130, &KeyStateSlc1, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 2, 130, &KeyStateSlc2, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 3, 130, &KeyStateSlc3, 0);
    DGOptnsProcBox(eax, edx, 27, 139, &KeyStateSlc4, 0);
    DGOptnsProcBox(eax, edx, 27 + 45, 139, &KeyStateSlc5, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 2, 139, &KeyStateSlc6, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 3, 139, &KeyStateSlc7, 0);
    DGOptnsProcBox(eax, edx, 27, 148, &KeyStateSlc8, 0);
    DGOptnsProcBox(eax, edx, 27 + 45, 148, &KeyStateSlc9, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 2, 148, &KeyIncStateSlot, 0);
    DGOptnsProcBox(eax, edx, 27 + 45 * 3, 148, &KeyDecStateSlot, 0);
    DGOptnsProcBox(eax, edx, 8 + 25, 157, &KeySaveState, 0);
    DGOptnsProcBox(eax, edx, 8 + 57 + 25, 157, &KeyLoadState, 0);
    DGOptnsProcBox(eax, edx, 8 + 114 + 25, 157, &KeyStateSelct, 0);
    DGOptnsProcBox(eax, edx, 8 + 38, 166, &KeyRewind, 0);
}

static void DisplayGUISpeedClick(void)
{
    // This updates the mouse location, effectively 'grabbing' the cursor.
    s4 const eax = GUImouseposx - GUIwinposx[21];
    s4 const edx = GUImouseposy - GUIwinposy[21];

    GUIClickCButton(eax, edx, 11, 135, &FastFwdToggle); // Checkboxes
    GUIClickCButtonfr(eax, edx, 11, 145, &frameskip);
    GUIClickCButtoncpu(eax, edx, 11, 155, &frameskip);

    if (frameskip == 0) {
        DGOptnsProcBox(eax, edx, 12, 78, &KeyEmuSpeedUp, 0);
        DGOptnsProcBox(eax, edx, 12, 98, &KeyEmuSpeedDown, 0);
    } else {
        DGOptnsProcBox(eax, edx, 12, 78, &KeyFRateUp, 0);
        DGOptnsProcBox(eax, edx, 12, 98, &KeyFRateDown, 0);
    }

    DGOptnsProcBox(eax, edx, 12, 58, &KeyFastFrwrd, 0); // Shortcut Boxes
    DGOptnsProcBox(eax, edx, 12, 68, &KeySlowDown, 0);
    DGOptnsProcBox(eax, edx, 12, 88, &KeyResetSpeed, 0);
    DGOptnsProcBox(eax, edx, 12, 108, &EMUPauseKey, 0);
    DGOptnsProcBox(eax, edx, 12, 118, &INCRFrameKey, 0);

    GUIPHoldbutton2(eax, edx, 118, 24, 126, 32, 74, &FFRatio, 1, 28);
    GUIPHoldbutton2(eax, edx, 129, 24, 137, 32, 75, &FFRatio, -1, 0);
    GUIPHoldbutton2(eax, edx, 118, 35, 126, 43, 76, &SDRatio, 1, 28);
    GUIPHoldbutton2(eax, edx, 129, 35, 137, 43, 77, &SDRatio, -1, 0);
    if (frameskip != 0) {
        GUIPHoldbutton2(eax, edx, 118, 13, 126, 21, 78, &frameskip, 1, 10);
        GUIPHoldbutton2(eax, edx, 129, 13, 137, 21, 79, &frameskip, -1, 1);
    } else {
        GUIPHoldbutton2(eax, edx, 118, 13, 126, 21, 78, &maxskip, 1, 9);
        GUIPHoldbutton2(eax, edx, 129, 13, 137, 21, 79, &maxskip, -1, 0);

        // Speed Slider
        if (7 <= eax && eax <= 7 + 116 && // X-Range for click-area
            173 <= edx && edx <= 177) // Y-Range for click-area
        {
            EmuSpeed = (u4)(eax - 7) / 2;
            GUIHold = 6; // Lock mouse to bar when clicked
            GUIHoldYlim = GUIwinposy[21] + 175;
            s4 const eax = GUIwinposx[21] + 7;
            GUIHoldXlimL = eax;
            GUIHoldXlimR = eax + 116;
        }
    }
}

static void GUIWindowMove(void)
{
    u1 const id = GUIwinorder[GUIwinptr - 1];
    u4 const rx = GUImouseposx - GUIwinposx[id];
    u4 const ry = GUImouseposy - GUIwinposy[id];
    switch (id) {
    case 3:
        DisplayGUIInputClick2(rx, ry);
        break;
    case 5:
        DisplayGUIVideoClick2(rx, ry);
        break;
    case 7:
        DisplayGUICheatClick2(rx, ry);
        break;
    case 13:
        DisplayGUICheatSearchClick2(rx, ry);
        break;
    case 16:
        DisplayGUIComboClick2(rx, ry);
        break;
    default:
        DisplayGUIConfirmClick2(rx, ry);
        break;
    }
}

static void GUIWinClicked(u4 const i, u4 const id)
{
    u4 const rx = GUImouseposx - GUIwinposx[id];
    u4 const ry = GUImouseposy - GUIwinposy[id];
    s4 const esi = rx - GUIwinsizex[id] + 10;
    if (0 <= esi && esi < 10 && 0 < ry && ry < 10) {
        GUIwinorder[i] = 0;
        GUIwinactiv[id] = 0;
        GUIInputBox = 0;
        if (--GUIwinptr == 0)
            GUIcmenupos = GUIpmenupos;
        if (id == 5)
            SetCustomXY();
        else if (id == 15)
            SetMovieForcedLength();
        else if (id == 19)
            init_save_paths();
    } else if (ry < 10) {
        GUIHold = 1;
        GUIHoldxm = (short)GUIwinposx[id];
        GUIHoldym = (short)GUIwinposy[id];
        GUIHoldx = GUImouseposx;
        GUIHoldy = GUImouseposy;
    } else {
        GUIInputBox = 0;
        switch (id) {
        case 1:
            DisplayGUIConfirmClick(rx, ry);
            break;
        case 2:
            DisplayGUIChoseSaveClick(rx, ry);
            break;
        case 3:
            DisplayGUIInputClick(rx, ry);
            break;
        case 4:
            DisplayGUIOptionClick(rx, ry);
            break;
        case 5:
            DisplayGUIVideoClick(rx, ry);
            break;
        case 6:
            DisplayGUISoundClick();
            break;
        case 7:
            DisplayGUICheatClick(rx, ry);
            break;
        case 8:
            DisplayNetOptnsClick();
            break;
        case 9:
            DisplayGameOptnsClick(rx, ry);
            break;
        case 10:
            DisplayGUIOptnsClick();
            break;
        case 11:
            DisplayGUIAboutClick(rx, ry);
            break;
        case 12:
            DisplayGUIResetClick(rx, ry);
            break;
        case 13:
            DisplayGUICheatSearchClick(rx, ry);
            break;
        case 14:
            DisplayGUIStatesClick(rx, ry);
            break;
        case 15:
            DisplayGUIMovieClick(rx, ry);
            break;
        case 16:
            DisplayGUIComboClick(rx, ry);
            break;
        case 17:
            DisplayGUIAddOnClick(rx, ry);
            break;
        case 18:
            DisplayGUIChipClick(rx, ry);
            break;
        case 19:
            DisplayGUIPathsClick(rx, ry);
            break;
        case 20:
            DisplayGUISaveClick(rx, ry);
            break;
        case 21:
            DisplayGUISpeedClick();
            break;
        default:
            break;
        }
    }
}

static void ProcessMouseButtons(void)
{
    static u1 GUIOnMenuItm;

    u2 const x = GUImouseposx;
    u2 const y = GUImouseposy;

    if (MouseMoveOkay == 1) {
        GUIOnMenuItm = 0;
        if (GUIMenuL < x && x < GUIMenuR && 18 < y && y < GUIMenuD) {
            // Mouse Menu
            GUIOnMenuItm = 2;
            u4 const row = (y - 18) / 10;
            if (GUICYLocPtr[row + 1] != 0) {
                GUIcrowpos = row;
                GUIOnMenuItm = 1;
            }
        }
    }

    // Check if mouse is clicked on menu
    u2 const buttons = mousebuttonstat;
    if (GUIRClick == 1) {
        if (buttons & 0x02) {
            if (romloadskip != 0 || MousePRClick != 0)
                goto norclick2;
            GUIQuit = 2;
        }
        MousePRClick = 0;
    norclick2:;
    }
    if (buttons & 0x01) {
        if (GUIpclicked != 1 && GUIOnMenuItm != 2) {
            if (GUIOnMenuItm == 1) {
                GUITryMenuItem();
            } else if (y <= 15) {
                if (3 <= y && y <= 14) {
                    if (233 <= x && x <= 242) {
                        GUIcwinpress =
#ifndef __UNIXSDL__
                            y > 8 ? 3 :
#endif
                                  1;
                        goto noclick;
                    } else if (244 <= x && x <= 253) {
                        GUIcwinpress = 2;
                        goto noclick;
                    }
                }

                if (3 <= y && y <= 13) {
                    if (4 <= x && x <= 12) {
                        GUIcmenupos = 1;
                        GUIcrowpos = 0;
                        goto noclick;
                    } else if (17 <= x && x <= 47) {
                        GUIcmenupos = 2;
                        GUIcrowpos = 0;
                        goto noclick;
                    } else if (52 <= x && x <= 94) {
                        GUIcmenupos = 3;
                        GUIcrowpos = 0;
                        goto noclick;
                    } else if (99 <= x && x <= 135) {
                        GUIcmenupos = 4;
                        GUIcrowpos = 0;
                        goto noclick;
                    } else if (140 <= x && x <= 188) {
                        GUIcmenupos = 5;
                        GUIcrowpos = 0;
                        goto noclick;
                    } else if (193 <= x && x <= 223) {
                        GUIcmenupos = 6;
                        GUIcrowpos = 0;
                        goto noclick;
                    } else if (224 <= x) {
                        goto noclick;
                    }
                }

                GUIpclicked = 1;
                GUIHold = 255;
                GUIHoldYlim = y;
                GUIHoldXlimL = x;
                MouseWindow();
            } else if (GUIcmenupos == 0) {
                GUIpclicked = 1;
                u4 i = GUIwinptr;
                if (i != 0) {
                    u1 const id = GUIwinorder[--i];
                    if (GUIwinposx[id] < x && x < GUIwinposx[id] + GUIwinsizex[id] && GUIwinposy[id] < y && y < GUIwinposy[id] + GUIwinsizey[id] + 10) {
                        GUIWinClicked(i, id);
                        return;
                    }
                    while (i != 0) {
                        u1 const id = GUIwinorder[--i];
                        if (x <= GUIwinposx[id] || GUIwinposx[id] + GUIwinsizex[id] <= x)
                            continue;
                        if (y <= GUIwinposy[id] || GUIwinposy[id] + GUIwinsizey[id] + 10 <= y)
                            continue;
                        // Shift all following windows downwards by 1
                        while (++i != GUIwinptr)
                            GUIwinorder[i - 1] = GUIwinorder[i];
                        GUIwinorder[i - 1] = id;
                        GUIpclicked = 0;
                        return;
                    }
                }
                if (SantaPos != 272 && ShowTimer == 0) {
                    MsgGiftLeft = 36 * 4;
                    ShowTimer = 1;
                }
                return;
            } else {
                GUIpmenupos = GUIcmenupos;
                GUIcmenupos = 0;
            }
        }

    noclick:;
        GUIpclicked = 1;
        switch (GUIHold) {
        case 2: { // Colour Slide Bar Hold
            GUImouseposy = GUIHoldYlim;
            u4 const minx = GUIHoldXlimL;
            if (x < minx)
                GUImouseposx = minx;
            u4 const maxx = GUIHoldXlimR;
            if (x > maxx)
                GUImouseposx = maxx;
            lastmouseholded = 1;
            DisplayGUIOptnsClick();
            return;
        }

        case 3: { // Box Hold
            u4 const miny = GUIHoldYlim;
            if (y <= miny)
                GUImouseposy = miny;
            u4 const minx = GUIHoldXlimL;
            if (x <= minx)
                GUImouseposx = minx;
            u4 const maxy = GUIHoldYlimR;
            if (y >= maxy)
                GUImouseposy = maxy;
            u4 const maxx = GUIHoldXlimR;
            if (x >= maxx)
                GUImouseposx = maxx;
            lastmouseholded = 1;
            GUIWindowMove();
            return;
        }

        case 4: {
            u1 const id = GUIwinorder[GUIwinptr - 1];
            u4 const rx = x - GUIwinposx[id];
            u4 const ry = y - GUIwinposy[id];
            GUICBHold = GUIHoldXlimL <= rx && rx <= GUIHoldXlimR && GUIHoldYlim <= ry && ry <= GUIHoldYlimR ? GUICBHold2 : 0;
            return;
        }

        case 5: { // Sound Slide Bar Hold
            GUImouseposy = GUIHoldYlim;
            u4 const minx = GUIHoldXlimL;
            if (x < minx)
                GUImouseposx = minx;
            u4 const maxx = GUIHoldXlimR;
            if (x > maxx)
                GUImouseposx = maxx;
            lastmouseholded = 1;
            DisplayGUISoundClick();
            return;
        }

        case 6: { // Speed Slide Bar Hold
            GUImouseposy = GUIHoldYlim;
            u4 const minx = GUIHoldXlimL;
            if (x < minx)
                GUImouseposx = minx;
            u4 const maxx = GUIHoldXlimR;
            if (x > maxx)
                GUImouseposx = maxx;
            lastmouseholded = 1;
            DisplayGUISpeedClick();
            return;
        }

        case 7:
        case 8: { // Video Slide Bar Hold
            GUImouseposy = GUIHoldYlim;
            u4 const minx = GUIHoldXlimL;
            if (x < minx)
                GUImouseposx = minx;
            u4 const maxx = GUIHoldXlimR;
            if (x > maxx)
                GUImouseposx = maxx;
            lastmouseholded = 1;
            DisplayGUIVideoClick(0, 0); // XXX eax and edx do not seem to hold sensible values here
            return;
        }

        case 1: {
        hold:
            if (GUImouseposy < 16) {
                if (mousewrap & 1) {
                    GUImouseposy += 224 - 16;
                    goto hold;
                }
                GUImouseposy = 16;
            }
            u1 const id = GUIwinorder[GUIwinptr - 1];
            GUIwinposy[id] = (s2)(GUImouseposy - GUIHoldy + GUIHoldym);
            GUIwinposx[id] = (s2)(GUImouseposx - GUIHoldx + GUIHoldxm);
            return;
        }

        case 255: {
            GUImouseposy = GUIHoldYlim;
            GUImouseposx = GUIHoldXlimL;
            Set_MousePosition(GUIHoldXlimL, GUIHoldYlim);
            return;
        }

        default:
            GUICHold = 0;
            return;
        }
    }
    GUICHold = 0;
    GUIpclicked = 0;
    GUIHold = 0;

#if defined __UNIXSDL__ || defined __WIN32__
    if (GUIcwinpress == 1) {
        GUIcwinpress = 0;
#ifdef __UNIXSDL__
        if (3 <= y && y <= 13)
#else
        if (3 <= y && y <= 7)
#endif
        {
            if (233 <= x && x <= 242) {
                SwitchFullScreen();
                return;
            }
        }
    }
#endif

    if (GUIcwinpress == 2) {
        GUIcwinpress = 0;
        if (3 <= y && y <= 13 && 44 <= x && x <= 253) {
            GUIQuit = 1;
            return;
        }
    }

    if (GUIcwinpress == 3) {
        GUIcwinpress = 0;
        if (9 <= y && y <= 13 && 233 <= x && x <= 242) {
#ifdef __WIN32__
            MinimizeWindow();
#endif
            return;
        }
    }

    // ButtonProcess
    switch (GUICBHold) {
    case 1:
        GUILoadData();
        return;
    case 2:
        GUIProcReset();
        return;
    case 3:
        GUIProcReset();
        return;
    case 4:
        GUIProcVideo();
        return; // set video mode
    case 12:
        GUIProcCustomVideo();
        return; // set custom video mode
    case 37:
    case 38:
    case 39:
        GUINTSCReset();
        return; // reset ntsc options
    case 81:
    case 82:
    case 83:
    case 84:
        GUINTSCPreset();
        return; // ntsc preset
    case 10:
    case 11:
        GUIProcStates();
        return;
    case 5:
        CheatCodeRemove();
        return;
    case 6:
        CheatCodeToggle();
        return;
    case 7:
        CheatCodeSave();
        return;
    case 8:
        CheatCodeLoad();
        return;
    case 9:
        ProcessCheatCode();
        return;
    case 33:
        CheatCodeFix();
        return;
    case 14:
        SetDevice();
        return;
    case 15:
        CalibrateDev1();
        return;
    case 16:
        GUICBHold = 0;
        MoviePlay();
        return; // movie replay
    case 17:
        GUICBHold = 0;
        MovieRecord();
        return; // movie record
    case 18:
        GUICBHold = 0;
        MovieStop();
        return; // movie stop
    case 19:
        GUICBHold = 0;
        MovieRecord();
        return; // overwrite zmv ? yes
    case 20:
        GUICBHold = 0;
        SkipMovie();
        return; // overwrite zmv ? no
    case 29:
        GUICBHold = 0;
        MovieInsertChapter();
        return; // insert chapter

    case 30:
        GUICBHold = 0;
        GUIQuit = 2;
        MovieSeekBehind();
        return; // back to previous chapter
    case 31:
        GUICBHold = 0;
        GUIQuit = 2;
        MovieSeekAhead();
        return; // jump to next chapter
    case 32:
        GUICBHold = 0;
        GUIQuit = 2;
        MovieAppend();
        return; // append movie

    case 34: // dump raw
    {
        GUICBHold = 0;
        GUIQuit = 2;
        SetMovieForcedLength();
        MovieDumpRaw();
        if (MovieVideoMode >= 2 && mencoderExists == 0) {
            guimencodermsg();
        }
        if (MovieAudio != 0 && MovieAudioCompress != 0 && lameExists == 0) {
            guilamemsg();
        }
        return;
    }

    case 35:
        GUICBHold = 0;
        GUIQuit = 2;
        MovieStop();
        return; // stop dump
    case 40:
        SetAllKeys();
        return;

    case 50:
        CheatCodeSearchInit();
        break;

    case 60:
        GUIComboTextH[0] = '\0';
        GUINumCombo = 0;
        GUIComboKey = 0;
        break;

    case 61:
        if (NumCombo != 50)
            ComboAdder();
        break;
    case 62:
        if (NumCombo != 0)
            ComboReplace();
        break;
    case 63:
        if (NumCombo != 0)
            ComboRemoval();
        break;

    case 51:
        CheatWinMode = 0;
        CheatSearchStatus = 0;
        break;

    case 52:
        CheatWinMode = 2;
        break;
    case 53:
        CheatCodeSearchProcess();
        break;
    case 54:
        CheatWinMode = 1;
        break;

    case 55:
        if (NumCheatSrc != 0) {
            CheatWinMode = 3;
            CurCStextpos = 0;
            CSInputDisplay[0] = '_';
            CSInputDisplay[1] = '\0';
            CSDescDisplay[0] = '\0';
        }
        break;

    case 56:
        CheatWinMode = 2;
        break;
    case 57:
        AddCSCheatCode();
        break;

    case 65:
        ProjectPage();
        break;
    case 85:
        NetplayHostSession();
        break;
    case 86:
        NetplayJoinSession();
        break;
    case 87:
        NetplayDisconnectSession();
        break;
    }
    GUICBHold = 0;
}

void ProcessMouse(void)
{
    // Process holds
    if (LastHoldEnable != GUIHold) {
        switch (GUIHold) {
        case 0:
            if (LastHoldEnable == 7) // ntsc sliders
            {
                u4 const idx = ntscWhVar;
                u1 const cur = ntscCurVar;
                if (ntscLastVar[idx] != cur) {
                    NTSCFilterInit();
                    ntscLastVar[idx] = cur;
                }
            }
            Set_MouseXMax(0, 255);
            Set_MouseYMax(0, 223);
            break;

        case 1: // GUI Windows
            Set_MouseXMax(0, 255);
            Set_MouseYMax(16, 223);
            break;

        case 2: // Colour Slider
        case 5: // Sound Slider
        case 6: // Speed Slider
        case 7: // Video Slider
        case 8: // Scanline Slider
            // Sets min/max move range for mouse once holding slider
            Set_MouseXMax(GUIHoldXlimL, GUIHoldXlimR);
            // Locks pointer on slider
            Set_MouseYMax(GUIHoldYlim, GUIHoldYlim);
            break;

        case 3: // Scrollbars
            Set_MouseXMax(GUIHoldXlimL, GUIHoldXlimR);
            Set_MouseYMax(GUIHoldYlim, GUIHoldYlimR);
            break;
        }
        LastHoldEnable = GUIHold;
    }
    MouseMoveOkay = 0;
    u4 buttons = Get_MouseData() & 0xFFFF;
    if (lhguimouse == 1)
        buttons = SwapMouseButtons(buttons);
    mousebuttonstat = buttons;
    if (lastmouseholded != 0 && !(buttons & 0x01)) {
        lastmouseholded = 0;
        Set_MousePosition(GUImouseposx, GUImouseposy);
    }
    if (mousewrap == 1) {
        u4 const delta = Get_MousePositionDisplacement();

        u2 x = GUImouseposx + delta;
        while (x & 0x8000)
            x += 256;
        while (x > 255)
            x -= 256;
        GUImouseposx = x;

        u2 y = GUImouseposy + (delta >> 16);
        while (y & 0x8000)
            y += 224;
        while (y > 223)
            y -= GUIHold == 1 ? 224 - 16 : 224;
        GUImouseposy = y;

        if (delta != 0)
            MouseMoveOkay = 1;
    } else {
        u4 const data = Get_MouseData();
        u2 x = data >> 16 & 0xFF;
        u2 y = data >> 24;
        if (GUImouseposx != x || GUImouseposy != y)
            MouseMoveOkay = 1;
        if (x & 0x8000)
            x = 0;
        if (x > 255)
            x = 255;
        GUImouseposx = x;
        if (y & 0x8000)
            y = 0;
        if (y > 223)
            y = 100;
        GUImouseposy = y;
    }
    ProcessMouseButtons();
}

u4 guipresstest(void)
{
    memset(pressed, 0, sizeof(pressed));
    GUIUnBuffer();
    DisplayBoxes();
    DisplayMenu();
    GUIBox3D(75, 95, 180, 131);
    GUIOuttextShadowed(80, 100, guipresstext1);
    GUIOuttextShadowed(80, 110, guipresstext2);
    GUIOuttextShadowed(80, 120, "(ESC TO CLEAR)");
    vidpastecopyscr();
    u1* key;
    do
        JoyRead();
    while (!(key = GetAnyPressedKey()));
    for (u1* i = pressed; i != endof(pressed); ++i)
        if (*i != 0)
            *i = 2;
    while (Check_Key() != 0)
        Get_Key();
    return key - pressed;
}

void guipresstestb(u4* const guicpressptr, char const* const guipressptr)
{
    GUIUnBuffer();
    DisplayBoxes();
    DisplayMenu();
    GUIBox3D(65, 80, 194, 126);
    GUIOuttextShadowed(70, 85, guipresstext1);
    GUIOuttextShadowed(70, 95, guipresstext2);
    GUIOuttextShadowed(70, 105, guipressptr);
    GUIOuttextShadowed(70, 115, "(ESC TO SKIP)");
    vidpastecopyscr();
    delay(8192);
    do
        JoyRead();
    while (GetAnyPressedKey());

    u1* key;
    do
        JoyRead();
    while (!(key = GetAnyPressedKey()));
    u4 const key_id = key - pressed;
    while (Check_Key() != 0)
        Get_Key();
    if (key_id != 1 && key_id != 0x3B)
        *guicpressptr = key_id;
}

void DrawMouse(void)
{
    static u1 const GUIMousePtr[] = {
        50, 47, 45, 43, 40, 0, 0, 0,
        53, 52, 46, 42, 0, 0, 0, 0,
        55, 54, 54, 44, 0, 0, 0, 0,
        57, 57, 56, 52, 45, 0, 0, 0,
        59, 0, 0, 55, 50, 45, 0, 0,
        0, 0, 0, 0, 55, 50, 45, 0,
        0, 0, 0, 0, 0, 55, 50, 47,
        0, 0, 0, 0, 0, 0, 52, 0
    };

    u1* dst = vidbuffer + 16 + GUImouseposx + GUImouseposy * 288;
    u1 const* src = GUIMousePtr;
    u4 y = 8;
    do {
        u4 x = 8;
        do {
            u1 const al = *src++;
            if (al == 0)
                continue;

            if (mouseshad & 1) {
                u1* const px = &dst[288 * 10 + 8];
                if (*px < 32) {
                    *px = 96;
                } else {
                    u1* const px = &dst[288 * 4 + 3];
                    u1 const c = *px;
                    if (32 <= c && c <= 63) {
                        *px = (c - 32U) / 2 + 32U;
                    } else if ((c & 0xF0) == 64) {
                        *px = c + 16;
                    } else {
                        u1* const px = &dst[288 * 7 + 5];
                        u1 const c = *px;
                        if (148 <= c && c <= 167) {
                            *px = c + 20;
                        } else if (189 <= c && c <= 220) {
                            *px = (c - 189U) / 2 + 189U;
                        }
                    }
                }
            }
            *dst = al + 88;
        } while (++dst, --x != 0);
        dst += 288 - 8;
    } while (--y != 0);
}
