/*  Copyright 2008 Theo Berkau 
    This file is part of Yabause.

    Yabause 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
    (at your option) any later version.

    Yabause 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 Yabause; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <wiiuse/wpad.h>
#include <ogcsys.h>
#include <gccore.h>
#include <fat.h>
#include <sdcard/wiisd_io.h>
//#include <di/di.h>
#include "../cs0.h"
#include "../m68kcore.h"
#include "../peripheral.h"
#include "../vidsoft.h"
#include "../vdp2.h"
#include "../yui.h"
#include "../sndsdl.h"
#include "perwii.h"
#include "sndwii.h"
#include "menu.h"

#include "../yui.h"

/* Wii includes */
#include "iowii.h"
//#include "syswii.h"

int YuiExec(void);

void Check_Bios_Exist(void);
void Check_SD_Init(void);
void Check_Read_Dir(void);
void scroll_filelist(s8 delta);
void print_filelist(void);
void scroll_cartlist(s8 delta);
void print_cartlist(void);

/* Constants */

static u32 *xfb[2] = { NULL, NULL };
int fbsel = 0;
static GXRModeObj *rmode = NULL;
volatile int done=0;
volatile int resetemu=0;
int running=1;
static void *_console_buffer = NULL;
void __console_init_ex(void *conbuffer,int tgt_xstart,int tgt_ystart,int tgt_stride,int con_xres,int con_yres,int con_stride);

#ifdef USE_WIIGX
#define DEFAULT_FIFO_SIZE       (256*1024)
GXColor background = {0, 0, 0, 0xff};
GXTexObj texObj;
Mtx GXmodelView2D, view;
Mtx44 perspective;
u32 *wiidispbuffer=NULL;
#endif

SH2Interface_struct *SH2CoreList[] = {
&SH2Interpreter,
&SH2DebugInterpreter,
NULL
};

PerInterface_struct *PERCoreList[] = {
&PERDummy,
&PERWiiKeyboard,
&PERWiiClassic,
NULL
};

CDInterface *CDCoreList[] = {
&DummyCD,
&ISOCD,
NULL
};

SoundInterface_struct *SNDCoreList[] = {
&SNDDummy,
#ifdef HAVE_LIBSDL
&SNDSDL,
#else
&SNDDummy,
#endif
&SNDWII,
NULL
};

VideoInterface_struct *VIDCoreList[] = {
&VIDDummy,
#ifdef HAVE_LIBGL //OGL driver on wii does not work
&VIDOGL,
#endif
&VIDSoft,
NULL
};

M68K_struct *M68KCoreList[] = {
&M68KDummy,
&M68KC68K,
#ifdef HAVE_Q68
&M68KQ68,
#endif
NULL
};


char roms_dir[512]="sd:/SSROMS"; //default
static char buppath[512];
static char biospath[512];
static char cartpath[512];
static char bupfilename[512]="/bkram.bin";
static char biosfilename[512]="/bios.bin";
static char isofilename[512]="/game.cue";
static char cartfilename[11][512]={
 "",
 "/actionreplay.bin",
 "/bkram4M.bin",
 "/bkram8M.bin",
 "/bkram16M.bin",
 "/bkram32M.bin",
 "",
 "",
 "",
 "/ROM16M.bin",
 ""
};
static char menuitemwithoutbios[512]="Without Bios (Now With)";
static char menuitemwithbios[512]="With Bios (Now Without)";
static char menuitemoffframeskip[512]="Off Frameskip (Now On)";
static char menuitemonframeskip[512]="On Frameskip (Now Off)";

extern int vdp2width, vdp2height;
int wii_width, wii_height;
extern int wii_ir_x;
extern int wii_ir_y;
extern int wii_ir_valid;

#define FILES_PER_PAGE	10

struct file *filelist = NULL;
u32 nb_files = 0;
#define CART_TYPE_NUM 11

static s32 selected = 0, start = 0;
static s32 selectedcart = 7, startcart = 0;
static int submenuflag = 0;
static int bioswith = 0;
static int frameskipoff = 0;

void gotoxy(int x, int y);
void OnScreenDebugMessage(char *string, ...);
void DoMenu();
int GameExec();
int LoadCue();
int Settings();
int CartSet();
int CartSetExec();
int BiosWith();
int BiosOnlySet();
int FrameskipOff();
int SoundDriver();
int VideoDriver();
int M68KDriver();
int SoundDriverSetExec();
int VideoDriverSetExec();
int M68KDriverSetExec();
int About();

int menuselect=1;
int setmenuselect=0;
int sounddriverselect=2;
int videodriverselect=2;
int m68kdriverselect=1;
int aboutmenuselect=3;
int numsetmenu;
int numcartmenu;
int numsounddriver;
int numvideodriver;
int numm68kdriver;
int numaboutmenu;

static BOOL flag_mount = FALSE;

