/*
* Glide64 - Glide video plugin for Nintendo 64 emulators.
* Copyright (c) 2002  Dave2001
* Copyright (c) 2003-2009  Sergey 'Gonetz' Lipski
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

//****************************************************************
//
// Glide64 - Glide Plugin for Nintendo 64 emulators
// Project started on December 29th, 2001
//
// Authors:
// Dave2001, original author, founded the project in 2001, left it in 2002
// Gugaman, joined the project in 2002, left it in 2002
// Sergey 'Gonetz' Lipski, joined the project in 2002, main author since fall of 2002
// Hiroshi 'KoolSmoky' Morii, joined the project in 2007
//
//****************************************************************
//
// To modify Glide64:
// * Write your name and (optional)email, commented by your work, so I know who did it, and so that you can find which parts you modified when it comes time to send it to me.
// * Do NOT send me the whole project or file that you modified.  Take out your modified code sections, and tell me where to put them.  If people sent the whole thing, I would have many different versions, but no idea how to combine them all.
//
//****************************************************************

#include "Gfx_1.3.h"
#include "Config.h"
#include "Util.h"
#include "3dmath.h"
#include "Debugger.h"
#include "Combine.h"
#include "TexCache.h"
#include "CRC.h"
#include "FBtoScreen.h"
#include "DepthBufferRender.h"

#if defined(__GNUC__)
#include <sys/time.h>
#elif defined(__MSC__)
#include <time.h>
#define PATH_MAX MAX_PATH
#endif
#include "osal_dynamiclib.h"
#ifdef TEXTURE_FILTER // Hiroshi Morii <koolsmoky@users.sourceforge.net>
#include <stdarg.h>
int  ghq_dmptex_toggle_key = 0;
#endif
#if defined(__MINGW32__)
#define swprintf _snwprintf
#define vswprintf _vsnwprintf
#endif

#define G64_VERSION "G64 Mk2"
#define RELTIME "Date: " __DATE__// " Time: " __TIME__

#ifdef EXT_LOGGING
std::ofstream extlog;
#endif

#ifdef LOGGING
std::ofstream loga;
#endif

#ifdef RDP_LOGGING
int log_open = FALSE;
std::ofstream rdp_log;
#endif

#ifdef RDP_ERROR_LOG
int elog_open = FALSE;
std::ofstream rdp_err;
#endif

GFX_INFO gfx;

/* definitions of pointers to Core config functions */
ptr_ConfigOpenSection      ConfigOpenSection = NULL;
ptr_ConfigSetParameter     ConfigSetParameter = NULL;
ptr_ConfigGetParameter     ConfigGetParameter = NULL;
ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
ptr_ConfigSetDefaultInt    ConfigSetDefaultInt = NULL;
ptr_ConfigSetDefaultFloat  ConfigSetDefaultFloat = NULL;
ptr_ConfigSetDefaultBool   ConfigSetDefaultBool = NULL;
ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
ptr_ConfigGetParamInt      ConfigGetParamInt = NULL;
ptr_ConfigGetParamFloat    ConfigGetParamFloat = NULL;
ptr_ConfigGetParamBool     ConfigGetParamBool = NULL;
ptr_ConfigGetParamString   ConfigGetParamString = NULL;

ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath = NULL;
ptr_ConfigGetUserConfigPath     ConfigGetUserConfigPath = NULL;
ptr_ConfigGetUserDataPath       ConfigGetUserDataPath = NULL;
ptr_ConfigGetUserCachePath      ConfigGetUserCachePath = NULL;

/* definitions of pointers to Core video extension functions */
ptr_VidExt_Init                  CoreVideo_Init = NULL;
ptr_VidExt_Quit                  CoreVideo_Quit = NULL;
ptr_VidExt_ListFullscreenModes   CoreVideo_ListFullscreenModes = NULL;
ptr_VidExt_SetVideoMode          CoreVideo_SetVideoMode = NULL;
ptr_VidExt_SetCaption            CoreVideo_SetCaption = NULL;
ptr_VidExt_ToggleFullScreen      CoreVideo_ToggleFullScreen = NULL;
ptr_VidExt_ResizeWindow          CoreVideo_ResizeWindow = NULL;
ptr_VidExt_GL_GetProcAddress     CoreVideo_GL_GetProcAddress = NULL;
ptr_VidExt_GL_SetAttribute       CoreVideo_GL_SetAttribute = NULL;
ptr_VidExt_GL_SwapBuffers        CoreVideo_GL_SwapBuffers = NULL;
int to_fullscreen = FALSE;
int fullscreen = FALSE;
int romopen = FALSE;
GrContext_t gfx_context = 0;
int debugging = FALSE;
int exception = FALSE;

int evoodoo = 0;
int ev_fullscreen = 0;

enum {
	NONE,
	ASB,
	BANJO2,
	BAR,
	CHOPPER,
	DIDDY,
	FIFA98,
	FZERO,
	GOLDENEYE,
	HYPERBIKE,
	ISS64,
	KI,
	KNOCKOUT,
	LEGO,
	MK64,
	MEGAMAN,
	MAKERS,
	WCWNITRO,
	OGRE64,
	PILOTWINGS,
	PMARIO,
	PPL,
	RE2,
	STARCRAFT,
	SUPERCROSS,
	TGR,
	TGR2,
	TONIC,
	YOSHI,
	ZELDA
};

#ifdef __WINDOWS__
#define WINPROC_OVERRIDE
#endif

#ifdef WINPROC_OVERRIDE
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC oldWndProc = NULL;
WNDPROC myWndProc = NULL;
#endif

#ifdef ALTTAB_FIX
HHOOK hhkLowLevelKybd = NULL;
LRESULT CALLBACK LowLevelKeyboardProc(int nCode,
                                      WPARAM wParam, LPARAM lParam);
#endif

#ifdef PERFORMANCE
int64 perf_cur;
int64 perf_next;
#endif

#ifdef FPS
LARGE_INTEGER perf_freq;
LARGE_INTEGER fps_last;
LARGE_INTEGER fps_next;
float      fps = 0.0f;
wxUint32   fps_count = 0;

wxUint32   vi_count = 0;
float      vi = 0.0f;

wxUint32   region = 0;

float      ntsc_percent = 0.0f;
float      pal_percent = 0.0f;

#endif

// ref rate
// 60=0x0, 70=0x1, 72=0x2, 75=0x3, 80=0x4, 90=0x5, 100=0x6, 85=0x7, 120=0x8, none=0xff

unsigned long BMASK = 0x7FFFFF;
// Reality display processor structure
RDP rdp;

SETTINGS settings = { FALSE, 640, 480, GR_RESOLUTION_640x480, 0 };

HOTKEY_INFO hotkey_info;

VOODOO voodoo = {0, 0, 0, 0,
                 0, 0, 0, 0,
                 0, 0, 0, 0
                };

GrTexInfo fontTex;
GrTexInfo cursorTex;
wxUint32   offset_font = 0;
wxUint32   offset_cursor = 0;
wxUint32   offset_textures = 0;
wxUint32   offset_texbuf1 = 0;

int    capture_screen = 0;
char    capture_path[256];

SDL_sem *mutexProcessDList = SDL_CreateSemaphore(1);

// SOME FUNCTION DEFINITIONS 

static void DrawFrameBuffer ();


void (*renderCallback)(int) = NULL;
static void (*l_DebugCallback)(void *, int, const char *) = NULL;
static void *l_DebugCallContext = NULL;

void _ChangeSize ()
{
  rdp.scale_1024 = settings.scr_res_x / 1024.0f;
  rdp.scale_768 = settings.scr_res_y / 768.0f;

//  float res_scl_x = (float)settings.res_x / 320.0f;
  float res_scl_y = (float)settings.res_y / 240.0f;

  wxUint32 scale_x = *gfx.VI_X_SCALE_REG & 0xFFF;
  if (!scale_x) return;
  wxUint32 scale_y = *gfx.VI_Y_SCALE_REG & 0xFFF;
  if (!scale_y) return;

  float fscale_x = (float)scale_x / 1024.0f;
  float fscale_y = (float)scale_y / 2048.0f;

  wxUint32 dwHStartReg = *gfx.VI_H_START_REG;
  wxUint32 dwVStartReg = *gfx.VI_V_START_REG;

  wxUint32 hstart = dwHStartReg >> 16;
  wxUint32 hend = dwHStartReg & 0xFFFF;

  // dunno... but sometimes this happens
  if (hend == hstart) hend = (int)(*gfx.VI_WIDTH_REG / fscale_x);

  wxUint32 vstart = dwVStartReg >> 16;
  wxUint32 vend = dwVStartReg & 0xFFFF;

  rdp.vi_width = (hend - hstart) * fscale_x;
  rdp.vi_height = (vend - vstart) * fscale_y * 1.0126582f;
  float aspect = (settings.adjust_aspect && (fscale_y > fscale_x) && (rdp.vi_width > rdp.vi_height)) ? fscale_x/fscale_y : 1.0f;

#ifdef LOGGING
  sprintf (out_buf, "hstart: %d, hend: %d, vstart: %d, vend: %d\n", hstart, hend, vstart, vend);
  LOG (out_buf);
  sprintf (out_buf, "size: %d x %d\n", (int)rdp.vi_width, (int)rdp.vi_height);
  LOG (out_buf);
#endif

  rdp.scale_x = (float)settings.res_x / rdp.vi_width;
  if (region > 0 && settings.pal230)
  {
    // odd... but pal games seem to want 230 as height...
    rdp.scale_y = res_scl_y * (230.0f / rdp.vi_height)  * aspect;
  }
  else
  {
    rdp.scale_y = (float)settings.res_y / rdp.vi_height * aspect;
  }
  //  rdp.offset_x = settings.offset_x * res_scl_x;
  //  rdp.offset_y = settings.offset_y * res_scl_y;
  //rdp.offset_x = 0;
  //  rdp.offset_y = 0;
  rdp.offset_y = ((float)settings.res_y - rdp.vi_height * rdp.scale_y) * 0.5f;
  if (((wxUint32)rdp.vi_width <= (*gfx.VI_WIDTH_REG)/2) && (rdp.vi_width > rdp.vi_height))
    rdp.scale_y *= 0.5f;

  rdp.scissor_o.ul_x = 0;
  rdp.scissor_o.ul_y = 0;
  rdp.scissor_o.lr_x = (wxUint32)rdp.vi_width;
  rdp.scissor_o.lr_y = (wxUint32)rdp.vi_height;

  rdp.update |= UPDATE_VIEWPORT | UPDATE_SCISSOR;
}

void ChangeSize ()
{
  if (debugging)
  {
    _ChangeSize ();
    return;
  }
  switch (settings.aspectmode)
  {
  case 0: //4:3
    if (settings.scr_res_x >= settings.scr_res_y * 4.0f / 3.0f) {
      settings.res_y = settings.scr_res_y;
      settings.res_x = (wxUint32)(settings.res_y * 4.0f / 3.0f);
    } else {
      settings.res_x = settings.scr_res_x;
      settings.res_y = (wxUint32)(settings.res_x / 4.0f * 3.0f);
    }
    break;
  case 1: //16:9
    if (settings.scr_res_x >= settings.scr_res_y * 16.0f / 9.0f) {
      settings.res_y = settings.scr_res_y;
      settings.res_x = (wxUint32)(settings.res_y * 16.0f / 9.0f);
    } else {
      settings.res_x = settings.scr_res_x;
      settings.res_y = (wxUint32)(settings.res_x / 16.0f * 9.0f);
    }
    break;
  default: //stretch or original
    settings.res_x = settings.scr_res_x;
    settings.res_y = settings.scr_res_y;
  }
  _ChangeSize ();
  rdp.offset_x = (settings.scr_res_x - settings.res_x) / 2.0f;
  float offset_y = (settings.scr_res_y - settings.res_y) / 2.0f;
  settings.res_x += (wxUint32)rdp.offset_x;
  settings.res_y += (wxUint32)offset_y;
  rdp.offset_y += offset_y;
  if (settings.aspectmode == 3) // original
  {
	  rdp.scale_x = rdp.scale_y = 1.0f;
	  rdp.offset_x = (settings.scr_res_x - rdp.vi_width) / 2.0f;
	  rdp.offset_y = (settings.scr_res_y - rdp.vi_height) / 2.0f;
  }
  //	settings.res_x = settings.scr_res_x;
  //	settings.res_y = settings.scr_res_y;
}

void ConfigWrapper()
{
  char strConfigWrapperExt[] = "grConfigWrapperExt";
  GRCONFIGWRAPPEREXT grConfigWrapperExt = (GRCONFIGWRAPPEREXT)grGetProcAddress(strConfigWrapperExt);
  if (grConfigWrapperExt)
    grConfigWrapperExt(settings.wrpResolution, settings.wrpVRAM * 1024 * 1024, settings.wrpFBO, settings.wrpAnisotropic);
}
/*
static wxConfigBase * OpenIni()
{
  wxConfigBase * ini = wxConfigBase::Get(false);
  if (!ini)
  {
    if (iniName.IsEmpty())
      iniName = pluginPath + wxT("/Glide64mk2.ini");
    if (wxFileExists(iniName))
    {
      wxFileInputStream is(iniName);
      wxFileConfig * fcfg = new wxFileConfig(is, wxConvISO8859_1);
      wxConfigBase::Set(fcfg);
      ini = fcfg;
    }
  }
  if (!ini)
    wxMessageBox(_T("Can not find ini file! Plugin will not run properly."), _T("File not found"), wxOK|wxICON_EXCLAMATION);
  return ini;
}
*/
#ifndef OLDAPI
void WriteLog(m64p_msg_level level, const char *msg, ...)
{
  char buf[1024];
  va_list args;
  va_start(args, msg);
  vsnprintf(buf, 1023, msg, args);
  buf[1023]='\0';
  va_end(args);
  if (l_DebugCallback)
  {
    l_DebugCallback(l_DebugCallContext, level, buf);
  }
}
#endif

void ReadSettings ()
{
  //  LOG("ReadSettings\n");
  if (!Config_Open())
  {
    ERRLOG("Could not open configuration!");
    return;
  }

  settings.card_id = (BYTE)Config_ReadInt ("card_id", "Card ID", 0, TRUE, FALSE);
  //settings.lang_id not needed
  // depth_bias = -Config_ReadInt ("depth_bias", "Depth bias level", 0, TRUE, FALSE);
  settings.res_data = 0;
  settings.scr_res_x = settings.res_x = Config_ReadScreenInt("ScreenWidth");
  settings.scr_res_y = settings.res_y = Config_ReadScreenInt("ScreenHeight");

  settings.vsync = (BOOL)Config_ReadInt ("vsync", "Vertical sync", 0);
  settings.ssformat = (BOOL)Config_ReadInt("ssformat", "TODO:ssformat", 0);
  //settings.fast_crc = (BOOL)Config_ReadInt ("fast_crc", "Fast CRC", 0);

  settings.show_fps = (BYTE)Config_ReadInt ("show_fps", "Display performance stats (add together desired flags): 1=FPS counter, 2=VI/s counter, 4=% speed, 8=FPS transparent", 0, TRUE, FALSE);
  settings.clock = (BOOL)Config_ReadInt ("clock", "Clock enabled", 0);
  settings.clock_24_hr = (BOOL)Config_ReadInt ("clock_24_hr", "Clock is 24-hour", 0);
  // settings.advanced_options only good for GUI config
  // settings.texenh_options = only good for GUI config
  //settings.use_hotkeys = ini->Read(_T("hotkeys"), 1l);

  settings.wrpResolution = (BYTE)Config_ReadInt ("wrpResolution", "Wrapper resolution", 0, TRUE, FALSE);
  settings.wrpVRAM = (BYTE)Config_ReadInt ("wrpVRAM", "Wrapper VRAM", 0, TRUE, FALSE);
  settings.wrpFBO = (BOOL)Config_ReadInt ("wrpFBO", "Wrapper FBO", 1, TRUE, TRUE);
  settings.wrpAnisotropic = (BOOL)Config_ReadInt ("wrpAnisotropic", "Wrapper Anisotropic Filtering", 0, TRUE, TRUE);

#ifndef _ENDUSER_RELEASE_
  settings.autodetect_ucode = (BOOL)Config_ReadInt ("autodetect_ucode", "Auto-detect microcode", 1);
  settings.ucode = (wxUint32)Config_ReadInt ("ucode", "Force microcode", 2, TRUE, FALSE);
  settings.wireframe = (BOOL)Config_ReadInt ("wireframe", "Wireframe display", 0);
  settings.wfmode = (int)Config_ReadInt ("wfmode", "Wireframe mode: 0=Normal colors, 1=Vertex colors, 2=Red only", 1, TRUE, FALSE);

  settings.logging = (BOOL)Config_ReadInt ("logging", "Logging", 0);
  settings.log_clear = (BOOL)Config_ReadInt ("log_clear", "", 0);

  settings.run_in_window = (BOOL)Config_ReadInt ("run_in_window", "", 0);

  settings.elogging = (BOOL)Config_ReadInt ("elogging", "", 0);
  settings.filter_cache = (BOOL)Config_ReadInt ("filter_cache", "Filter cache", 0);
  settings.unk_as_red = (BOOL)Config_ReadInt ("unk_as_red", "Display unknown combines as red", 0);
  settings.log_unk = (BOOL)Config_ReadInt ("log_unk", "Log unknown combines", 0);
  settings.unk_clear = (BOOL)Config_ReadInt ("unk_clear", "", 0);
#else
  settings.autodetect_ucode = TRUE;
  settings.ucode = 2;
  settings.wireframe = FALSE;
  settings.wfmode = 0;
  settings.logging = FALSE;
  settings.log_clear = FALSE;
  settings.run_in_window = FALSE;
  settings.elogging = FALSE;
  settings.filter_cache = FALSE;
  settings.unk_as_red = FALSE;
  settings.log_unk = FALSE;
  settings.unk_clear = FALSE;
#endif

#ifdef TEXTURE_FILTER
  
  // settings.ghq_fltr range is 0 through 6
  // Filters:\nApply a filter to either smooth or sharpen textures.\nThere are 4 different smoothing filters and 2 different sharpening filters.\nThe higher the number, the stronger the effect,\ni.e. \"Smoothing filter 4\" will have a much more noticeable effect than \"Smoothing filter 1\".\nBe aware that performance may have an impact depending on the game and/or the PC.\n[Recommended: your preference]
  // _("None"),
  // _("Smooth filtering 1"),
  // _("Smooth filtering 2"),
  // _("Smooth filtering 3"),
  // _("Smooth filtering 4"),
  // _("Sharp filtering 1"),
  // _("Sharp filtering 2")

// settings.ghq_cmpr 0=S3TC and 1=FXT1

//settings.ghq_ent is ___
// "Texture enhancement:\n7 different filters are selectable here, each one with a distinctive look.\nBe aware of possible performance impacts.\n\nIMPORTANT: 'Store' mode - saves textures in cache 'as is'. It can improve performance in games, which load many textures.\nDisable 'Ignore backgrounds' option for better result.\n\n[Recommended: your preference]"



  settings.ghq_fltr = Config_ReadInt ("ghq_fltr", "Texture Enhancement: Smooth/Sharpen Filters", 0, TRUE, FALSE);
  settings.ghq_cmpr = Config_ReadInt ("ghq_cmpr", "Texture Compression: 0 for S3TC, 1 for FXT1", 0, TRUE, FALSE);
  settings.ghq_enht = Config_ReadInt ("ghq_enht", "Texture Enhancement: More filters", 0, TRUE, FALSE);
  settings.ghq_hirs = Config_ReadInt ("ghq_hirs", "Hi-res texture pack format (0 for none, 1 for Rice)", 0, TRUE, FALSE);
  settings.ghq_enht_cmpr  = Config_ReadInt ("ghq_enht_cmpr", "Compress texture cache with S3TC or FXT1", 0, TRUE, TRUE);
  settings.ghq_enht_tile = Config_ReadInt ("ghq_enht_tile", "Tile textures (saves memory but could cause issues)", 0, TRUE, FALSE);
  settings.ghq_enht_f16bpp = Config_ReadInt ("ghq_enht_f16bpp", "Force 16bpp textures (saves ram but lower quality)", 0, TRUE, TRUE);
  settings.ghq_enht_gz  = Config_ReadInt ("ghq_enht_gz", "Compress texture cache", 1, TRUE, TRUE);
  settings.ghq_enht_nobg  = Config_ReadInt ("ghq_enht_nobg", "Don't enhance textures for backgrounds", 0, TRUE, TRUE);
  settings.ghq_hirs_cmpr  = Config_ReadInt ("ghq_hirs_cmpr", "Enable S3TC and FXT1 compression", 0, TRUE, TRUE);
  settings.ghq_hirs_tile = Config_ReadInt ("ghq_hirs_tile", "Tile hi-res textures (saves memory but could cause issues)", 0, TRUE, TRUE);
  settings.ghq_hirs_f16bpp = Config_ReadInt ("ghq_hirs_f16bpp", "Force 16bpp hi-res textures (saves ram but lower quality)", 0, TRUE, TRUE);
  settings.ghq_hirs_gz  = Config_ReadInt ("ghq_hirs_gz", "Compress hi-res texture cache", 1, TRUE, TRUE);
  settings.ghq_hirs_altcrc = Config_ReadInt ("ghq_hirs_altcrc", "Alternative CRC calculation -- emulates Rice bug", 1, TRUE, TRUE);
  settings.ghq_cache_save = Config_ReadInt ("ghq_cache_save", "Save tex cache to disk", 1, TRUE, TRUE);
  settings.ghq_cache_size = Config_ReadInt ("ghq_cache_size", "Texture Cache Size (MB)", 128, TRUE, FALSE);
  settings.ghq_hirs_let_texartists_fly = Config_ReadInt ("ghq_hirs_let_texartists_fly", "Use full alpha channel -- could cause issues for some tex packs", 0, TRUE, TRUE);
  settings.ghq_hirs_dump = Config_ReadInt ("ghq_hirs_dump", "Dump textures", 0, FALSE, TRUE);
#endif
  //TODO-PORT: remove?
  ConfigWrapper();
}

void ReadSpecialSettings (const char * name)
{
  //  char buf [256];
  //  sprintf(buf, "ReadSpecialSettings. Name: %s\n", name);
  //  LOG(buf);

  int EnableHacksForGame = (int)Config_ReadInt ("enable_hacks_for_game", "???", 0, TRUE, FALSE);

  switch (EnableHacksForGame)
  {
    case NONE:
		break;
    case ASB:
		settings.hacks |= hack_ASB;
		break;
    case BANJO2:
		settings.hacks |= hack_Banjo2;
		break;
    case BAR:
		settings.hacks |= hack_BAR;
		break;
    case CHOPPER:
		  settings.hacks |= hack_Chopper;
		break;
    case DIDDY:
		settings.hacks |= hack_Diddy;
		break;
    case FIFA98:
		settings.hacks |= hack_Fifa98;
		break;
    case FZERO:
		settings.hacks |= hack_Fzero;
		break;
    case GOLDENEYE:
		settings.hacks |= hack_GoldenEye;
		break;
    case HYPERBIKE:
		settings.hacks |= hack_Hyperbike;
		break;
    case ISS64:
		settings.hacks |= hack_ISS64;
		break;
    case KI:
		settings.hacks |= hack_KI;
		break;
    case KNOCKOUT:
		settings.hacks |= hack_Knockout;
		break;
    case LEGO:
		settings.hacks |= hack_Lego;
		break;
    case MK64:
		 settings.hacks |= hack_MK64;
		break;
    case MEGAMAN:
		settings.hacks |= hack_Megaman;
		break;
    case MAKERS:
		settings.hacks |= hack_Makers;
		break;
    case WCWNITRO:
		settings.hacks |= hack_WCWnitro;
		break;
    case OGRE64:
		settings.hacks |= hack_Ogre64;
		break;
    case PILOTWINGS:
		settings.hacks |= hack_Pilotwings;
		break;
    case PMARIO:
		settings.hacks |= hack_PMario;
		break;
    case PPL:
		settings.hacks |= hack_PPL;
		break;
    case RE2:
		settings.hacks |= hack_RE2;
		break;
    case STARCRAFT:
		settings.hacks |= hack_Starcraft;
		break;
    case SUPERCROSS:
		 settings.hacks |= hack_Supercross;
		break;
    case TGR:
		settings.hacks |= hack_TGR;
		break;
    case TGR2:
		 settings.hacks |= hack_TGR2;
		break;
    case TONIC:
		 settings.hacks |= hack_Tonic;
		break;
    case YOSHI:
		settings.hacks |= hack_Yoshi;
		break;
    case ZELDA:
		settings.hacks |= hack_Zelda;
		break;
  }

  settings.alt_tex_size = (BOOL)Config_ReadInt("alt_tex_size", "???", TRUE);
  settings.use_sts1_only = (BOOL)Config_ReadInt("use_sts1_only", "???", FALSE);
  settings.force_calc_sphere = (BOOL)Config_ReadInt("force_calc_sphere", "???", FALSE);
  settings.correct_viewport = (BOOL)Config_ReadInt("correct_viewport", "???", FALSE);
  settings.increase_texrect_edge = (BOOL)Config_ReadInt("increase_texrect_edge", "???", FALSE);
  settings.decrease_fillrect_edge = (BOOL)Config_ReadInt("decrease_fillrect_edge", "???", FALSE);
  settings.texture_correction = (BOOL)Config_ReadInt("texture_correction", "???", TRUE);

  settings.force_microcheck = (BOOL)Config_ReadInt("force_microcheck", "???", FALSE);
  settings.fog = (BOOL)Config_ReadInt("fog", "???", TRUE);
  settings.buff_clear = (BOOL)Config_ReadInt("buff_clear", "???", TRUE);
  settings.swapmode = (int)Config_ReadInt("swapmode", "???", 1, TRUE, FALSE);
  int stipple_pattern = (int)Config_ReadInt("stipple_pattern", "???", 1041204192, TRUE, FALSE);
  if (stipple_pattern > 0) settings.stipple_pattern = (wxUint32)stipple_pattern;
  settings.stipple_mode = (int)Config_ReadInt("stipple_mode", "???", 2, TRUE, FALSE);
  settings.lodmode = (int)Config_ReadInt("lodmode", "???", 0, TRUE, FALSE);
  settings.filtering = (int)Config_ReadInt("filtering", "???", 0, TRUE, FALSE);
  settings.pal230 = (BOOL)Config_ReadInt("pal230", "???", FALSE);
  settings.texture_correction = (BOOL)Config_ReadInt("texture_correction", "???", TRUE);
  settings.n64_z_scale = (BOOL)Config_ReadInt("n64_z_scale", "???", FALSE);
  settings.old_style_adither = (BOOL)Config_ReadInt("old_style_adither", "???", FALSE);
  settings.zmode_compare_less = (BOOL)Config_ReadInt("zmode_compare_less", "???", FALSE);
  settings.adjust_aspect = (BOOL)Config_ReadInt("adjust_aspect", "???", TRUE);
  settings.clip_zmax = (BOOL)Config_ReadInt("clip_zmax", "???", TRUE);
  settings.clip_zmin = (BOOL)Config_ReadInt("clip_zmin", "???", FALSE);
  settings.force_quad3d = (BOOL)Config_ReadInt("force_quad3d", "???", FALSE);
  settings.aspectmode = (int)Config_ReadInt("aspectmode", "???", 0, TRUE, FALSE);
  settings.fast_crc = (BOOL)Config_ReadInt("fast_crc", "???", TRUE);

  if (settings.n64_z_scale)
    ZLUT_init();

  //frame buffer
  int optimize_texrect = (BOOL)Config_ReadInt("optimize_texrect", "???", TRUE);
  int ignore_aux_copy = (BOOL)Config_ReadInt("ignore_aux_copy", "???", FALSE);
  int hires_buf_clear = (BOOL)Config_ReadInt("hires_buf_clear", "???", TRUE);
  int read_alpha = (BOOL)Config_ReadInt("fb_read_alpha", "???", FALSE);
  int useless_is_useless = (BOOL)Config_ReadInt("useless_is_useless", "???", FALSE);
  int fb_crc_mode = (int)Config_ReadInt("fb_crc_mode", "???", 1, TRUE, FALSE);

  if (optimize_texrect > 0) settings.frame_buffer |= fb_optimize_texrect;
  else if (optimize_texrect == 0) settings.frame_buffer &= ~fb_optimize_texrect;
  if (ignore_aux_copy > 0) settings.frame_buffer |= fb_ignore_aux_copy;
  else if (ignore_aux_copy == 0) settings.frame_buffer &= ~fb_ignore_aux_copy;
  if (hires_buf_clear > 0) settings.frame_buffer |= fb_hwfbe_buf_clear;
  else if (hires_buf_clear == 0) settings.frame_buffer &= ~fb_hwfbe_buf_clear;
  if (read_alpha > 0) settings.frame_buffer |= fb_read_alpha;
  else if (read_alpha == 0) settings.frame_buffer &= ~fb_read_alpha;
  if (useless_is_useless > 0) settings.frame_buffer |= fb_useless_is_useless;
  else settings.frame_buffer &= ~fb_useless_is_useless;
  if (fb_crc_mode >= 0) settings.fb_crc_mode = (SETTINGS::FBCRCMODE)fb_crc_mode;

    //frame buffer
    int smart_read = (BOOL)Config_ReadInt("fb_smart", "???", FALSE);
    int hires = (BOOL)Config_ReadInt("fb_hires", "???", TRUE);
    int read_always = (BOOL)Config_ReadInt("fb_read_always", "???", FALSE);
    int read_back_to_screen = (int)Config_ReadInt("read_back_to_screen", "???", 0, TRUE, FALSE);
    int cpu_write_hack = (BOOL)Config_ReadInt("detect_cpu_write", "???", FALSE);
    int get_fbinfo = (BOOL)Config_ReadInt("fb_get_info", "???", FALSE);
    int depth_render = (BOOL)Config_ReadInt("fb_render", "???", TRUE);

    if (smart_read > 0) settings.frame_buffer |= fb_emulation;
    else if (smart_read == 0) settings.frame_buffer &= ~fb_emulation;
    if (hires > 0) settings.frame_buffer |= fb_hwfbe;
    else if (hires == 0) settings.frame_buffer &= ~fb_hwfbe;
    if (read_always > 0) settings.frame_buffer |= fb_ref;
    else if (read_always == 0) settings.frame_buffer &= ~fb_ref;
    if (read_back_to_screen == 1) settings.frame_buffer |= fb_read_back_to_screen;
    else if (read_back_to_screen == 2) settings.frame_buffer |= fb_read_back_to_screen2;
    else if (read_back_to_screen == 0) settings.frame_buffer &= ~(fb_read_back_to_screen|fb_read_back_to_screen2);
    if (cpu_write_hack > 0) settings.frame_buffer |= fb_cpu_write_hack;
    else if (cpu_write_hack == 0) settings.frame_buffer &= ~fb_cpu_write_hack;
    if (get_fbinfo > 0) settings.frame_buffer |= fb_get_info;
    else if (get_fbinfo == 0) settings.frame_buffer &= ~fb_get_info;
    if (depth_render > 0) settings.frame_buffer |= fb_depth_render;
    else if (depth_render == 0) settings.frame_buffer &= ~fb_depth_render;
    settings.frame_buffer |= fb_motionblur;

  settings.flame_corona = (settings.hacks & hack_Zelda) && !fb_depth_render_enabled;
}