void fat_remount()
{
   if(flag_mount)
   {
      fatUnmount("fat:/");
      flag_mount = FALSE;
   }
   fatInitDefault();
   flag_mount = TRUE;
}

void reset()
{
   resetemu=1;
}

void powerdown()
{
   done = 1;
}

#if 0
int DVDStopMotor()
{
   static char dvdstr[] ATTRIBUTE_ALIGN(32) = "/dev/di";
   s32 fd;
   u8 buf[0x20];
   u8 outbuf[0x20];

   if ((fd = IOS_Open(dvdstr,0)) < 0)
      return 0;
	
   ((u32 *)buf)[0x00] = 0xE3000000;
   ((u32 *)buf)[0x01] = 0;
   ((u32 *)buf)[0x02] = 0;

   IOS_Ioctl(fd, buf[0], buf, 0x20, outbuf, 0x20);

   IOS_Close(fd);

   return 1;
}
#endif

#ifdef USE_WIIGX
// from  Snes9x GX. thanks for Tantric.
void convertBufferToRGBA8(u32* rgbaBuffer, u16 bufferWidth, u16 bufferHeight) {
        u16 block, i, c, ar, gb;
        u32 bufferSize = (bufferWidth * bufferHeight) << 2;
        memset(wiidispbuffer, 0x00, bufferSize);

        u8 *src = (u8 *)rgbaBuffer;
        u8 *dst = (u8 *)wiidispbuffer;

        for(block = 0; block < bufferHeight; block += 4) {
                for(i = 0; i < bufferWidth; i += 4) {
                        for (c = 0; c < 4; c++) {
                                for (ar = 0; ar < 4; ar++) {
                                        *dst++ = src[(((i + ar) + ((block + c) * bufferWidth)) * 4) + 3];
                                        *dst++ = src[((i + ar) + ((block + c) * bufferWidth)) * 4];
                                }
                        }
                        for (c = 0; c < 4; c++) {
                                for (gb = 0; gb < 4; gb++) {
                                        *dst++ = src[(((i + gb) + ((block + c) * bufferWidth)) * 4) + 1];
                                        *dst++ = src[(((i + gb) + ((block + c) * bufferWidth)) * 4) + 2];
                                }
                        }
                }
        }
        DCFlushRange(wiidispbuffer, bufferSize);

}

void InitGX(void )
{
    // Initialize wii output buffer
    if ((wiidispbuffer = (u32 *)memalign(32, 704 * 512 * 2)) == NULL)
       exit(-1);

    // Setup the fifo
    void *gp_fifo = NULL;
    if ((gp_fifo = memalign(32,DEFAULT_FIFO_SIZE)) == NULL)
       exit(-1);
    memset(gp_fifo,0,DEFAULT_FIFO_SIZE);

    GX_Init(gp_fifo,DEFAULT_FIFO_SIZE);

    GX_SetCopyClear((GXColor){ 0, 0, 0, 0xff }, GX_MAX_Z24);


    GX_SetDispCopyGamma(GX_GM_1_0);

    GX_ClearVtxDesc();     
    GX_InvVtxCache();       
    GX_InvalidateTexAll(); 

    GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE);
    GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT);
    GX_SetVtxDesc(GX_VA_POS,  GX_DIRECT);
    GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);

    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS,  GX_POS_XYZ,  GX_F32, 0);
    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST,   GX_F32, 0);
    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
    GX_SetZMode(GX_FALSE, GX_LEQUAL, GX_TRUE);
    GX_SetNumChans(1);    
    GX_SetNumTexGens(1);  
    GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
    GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
    guMtxIdentity(GXmodelView2D);
    guMtxTransApply(GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -100.0F);
    GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0);

    guOrtho(perspective, 0, rmode->efbHeight, 0, rmode->fbWidth, 0, 1000.0f);
    GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC);

    GX_SetViewport(0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1);
    GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
    GX_SetAlphaUpdate(GX_TRUE);
    GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0);
    GX_SetColorUpdate(GX_ENABLE);
    GX_SetCullMode(GX_CULL_NONE);
    GX_SetClipMode(GX_CLIP_ENABLE);

    f32 yscale = GX_GetYScaleFactor(rmode->efbHeight,rmode->xfbHeight);
    u32 xfbHeight = GX_SetDispCopyYScale(yscale);
    GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
    GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight);
    GX_SetDispCopyDst(rmode->fbWidth,xfbHeight);
    GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter);
    GX_SetFieldMode(rmode->field_rendering,((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));
    if (rmode->aa)
       GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
    else
       GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);

    GX_SetCullMode(GX_CULL_NONE);
    GX_SetDispCopyGamma(GX_GM_1_0);
}
#endif