//TODO-PORT: more ini stuff
void WriteSettings (bool saveEmulationSettings)
{
/*
  wxConfigBase * ini = OpenIni();
  if (!ini || !ini->HasGroup(_T("/SETTINGS")))
    return;
  ini->SetPath(_T("/SETTINGS"));

  ini->Write(_T("card_id"), settings.card_id);
  ini->Write(_T("lang_id"), settings.lang_id);
  ini->Write(_T("resolution"), (int)settings.res_data);
  ini->Write(_T("ssformat"), settings.ssformat);
  ini->Write(_T("vsync"), settings.vsync);
  ini->Write(_T("show_fps"), settings.show_fps);
  ini->Write(_T("clock"), settings.clock);
  ini->Write(_T("clock_24_hr"), settings.clock_24_hr);
  ini->Write(_T("advanced_options"), settings.advanced_options);
  ini->Write(_T("texenh_options"), settings.texenh_options);

  ini->Write(_T("wrpResolution"), settings.wrpResolution);
  ini->Write(_T("wrpVRAM"), settings.wrpVRAM);
  ini->Write(_T("wrpFBO"), settings.wrpFBO);
  ini->Write(_T("wrpAnisotropic"), settings.wrpAnisotropic);

#ifndef _ENDUSER_RELEASE_
  ini->Write(_T("autodetect_ucode"), settings.autodetect_ucode);
  ini->Write(_T("ucode"), (int)settings.ucode);
  ini->Write(_T("wireframe"), settings.wireframe);
  ini->Write(_T("wfmode"), settings.wfmode);
  ini->Write(_T("logging"), settings.logging);
  ini->Write(_T("log_clear"), settings.log_clear);
  ini->Write(_T("run_in_window"), settings.run_in_window);
  ini->Write(_T("elogging"), settings.elogging);
  ini->Write(_T("filter_cache"), settings.filter_cache);
  ini->Write(_T("unk_as_red"), settings.unk_as_red);
  ini->Write(_T("log_unk"), settings.log_unk);
  ini->Write(_T("unk_clear"), settings.unk_clear);
#endif //_ENDUSER_RELEASE_

#ifdef TEXTURE_FILTER
  ini->Write(_T("ghq_fltr"), settings.ghq_fltr);
  ini->Write(_T("ghq_cmpr"), settings.ghq_cmpr);
  ini->Write(_T("ghq_enht"), settings.ghq_enht);
  ini->Write(_T("ghq_hirs"), settings.ghq_hirs);
  ini->Write(_T("ghq_enht_cmpr"), settings.ghq_enht_cmpr);
  ini->Write(_T("ghq_enht_tile"), settings.ghq_enht_tile);
  ini->Write(_T("ghq_enht_f16bpp"), settings.ghq_enht_f16bpp);
  ini->Write(_T("ghq_enht_gz"), settings.ghq_enht_gz);
  ini->Write(_T("ghq_enht_nobg"), settings.ghq_enht_nobg);
  ini->Write(_T("ghq_hirs_cmpr"), settings.ghq_hirs_cmpr);
  ini->Write(_T("ghq_hirs_tile"), settings.ghq_hirs_tile);
  ini->Write(_T("ghq_hirs_f16bpp"), settings.ghq_hirs_f16bpp);
  ini->Write(_T("ghq_hirs_gz"), settings.ghq_hirs_gz);
  ini->Write(_T("ghq_hirs_altcrc"), settings.ghq_hirs_altcrc);
  ini->Write(_T("ghq_cache_save"), settings.ghq_cache_save);
  ini->Write(_T("ghq_cache_size"), settings.ghq_cache_size);
  ini->Write(_T("ghq_hirs_let_texartists_fly"), settings.ghq_hirs_let_texartists_fly);
  ini->Write(_T("ghq_hirs_dump"), settings.ghq_hirs_dump);
#endif

  if (saveEmulationSettings)
  {
    if (romopen)
    {
      wxString S = _T("/");
      ini->SetPath(S+rdp.RomName);
    }
    else
      ini->SetPath(_T("/DEFAULT"));
    ini->Write(_T("filtering"), settings.filtering);
    ini->Write(_T("fog"), settings.fog);
    ini->Write(_T("buff_clear"), settings.buff_clear);
    ini->Write(_T("swapmode"), settings.swapmode);
    ini->Write(_T("lodmode"), settings.lodmode);
    ini->Write(_T("aspect"), settings.aspectmode);

    ini->Write(_T("fb_read_always"), settings.frame_buffer&fb_ref ? 1 : 0l);
    ini->Write(_T("fb_smart"), settings.frame_buffer & fb_emulation ? 1 : 0l);
    //    ini->Write("motionblur", settings.frame_buffer & fb_motionblur ? 1 : 0);
    ini->Write(_T("fb_hires"), settings.frame_buffer & fb_hwfbe ? 1 : 0l);
    ini->Write(_T("fb_get_info"), settings.frame_buffer & fb_get_info ? 1 : 0l);
    ini->Write(_T("fb_render"), settings.frame_buffer & fb_depth_render ? 1 : 0l);
    ini->Write(_T("detect_cpu_write"), settings.frame_buffer & fb_cpu_write_hack ? 1 : 0l);
    if (settings.frame_buffer & fb_read_back_to_screen)
      ini->Write(_T("read_back_to_screen"), 1);
    else if (settings.frame_buffer & fb_read_back_to_screen2)
      ini->Write(_T("read_back_to_screen"), 2);
    else
      ini->Write(_T("read_back_to_screen"), 0l);
  }

  wxFileOutputStream os(iniName);
  ((wxFileConfig*)ini)->Save(os);
*/
}

GRTEXBUFFEREXT   grTextureBufferExt = NULL;
GRTEXBUFFEREXT   grTextureAuxBufferExt = NULL;
GRAUXBUFFEREXT   grAuxBufferExt = NULL;
GRSTIPPLE grStippleModeExt = NULL;
GRSTIPPLE grStipplePatternExt = NULL;
FxBool (FX_CALL *grKeyPressed)(FxU32) = NULL;

int GetTexAddrUMA(int tmu, int texsize)
{
  int addr = voodoo.tex_min_addr[0] + voodoo.tmem_ptr[0];
  voodoo.tmem_ptr[0] += texsize;
  voodoo.tmem_ptr[1] = voodoo.tmem_ptr[0];
  return addr;
}
int GetTexAddrNonUMA(int tmu, int texsize)
{
  int addr = voodoo.tex_min_addr[tmu] + voodoo.tmem_ptr[tmu];
  voodoo.tmem_ptr[tmu] += texsize;
  return addr;
}
GETTEXADDR GetTexAddr = GetTexAddrNonUMA;

// guLoadTextures - used to load the cursor and font textures
void guLoadTextures ()
{
  if (grTextureBufferExt)
  {
    int tbuf_size = 0;
    if (voodoo.max_tex_size <= 256)
    {
      grTextureBufferExt(  GR_TMU1, voodoo.tex_min_addr[GR_TMU1], GR_LOD_LOG2_256, GR_LOD_LOG2_256,
        GR_ASPECT_LOG2_1x1, GR_TEXFMT_RGB_565, GR_MIPMAPLEVELMASK_BOTH );
      tbuf_size = 8 * grTexCalcMemRequired(GR_LOD_LOG2_256, GR_LOD_LOG2_256,
        GR_ASPECT_LOG2_1x1, GR_TEXFMT_RGB_565);
    }
    else if (settings.scr_res_x <= 1024)
    {
      grTextureBufferExt(  GR_TMU0, voodoo.tex_min_addr[GR_TMU0], GR_LOD_LOG2_1024, GR_LOD_LOG2_1024,
        GR_ASPECT_LOG2_1x1, GR_TEXFMT_RGB_565, GR_MIPMAPLEVELMASK_BOTH );
      tbuf_size = grTexCalcMemRequired(GR_LOD_LOG2_1024, GR_LOD_LOG2_1024,
        GR_ASPECT_LOG2_1x1, GR_TEXFMT_RGB_565);
      grRenderBuffer( GR_BUFFER_TEXTUREBUFFER_EXT );
      grBufferClear (0, 0, 0xFFFF);
      grRenderBuffer( GR_BUFFER_BACKBUFFER );
    }
    else
    {
      grTextureBufferExt(  GR_TMU0, voodoo.tex_min_addr[GR_TMU0], GR_LOD_LOG2_2048, GR_LOD_LOG2_2048,
        GR_ASPECT_LOG2_1x1, GR_TEXFMT_RGB_565, GR_MIPMAPLEVELMASK_BOTH );
      tbuf_size = grTexCalcMemRequired(GR_LOD_LOG2_2048, GR_LOD_LOG2_2048,
        GR_ASPECT_LOG2_1x1, GR_TEXFMT_RGB_565);
      grRenderBuffer( GR_BUFFER_TEXTUREBUFFER_EXT );
      grBufferClear (0, 0, 0xFFFF);
      grRenderBuffer( GR_BUFFER_BACKBUFFER );
    }

    rdp.texbufs[0].tmu = GR_TMU0;
    rdp.texbufs[0].begin = voodoo.tex_min_addr[GR_TMU0];
    rdp.texbufs[0].end = rdp.texbufs[0].begin+tbuf_size;
    rdp.texbufs[0].count = 0;
    rdp.texbufs[0].clear_allowed = TRUE;
    offset_font = tbuf_size;
    if (voodoo.num_tmu > 1)
    {
      rdp.texbufs[1].tmu = GR_TMU1;
      rdp.texbufs[1].begin = voodoo.tex_UMA ? rdp.texbufs[0].end : voodoo.tex_min_addr[GR_TMU1];
      rdp.texbufs[1].end = rdp.texbufs[1].begin+tbuf_size;
      rdp.texbufs[1].count = 0;
      rdp.texbufs[1].clear_allowed = TRUE;
      if (voodoo.tex_UMA)
        offset_font += tbuf_size;
      else
        offset_texbuf1 = tbuf_size;
    }
  }
  else
    offset_font = 0;

#include "font.h"
  wxUint32 *data = (wxUint32*)font;
  wxUint32 cur;

  // ** Font texture **
  wxUint8 *tex8 = (wxUint8*)malloc(256*64);

  fontTex.smallLodLog2 = fontTex.largeLodLog2 = GR_LOD_LOG2_256;
  fontTex.aspectRatioLog2 = GR_ASPECT_LOG2_4x1;
  fontTex.format = GR_TEXFMT_ALPHA_8;
  fontTex.data = tex8;

  // Decompression: [1-bit inverse alpha --> 8-bit alpha]
  wxUint32 i,b;
  for (i=0; i<0x200; i++)
  {
    // cur = ~*(data++), byteswapped
#ifdef __VISUALC__
    cur = _byteswap_ulong(~*(data++));
#else
    cur = ~*(data++);
    cur = ((cur&0xFF)<<24)|(((cur>>8)&0xFF)<<16)|(((cur>>16)&0xFF)<<8)|((cur>>24)&0xFF);
#endif

    for (b=0x80000000; b!=0; b>>=1)
    {
      if (cur&b) *tex8 = 0xFF;
      else *tex8 = 0x00;
      tex8 ++;
    }
  }

  grTexDownloadMipMap (GR_TMU0,
    voodoo.tex_min_addr[GR_TMU0] + offset_font,
    GR_MIPMAPLEVELMASK_BOTH,
    &fontTex);

  offset_cursor = offset_font + grTexTextureMemRequired (GR_MIPMAPLEVELMASK_BOTH, &fontTex);

  free (fontTex.data);

  // ** Cursor texture **
#include "cursor.h"
  data = (wxUint32*)cursor;

  wxUint16 *tex16 = (wxUint16*)malloc(32*32*2);

  cursorTex.smallLodLog2 = cursorTex.largeLodLog2 = GR_LOD_LOG2_32;
  cursorTex.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
  cursorTex.format = GR_TEXFMT_ARGB_1555;
  cursorTex.data = tex16;

  // Conversion: [16-bit 1555 (swapped) --> 16-bit 1555]
  for (i=0; i<0x200; i++)
  {
    cur = *(data++);
    *(tex16++) = (wxUint16)(((cur&0x000000FF)<<8)|((cur&0x0000FF00)>>8));
    *(tex16++) = (wxUint16)(((cur&0x00FF0000)>>8)|((cur&0xFF000000)>>24));
  }

  grTexDownloadMipMap (GR_TMU0,
    voodoo.tex_min_addr[GR_TMU0] + offset_cursor,
    GR_MIPMAPLEVELMASK_BOTH,
    &cursorTex);

  // Round to higher 16
  offset_textures = ((offset_cursor + grTexTextureMemRequired (GR_MIPMAPLEVELMASK_BOTH, &cursorTex))
    & 0xFFFFFFF0) + 16;
  free (cursorTex.data);
}

#ifdef TEXTURE_FILTER
void DisplayLoadProgress(const wchar_t *format, ...)
{
  va_list args;
  wchar_t wbuf[INFO_BUF];
  char buf[INFO_BUF];

  // process input
  va_start(args, format);
  vswprintf(wbuf, INFO_BUF, format, args);
  va_end(args);

  // XXX: convert to multibyte
  wcstombs(buf, wbuf, INFO_BUF);

  if (fullscreen)
  {
    float x;
    set_message_combiner ();
    output (382, 380, 1, "LOADING TEXTURES. PLEASE WAIT...");
    int len = min (strlen(buf)*8, 1024);
    x = (1024-len)/2.0f;
    output (x, 360, 1, buf);
    grBufferSwap (0);
    grColorMask (FXTRUE, FXTRUE);
    grBufferClear (0, 0, 0xFFFF);
  }
}
#endif

int InitGfx ()
{
#ifdef TEXTURE_FILTER
  wchar_t romname[256];
  wchar_t foldername[PATH_MAX + 64];
  wchar_t cachename[PATH_MAX + 64];
#endif
  if (fullscreen)
    ReleaseGfx ();

  OPEN_RDP_LOG ();  // doesn't matter if opens again; it will check for it
  OPEN_RDP_E_LOG ();
  VLOG ("InitGfx ()\n");

  debugging = FALSE;
  rdp_reset ();

  // Initialize Glide
  grGlideInit ();

  // Select the Glide device
  grSstSelect (settings.card_id);

  // Is mirroring allowed?
  const char *extensions = grGetString (GR_EXTENSION);

  // Check which SST we are using and initialize stuff
  // Hiroshi Morii <koolsmoky@users.sourceforge.net>
  enum {
    GR_SSTTYPE_VOODOO  = 0,
    GR_SSTTYPE_SST96   = 1,
    GR_SSTTYPE_AT3D    = 2,
    GR_SSTTYPE_Voodoo2 = 3,
    GR_SSTTYPE_Banshee = 4,
    GR_SSTTYPE_Voodoo3 = 5,
    GR_SSTTYPE_Voodoo4 = 6,
    GR_SSTTYPE_Voodoo5 = 7
  };
  const char *hardware = grGetString(GR_HARDWARE);
  unsigned int SST_type = GR_SSTTYPE_VOODOO;
  if (strstr(hardware, "Rush")) {
    SST_type = GR_SSTTYPE_SST96;
  } else if (strstr(hardware, "Voodoo2")) {
    SST_type = GR_SSTTYPE_Voodoo2;
  } else if (strstr(hardware, "Voodoo Banshee")) {
    SST_type = GR_SSTTYPE_Banshee;
  } else if (strstr(hardware, "Voodoo3")) {
    SST_type = GR_SSTTYPE_Voodoo3;
  } else if (strstr(hardware, "Voodoo4")) {
    SST_type = GR_SSTTYPE_Voodoo4;
  } else if (strstr(hardware, "Voodoo5")) {
    SST_type = GR_SSTTYPE_Voodoo5;
  }
  // 2Mb Texture boundary
  voodoo.has_2mb_tex_boundary = (SST_type < GR_SSTTYPE_Banshee) && !evoodoo;
  // use UMA if available
  voodoo.tex_UMA = FALSE;
  //*
  if (strstr(extensions, " TEXUMA ")) {
    // we get better texture cache hits with UMA on
    grEnable(GR_TEXTURE_UMA_EXT);
    voodoo.tex_UMA = TRUE;
    LOG ("Using TEXUMA extension.\n");
  }
  //*/
//TODO-PORT: fullscreen stuff
  wxUint32 res_data = settings.res_data;
  char strWrapperFullScreenResolutionExt[] = "grWrapperFullScreenResolutionExt";
  if (ev_fullscreen)
  {
      GRWRAPPERFULLSCREENRESOLUTIONEXT grWrapperFullScreenResolutionExt =
        (GRWRAPPERFULLSCREENRESOLUTIONEXT)grGetProcAddress(strWrapperFullScreenResolutionExt);
      if (grWrapperFullScreenResolutionExt) {
        wxUint32 _width, _height = 0;
        settings.res_data = grWrapperFullScreenResolutionExt(&_width, &_height);
        settings.scr_res_x = settings.res_x = _width;
        settings.scr_res_y = settings.res_y = _height;
      }
      res_data = settings.res_data;
  }
  else if (evoodoo)
  {
      GRWRAPPERFULLSCREENRESOLUTIONEXT grWrapperFullScreenResolutionExt =
        (GRWRAPPERFULLSCREENRESOLUTIONEXT)grGetProcAddress(strWrapperFullScreenResolutionExt);
      if (grWrapperFullScreenResolutionExt != NULL)
      {
/*
        TODO-port: fix resolutions
        settings.res_data = settings.res_data_org;
        settings.scr_res_x = settings.res_x = resolutions[settings.res_data][0];
        settings.scr_res_y = settings.res_y = resolutions[settings.res_data][1];
*/
      }
      res_data = settings.res_data | 0x80000000;
  }

  gfx_context = 0;

  // Select the window

  if (fb_hwfbe_enabled)
  {
    char strSstWinOpenExt[] ="grSstWinOpenExt";
    GRWINOPENEXT grSstWinOpenExt = (GRWINOPENEXT)grGetProcAddress(strSstWinOpenExt);
    if (grSstWinOpenExt)
      gfx_context = grSstWinOpenExt ((FxU32)NULL,
      res_data,
      GR_REFRESH_60Hz,
      GR_COLORFORMAT_RGBA,
      GR_ORIGIN_UPPER_LEFT,
      fb_emulation_enabled?GR_PIXFMT_RGB_565:GR_PIXFMT_ARGB_8888, //32b color is not compatible with fb emulation
      2,    // Double-buffering
      1);   // 1 auxillary buffer
  }
  if (!gfx_context)
    gfx_context = grSstWinOpen ((FxU32)NULL,
    res_data,
    GR_REFRESH_60Hz,
    GR_COLORFORMAT_RGBA,
    GR_ORIGIN_UPPER_LEFT,
    2,    // Double-buffering
    1);   // 1 auxillary buffer

  if (!gfx_context)
  {
    ERRLOG("Error setting display mode");
    //    grSstWinClose (gfx_context);
    grGlideShutdown ();
    return FALSE;
  }

  fullscreen = TRUE;
  to_fullscreen = FALSE;


  // get the # of TMUs available
  grGet (GR_NUM_TMU, 4, (FxI32*)&voodoo.num_tmu);
  // get maximal texture size
  grGet (GR_MAX_TEXTURE_SIZE, 4, (FxI32*)&voodoo.max_tex_size);
  voodoo.sup_large_tex = (voodoo.max_tex_size > 256 && !(settings.hacks & hack_PPL));

  //num_tmu = 1;
  if (voodoo.tex_UMA)
  {
    GetTexAddr = GetTexAddrUMA;
    voodoo.tex_min_addr[0] = voodoo.tex_min_addr[1] = grTexMinAddress(GR_TMU0);
    voodoo.tex_max_addr[0] = voodoo.tex_max_addr[1] = grTexMaxAddress(GR_TMU0);
  }
  else
  {
    GetTexAddr = GetTexAddrNonUMA;
    voodoo.tex_min_addr[0] = grTexMinAddress(GR_TMU0);
    voodoo.tex_min_addr[1] = grTexMinAddress(GR_TMU1);
    voodoo.tex_max_addr[0] = grTexMaxAddress(GR_TMU0);
    voodoo.tex_max_addr[1] = grTexMaxAddress(GR_TMU1);
  }

  if (strstr (extensions, "TEXMIRROR") && !(settings.hacks&hack_Zelda)) //zelda's trees suffer from hardware mirroring
    voodoo.sup_mirroring = 1;
  else
    voodoo.sup_mirroring = 0;

  if (strstr (extensions, "TEXFMT"))  //VSA100 texture format extension
    voodoo.sup_32bit_tex = TRUE;
  else
    voodoo.sup_32bit_tex = FALSE;

  voodoo.gamma_correction = 0;
  if (strstr(extensions, "GETGAMMA"))
    grGet(GR_GAMMA_TABLE_ENTRIES, sizeof(voodoo.gamma_table_size), &voodoo.gamma_table_size);

  if (fb_hwfbe_enabled)
  {
    if (char * extstr = (char*)strstr(extensions, "TEXTUREBUFFER"))
    {
      if (!strncmp(extstr, "TEXTUREBUFFER", 13))
      {
        char strTextureBufferExt[] = "grTextureBufferExt";
        grTextureBufferExt = (GRTEXBUFFEREXT) grGetProcAddress(strTextureBufferExt);
        char strTextureAuxBufferExt[] = "grTextureAuxBufferExt";
        grTextureAuxBufferExt = (GRTEXBUFFEREXT) grGetProcAddress(strTextureAuxBufferExt);
        char strAuxBufferExt[] = "grAuxBufferExt";
        grAuxBufferExt = (GRAUXBUFFEREXT) grGetProcAddress(strAuxBufferExt);
      }
    }
    else
      settings.frame_buffer &= ~fb_hwfbe;
  }
  else
    grTextureBufferExt = 0;

  grStippleModeExt = (GRSTIPPLE)grStippleMode;
  grStipplePatternExt = (GRSTIPPLE)grStipplePattern;

  if (grStipplePatternExt)
    grStipplePatternExt(settings.stipple_pattern);

//  char strKeyPressedExt[] = "grKeyPressedExt";
//  grKeyPressed = (FxBool (FX_CALL *)(FxU32))grGetProcAddress (strKeyPressedExt);

  InitCombine();

#ifdef SIMULATE_VOODOO1
  voodoo.num_tmu = 1;
  voodoo.sup_mirroring = 0;
#endif

#ifdef SIMULATE_BANSHEE
  voodoo.num_tmu = 1;
  voodoo.sup_mirroring = 1;
#endif

  grCoordinateSpace (GR_WINDOW_COORDS);
  grVertexLayout (GR_PARAM_XY, offsetof(VERTEX,x), GR_PARAM_ENABLE);
  grVertexLayout (GR_PARAM_Q, offsetof(VERTEX,q), GR_PARAM_ENABLE);
  grVertexLayout (GR_PARAM_Z, offsetof(VERTEX,z), GR_PARAM_ENABLE);
  grVertexLayout (GR_PARAM_ST0, offsetof(VERTEX,coord[0]), GR_PARAM_ENABLE);
  grVertexLayout (GR_PARAM_ST1, offsetof(VERTEX,coord[2]), GR_PARAM_ENABLE);
  grVertexLayout (GR_PARAM_PARGB, offsetof(VERTEX,b), GR_PARAM_ENABLE);

  grCullMode(GR_CULL_NEGATIVE);

  if (settings.fog) //"FOGCOORD" extension
  {
    if (strstr (extensions, "FOGCOORD"))
    {
      GrFog_t fog_t[64];
      guFogGenerateLinear (fog_t, 0.0f, 255.0f);//(float)rdp.fog_multiplier + (float)rdp.fog_offset);//256.0f);

      for (int i = 63; i > 0; i--)
      {
        if (fog_t[i] - fog_t[i-1] > 63)
        {
          fog_t[i-1] = fog_t[i] - 63;
        }
      }
      fog_t[0] = 0;
      //      for (int f = 0; f < 64; f++)
      //      {
      //        FRDP("fog[%d]=%d->%f\n", f, fog_t[f], guFogTableIndexToW(f));
      //      }
      grFogTable (fog_t);
      grVertexLayout (GR_PARAM_FOG_EXT, offsetof(VERTEX,f), GR_PARAM_ENABLE);
    }
    else //not supported
      settings.fog = FALSE;
  }

  grDepthBufferMode (GR_DEPTHBUFFER_ZBUFFER);
  grDepthBufferFunction(GR_CMP_LESS);
  grDepthMask(FXTRUE);

  settings.res_x = settings.scr_res_x;
  settings.res_y = settings.scr_res_y;
  ChangeSize ();

  guLoadTextures ();
  ClearCache ();

  grCullMode (GR_CULL_DISABLE);
  grDepthBufferMode (GR_DEPTHBUFFER_ZBUFFER);
  grDepthBufferFunction (GR_CMP_ALWAYS);
  grRenderBuffer(GR_BUFFER_BACKBUFFER);
  grColorMask (FXTRUE, FXTRUE);
  grDepthMask (FXTRUE);
  grBufferClear (0, 0, 0xFFFF);
  grBufferSwap (0);
  grBufferClear (0, 0, 0xFFFF);
  grDepthMask (FXFALSE);
  grTexFilterMode (0, GR_TEXTUREFILTER_BILINEAR, GR_TEXTUREFILTER_BILINEAR);
  grTexFilterMode (1, GR_TEXTUREFILTER_BILINEAR, GR_TEXTUREFILTER_BILINEAR);
  grTexClampMode (0, GR_TEXTURECLAMP_CLAMP, GR_TEXTURECLAMP_CLAMP);
  grTexClampMode (1, GR_TEXTURECLAMP_CLAMP, GR_TEXTURECLAMP_CLAMP);
  grClipWindow (0, 0, settings.scr_res_x, settings.scr_res_y);
  rdp.update |= UPDATE_SCISSOR | UPDATE_COMBINE | UPDATE_ZBUF_ENABLED | UPDATE_CULL_MODE;

#ifdef TEXTURE_FILTER // Hiroshi Morii <koolsmoky@users.sourceforge.net>
  if (!settings.ghq_use)
  {
    settings.ghq_use = settings.ghq_fltr || settings.ghq_enht /*|| settings.ghq_cmpr*/ || settings.ghq_hirs;
    if (settings.ghq_use)
    {
      /* Plugin path */
      int options = texfltr[settings.ghq_fltr]|texenht[settings.ghq_enht]|texcmpr[settings.ghq_cmpr]|texhirs[settings.ghq_hirs];
      if (settings.ghq_enht_cmpr)
        options |= COMPRESS_TEX;
      if (settings.ghq_hirs_cmpr)
        options |= COMPRESS_HIRESTEX;
      //      if (settings.ghq_enht_tile)
      //        options |= TILE_TEX;
      if (settings.ghq_hirs_tile)
        options |= TILE_HIRESTEX;
      if (settings.ghq_enht_f16bpp)
        options |= FORCE16BPP_TEX;
      if (settings.ghq_hirs_f16bpp)
        options |= FORCE16BPP_HIRESTEX;
      if (settings.ghq_enht_gz)
        options |= GZ_TEXCACHE;
      if (settings.ghq_hirs_gz)
        options |= GZ_HIRESTEXCACHE;
      if (settings.ghq_cache_save)
        options |= (DUMP_TEXCACHE|DUMP_HIRESTEXCACHE);
      if (settings.ghq_hirs_let_texartists_fly)
        options |= LET_TEXARTISTS_FLY;
      if (settings.ghq_hirs_dump)
        options |= DUMP_TEX;

      ghq_dmptex_toggle_key = 0;

      swprintf(romname, sizeof(romname) / sizeof(*romname), L"%hs", rdp.RomName);
      swprintf(foldername, sizeof(foldername) / sizeof(*foldername), L"%hs", ConfigGetUserDataPath());
      swprintf(cachename, sizeof(cachename) / sizeof(*cachename), L"%hs", ConfigGetUserCachePath());

      settings.ghq_use = (int)ext_ghq_init(voodoo.max_tex_size, // max texture width supported by hardware
        voodoo.max_tex_size, // max texture height supported by hardware
        voodoo.sup_32bit_tex?32:16, // max texture bpp supported by hardware
        options,
        settings.ghq_cache_size * 1024*1024, // cache texture to system memory
        foldername,
        cachename,
        romname, // name of ROM. must be no longer than 256 characters
        DisplayLoadProgress);
    }
  }
  if (settings.ghq_use && strstr (extensions, "TEXMIRROR"))
    voodoo.sup_mirroring = 1;
#endif

  return TRUE;
}