int main(int argc, char **argv)
{
   int retry;
   WPAD_Init();
   PAD_Init();
   SYS_SetResetCallback(reset);
   SYS_SetPowerCallback(powerdown);
	
   usleep(500000);

   //fatInitDefault();

   //for stable mout
   retry = 3;
   while(retry)
   {
      if(fatMountSimple("sd", &__io_wiisd))
      {
        break;
      } else {
        usleep(500000);
      }
      retry--;
   } 
   
   retry = 3;
   while(retry)
   {
      if(fatMountSimple("usb", &__io_usbstorage))
      {
        break;
      } else {
        usleep(500000);
      }
      retry--;
   } 


   if (!diropen(roms_dir)) {
      strcpy(roms_dir, "usb:/SSROMS");
      if (!diropen(roms_dir)) strcpy(roms_dir, "sd:/SSROMS");
   } else {
     strcpy(roms_dir, "sd:/SSROMS");
   }

   DoMenu();

   exit(0);
   return 0;
}

int YuiExec()
{
   yabauseinit_struct yinit;
   int ret;
   FILE *fp;

   VIDEO_Init();

#ifndef USE_WIIGX

   switch(VIDEO_GetCurrentTvMode()) 
   {
      case VI_NTSC:
         rmode = &TVNtsc240Ds;
	 break;
      case VI_PAL:
         rmode = &TVPal264Ds;
 	 break;
      case VI_MPAL:
	 rmode = &TVMpal480IntDf;
	 break;
      default:
         rmode = &TVNtsc240Ds;
	 break;
   }
#endif



#ifndef USE_WIIGX
   // Allocate two buffers(may not be necessary)
   xfb[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
   xfb[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	
   VIDEO_Configure(rmode);
   VIDEO_ClearFrameBuffer (rmode, xfb[0], COLOR_BLACK);
   VIDEO_ClearFrameBuffer (rmode, xfb[1], COLOR_BLACK);
   VIDEO_SetNextFramebuffer(xfb[0]);
   VIDEO_SetBlack(FALSE);
   VIDEO_Flush();
   VIDEO_WaitVSync();
   if(rmode->viTVMode&VI_NON_INTERLACE) 
      VIDEO_WaitVSync();

#endif


#ifdef USE_WIIGX
   wii_width  = rmode->fbWidth;
#else
   wii_width  = rmode->fbWidth / 2;
#endif
   wii_height = rmode->xfbHeight;

   WPAD_SetDataFormat(WPAD_CHAN_ALL,WPAD_FMT_BTNS_ACC_IR);
   WPAD_SetVRes(WPAD_CHAN_ALL, wii_width, wii_height);

   memset(&yinit, 0, sizeof(yabauseinit_struct));
   yinit.percoretype = PERCORE_WIICLASSIC;
   yinit.sh2coretype = SH2CORE_INTERPRETER;
   yinit.vidcoretype = videodriverselect;
   yinit.sndcoretype = sounddriverselect;
   yinit.cdcoretype = CDCORE_ISO;
   yinit.m68kcoretype = m68kdriverselect;
   yinit.carttype = selectedcart;
   yinit.regionid = REGION_AUTODETECT;
   strcpy(biospath, roms_dir);
   strcat(biospath, biosfilename);
   if (!bioswith || ((fp=fopen(biospath, "rb")) == NULL)) {
      yinit.biospath = NULL;
   } else {
      yinit.biospath = biospath;
      fclose(fp);
   }
   yinit.cdpath = isofilename;
   strcpy(buppath, roms_dir);
   strcat(buppath, bupfilename);
   yinit.buppath = buppath;
   yinit.mpegpath = NULL;
   strcpy(cartpath, roms_dir);
   strcat(cartpath, cartfilename[selectedcart]);
   yinit.cartpath = cartpath;
   yinit.netlinksetting = NULL;
   yinit.videoformattype = VIDEOFORMATTYPE_NTSC;

   // Hijack the fps display
   VIDSoft.OnScreenDebugMessage = OnScreenDebugMessage;


   if ((ret = YabauseInit(&yinit)) == 0)
   {
      if(frameskipoff)
         DisableAutoFrameSkip();
      else
         EnableAutoFrameSkip();

      ScspSetFrameAccurate(0);

      VIDEO_ClearFrameBuffer(rmode, xfb[fbsel], COLOR_BLACK);

      while(!done)
      {
         if (PERCore->HandleEvents() != 0)
            return -1;
         if (resetemu)
         {
            YabauseReset();
            resetemu = 0;
            SYS_SetResetCallback(reset);
         }
      }
      YabauseDeInit();
      done=0;
      resetemu=0;
   }
   else
   {
      while(!done)
         VIDEO_WaitVSync();
   }

   return 0;
}

void YuiErrorMsg(const char *string)
{
   if (strncmp(string, "Master SH2 invalid opcode", 25) == 0)
   {
      if (!running)
         return;
      running = 0;
      printf("%s\n", string);
   }
}

void YuiSwapBuffers()
{


#ifdef USE_WIIGX

   f32 stretch_width = (f32)wii_width/(f32)vdp2width;
   f32 stretch_height = (f32)wii_height/(f32)vdp2height;
   f32 ref_width = (f32)vdp2width/2.0,
       ref_height = (f32)vdp2height/2.0;

   //Simple pointer of Wii IR for the functions: save & load states,....
   if(wii_ir_valid)
   {
      int i, j;
      int disp_x = (int)((double)wii_ir_x/stretch_width);
      int disp_y = (int)((double)wii_ir_y/stretch_width);
      for(j=-8; j<8; j++)
      {
        for(i=-8; i<8; i++)
        {
           if(!(disp_x+i<0) && !(disp_x+i>=vdp2width) &&
              !(disp_y+j<0) && !(disp_y+j>=vdp2height))
           {
              dispbuffer[disp_x+i+(disp_y+j)*vdp2width]=
              ((j>-4) && (j<4) && (i>-4) && (i<4)) ?
              0x00000000:0xffffffff;
           }
        }
      }
   }

   convertBufferToRGBA8(dispbuffer,vdp2width,vdp2height);
   GX_InvalidateTexAll();
   GX_InitTexObj(&texObj, wiidispbuffer, vdp2width, vdp2height, GX_TF_RGBA8,GX_CLAMP, GX_CLAMP,GX_FALSE);
   GX_InitTexObjLOD(&texObj,GX_NEAR,GX_NEAR_MIP_NEAR,2.5f,9.0f,0.0f,0,0,GX_ANISO_1);
   GX_LoadTexObj(&texObj, GX_TEXMAP0);

   Mtx model, tmp1, tmp2, mv;
   guMtxIdentity(tmp1);
   guMtxScaleApply(tmp1, tmp1, stretch_width, stretch_height, 1.0);
   guVector axis = (guVector) {0, 0, 1};
   guMtxRotAxisDeg(tmp2, &axis, 0.0);
   guMtxConcat(tmp2, tmp1, model);
   guMtxTransApply(model, model, (f32)wii_width/2.0, (f32)wii_height/2.0, 0.0f);
   guMtxConcat(GXmodelView2D, model, mv);
   GX_LoadPosMtxImm(mv, GX_PNMTX0);
     GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
        GX_Position3f32(-ref_width, -ref_height, 0.0);
        GX_Color1u32(0xffffffff);
        GX_TexCoord2f32(0.0, 0.0);
        GX_Position3f32(ref_width, -ref_height, 0.0);
        GX_Color1u32(0xffffffff);
        GX_TexCoord2f32(1.0, 0.0);
        GX_Position3f32(ref_width, ref_height, 0.0);
        GX_Color1u32(0xffffffff);
        GX_TexCoord2f32(1.0, 1.0);
        GX_Position3f32(-ref_width, ref_height, 0.0);
        GX_Color1u32(0xffffffff);
        GX_TexCoord2f32(0.0, 1.0);
     GX_End();

   fbsel ^= 1;
   GX_DrawDone();
   GX_InvalidateTexAll();

   GX_CopyDisp(xfb[fbsel],GX_TRUE);
   GX_Flush();

#else
   int i, j;
   u32 *curfb;
   u32 *buf;
   int index;

   fbsel ^= 1;
   curfb = xfb[fbsel];
   buf = dispbuffer;

   for (j = 0; j < wii_height; j++)
   {
      for (i = 0; i < wii_width; i++)
      {
         u8 y, cb, cr;
         u8 r, g, b;

         index = ((int)((double)j*(double)vdp2height/(double)wii_height)*vdp2width) + (int)((double)i*(double)vdp2width/(double)wii_width);

         r = buf[index] >> 24;
         g = buf[index] >> 16;
         b = buf[index] >> 8;

         y  = ( 257 * r + 504 * g +  98 * b +  16000) / 1000;
         cb = (-148 * r - 291 * g + 439 * b + 128000) / 1000; //b-y
         cr = ( 439 * r - 368 * g -  71 * b + 128000) / 1000; //r-y

         curfb[0] = (y << 24) | (cb << 16) | (y << 8) | cr;
         curfb++;
      }
   }

#endif

   VIDEO_SetNextFramebuffer (xfb[fbsel]);
   VIDEO_Flush ();
}

void gotoxy(int x, int y)
{
   printf("\033[%d;%dH", y, x);
}

void OnScreenDebugMessage(char *string, ...)
{
#if 0
   va_list arglist;

   va_start(arglist, string);
   gotoxy(0, 1);
   vprintf(string, arglist);
   printf("\n");
   gotoxy(0, 1);
   va_end(arglist);
#endif
}

int ClearMenu(const unsigned long *bmp)
{
   fbsel ^= 1;
   memcpy (xfb[fbsel], bmp, MENU_SIZE * 2);
   VIDEO_SetNextFramebuffer (xfb[fbsel]);
   VIDEO_Flush ();
   return 0;   
}

typedef struct
{
   char *name;
   int (*func)();
} menuitem_struct;

int MenuNone()
{
   return 0;
}

menuitem_struct menuitem[] = {
{ "Start emulation", YuiExec },
{ "Load ISO/CUE", LoadCue },
{ "Settings", Settings},
{ "About", About},
{ "Exit", NULL },
{ NULL, NULL }
};

menuitem_struct setmenuitem[] = {
{ "Cartridge", CartSet},
{ "With Bios (Now Without)", BiosWith},
{ "Bios only", BiosOnlySet},
{ "Sound Driver", SoundDriver},
{ "Video Driver", VideoDriver},
{ "M68K Driver", M68KDriver},
{ "Off Frameskip (Now On)", FrameskipOff},
{ NULL, NULL }
};

menuitem_struct cartmenuitem[] = {
{ "None", CartSetExec},
{ "Action Relay", CartSetExec},
{ "Backup RAM  4MBIT", CartSetExec},
{ "Backup RAM  8MBIT", CartSetExec},
{ "Backup RAM 16MBIT", CartSetExec},
{ "Backup RAM 32MBIT", CartSetExec},
{ "DRAM  8MBIT", CartSetExec},
{ "DRAM 32MBIT", CartSetExec},
{ "NETLINK(fake)", MenuNone},
{ "ROM 16MBIT", CartSetExec},
{ "Japanese Modem", CartSetExec},
{ NULL, NULL }
};

menuitem_struct sounddriveritem[] = {
{ "None", SoundDriverSetExec},
#ifdef HAVE_LIBSDL
{ "SDL Driver", SoundDriverSetExec},
#else
{ "(dummy)", MenuNone},
#endif
{ "Wii DMA Driver", SoundDriverSetExec},
{ NULL, NULL }
};

menuitem_struct videodriveritem[] = {
{ "None", VideoDriverSetExec},
#ifdef HAVE_LIBGL
//{ "GX (based on OGL) Driver", VideoDriverSetExec},
{ "NO exist OGL Driver", VideoDriverSetExec},
#else
{ "(dummy)", MenuNone},
#endif
{ "Software Driver", VideoDriverSetExec},
{ NULL, NULL }
};

menuitem_struct m68kdriveritem[] = {
{ "(dummy)", MenuNone},
{ "C68K Driver", M68KDriverSetExec},
#ifdef HAVE_Q68
{ "Q68 Driver", M68KDriverSetExec},
#else
{ "(dummy)", MenuNone},
#endif
{ NULL, NULL }
};

menuitem_struct aboutmenuitem[] = {
{ "Yabause, a Sega Saturn Emulator", MenuNone},
{ VERSION, MenuNone},
{ "Original Yabause by Yabause Team", MenuNone},
{ "Return", NULL},
{ NULL, NULL }
};

void InitMenu()
{
   VIDEO_Init();
   switch(VIDEO_GetCurrentTvMode()) 
   {
      case VI_NTSC:
         rmode = &TVNtsc480IntDf;
	 break;
      case VI_PAL:
         rmode = &TVPal528IntDf;
 	 break;
      case VI_MPAL:
	 rmode = &TVMpal480IntDf;
	 break;
      default:
         rmode = VIDEO_GetPreferredMode(NULL);
	 break;
   }

   xfb[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
   xfb[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
   VIDEO_Configure(rmode);
   VIDEO_ClearFrameBuffer (rmode, xfb[0], COLOR_BLACK);
   VIDEO_ClearFrameBuffer (rmode, xfb[1], COLOR_BLACK);
   VIDEO_SetNextFramebuffer(xfb[0]);
   VIDEO_SetBlack(FALSE);
   VIDEO_Flush();
   VIDEO_WaitVSync();
   if(rmode->viTVMode&VI_NON_INTERLACE) 
      VIDEO_WaitVSync();
   if(_console_buffer != NULL) free(_console_buffer);
   _console_buffer = malloc(280*185*VI_DISPLAY_PIX_SZ);
   __console_init_ex(_console_buffer,180,193,rmode->fbWidth*VI_DISPLAY_PIX_SZ,280,185,280*VI_DISPLAY_PIX_SZ);
}

void DoMenu()
{
   int i;
   int nummenu=0;
   expansion_t exp;
   s8 claX, claY;
   s8 gcX, gcY;
   u32 buttonsDown;
   u32 gcbuttonsDown;
   u32 buttonsDown1;
   u32 gcbuttonsDown1;

   InitMenu();
#ifdef USE_WIIGX
   InitGX();
#endif

   if(bioswith)
      setmenuitem[1].name=menuitemwithoutbios;
   else
      setmenuitem[1].name=menuitemwithbios;

   for (i = 0; menuitem[i].name != NULL; i++)
      nummenu++;

   for (;;)
   {
      VIDEO_WaitVSync();
      WPAD_ScanPads();
      PAD_ScanPads();
      WPAD_Expansion(0, &exp);
      claX = classic_analog_val(&exp, true, false);
      claY = classic_analog_val(&exp, false, false);
      gcX = PAD_StickX(0);
      gcY = PAD_StickY(0);
      buttonsDown = WPAD_ButtonsHeld(0);
      gcbuttonsDown = PAD_ButtonsHeld(0);
      buttonsDown1 = WPAD_ButtonsDown(0);
      gcbuttonsDown1 = PAD_ButtonsDown(0);

      // Get Wii Remote/Keyboard/etc. presses
      if ((buttonsDown & WPAD_CLASSIC_BUTTON_UP) ||
          (buttonsDown & WPAD_BUTTON_RIGHT) ||
          (gcbuttonsDown & PAD_BUTTON_UP) ||
          stick_up(claY, gcY))
      {
         switch (submenuflag) {
           case 0: //main menu
             menuselect--;
             if (menuselect < 0)
               menuselect = nummenu-1;
             break;
           case 1: //file selection menu
             scroll_filelist(-1);
             break;
           case 2: //setting menu
             setmenuselect--;
             if (setmenuselect < 0)
               setmenuselect = numsetmenu-1;
             break;
           case 20: //setting cart menu
             scroll_cartlist(-1);
             break;
           case 23: //sound driver menu
             sounddriverselect--;
             if (sounddriverselect < 0)
               sounddriverselect = numsounddriver-1;
             break;
           case 24: //video driver menu
             videodriverselect--;
             if (videodriverselect < 0)
               videodriverselect = numvideodriver-1;
             break;
           case 25: //m68k driver menu
             m68kdriverselect--;
             if (m68kdriverselect < 0)
               m68kdriverselect = numm68kdriver-1;
             break;
         }
      }
      else if ((buttonsDown & WPAD_CLASSIC_BUTTON_DOWN) ||
               (buttonsDown & WPAD_BUTTON_LEFT) ||
               (gcbuttonsDown & PAD_BUTTON_DOWN) ||
               stick_down(claY, gcY))
      {          
         switch (submenuflag) {
           case 0: //main menu
             menuselect++;
             if (menuselect == nummenu)
               menuselect = 0;
             break;
           case 1: //file selection menu
             scroll_filelist(1);
             break;
           case 2: //setting menu
             setmenuselect++;
             if (setmenuselect == numsetmenu)
               setmenuselect = 0;
             break;
           case 20: //setting cart menu
             scroll_cartlist(1);
             break;
           case 23: //sound driver menu
             sounddriverselect++;
             if (sounddriverselect == numsounddriver)
               sounddriverselect = 0;
             break;
           case 24: //video driver menu
             videodriverselect++;
             if (videodriverselect == numvideodriver)
               videodriverselect = 0;
             break;
           case 25: //m68k driver menu
             m68kdriverselect++;
             if (m68kdriverselect == numm68kdriver)
               m68kdriverselect = 0;
             break;
         }
      }

      if ((buttonsDown1 & WPAD_CLASSIC_BUTTON_A) ||
          (buttonsDown1 & WPAD_BUTTON_A) ||
          (buttonsDown1 & WPAD_BUTTON_2) ||
          (gcbuttonsDown1 & PAD_BUTTON_A))
      {
          switch (submenuflag) {
             case 0: //main menu
               if (menuitem[menuselect].func)
               {
                 if (menuitem[menuselect].func())
                   return;
                 InitMenu();
               }
               else
                 return;
                 break;
             case 1: //file selection menu
                GameExec();
                break;
             case 2: //setting menu
               if (setmenuitem[setmenuselect].func)
               {
                 if (setmenuitem[setmenuselect].func())
                   return;
                 InitMenu();
               }
               else
                 return;
                 break;
             case 20: //setting cart menu
               if (cartmenuitem[selectedcart].func)
               {
                 if (cartmenuitem[selectedcart].func())
                   return;
                 InitMenu();
               }
               else
                 return;
                 break;
             case 23: //sound driver menu
               if (sounddriveritem[sounddriverselect].func)
               {
                 if (sounddriveritem[sounddriverselect].func())
                   return;
                 InitMenu();
               }
               else
                 return;
                 break;
             case 24: //video driver menu
               if (videodriveritem[videodriverselect].func)
               {
                 if (videodriveritem[videodriverselect].func())
                   return;
                 InitMenu();
               }
               else
                 return;
                 break;
             case 25: //m68k driver menu
               if (m68kdriveritem[m68kdriverselect].func)
               {
                 if (m68kdriveritem[m68kdriverselect].func())
                   return;
                 InitMenu();
               }
               else
                 return;
                 break;
             case 3: //about menu
               if (aboutmenuitem[aboutmenuselect].func)
               {
                 if (aboutmenuitem[aboutmenuselect].func())
                   return;
                 InitMenu();
               }
               else
               {
                 submenuflag = 0;
                 menuselect = 1;
                 InitMenu();
                 //return;
               }
               break;
          }
      }

      // Draw menu
      ClearMenu(menu_bmp);

      printf("\033[2J");
      //printf("\033[2;0H");
      gotoxy(0, 0);
      switch (submenuflag) {
        case 0: //main menu
          // Draw menu items
          for (i = 0; i < nummenu; i++)
          {
            if (menuselect == i) {
              printf("\033[%d;%dm\033[35m", 30+9, 0);
              printf("%s\033[0m\n", menuitem[i].name);
            } else {
              printf("\033[%d;%dm", 30+7, 0);
              printf("%s\n", menuitem[i].name);
            }
          }
          break;
        case 1: //file selection menu
          print_filelist();
          break;
        case 2: //setting menu
          for (i = 0; i < numsetmenu; i++)
          {
            if (setmenuselect == i) {
              printf("\033[%d;%dm\033[35m", 30+9, 0);
              printf("%s\033[0m\n", setmenuitem[i].name);
            } else {
              printf("\033[%d;%dm", 30+7, 0);
              printf("%s\n", setmenuitem[i].name);
            }
          }
          break;
        case 20: //setting cart menu
          print_cartlist();
          break;
        case 23: //sound driver menu
          for (i = 0; i < numsounddriver; i++)
          {
            if (sounddriverselect == i) {
              printf("\033[%d;%dm\033[35m", 30+9, 0);
              printf("%s\033[0m\n", sounddriveritem[i].name);
            } else {
              printf("\033[%d;%dm", 30+7, 0);
              printf("%s\n", sounddriveritem[i].name);
            }
          }
          break;
        case 24: //video driver menu
          for (i = 0; i < numvideodriver; i++)
          {
            if (videodriverselect == i) {
              printf("\033[%d;%dm\033[35m", 30+9, 0);
              printf("%s\033[0m\n", videodriveritem[i].name);
            } else {
              printf("\033[%d;%dm", 30+7, 0);
              printf("%s\n", videodriveritem[i].name);
            }
          }
          break;
        case 25: //m68k driver menu
          for (i = 0; i < numm68kdriver; i++)
          {
            if (m68kdriverselect == i) {
              printf("\033[%d;%dm\033[35m", 30+9, 0);
              printf("%s\033[0m\n", m68kdriveritem[i].name);
            } else {
              printf("\033[%d;%dm", 30+7, 0);
              printf("%s\n", m68kdriveritem[i].name);
            }
          }
          break;
        case 3: //about menu
          for (i = 0; i < numaboutmenu; i++)
          {
            if (aboutmenuselect == i) {
              printf("\033[%d;%dm\033[35m", 30+9, 0);
              printf("%s\033[0m\n", aboutmenuitem[i].name);
            } else {
              printf("\033[%d;%dm", 30+7, 0);
              printf("%s\n", aboutmenuitem[i].name);
            }
          }
          break;
      }
      usleep(100000); //for too fast moving by stick
   }
}

int GameExec()
{
   submenuflag = 20;
   printf("\033[2;0H");
   strcpy(isofilename, roms_dir);
   strcat(isofilename, "/");
   strcat(isofilename, filelist[selected].filename);
   menuselect=0;
   return 0;
}
////////



void Check_SD_Init(void)
{
	s32 ret;
	
	//printf("\t[*] Initializing libfat...");
	fflush(stdout);
	ret = sd_init();
	if (!ret) {
		printf(" ERROR! Return to Wii Menu in 5s...");
		sleep(5);
		//Exit_ToWiiMenu();
		exit(0);
	}
	//printf(" OK!\n");
}

int compare(const void *x, const void *y) {
        return strcasecmp( (char *)(((struct file *)x)->filename), (char *)(((struct file *)y)->filename));
}


void Check_Read_Dir(void)
{
	s32 ret;
	
	//printf("\t[*] Reading SD card...");
	fflush(stdout);
	ret = sd_readdir(roms_dir, &filelist, &nb_files);
	if (ret < 0) {
		printf(" ERROR! (ret = %d) Return to Wii Menu in 5s...", ret);
		sleep(5);
		//Exit_ToWiiMenu();
		exit(0);
	}
	//printf(" OK!\n");
	qsort(filelist, nb_files, sizeof(struct file), compare);
}

void scroll_filelist(s8 delta)
{
	s32 index;

	/* No files */
	if (!nb_files)
		return;

	/* Select next entry */
	selected += delta;

	/* Out of the list? */
	if (selected <= -1)
		selected = nb_files - 1;
	if (selected >= nb_files)
		selected = 0;

	/* List scrolling */
	index = (selected - start);
	if (index >= FILES_PER_PAGE)
		start += (index - FILES_PER_PAGE) + 1;
	if (index <= -1)
		start += index;
}

void print_filelist(void)
{
	u32 cnt;

	//printf("\t[*] Available files on the SD card:\n\n");

	/* No files */
	if (!nb_files) {
		printf("No files available!\n");
		return;
	}

	for (cnt = start; cnt < nb_files; cnt++) {
		/* Files per page limit */
		if ((cnt - start) >= FILES_PER_PAGE)
			break;

		/* Selected file */
		if (cnt == selected) {
			//printf("\t\t>> ");
                      printf("\033[%d;%dm\033[35m", 30+9, 0);
		      printf("%s\033[0m\n", filelist[cnt].filename);
		} else {
			//printf("\t\t   ");
		      printf("\033[%d;%dm", 30+7, 0);
		      printf("%s\n", filelist[cnt].filename);
		}
		fflush(stdout);

	}
}

void scroll_cartlist(s8 delta)
{
        s32 index;

        /* Select next entry */
        selectedcart += delta;

        /* Out of the list? */
        if (selectedcart <= -1)
                selectedcart = CART_TYPE_NUM - 1;
        if (selectedcart >= CART_TYPE_NUM)
                selectedcart = 0;

        /* List scrolling */
        index = (selectedcart - startcart);
        if (index >= FILES_PER_PAGE)
                startcart += (index - FILES_PER_PAGE) + 1;
        if (index <= -1)
                startcart += index;
}

void print_cartlist(void)
{
        u32 cnt;

        for (cnt = startcart; cnt < CART_TYPE_NUM; cnt++) {
                /* Files per page limit */
                if ((cnt - startcart) >= FILES_PER_PAGE)
                        break;

                /* Selected file */
                if (cnt == selectedcart) {
                      printf("\033[%d;%dm\033[35m", 30+9, 0);
                      printf("%s\033[0m\n", cartmenuitem[cnt].name);
                } else {
                      printf("\033[%d;%dm", 30+7, 0);
                      printf("%s\n", cartmenuitem[cnt].name);
                }
                fflush(stdout);
        }
}

////////
int LoadCue()
{
   submenuflag = 1;
   printf("\033[2;0H");
   gotoxy(0, 0);
   Check_Read_Dir();

   return 0;
}


int Settings()
{
   int i;
   submenuflag = 2;
   printf("\033[2;0H");
   gotoxy(0, 0);
   numsetmenu=0;
   for (i = 0; setmenuitem[i].name != NULL; i++)
      numsetmenu++;

   return 0;
}

int CartSet()
{
   int i;
   submenuflag = 20;
   printf("\033[2;0H");
   gotoxy(0, 0);
   numcartmenu=0;
   for (i = 0; cartmenuitem[i].name != NULL; i++)
      numcartmenu++;

   return 0;
}

int CartSetExec()
{
   submenuflag = 0;
   menuselect=0;
   return 0;
}

int BiosWith()
{
   submenuflag = 0;
   menuselect=0;
   if(bioswith)
   {
     setmenuitem[1].name=menuitemwithbios;
     bioswith=0;
   }
   else
   {
     setmenuitem[1].name=menuitemwithoutbios;
     bioswith=1;
   }
   return 0;
}

int BiosOnlySet()
{
   submenuflag = 0;
   menuselect=0;
   isofilename[0]=0;
   setmenuitem[1].name=menuitemwithoutbios;
   bioswith=1;
   return 0;
}

int FrameskipOff()
{
   submenuflag = 0;
   menuselect=0;
   if(frameskipoff)
   {
     setmenuitem[3].name=menuitemoffframeskip;
     frameskipoff=0;
   }
   else
   {
     setmenuitem[3].name=menuitemonframeskip;
     frameskipoff=1;
   }
   return 0;
}

int SoundDriver()
{
   int i;
   submenuflag = 23;
   printf("\033[2;0H");
   gotoxy(0, 0);
   numsounddriver=0;
   for (i = 0; sounddriveritem[i].name != NULL; i++)
      numsounddriver++;

   return 0;
}

int VideoDriver()
{
   int i;
   submenuflag = 24;
   printf("\033[2;0H");
   gotoxy(0, 0);
   numvideodriver=0;
   for (i = 0; videodriveritem[i].name != NULL; i++)
      numvideodriver++;

   return 0;
}

int M68KDriver()
{
   int i;
   submenuflag = 25;
   printf("\033[2;0H");
   gotoxy(0, 0);
   numm68kdriver=0;
   for (i = 0; m68kdriveritem[i].name != NULL; i++)
      numm68kdriver++;

   return 0;
}

int SoundDriverSetExec()
{
   submenuflag = 0;
   menuselect=0;
#ifndef HAVE_LIBSDL
   if(sounddriverselect==1) sounddriverselect=0;
#endif
   return 0;
}

int VideoDriverSetExec()
{
   submenuflag = 0;
   menuselect=0;
   return 0;
}

int M68KDriverSetExec()
{
   submenuflag = 0;
   menuselect=0;
   return 0;
}

int About()
{
   int i;
   submenuflag = 3;
   printf("\033[2;0H");
   gotoxy(0, 0);
   numaboutmenu=0;
   for (i = 0; aboutmenuitem[i].name != NULL; i++)
      numaboutmenu++;

   return 0;
}