void ReleaseGfx ()
{
  VLOG("ReleaseGfx ()\n");

  // Restore gamma settings
  if (voodoo.gamma_correction)
  {
    if (voodoo.gamma_table_r)
      grLoadGammaTable(voodoo.gamma_table_size, voodoo.gamma_table_r, voodoo.gamma_table_g, voodoo.gamma_table_b);
    else
      guGammaCorrectionRGB(1.3f, 1.3f, 1.3f); //1.3f is default 3dfx gamma for everything but desktop
    voodoo.gamma_correction = 0;
  }

  // Release graphics
  grSstWinClose (gfx_context);

  // Shutdown glide
  grGlideShutdown();

  fullscreen = FALSE;
  rdp.window_changed = TRUE;
}

// new API code begins here!

#ifdef __cplusplus
extern "C" {
#endif

EXPORT void* CALL GetScreenTextureID()
{
	//__asm int 3;
 // GrLfbInfo_t info;
 // info.size = sizeof(GrLfbInfo_t);
 // if (grLfbLock (GR_LFB_READ_ONLY,
 //   GR_BUFFER_BACKBUFFER,
 //   GR_LFBWRITEMODE_888,
 //   GR_ORIGIN_UPPER_LEFT,
 //   FXFALSE,
 //   &info))

	return NULL;
}

EXPORT void CALL ReadScreen2(void *dest, int *width, int *height, int front)
{
  VLOG("CALL ReadScreen2 ()\n");
  *width = settings.res_x;
  *height = settings.res_y;
  if (dest)
  {
    BYTE * line = (BYTE*)dest;
    if (!fullscreen)
    {
      for (wxUint32 y=0; y<settings.res_y; y++)
      {
        for (wxUint32 x=0; x<settings.res_x; x++)
        {
          line[x*3] = 0x20;
          line[x*3+1] = 0x7f;
          line[x*3+2] = 0x40;
        }
      }
      // LOG ("ReadScreen. not in the fullscreen!\n");
      WARNLOG("[Glide64] Cannot save screenshot in windowed mode?\n");

      return;
    }

  GrLfbInfo_t info;
  info.size = sizeof(GrLfbInfo_t);
  if (grLfbLock (GR_LFB_READ_ONLY,
    GR_BUFFER_BACKBUFFER,
    GR_LFBWRITEMODE_888,
    GR_ORIGIN_UPPER_LEFT,
    FXFALSE,
    &info))
  {
    // Copy the screen, let's hope this works.
      for (wxUint32 y=0; y<settings.res_y; y++)
      {
        BYTE *ptr = (BYTE*) info.lfbPtr + (info.strideInBytes * y);
        for (wxUint32 x=0; x<settings.res_x; x++)
        {
          line[x*4+2]   = ptr[2];  // red
          line[x*4+1] = ptr[1];  // green
          line[x*4] = ptr[0];  // blue
          ptr += 4;
        }
        line += settings.res_x * 4;
      }

      // Unlock the frontbuffer
      grLfbUnlock (GR_LFB_READ_ONLY, GR_BUFFER_BACKBUFFER);
    }
    VLOG ("ReadScreen. Success.\n");
  }
}

EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
                                   void (*DebugCallback)(void *, int, const char *))
{
  VLOG("CALL PluginStartup ()\n");
    l_DebugCallback = DebugCallback;
    l_DebugCallContext = Context;

    /* attach and call the CoreGetAPIVersions function, check Config and Video Extension API versions for compatibility */
    ptr_CoreGetAPIVersions CoreAPIVersionFunc;
    CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
    if (CoreAPIVersionFunc == NULL)
    {
        ERRLOG("Core emulator broken; no CoreAPIVersionFunc() function found.");
        return M64ERR_INCOMPATIBLE;
    }
    int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion;
    (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
    if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000))
    {
        ERRLOG("Emulator core Config API incompatible with this plugin");
        return M64ERR_INCOMPATIBLE;
    }
    if ((VidextAPIVersion & 0xffff0000) != (VIDEXT_API_VERSION & 0xffff0000))
    {
        ERRLOG("Emulator core Video Extension API incompatible with this plugin");
        return M64ERR_INCOMPATIBLE;
    }

    ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
    ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
    ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
    ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt");
    ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
    ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
    ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString");
    ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt");
    ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat");
    ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
    ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString");

    ConfigGetSharedDataFilepath = (ptr_ConfigGetSharedDataFilepath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetSharedDataFilepath");
    ConfigGetUserConfigPath = (ptr_ConfigGetUserConfigPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserConfigPath");
    ConfigGetUserDataPath = (ptr_ConfigGetUserDataPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserDataPath");
    ConfigGetUserCachePath = (ptr_ConfigGetUserCachePath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserCachePath");

    if (!ConfigOpenSection   || !ConfigSetParameter    || !ConfigGetParameter ||
        !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString ||
        !ConfigGetParamInt   || !ConfigGetParamFloat   || !ConfigGetParamBool   || !ConfigGetParamString ||
        !ConfigGetSharedDataFilepath || !ConfigGetUserConfigPath || !ConfigGetUserDataPath || !ConfigGetUserCachePath)
    {
        ERRLOG("Couldn't connect to Core configuration functions");
        return M64ERR_INCOMPATIBLE;
    }

    /* Get the core Video Extension function pointers from the library handle */
    CoreVideo_Init = (ptr_VidExt_Init) osal_dynlib_getproc(CoreLibHandle, "VidExt_Init");
    CoreVideo_Quit = (ptr_VidExt_Quit) osal_dynlib_getproc(CoreLibHandle, "VidExt_Quit");
    CoreVideo_ListFullscreenModes = (ptr_VidExt_ListFullscreenModes) osal_dynlib_getproc(CoreLibHandle, "VidExt_ListFullscreenModes");
    CoreVideo_SetVideoMode = (ptr_VidExt_SetVideoMode) osal_dynlib_getproc(CoreLibHandle, "VidExt_SetVideoMode");
    CoreVideo_SetCaption = (ptr_VidExt_SetCaption) osal_dynlib_getproc(CoreLibHandle, "VidExt_SetCaption");
    CoreVideo_ToggleFullScreen = (ptr_VidExt_ToggleFullScreen) osal_dynlib_getproc(CoreLibHandle, "VidExt_ToggleFullScreen");
    CoreVideo_ResizeWindow = (ptr_VidExt_ResizeWindow) osal_dynlib_getproc(CoreLibHandle, "VidExt_ResizeWindow");
    CoreVideo_GL_GetProcAddress = (ptr_VidExt_GL_GetProcAddress) osal_dynlib_getproc(CoreLibHandle, "VidExt_GL_GetProcAddress");
    CoreVideo_GL_SetAttribute = (ptr_VidExt_GL_SetAttribute) osal_dynlib_getproc(CoreLibHandle, "VidExt_GL_SetAttribute");
    CoreVideo_GL_SwapBuffers = (ptr_VidExt_GL_SwapBuffers) osal_dynlib_getproc(CoreLibHandle, "VidExt_GL_SwapBuffers");

    if (!CoreVideo_Init || !CoreVideo_Quit || !CoreVideo_ListFullscreenModes || !CoreVideo_SetVideoMode ||
        !CoreVideo_SetCaption || !CoreVideo_ToggleFullScreen || !CoreVideo_ResizeWindow || !CoreVideo_GL_GetProcAddress ||
        !CoreVideo_GL_SetAttribute || !CoreVideo_GL_SwapBuffers)
    {
        ERRLOG("Couldn't connect to Core video functions");
        return M64ERR_INCOMPATIBLE;
    }

    CoreVideo_Init();
    ReadSettings();
	return M64ERR_SUCCESS;
}

EXPORT m64p_error CALL PluginShutdown(void)
{
  VLOG("CALL PluginShutdown ()\n");
    return M64ERR_SUCCESS;
}

EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
{
  VLOG("CALL PluginGetVersion ()\n");
    /* set version info */
    if (PluginType != NULL)
        *PluginType = M64PLUGIN_GFX;

    if (PluginVersion != NULL)
        *PluginVersion = PLUGIN_VERSION;

    if (APIVersion != NULL)
        *APIVersion = VIDEO_PLUGIN_API_VERSION;

    if (PluginNamePtr != NULL)
        *PluginNamePtr = PLUGIN_NAME;

    if (Capabilities != NULL)
    {
        *Capabilities = 0;
    }

    return M64ERR_SUCCESS;
}

/******************************************************************
Function: CaptureScreen
Purpose:  This function dumps the current frame to a file
input:    pointer to the directory to save the file to
output:   none
*******************************************************************/
EXPORT void CALL CaptureScreen ( char * Directory )
{
  capture_screen = 1;
  strcpy (capture_path, Directory);
}

/******************************************************************
Function: ChangeWindow
Purpose:  to change the window between fullscreen and window
mode. If the window was in fullscreen this should
change the screen to window mode and vice vesa.
input:    none
output:   none
*******************************************************************/
//#warning ChangeWindow unimplemented
EXPORT void CALL ChangeWindow (void)
{
  VLOG ("ChangeWindow()\n");
  #ifdef OLDAPI
  if (evoodoo)
  {
    if (!ev_fullscreen)
    {
      to_fullscreen = TRUE;
      ev_fullscreen = TRUE;
#ifdef __WINDOWS__
      if (gfx.hStatusBar)
        ShowWindow( gfx.hStatusBar, SW_HIDE );
      ShowCursor( FALSE );
#endif
    }
    else
    {
      ev_fullscreen = FALSE;
      InitGfx ();
#ifdef __WINDOWS__
      ShowCursor( TRUE );
      if (gfx.hStatusBar)
        ShowWindow( gfx.hStatusBar, SW_SHOW );
      SetWindowLong (gfx.hWnd, GWL_WNDPROC, (long)oldWndProc);
#endif
    }
  }
  else
  {
    // Go to fullscreen at next dlist
    // This is for compatibility with 1964, which reloads the plugin
    //  when switching to fullscreen
    if (!fullscreen)
    {
      to_fullscreen = TRUE;
#ifdef __WINDOWS__
      if (gfx.hStatusBar)
        ShowWindow( gfx.hStatusBar, SW_HIDE );
      ShowCursor( FALSE );
#endif
    }
    else
    {
      ReleaseGfx ();
#ifdef __WINDOWS__
      ShowCursor( TRUE );
      if (gfx.hStatusBar)
        ShowWindow( gfx.hStatusBar, SW_SHOW );
      // SetWindowLong fixes the following Windows XP Banshee issues:
      // 1964 crash error when loading another rom.
      // All N64 emu's minimize, restore crashes.
      SetWindowLong (gfx.hWnd, GWL_WNDPROC, (long)oldWndProc);
#endif
    }
  }
#endif
}

/******************************************************************
Function: CloseDLL
Purpose:  This function is called when the emulator is closing
down allowing the dll to de-initialise.
input:    none
output:   none
*******************************************************************/
void CALL CloseDLL (void)
{
  VLOG ("CloseDLL ()\n");

  // re-set the old window proc
#ifdef WINPROC_OVERRIDE
  SetWindowLong (gfx.hWnd, GWL_WNDPROC, (long)oldWndProc);
#endif

#ifdef ALTTAB_FIX
  if (hhkLowLevelKybd)
  {
    UnhookWindowsHookEx(hhkLowLevelKybd);
    hhkLowLevelKybd = 0;
  }
#endif

  //CLOSELOG ();

#ifdef TEXTURE_FILTER // Hiroshi Morii <koolsmoky@users.sourceforge.net>
  if (settings.ghq_use)
  {
    ext_ghq_shutdown();
    settings.ghq_use = 0;
  }
#endif
  if (fullscreen)
    ReleaseGfx ();
  ZLUT_release();
  ClearCache ();
  delete[] voodoo.gamma_table_r;
  voodoo.gamma_table_r = 0;
  delete[] voodoo.gamma_table_g;
  voodoo.gamma_table_g = 0;
  delete[] voodoo.gamma_table_b;
  voodoo.gamma_table_b = 0;
}

/******************************************************************
Function: DllTest
Purpose:  This function is optional function that is provided
to allow the user to test the dll
input:    a handle to the window that calls this function
output:   none
*******************************************************************/
void CALL DllTest ( HWND hParent )
{
}

/******************************************************************
Function: DrawScreen
Purpose:  This function is called when the emulator receives a
WM_PAINT message. This allows the gfx to fit in when
it is being used in the desktop.
input:    none
output:   none
*******************************************************************/
void CALL DrawScreen (void)
{
  VLOG ("DrawScreen ()\n");
}

/******************************************************************
Function: GetDllInfo
Purpose:  This function allows the emulator to gather information
about the dll by filling in the PluginInfo structure.
input:    a pointer to a PLUGIN_INFO stucture that needs to be
filled by the function. (see def above)
output:   none
*******************************************************************/
void CALL GetDllInfo ( PLUGIN_INFO * PluginInfo )
{
  VLOG ("GetDllInfo ()\n");
  PluginInfo->Version = 0x0103;     // Set to 0x0103
  PluginInfo->Type  = PLUGIN_TYPE_GFX;  // Set to PLUGIN_TYPE_GFX
  sprintf (PluginInfo->Name, "Glide64mk2 " G64_VERSION RELTIME);  // Name of the DLL

  // If DLL supports memory these memory options then set them to TRUE or FALSE
  //  if it does not support it
  PluginInfo->NormalMemory = TRUE;  // a normal wxUint8 array
  PluginInfo->MemoryBswaped = TRUE; // a normal wxUint8 array where the memory has been pre
  // bswap on a dword (32 bits) boundry
}

#ifndef WIN32
BOOL WINAPI QueryPerformanceCounter(PLARGE_INTEGER counter)
{
   struct timeval tv;

   /* generic routine */
   gettimeofday( &tv, NULL );
   counter->QuadPart = (LONGLONG)tv.tv_usec + (LONGLONG)tv.tv_sec * 1000000;
   return TRUE;
}

BOOL WINAPI QueryPerformanceFrequency(PLARGE_INTEGER frequency)
{
   frequency->s.LowPart= 1000000;
   frequency->s.HighPart= 0;
   return TRUE;
}
#endif

/******************************************************************
Function: InitiateGFX
Purpose:  This function is called when the DLL is started to give
information from the emulator that the n64 graphics
uses. This is not called from the emulation thread.
Input:    Gfx_Info is passed to this function which is defined
above.
Output:   TRUE on success
FALSE on failure to initialise

** note on interrupts **:
To generate an interrupt set the appropriate bit in MI_INTR_REG
and then call the function CheckInterrupts to tell the emulator
that there is a waiting interrupt.
*******************************************************************/

EXPORT int CALL InitiateGFX (GFX_INFO Gfx_Info)
{
  VLOG ("InitiateGFX (*)\n");
  voodoo.num_tmu = 2;

  // Assume scale of 1 for debug purposes
  rdp.scale_x = 1.0f;
  rdp.scale_y = 1.0f;

  memset (&settings, 0, sizeof(SETTINGS));
  ReadSettings ();
  char name[21] = "DEFAULT";
  ReadSpecialSettings (name);
  settings.res_data_org = settings.res_data;
#ifdef FPS
  QueryPerformanceFrequency (&perf_freq);
  QueryPerformanceCounter (&fps_last);
#endif

  debug_init ();    // Initialize debugger

  gfx = Gfx_Info;

#ifdef WINPROC_OVERRIDE
  // [H.Morii] inject our own winproc so that "alt-enter to fullscreen"
  // message is shown when the emulator window is activated.
  WNDPROC curWndProc = (WNDPROC)GetWindowLong(gfx.hWnd, GWL_WNDPROC);
  if (curWndProc && curWndProc != (WNDPROC)WndProc) {
    oldWndProc = (WNDPROC)SetWindowLong (gfx.hWnd, GWL_WNDPROC, (long)WndProc);
  }
#endif

  util_init ();
  math_init ();
  TexCacheInit ();
  CRC_BuildTable();
  CountCombine();
  if (fb_depth_render_enabled)
    ZLUT_init();

  char strConfigWrapperExt[] = "grConfigWrapperExt";
  GRCONFIGWRAPPEREXT grConfigWrapperExt = (GRCONFIGWRAPPEREXT)grGetProcAddress(strConfigWrapperExt);
  if (grConfigWrapperExt)
    grConfigWrapperExt(settings.wrpResolution, settings.wrpVRAM * 1024 * 1024, settings.wrpFBO, settings.wrpAnisotropic);

  grGlideInit ();
  grSstSelect (0);
  const char *extensions = grGetString (GR_EXTENSION);
  grGlideShutdown ();
  if (strstr (extensions, "EVOODOO"))
  {
    evoodoo = 1;
    voodoo.has_2mb_tex_boundary = 0;
  }
  else {
    evoodoo = 0;
    voodoo.has_2mb_tex_boundary = 1;
  }

  return TRUE;
}

/******************************************************************
Function: MoveScreen
Purpose:  This function is called in response to the emulator
receiving a WM_MOVE passing the xpos and ypos passed
from that message.
input:    xpos - the x-coordinate of the upper-left corner of the
client area of the window.
ypos - y-coordinate of the upper-left corner of the
client area of the window.
output:   none
*******************************************************************/
EXPORT void CALL MoveScreen (int xpos, int ypos)
{
  rdp.window_changed = TRUE;
}

/******************************************************************
Function: ResizeVideoOutput
Purpose:  This function is called to force us to resize our output OpenGL window.
          This is currently unsupported, and should never be called because we do
          not pass the RESIZABLE flag to VidExt_SetVideoMode when initializing.
input:    new width and height
output:   none
*******************************************************************/
EXPORT void CALL ResizeVideoOutput(int Width, int Height)
{
}

/******************************************************************
Function: RomClosed
Purpose:  This function is called when a rom is closed.
input:    none
output:   none
*******************************************************************/
EXPORT void CALL RomClosed (void)
{
  VLOG ("RomClosed ()\n");

  CLOSE_RDP_LOG ();
  CLOSE_RDP_E_LOG ();
  rdp.window_changed = TRUE;
  romopen = FALSE;
  if (fullscreen && evoodoo)
    ReleaseGfx ();
}

static void CheckDRAMSize()
{
  wxUint32 test;
  GLIDE64_TRY
  {
    test = gfx.RDRAM[0x007FFFFF] + 1;
  }
  GLIDE64_CATCH
  {
    test = 0;
  }
  if (test)
    BMASK = 0x7FFFFF;
  else
    BMASK = WMASK;
#ifdef LOGGING
  sprintf (out_buf, "Detected RDRAM size: %08lx\n", BMASK);
  LOG (out_buf);
#endif
}

/******************************************************************
Function: RomOpen
Purpose:  This function is called when a rom is open. (from the
emulation thread)
input:    none
output:   none
*******************************************************************/
EXPORT int CALL RomOpen (void)
{
  VLOG ("RomOpen ()\n");
  no_dlist = true;
  romopen = TRUE;
  ucode_error_report = TRUE;	// allowed to report ucode errors
  rdp_reset ();

  // Get the country code & translate to NTSC(0) or PAL(1)
  wxUint16 code = ((wxUint16*)gfx.HEADER)[0x1F^1];

  if (code == 0x4400) region = 1; // Germany (PAL)
  if (code == 0x4500) region = 0; // USA (NTSC)
  if (code == 0x4A00) region = 0; // Japan (NTSC)
  if (code == 0x5000) region = 1; // Europe (PAL)
  if (code == 0x5500) region = 0; // Australia (NTSC)

  char name[21] = "DEFAULT";
  ReadSpecialSettings (name);

  // get the name of the ROM
  for (int i=0; i<20; i++)
    name[i] = gfx.HEADER[(32+i)^3];
  name[20] = 0;

  // remove all trailing spaces
  while (name[strlen(name)-1] == ' ')
    name[strlen(name)-1] = 0;

  strncpy(rdp.RomName, name, sizeof(name));
  ReadSpecialSettings (name);
  ClearCache ();

  CheckDRAMSize();

  OPEN_RDP_LOG ();
  OPEN_RDP_E_LOG ();


  // ** EVOODOO EXTENSIONS **
  if (!fullscreen)
  {
    grGlideInit ();
    grSstSelect (0);
  }
  const char *extensions = grGetString (GR_EXTENSION);
  if (!fullscreen)
  {
    grGlideShutdown ();

    if (strstr (extensions, "EVOODOO"))
      evoodoo = 1;
    else
      evoodoo = 0;

    if (evoodoo)
      InitGfx ();
  }

  if (strstr (extensions, "ROMNAME"))
  {
    char strSetRomName[] = "grSetRomName";
    void (FX_CALL *grSetRomName)(char*);
    grSetRomName = (void (FX_CALL *)(char*))grGetProcAddress (strSetRomName);
    grSetRomName (name);
  }
  // **
  return true;
}

/******************************************************************
Function: ShowCFB
Purpose:  Useally once Dlists are started being displayed, cfb is
ignored. This function tells the dll to start displaying
them again.
input:    none
output:   none
*******************************************************************/
bool no_dlist = true;
EXPORT void CALL ShowCFB (void)
{
  no_dlist = true;
  VLOG ("ShowCFB ()\n");
}

EXPORT void CALL SetRenderingCallback(void (*callback)(int))
{
  VLOG("CALL SetRenderingCallback (*)\n");
    renderCallback = callback;
}

void drawViRegBG()
{
  LRDP("drawViRegBG\n");
  const wxUint32 VIwidth = *gfx.VI_WIDTH_REG;
  FB_TO_SCREEN_INFO fb_info;
  fb_info.width  = VIwidth;
  fb_info.height = (wxUint32)rdp.vi_height;
  if (fb_info.height == 0)
  {
    LRDP("Image height = 0 - skipping\n");
    return;
  }
  fb_info.ul_x = 0;

  fb_info.lr_x = VIwidth - 1;
  //  fb_info.lr_x = (wxUint32)rdp.vi_width - 1;
  fb_info.ul_y = 0;
  fb_info.lr_y = fb_info.height - 1;
  fb_info.opaque = 1;
  fb_info.addr = *gfx.VI_ORIGIN_REG;
  fb_info.size = *gfx.VI_STATUS_REG & 3;
  rdp.last_bg = fb_info.addr;

  bool drawn = DrawFrameBufferToScreen(fb_info);
  if (settings.hacks&hack_Lego && drawn)
  {
    rdp.updatescreen = 1;
    newSwapBuffers ();
    DrawFrameBufferToScreen(fb_info);
  }
}

}

void drawNoFullscreenMessage();

void DrawFrameBuffer ()
{
  if (!fullscreen)
  {
    drawNoFullscreenMessage();
  }
  if (to_fullscreen)
    GoToFullScreen();

  if (fullscreen)
  {
    grDepthMask (FXTRUE);
    grColorMask (FXTRUE, FXTRUE);
    grBufferClear (0, 0, 0xFFFF);
    drawViRegBG();
  }
}

extern "C" {
/******************************************************************
Function: UpdateScreen
Purpose:  This function is called in response to a vsync of the
screen were the VI bit in MI_INTR_REG has already been
set
input:    none
output:   none
*******************************************************************/
wxUint32 update_screen_count = 0;
EXPORT void CALL UpdateScreen (void)
{
#ifdef LOG_KEY
  if (CheckKeyPressed(G64_VK_SPACE, 0x0001))
  {
    LOG ("KEY!!!\n");
  }
#endif
  char out_buf[128];
  sprintf (out_buf, "UpdateScreen (). Origin: %08x, Old origin: %08x, width: %d\n", *gfx.VI_ORIGIN_REG, rdp.vi_org_reg, *gfx.VI_WIDTH_REG);
  VLOG (out_buf);
  LRDP(out_buf);

  wxUint32 width = (*gfx.VI_WIDTH_REG) << 1;
  if (fullscreen && (*gfx.VI_ORIGIN_REG  > width))
    update_screen_count++;
//TODO-PORT: wx times
#ifdef FPS
  // vertical interrupt has occurred, increment counter
  vi_count ++;

  // Check frames per second
  LARGE_INTEGER difference;
  QueryPerformanceCounter (&fps_next);
  difference.QuadPart = fps_next.QuadPart - fps_last.QuadPart;
  float diff_secs = (float)((double)difference.QuadPart / (double)perf_freq.QuadPart);
  if (diff_secs > 0.5f)
  {
    fps = (float)(fps_count / diff_secs);
    vi = (float)(vi_count / diff_secs);
    ntsc_percent = vi / 0.6f;
    pal_percent = vi / 0.5f;
    fps_last = fps_next;
    fps_count = 0;
    vi_count = 0;
  }
#endif
  //*
  wxUint32 limit = (settings.hacks&hack_Lego) ? 15 : 30;
  if ((settings.frame_buffer&fb_cpu_write_hack) && (update_screen_count > limit) && (rdp.last_bg == 0))
  {
    LRDP("DirectCPUWrite hack!\n");
    update_screen_count = 0;
    no_dlist = true;
    ClearCache ();
    UpdateScreen();
    return;
  }
  //*/
  //*
  if( no_dlist )
  {
    if( *gfx.VI_ORIGIN_REG  > width )
    {
      ChangeSize ();
      LRDP("ChangeSize done\n");
      DrawFrameBuffer();
      LRDP("DrawFrameBuffer done\n");
      rdp.updatescreen = 1;
      newSwapBuffers ();
    }
    return;
  }
  //*/
  if (settings.swapmode == 0)
    newSwapBuffers ();
}

static void DrawWholeFrameBufferToScreen()
{
  static wxUint32 toScreenCI = 0;
  if (rdp.ci_width < 200)
    return;
  if (rdp.cimg == toScreenCI)
    return;
  toScreenCI = rdp.cimg;
  FB_TO_SCREEN_INFO fb_info;
  fb_info.addr   = rdp.cimg;
  fb_info.size   = rdp.ci_size;
  fb_info.width  = rdp.ci_width;
  fb_info.height = rdp.ci_height;
  if (fb_info.height == 0)
    return;
  fb_info.ul_x = 0;
  fb_info.lr_x = rdp.ci_width-1;
  fb_info.ul_y = 0;
  fb_info.lr_y = rdp.ci_height-1;
  fb_info.opaque = 0;
  DrawFrameBufferToScreen(fb_info);
  if (!(settings.frame_buffer & fb_ref))
    memset(gfx.RDRAM+rdp.cimg, 0, (rdp.ci_width*rdp.ci_height)<<rdp.ci_size>>1);
}

static void GetGammaTable()
{
  char strGetGammaTableExt[] = "grGetGammaTableExt";
  void (FX_CALL *grGetGammaTableExt)(FxU32, FxU32*, FxU32*, FxU32*) =
    (void (FX_CALL *)(FxU32, FxU32*, FxU32*, FxU32*))grGetProcAddress(strGetGammaTableExt);
  if (grGetGammaTableExt)
  {
    voodoo.gamma_table_r = new FxU32[voodoo.gamma_table_size];
    voodoo.gamma_table_g = new FxU32[voodoo.gamma_table_size];
    voodoo.gamma_table_b = new FxU32[voodoo.gamma_table_size];
    grGetGammaTableExt(voodoo.gamma_table_size, voodoo.gamma_table_r, voodoo.gamma_table_g, voodoo.gamma_table_b);
  }
}

}
wxUint32 curframe = 0;
void newSwapBuffers()
{
  if (!rdp.updatescreen)
    return;

  rdp.updatescreen = 0;

  LRDP("swapped\n");

  // Allow access to the whole screen
  if (fullscreen)
  {
    rdp.update |= UPDATE_SCISSOR | UPDATE_COMBINE | UPDATE_ZBUF_ENABLED | UPDATE_CULL_MODE;
    grClipWindow (0, 0, settings.scr_res_x, settings.scr_res_y);
    grDepthBufferFunction (GR_CMP_ALWAYS);
    grDepthMask (FXFALSE);
    grCullMode (GR_CULL_DISABLE);

    if ((settings.show_fps & 0xF) || settings.clock)
      set_message_combiner ();
#ifdef FPS
    float y = (float)settings.res_y;
    if (settings.show_fps & 0x0F)
    {
      if (settings.show_fps & 4)
      {
        if (region)   // PAL
          output (0, y, 0, "%d%% ", (int)pal_percent);
        else
          output (0, y, 0, "%d%% ", (int)ntsc_percent);
        y -= 16;
      }
      if (settings.show_fps & 2)
      {
        output (0, y, 0, "VI/s: %.02f ", vi);
        y -= 16;
      }
      if (settings.show_fps & 1)
        output (0, y, 0, "FPS: %.02f ", fps);
    }
#endif

    if (settings.clock)
    {
      if (settings.clock_24_hr)
      {
          time_t ltime;
          time (&ltime);
          tm *cur_time = localtime (&ltime);

          sprintf (out_buf, "%.2d:%.2d:%.2d", cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec);
      }
      else
      {
          char ampm[] = "AM";
          time_t ltime;

          time (&ltime);
          tm *cur_time = localtime (&ltime);

          if (cur_time->tm_hour >= 12)
          {
            strcpy (ampm, "PM");
            if (cur_time->tm_hour != 12)
              cur_time->tm_hour -= 12;
          }
          if (cur_time->tm_hour == 0)
            cur_time->tm_hour = 12;

          if (cur_time->tm_hour >= 10)
            sprintf (out_buf, "%.5s %s", asctime(cur_time) + 11, ampm);
          else
            sprintf (out_buf, " %.4s %s", asctime(cur_time) + 12, ampm);
        }
        output ((float)(settings.res_x - 68), y, 0, out_buf, 0);
      }
    //hotkeys
    if (CheckKeyPressed(G64_VK_BACK, 0x0001))
    {
      hotkey_info.hk_filtering = 100;
      if (settings.filtering < 2)
        settings.filtering++;
      else
        settings.filtering = 0;
    }
    if ((abs((int)(frame_count - curframe)) > 3 ) && CheckKeyPressed(G64_VK_ALT, 0x8000))  //alt +
    {
      if (CheckKeyPressed(G64_VK_B, 0x8000))  //b
      {
        hotkey_info.hk_motionblur = 100;
        hotkey_info.hk_ref = 0;
        curframe = frame_count;
        settings.frame_buffer ^= fb_motionblur;
      }
      else if (CheckKeyPressed(G64_VK_V, 0x8000))  //v
      {
        hotkey_info.hk_ref = 100;
        hotkey_info.hk_motionblur = 0;
        curframe = frame_count;
        settings.frame_buffer ^= fb_ref;
      }
    }
    if (settings.buff_clear && (hotkey_info.hk_ref || hotkey_info.hk_motionblur || hotkey_info.hk_filtering))
    {
      set_message_combiner ();
      char buf[256];
      buf[0] = 0;
      char * message = 0;
      if (hotkey_info.hk_ref)
      {
        if (settings.frame_buffer & fb_ref)
          message = strcat(buf, "FB READ ALWAYS: ON");
        else
          message = strcat(buf, "FB READ ALWAYS: OFF");
        hotkey_info.hk_ref--;
      }
      if (hotkey_info.hk_motionblur)
      {
        if (settings.frame_buffer & fb_motionblur)
          message = strcat(buf, "  MOTION BLUR: ON");
        else
          message = strcat(buf, "  MOTION BLUR: OFF");
        hotkey_info.hk_motionblur--;
      }
      if (hotkey_info.hk_filtering)
      {
        switch (settings.filtering)
        {
        case 0:
          message = strcat(buf, "  FILTERING MODE: AUTOMATIC");
          break;
        case 1:
          message = strcat(buf, "  FILTERING MODE: FORCE BILINEAR");
          break;
        case 2:
          message = strcat(buf, "  FILTERING MODE: FORCE POINT-SAMPLED");
          break;
        }
        hotkey_info.hk_filtering--;
      }
      output (120.0f, (float)settings.res_y, 0, message, 0);
    }
  }
    #ifdef OLDAPI
  if (capture_screen)
  {
    //char path[256];
    // Make the directory if it doesn't exist
    if (!wxDirExists(capture_path))
      wxMkdir(capture_path);
    wxString path;
    wxString romName = rdp.RomName;
    romName.Replace(wxT(" "), wxT("_"), true);
    romName.Replace(wxT(":"), wxT(";"), true);

    for (int i=1; ; i++)
    {
      path = capture_path;
      path += wxT("Glide64mk2_");
      path += romName;
      path += wxT("_");
      if (i < 10)
        path += wxT("0");
      path << i << wxT(".") << ScreenShotFormats[settings.ssformat].extension;
      if (!wxFileName::FileExists(path))
        break;
    }

    const wxUint32 offset_x = (wxUint32)rdp.offset_x;
    const wxUint32 offset_y = (wxUint32)rdp.offset_y;
    const wxUint32 image_width = settings.scr_res_x - offset_x*2;
    const wxUint32 image_height = settings.scr_res_y - offset_y*2;

    GrLfbInfo_t info;
    info.size = sizeof(GrLfbInfo_t);
    if (grLfbLock (GR_LFB_READ_ONLY,
      GR_BUFFER_BACKBUFFER,
      GR_LFBWRITEMODE_565,
      GR_ORIGIN_UPPER_LEFT,
      FXFALSE,
      &info))
    {
      wxUint8 *ssimg = (wxUint8*)malloc(image_width * image_height * 3); // will be free in wxImage destructor
      int sspos = 0;
      wxUint32 offset_src = info.strideInBytes * offset_y;

      // Copy the screen
      if (info.writeMode == GR_LFBWRITEMODE_8888)
      {
        wxUint32 col;
        for (wxUint32 y = 0; y < image_height; y++)
        {
          wxUint32 *ptr = (wxUint32*)((wxUint8*)info.lfbPtr + offset_src);
          ptr += offset_x;
          for (wxUint32 x = 0; x < image_width; x++)
          {
            col = *(ptr++);
            ssimg[sspos++] = (wxUint8)((col >> 16) & 0xFF);
            ssimg[sspos++] = (wxUint8)((col >> 8) & 0xFF);
            ssimg[sspos++] = (wxUint8)(col & 0xFF);
          }
          offset_src += info.strideInBytes;
        }
      }
      else
      {
        wxUint16 col;
        for (wxUint32 y = 0; y < image_height; y++)
        {
          wxUint16 *ptr = (wxUint16*)((wxUint8*)info.lfbPtr + offset_src);
          ptr += offset_x;
          for (wxUint32 x = 0; x < image_width; x++)
          {
            col = *(ptr++);
            ssimg[sspos++] = (wxUint8)((float)(col >> 11) / 31.0f * 255.0f);
            ssimg[sspos++] = (wxUint8)((float)((col >> 5) & 0x3F) / 63.0f * 255.0f);
            ssimg[sspos++] = (wxUint8)((float)(col & 0x1F) / 31.0f * 255.0f);
          }
          offset_src += info.strideInBytes;
        }
      }
      // Unlock the backbuffer
      grLfbUnlock (GR_LFB_READ_ONLY, GR_BUFFER_BACKBUFFER);
      wxImage screenshot(image_width, image_height, ssimg);
      screenshot.SaveFile(path, ScreenShotFormats[settings.ssformat].type);
      capture_screen = 0;
    }
  }
    #endif

  // Capture the screen if debug capture is set
  if (_debugger.capture)
  {
    // Allocate the screen
    _debugger.screen = new wxUint8 [(settings.res_x*settings.res_y) << 1];

    // Lock the backbuffer (already rendered)
    GrLfbInfo_t info;
    info.size = sizeof(GrLfbInfo_t);
    while (!grLfbLock (GR_LFB_READ_ONLY,
      GR_BUFFER_BACKBUFFER,
      GR_LFBWRITEMODE_565,
      GR_ORIGIN_UPPER_LEFT,
      FXFALSE,
      &info));

    wxUint32 offset_src=0, offset_dst=0;

    // Copy the screen
    for (wxUint32 y=0; y<settings.res_y; y++)
    {
      if (info.writeMode == GR_LFBWRITEMODE_8888)
      {
        wxUint32 *src = (wxUint32*)((wxUint8*)info.lfbPtr + offset_src);
        wxUint16 *dst = (wxUint16*)(_debugger.screen + offset_dst);
        wxUint8 r, g, b;
        wxUint32 col;
        for (unsigned int x = 0; x < settings.res_x; x++)
        {
          col = src[x];
          r = (wxUint8)((col >> 19) & 0x1F);
          g = (wxUint8)((col >> 10) & 0x3F);
          b = (wxUint8)((col >> 3)  & 0x1F);
          dst[x] = (r<<11)|(g<<5)|b;
        }
      }
      else
      {
        memcpy (_debugger.screen + offset_dst, (wxUint8*)info.lfbPtr + offset_src, settings.res_x << 1);
      }
      offset_dst += settings.res_x << 1;
      offset_src += info.strideInBytes;
    }

    // Unlock the backbuffer
    grLfbUnlock (GR_LFB_READ_ONLY, GR_BUFFER_BACKBUFFER);
  }

  if (fullscreen && debugging)
  {
    debug_keys ();
    debug_cacheviewer ();
    debug_mouse ();
  }

  if (settings.frame_buffer & fb_read_back_to_screen)
    DrawWholeFrameBufferToScreen();

  if (fullscreen)
  {
    if (fb_hwfbe_enabled && !(settings.hacks&hack_RE2) && !evoodoo)
      grAuxBufferExt( GR_BUFFER_AUXBUFFER );
    grBufferSwap (settings.vsync);
    fps_count ++;
    if (*gfx.VI_STATUS_REG&0x08) //gamma correction is used
    {
      if (!voodoo.gamma_correction)
      {
        if (voodoo.gamma_table_size && !voodoo.gamma_table_r)
          GetGammaTable(); //save initial gamma tables
        guGammaCorrectionRGB(2.0f, 2.0f, 2.0f); //with gamma=2.0 gamma table is the same, as in N64
        voodoo.gamma_correction = 1;
      }
    }
    else
    {
      if (voodoo.gamma_correction)
      {
        if (voodoo.gamma_table_r)
          grLoadGammaTable(voodoo.gamma_table_size, voodoo.gamma_table_r, voodoo.gamma_table_g, voodoo.gamma_table_b);
        else
          guGammaCorrectionRGB(1.3f, 1.3f, 1.3f); //1.3f is default 3dfx gamma for everything but desktop
        voodoo.gamma_correction = 0;
      }
    }
  }

  if (_debugger.capture)
    debug_capture ();

  if (fullscreen)
  {
    if  (debugging || settings.wireframe || settings.buff_clear || (settings.hacks&hack_PPL && settings.ucode == 6))
    {
      if (settings.hacks&hack_RE2 && fb_depth_render_enabled)
        grDepthMask (FXFALSE);
      else
        grDepthMask (FXTRUE);
      grBufferClear (0, 0, 0xFFFF);
    }
    /* //let the game to clear the buffers
    else
    {
    grDepthMask (FXTRUE);
    grColorMask (FXFALSE, FXFALSE);
    grBufferClear (0, 0, 0xFFFF);
    grColorMask (FXTRUE, FXTRUE);
    }
    */
  }

  if (settings.frame_buffer & fb_read_back_to_screen2)
    DrawWholeFrameBufferToScreen();

  frame_count ++;

  // Open/close debugger?
  if (CheckKeyPressed(G64_VK_SCROLL, 0x0001))
  {
    if (!debugging)
    {
      //if (settings.scr_res_x == 1024 && settings.scr_res_y == 768)
      {
        debugging = 1;

        // Recalculate screen size, don't resize screen
        settings.res_x = (wxUint32)(settings.scr_res_x * 0.625f);
        settings.res_y = (wxUint32)(settings.scr_res_y * 0.625f);

        ChangeSize ();
      }
    } 
    else
    {
      debugging = 0;

      settings.res_x = settings.scr_res_x;
      settings.res_y = settings.scr_res_y;

      ChangeSize ();
    }
  }

  // Debug capture?
  if (/*fullscreen && */debugging && CheckKeyPressed(G64_VK_INSERT, 0x0001))
  {
    _debugger.capture = 1;
  }
}

extern "C"
{

/******************************************************************
Function: ViStatusChanged
Purpose:  This function is called to notify the dll that the
ViStatus registers value has been changed.
input:    none
output:   none
*******************************************************************/
EXPORT void CALL ViStatusChanged (void)
{
}

/******************************************************************
Function: ViWidthChanged
Purpose:  This function is called to notify the dll that the
ViWidth registers value has been changed.
input:    none
output:   none
*******************************************************************/
EXPORT void CALL ViWidthChanged (void)
{
}

#ifdef WINPROC_OVERRIDE
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
  case WM_ACTIVATEAPP:
    if (wParam == TRUE && !fullscreen) rdp.window_changed = TRUE;
    break;
  case WM_PAINT:
    if (!fullscreen) rdp.window_changed = TRUE;
    break;

    /*    case WM_DESTROY:
    SetWindowLong (gfx.hWnd, GWL_WNDPROC, (long)oldWndProc);
    break;*/
  }

  return CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
}
#endif

}

int CheckKeyPressed(int key, int mask)
{
static Glide64Keys g64Keys;
  if (settings.use_hotkeys == 0)
    return 0;
#ifdef __WINDOWS__
  return (GetAsyncKeyState(g64Keys[key]) & mask);
#else
  if (grKeyPressed)
    return grKeyPressed(g64Keys[key]);
#endif
  return 0;
}


#ifdef ALTTAB_FIX
int k_ctl=0, k_alt=0, k_del=0;

LRESULT CALLBACK LowLevelKeyboardProc(int nCode,
                                      WPARAM wParam, LPARAM lParam)
{
  if (!fullscreen) return CallNextHookEx(NULL, nCode, wParam, lParam);

  int TabKey = FALSE;

  PKBDLLHOOKSTRUCT p;

  if (nCode == HC_ACTION)
  {
    switch (wParam) {
case WM_KEYUP:    case WM_SYSKEYUP:
  p = (PKBDLLHOOKSTRUCT) lParam;
  if (p->vkCode == 162) k_ctl = 0;
  if (p->vkCode == 164) k_alt = 0;
  if (p->vkCode == 46) k_del = 0;
  goto do_it;

case WM_KEYDOWN:  case WM_SYSKEYDOWN:
  p = (PKBDLLHOOKSTRUCT) lParam;
  if (p->vkCode == 162) k_ctl = 1;
  if (p->vkCode == 164) k_alt = 1;
  if (p->vkCode == 46) k_del = 1;
  goto do_it;

do_it:
  TabKey =
    ((p->vkCode == VK_TAB) && ((p->flags & LLKHF_ALTDOWN) != 0)) ||
    ((p->vkCode == VK_ESCAPE) && ((p->flags & LLKHF_ALTDOWN) != 0)) ||
    ((p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0)) ||
    (k_ctl && k_alt && k_del);

  break;
    }
  }

  if (TabKey)
  {
    k_ctl = 0;
    k_alt = 0;
    k_del = 0;
    ReleaseGfx ();
  }

  return CallNextHookEx(NULL, nCode, wParam, lParam);
}
#endif

//
// DllMain - called when the DLL is loaded, use this to get the DLL's instance
//
#ifdef OLDAPI
class wxDLLApp : public wxApp
{
public:
  virtual bool OnInit();
};

IMPLEMENT_APP_NO_MAIN(wxDLLApp)

bool wxDLLApp::OnInit()
{
  if (mutexProcessDList == NULL)
    mutexProcessDList = new wxMutex(wxMUTEX_DEFAULT);
  wxImage::AddHandler(new wxPNGHandler);
  wxImage::AddHandler(new wxJPEGHandler);
  return true;
}

#ifndef __WINDOWS__
int __attribute__ ((constructor)) DllLoad(void);
int __attribute__ ((destructor)) DllUnload(void);
#endif

// Called when the library is loaded and before dlopen() returns
int DllLoad(void)
{
    int argc = 0;
    char **argv = NULL;
    wxEntryStart(argc, argv);
    if (wxTheApp)
      return wxTheApp->CallOnInit() ? TRUE : FALSE;
    return 0;
}

// Called when the library is unloaded and before dlclose() returns
int DllUnload(void)
{
    if ( wxTheApp )
      wxTheApp->OnExit();
    wxEntryCleanup();
    return TRUE;
}

#ifdef __WINDOWS__
extern "C" int WINAPI DllMain (HINSTANCE hinstDLL,
                     wxUint32 fdwReason,
                     LPVOID lpReserved)
{
  sprintf (out_buf, "DllMain (%08lx - %d)\n", hinstDLL, fdwReason);
  LOG (out_buf);

  if (fdwReason == DLL_PROCESS_ATTACH)
  {
    wxSetInstance(hinstDLL);
    return DllLoad();
  }
  else if (fdwReason == DLL_PROCESS_DETACH)
  {
    if (GFXWindow != NULL)
      GFXWindow->SetHWND(NULL);
    return DllUnload();
  }
  return TRUE;
}

void CALL ReadScreen(void **dest, int *width, int *height)
{
  *width = settings.res_x;
  *height = settings.res_y;
  wxUint8 * buff = (wxUint8*)malloc(settings.res_x * settings.res_y * 3);
  wxUint8 * line = buff;
  *dest = (void*)buff;

  if (!fullscreen)
  {
    for (wxUint32 y=0; y<settings.res_y; y++)
    {
      for (wxUint32 x=0; x<settings.res_x; x++)
      {
        line[x*3] = 0x20;
        line[x*3+1] = 0x7f;
        line[x*3+2] = 0x40;
      }
    }
    LOG ("ReadScreen. not in the fullscreen!\n");
    return;
  }

  GrLfbInfo_t info;
  info.size = sizeof(GrLfbInfo_t);
  if (grLfbLock (GR_LFB_READ_ONLY,
    GR_BUFFER_FRONTBUFFER,
    GR_LFBWRITEMODE_565,
    GR_ORIGIN_UPPER_LEFT,
    FXFALSE,
    &info))
  {
    wxUint32 offset_src=info.strideInBytes*(settings.scr_res_y-1);

    // Copy the screen
    wxUint8 r, g, b;
    if (info.writeMode == GR_LFBWRITEMODE_8888)
    {
      wxUint32 col;
      for (wxUint32 y=0; y<settings.res_y; y++)
      {
        wxUint32 *ptr = (wxUint32*)((wxUint8*)info.lfbPtr + offset_src);
        for (wxUint32 x=0; x<settings.res_x; x++)
        {
          col = *(ptr++);
          r = (wxUint8)((col >> 16) & 0xFF);
          g = (wxUint8)((col >> 8) & 0xFF);
          b = (wxUint8)(col & 0xFF);
          line[x*3] = b;
          line[x*3+1] = g;
          line[x*3+2] = r;
        }
        line += settings.res_x * 3;
        offset_src -= info.strideInBytes;
      }
    }
    else
    {
      wxUint16 col;
      for (wxUint32 y=0; y<settings.res_y; y++)
      {
        wxUint16 *ptr = (wxUint16*)((wxUint8*)info.lfbPtr + offset_src);
        for (wxUint32 x=0; x<settings.res_x; x++)
        {
          col = *(ptr++);
          r = (wxUint8)((float)(col >> 11) / 31.0f * 255.0f);
          g = (wxUint8)((float)((col >> 5) & 0x3F) / 63.0f * 255.0f);
          b = (wxUint8)((float)(col & 0x1F) / 31.0f * 255.0f);
          line[x*3] = b;
          line[x*3+1] = g;
          line[x*3+2] = r;
        }
        line += settings.res_x * 3;
        offset_src -= info.strideInBytes;
      }
    }
    // Unlock the frontbuffer
    grLfbUnlock (GR_LFB_READ_ONLY, GR_BUFFER_FRONTBUFFER);
  }
  LOG ("ReadScreen. Success.\n");
}
#endif
#endif
