/*
WiiHandy/SDL : Port of the Handy/SDL Emulator for the Wii

Copyright (C) 2009
raz0red (www.twitchasylum.com)

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any
damages arising from the use of this software.

Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:

1.	The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.

2.	Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.

3.	This notice may not be removed or altered from any source
distribution.
*/

#include <sdcard/wiisd_io.h>
#include <fat.h>
#include <wiiuse/wpad.h>
#include <ogc/gx_struct.h>
#include <gccore.h>
#include <dirent.h>

#include <stdio.h>
#include <malloc.h>

#include "system.h"
#include "mikie.h"
#include "../handy_sdl_main.h"
#include "pngu/pngu.h"

#include "wii_main.h"
#include "wii_util.h"
#include "wii_cart_header.h"
#include "wiierrorhandler.h"

#define ABOUT_Y 20
#define MENU_HEAD_X 3
#define MENU_HEAD_Y 9
#define MENU_FOOT_Y 27
#define MENU_START_Y 12
#define MENU_START_X 8
#define MENU_PAGE_SIZE 13
#define MENU_BUFF_SIZE 256
 
/*
 * The different types of nodes in the menu
 */
enum NODETYPE
{
    NODETYPE_ROOT,
    NODETYPE_SCALE,
    NODETYPE_LCD,
    NODETYPE_LOAD_ROM,
    NODETYPE_ROM,
    NODETYPE_ORIENT,
    NODETYPE_EXIT,
    NODETYPE_SPACER,
    NODETYPE_CONTROLS_ORIENT,
    NODETYPE_RESUME,
    NODETYPE_ADVANCED,
    NODETYPE_SNAPSHOT_MANAGEMENT,
    NODETYPE_DEBUG_MODE,
    NODETYPE_MAX_FRAME_RATE,
    NODETYPE_TOP_MENU_EXIT,
    NODETYPE_AUTO_LOAD_SNAPSHOT,
    NODETYPE_AUTO_SAVE_SNAPSHOT,
    NODETYPE_RESET,
    NODETYPE_SAVE_SNAPSHOT,
    NODETYPE_LOAD_SNAPSHOT,
    NODETYPE_DELETE_SNAPSHOT,
    NODETYPE_SNAPSHOT,
    NODETYPE_WRITE_HEADER
};

struct treenode;
typedef treenode* TREENODEPTR;

/*
 * Simple hierarchical menu structure
 */
typedef struct treenode
{
    char *name;
    NODETYPE node_type;
    TREENODEPTR *children;
    u16 child_count;
    u16 max_children;
} TREENODE;

// The bits per pixel (from Handy/SDL)
extern int bpp;
// The frame buffer (from SDL)
extern u32 *frame_buffer;
// The display mode (from SDL)
extern GXRModeObj *display_mode;	

// Buffer containing the about image
static u32* about_buff = NULL;
// Properties of the about image
static PNGUPROP about_props;
// The root of the menu
static TREENODE *menu_root;
// The stack containing the menus the user has navigated
static TREENODEPTR menu_stack[4];
// The head of the menu stack
static s8 menu_stack_head = -1;
// Two framebuffers (double buffered)
static u32 *xfb[2] = { NULL, NULL };
// The current frame buffer
static u32 cur_xfb = 0;
// Are we still running?
static BOOL quit = 0;
// The status message display count down
static u8 wii_status_message_count = 0;
// Have we read the games list yet?
static BOOL games_read = FALSE;
// Have we read the snapshot list yet?
static BOOL snapshots_read = FALSE;
// Is the menu to be redrawn?
static BOOL redraw_menu = 1;
// Whether to return to the top menu after exiting a game
static BOOL top_menu_exit = TRUE;
// The current menu selection
static s16 menu_cur_idx = -1;
// The first item to display in the menu (paging, etc.)
static s16 menu_start_idx = 0;
// What is the display size?
static u8 scale = 4;
// Auto load snapshot?
static BOOL auto_load_snapshot = TRUE;
// Auto save snapshot?
static BOOL auto_save_snapshot = FALSE;
// The last rom that was successly executed
static char *last_rom = NULL;
// The base application path
static char app_path[WII_MAX_PATH];
// Is the SC card mounted?
static BOOL sd_mounted = FALSE;


// How should we rotate the controls?
int wii_rotate_controls = MIKIE_BAD_MODE;
// Whether to display debug info (FPS, etc.)
short wii_debug = 0;
// The maximum frame rate
int wii_max_frame_rate = 60;
// The most recent status message
char wii_status_message[MENU_BUFF_SIZE] = "";
// Hardware buttons (reset, power, etc.)
u8 wii_hw_button = 0;

// Forward refs
static void wii_menu_render( TREENODE *menu );
static BOOL wii_write_config();
static void wii_menu_move( TREENODE *menu, s16 steps );
static void wii_free_node( TREENODE* node );

/*
 * Unmounts the SD card
 */
static void wii_unmount_sd()
{
    if( sd_mounted )
    {
        fatUnmount( "sd:/" );
        __io_wiisd.shutdown();

        sd_mounted = FALSE;
    }
}

/*
 * Mounts the SD card
 */
static BOOL wii_mount_sd()
{
    if( !sd_mounted )
    {
        if( !__io_wiisd.startup() )
        {
            return FALSE;
        }
        else if( !fatMountSimple( "sd", &__io_wiisd ) )
        {
            return FALSE;
        }
        else
        {
            //fatSetReadAhead( "sd:/", 6, 64 );
        }

        sd_mounted = TRUE;
    }

    return TRUE;
}

/*
 * Remounts the SD card 
 */
void wii_remount_sd()
{ 
    // For some reason people were experiencing a lot of strange bugs on nwer
    // firmware version (3.3 and 3.4). These problems didn't seem to occur on
    // 3.2. The issue seems related to file caching. For example, a saved
    // snapshot would have an affect on the next ROM that was loaded, etc. To
    // workaround this, I remount the SD Card after each significant file 
    // operation. This *seems* to workaround the problem. 
    wii_unmount_sd();
    wii_mount_sd();
}

/*
 * Resets the menu indexes when an underlying menu in change (push/pop)
 */
static void wii_menu_reset_indexes()
{
    menu_cur_idx = -1;
    menu_start_idx = 0;
}

/*
 * Creates and returns a new menu tree node
 *
 * type     The type of the node
 * name     The name for the node
 * return   The newly created node
 */
static TREENODE* wii_create_tree_node( NODETYPE type, const char *name )
{
    TREENODE* nodep = (TREENODE*)malloc( sizeof( TREENODE ) );
    memset( nodep, 0, sizeof( TREENODE ) );
    nodep->node_type = type;
    nodep->name = strdup( name );

    return nodep;
}

/*
 * Adds the specified child node to the specified parent
 *
 * parent   The parent
 * child    The child to add to the parent
 */
static void wii_add_child( TREENODE *parent, TREENODE *childp  )
{
    // Do we have room?
    if( parent->child_count == parent->max_children )
    {
        parent->max_children += 10;
        parent->children = (TREENODEPTR*)
            realloc( parent->children, parent->max_children * 
            sizeof(TREENODEPTR));		
    }
    
    parent->children[parent->child_count++] = childp;
}

/*
 * Pushes the specified menu onto the menu stack (occurs when the user
 * navigates to a sub-menu)
 *
 * menu     The menu to push on the stack
 */
static void wii_push_menu( TREENODE *menu )
{    
#if 0 
    fprintf( stderr, "\npush: %s\n", menu->name );
    wii_pause();
#endif
    menu_stack[++menu_stack_head] = menu;

    wii_menu_reset_indexes();
    wii_menu_move( menu, 1 );
}

/*
 * Pops the latest menu off of the menu stack (occurs when the user leaves 
 * the current menu.
 *
 * return   The pop'd menu
 */
static TREENODE* wii_pop_menu()
{    
    if( menu_stack_head > 0 )
    {
        TREENODE *oldmenu = menu_stack[menu_stack_head--];
        wii_menu_reset_indexes();        
        wii_menu_move( menu_stack[menu_stack_head], 1 );
#if 0
    fprintf( stderr, "\npop: %s\n", menu->name );
    wii_pause();
#endif 
        return oldmenu;
    }

    return NULL;
}

/*
 * Pause and wait for input. Usually used when debugging.
 */
void wii_pause()
{
    int done = 0;
    while( !done )
    {		
        WPAD_ScanPads();
        u32 down = WPAD_ButtonsDown(0);
        if( down & WII_BUTTON_A )
        {
            done = 1;
        }
    }
}

/*
 * Stores a status message for display in the footer of the menu
 */ 
void wii_set_status_message( const char *message )
{
    wii_status_message_count = 3;
    snprintf( wii_status_message, sizeof(wii_status_message), "%s", message );
}

/*
 * Draws the image specified to the frame buffer
 *
 * props    The image properties
 * source   The bytes of the image
 * framebuff    The destination frame buffer
 * x        The x location to display the image in the buffer
 * y        The y location to disploay the image in the buffer
 */
static void wii_draw_image( 
    PNGUPROP *props, u32 *source, u32 *framebuff, u16 x, u16 y )
{
    u16 pix = 0, row = 0, col = 0;
    u16 offset = y * 320;	

    x >>= 1;

    for( row = 0; row < props->imgHeight; row++ )
    {
        for(col = 0; col < ( props->imgWidth >> 1 ); col++ )
        {
           framebuff[offset + col + x] = source[pix++];
        }
        offset += 320;
    }
}

/*
 * Used for comparing game names when sorting (qsort)
 *
 * a        The first tree node to compare
 * b        The second tree node to compare
 * return   The result of the comparison
 */
static int wii_game_name_compare( const void *a, const void *b )
{
    TREENODE** aptr = (TREENODE**)a;
    TREENODE** bptr = (TREENODE**)b;

    return stricmp( (*aptr)->name, (*bptr)->name );
}

/*
 * Reads the list of games into the specified menu
 *
 * menu     The menu to read the games into
 */
static void wii_read_game_list( TREENODE *menu )
{
#if 0
    for( int i = 0; i < 1019; i++ )
    {
        char buff[MENU_BUFF_SIZE];
        snprintf( buff, sizeof(buff), "game%d", i );
        TREENODE *child = wii_create_tree_node( NODETYPE_ROM, buff );
        wii_add_child( menu, child );
    }
#endif

    DIR_ITER *romdir = diropen( WII_ROMS_DIR );
    if( romdir != NULL)
    {
        struct stat statbuf;
        char filepath[WII_MAX_PATH];
        while( dirnext( romdir, filepath, &statbuf ) == 0 )
        {   
            /*
            struct dirent *entry = diropen( romdir );
            if( entry == NULL ) break;
            */
            
            if( strcmp( ".", filepath ) != 0 && 
                strcmp( "..", filepath ) != 0 )
            {				                
                if( !S_ISDIR( statbuf.st_mode ) )
                {
                    TREENODE *child = 
                        wii_create_tree_node( NODETYPE_ROM, filepath );

#if 0
                    LYNX_HEADER header;
                    if( cart_read_header( filepath, &header ) )
                    {
                        fprintf( stderr, "%s (%s)\n", header.cartname, header.manufname );
                    }
                    else
                    {
                        fprintf( stderr, "Not a valid cartridge.\n" );
                    }
                    wii_pause();
#endif
                    wii_add_child( menu, child );
                }				
            }
        }
        
        dirclose( romdir );
    }
    else
    {
        wii_set_status_message( "Error opening roms directory." );
    }
   
    // Sort the games list
    qsort( menu->children, menu->child_count, 
        sizeof(*(menu->children)), wii_game_name_compare );

    games_read = 1;

    // Remount the SD Card
    wii_remount_sd();
}


/*
 * Reads the list of snapshots into the specified menu
 *
 * menu     The menu to read the snapshots into
 */
static void wii_read_snapshot_list( TREENODE *menu )
{
    // Clear the previous list
    for( int i = 0; i < menu->child_count; i++ )
    {
        wii_free_node( menu->children[i] );
        menu->children[i] = NULL;
    }
    menu->child_count = 0;

    DIR_ITER *ssdir = diropen( WII_SAVES_DIR );
    if( ssdir != NULL)
    {
        struct stat statbuf;
        char ext[WII_MAX_PATH];
        char filepath[WII_MAX_PATH];
        while( dirnext( ssdir, filepath, &statbuf ) == 0 ) 
        {            
            if( strcmp( ".", filepath ) != 0 && 
                strcmp( "..", filepath) != 0 )
            {			                
                Util_getextension( filepath, ext );
                if( strcmp( ext, WII_SAVE_GAME_EXT ) == 0 )
                {                    
                    if( !S_ISDIR( statbuf.st_mode ) )
                    {
                        // TODO: Check to see if a rom exists for the snapshot

                        TREENODE *child = 
                            wii_create_tree_node( NODETYPE_SNAPSHOT, filepath );

                        wii_add_child( menu, child );
                    }				
                }
            }
        }
        
        dirclose( ssdir );
    }
    else
    {
        wii_set_status_message( "Error opening snapshots directory." );
    }
   
    // Sort the games list
    qsort( menu->children, menu->child_count, 
        sizeof(*(menu->children)), wii_game_name_compare );

    snapshots_read = TRUE;

    // Remount the SD Card
    wii_remount_sd();
}

/*
 * Loads the resource for the menu
 */
static void wii_load_resources()
{
    char about_loc[WII_MAX_PATH];
    wii_get_app_relative( "about.png", about_loc );

    IMGCTX about_ctx = PNGU_SelectImageFromDevice( about_loc );	

    if( about_ctx != NULL )
    {
        if( PNGU_GetImageProperties( about_ctx, &about_props ) != PNGU_OK )
        {
            PNGU_ReleaseImageContext( about_ctx );
            about_ctx = NULL;
        }
        else
        {
            u32 about_buff_size = 
                about_props.imgWidth * about_props.imgHeight * 2;
            about_buff = (u32*)malloc( about_buff_size );

            PNGU_DecodeToYCbYCr(
                about_ctx, 
                about_props.imgWidth, 
                about_props.imgHeight, 
                about_buff,
                0
            );

            PNGU_ReleaseImageContext( about_ctx );
            about_ctx = NULL;
        }
    }

    //
    // The root menu
    //

    menu_root = wii_create_tree_node( NODETYPE_ROOT, "root" );

    TREENODE* child = NULL;
    child = wii_create_tree_node( NODETYPE_RESUME, "Resume" );
    wii_add_child( menu_root, child );

    child = NULL;
    child = wii_create_tree_node( NODETYPE_RESET, "Reset" );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_LOAD_ROM, "Load cartridge" );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( menu_root, child );

    TREENODE *state = wii_create_tree_node( 
        NODETYPE_SNAPSHOT_MANAGEMENT, "Snapshot management" );
    wii_add_child( menu_root, state );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_SCALE, 
        "Screen size      " );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_ORIENT, 
        "Rotate screen    " );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_CONTROLS_ORIENT, 
        "Rotate controls  " );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_LCD, 
        "LCD emulation    " );
    wii_add_child( menu_root, child );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( menu_root, child );

    TREENODE *advanced = wii_create_tree_node( NODETYPE_ADVANCED, 
        "Advanced" );
    wii_add_child( menu_root, advanced );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( menu_root, child );

    /*
    child = wii_create_tree_node( NODETYPE_EXIT, "Exit" );
    wii_add_child( menu_root, child );
    */

    wii_push_menu( menu_root );	

    //
    // The advanced menu
    //

    child = wii_create_tree_node( NODETYPE_TOP_MENU_EXIT, 
        "Top menu exit   " );
    wii_add_child( advanced, child );

    child = wii_create_tree_node( NODETYPE_DEBUG_MODE, 
        "Debug mode      " );
    wii_add_child( advanced, child );

    child = wii_create_tree_node( NODETYPE_MAX_FRAME_RATE, 
        "Max frame rate  " );
    wii_add_child( advanced, child );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( advanced, child );

    child = wii_create_tree_node( NODETYPE_WRITE_HEADER, 
        "Write rotation (cartridge header)" );
    wii_add_child( advanced, child );

    //
    // Snapshot management
    //

    child = wii_create_tree_node( NODETYPE_AUTO_LOAD_SNAPSHOT, 
        "Auto load  " );
    wii_add_child( state, child );

    child = wii_create_tree_node( NODETYPE_AUTO_SAVE_SNAPSHOT, 
        "Auto save  " );
    wii_add_child( state, child );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( state, child );

    child = wii_create_tree_node( NODETYPE_LOAD_SNAPSHOT, 
        "Load snapshot" );
    wii_add_child( state, child );

    child = wii_create_tree_node( NODETYPE_SPACER, "" );
    wii_add_child( state, child );

    child = wii_create_tree_node( NODETYPE_SAVE_SNAPSHOT, 
        "Save snapshot (current game)" );
    wii_add_child( state, child );
        
    child = wii_create_tree_node( NODETYPE_DELETE_SNAPSHOT, 
        "Delete snapshot (current game)" );
    wii_add_child( state, child );
}

/*
 * Frees the specified tree node
 */
static void wii_free_node( TREENODE* node )
{
    if( node->child_count > 0 )
    {
        for( int i = 0; i < node->child_count; i++ )
        {
            wii_free_node( node->children[i] );
            node->children[i] = NULL;
        }
    }

    free( node->children );
    free( node->name );	
    free( node );
}

/*
 * Frees the menu resources 
 */
static void wii_free_resources()
{
    if( about_buff != NULL )
    {
        free( about_buff );
        about_buff = NULL;
    }

    if( menu_root != NULL )
    {
        wii_free_node( menu_root );
        menu_root = NULL;
    }

    if( last_rom != NULL )
    {
        free( last_rom );
        last_rom = NULL;
    }
}

/*
 * Writes the specified string to the VT display
 */
static void wii_write_vt( char *buffer )
{
    fprintf( stderr, buffer );
}

/*
 * Initializes the console output to the specified frame buffer. This 
 * allows us to swap between console output buffers.
 *
 * fb       The frame buffer to associated with the console
 */
static void wii_console_init( void *fb )
{
    console_init(
        fb, 20, 20,
        display_mode->fbWidth,
        display_mode->xfbHeight,
        display_mode->fbWidth * VI_DISPLAY_PIX_SZ );
}

/*
 * Determines the save name for the specified rom file
 *
 * romfile  The name of the rom file
 * buffer   The buffer to write the save name to
 */
static void wii_get_snapshot_name( const char *romfile, char *buffer )
{
    char filename[WII_MAX_PATH];            
    Util_splitpath( romfile, NULL, filename );
    snprintf( buffer, WII_MAX_PATH, "%s%s.%s",  
        WII_SAVES_DIR, filename, WII_SAVE_GAME_EXT );
}

/*
 * Deletes the specified snapshot
 */
static void wii_delete_snapshot()
{
    if( last_rom != NULL )
    {
        char filename[WII_MAX_PATH];     
        wii_get_snapshot_name( last_rom, filename );       

        int status = remove( filename );

        // Remount the SD Card
        wii_remount_sd();

        if( !status )
        {
            wii_menu_reset_indexes();
            wii_menu_move( menu_stack[menu_stack_head], 1 );
        }

        wii_set_status_message( 
            !status ?
            "Successly deleted snapshot." :
            "An error occurred attempting to delete snapshot."  );
    }
}

/*
 * Saves the current games state to the specified save file
 *
 * savefile The name of the save file to write state to. If this value is NULL,
 *          the default save name for the last rom is used.
 */
static void wii_save_snapshot( const char *savefile = NULL, BOOL status_update = TRUE )
{    
    bool succeeded = false;

    if( savefile == NULL ) 
    {
        if( last_rom != NULL )
        {
            char filename[WII_MAX_PATH];     
            wii_get_snapshot_name( last_rom, filename );
            succeeded = mpLynx->ContextSave( filename );
        }
    }
    else
    {
        succeeded = mpLynx->ContextSave( savefile );
    }

    if( status_update )
    {
        wii_set_status_message( 
            succeeded ?
                "Successly saved snapshot." :
                "An error occurred attempting to save snapshot." 
        );
    }

    // Remount the SD Card
    wii_remount_sd();
}

/*
 * Updates the header for the current cart to contain the current rotation
 * settings
 */
static BOOL wii_write_cart_header()
{
    BOOL succeeded = FALSE;
    char message[MENU_BUFF_SIZE] = "";

    if( last_rom != NULL && gForceRotate != MIKIE_BAD_MODE )
    {
        wii_status_message[0] = '\0';

        succeeded = cart_write_rotatation(  
            last_rom, gForceRotate, message, sizeof( message ) );

        // Remount the SD Card
        wii_remount_sd();
    }

    if( succeeded )
    {
        wii_set_status_message( "Successly wrote cartridge header with rotation." );
    }
    else
    {
        wii_set_status_message(
            message[0] == '\0' ?
                "An error occurred attempting to write cartridge header." :
                message );
    }

    return succeeded;
}

/*
 * Determines whether the specified snapshot is valid
 *
 * return   0  = valid
 *          -1 = does not exist
 *          -2 = not a valid save (wrong size)
 */
static int wii_check_snapshot( const char *savefile )
{
    struct stat statbuf;
    int result = stat( savefile, &statbuf );

    if( result < 0 )            
    {
        return -1;
    }
    else
    {
        if( statbuf.st_size != WII_SAVE_SIZE )
        {
            return -2;
        }
    }

    return 0;
}

/*
 * Starts the emulator for the specified rom file.
 *
 * romfile  The rom file to run in the emulator
 * savefile The name of the save file to load. If this value is NULL, no save
 *          is explicitly loaded (auto-load may occur). If the value is "", 
 *          no save is loaded and auto-load is ignored (used for reset).
 */
static void wii_start_emulation( char *romfile,  const char *savefile = NULL )
{
    wii_write_config();

    u32 *fb = xfb[0];
    wii_console_init( wii_debug ? fb : xfb[1] );
    VIDEO_ClearFrameBuffer( display_mode, fb, COLOR_BLACK );			
    VIDEO_SetNextFramebuffer( fb );
    VIDEO_Flush();
    VIDEO_WaitVSync();		

    LynxScale = scale;
    bpp = LynxScale == 1 ?  16 : 8;    

    BOOL succeeded = TRUE;

    try
    {  
        char autosavename[WII_MAX_PATH] = "";

        // Determine the name of the save file
        if( auto_save_snapshot || auto_load_snapshot )
        {
            wii_get_snapshot_name( romfile, autosavename );
        }

        // If a specific save file was not specified, and we are auto-loading 
        // see if a save file exists
        if( savefile == NULL &&
            ( auto_load_snapshot && wii_check_snapshot( autosavename ) == 0 ) )
        {
            savefile = autosavename;
        }
        
        BOOL reset = TRUE;
        if( savefile != NULL && strlen( savefile ) > 0 )
        {
            reset = FALSE;

            // Ensure the save is valid
            int sscheck = wii_check_snapshot( savefile );
            if( sscheck < 0 )
            {
                if( sscheck == -2 )            
                {
                    wii_set_status_message(
                        "The save specified is not valid." );                
                }
                else
                {
                    wii_set_status_message(
                        "Unable to find the specified snapshot file." );                
                }

                succeeded = FALSE;
            }
        }

        if( succeeded )
        {

#if 0
fprintf( stderr, "\n\nload: %s\n", ( reset ? NULL : savefile ) );
wii_pause();
#endif

            handy_start_emu( romfile, ( reset ? NULL : savefile ) );        

            // Remount the SD Card
            wii_remount_sd();

            // Save the current state (for resuming later)
            char curr_save_loc[WII_MAX_PATH];
            wii_get_app_relative( WII_CURRENT_SAVE, curr_save_loc );
            wii_save_snapshot( curr_save_loc, FALSE );

#if 0
fprintf( stderr, "\n\nsave: %s\n", curr_save_loc );
wii_pause();
#endif 

            // Auto save?
            if( auto_save_snapshot )
            {
                wii_save_snapshot( autosavename );

#if 0
fprintf( stderr, "\n\nsave: %s\n", autosavename );
wii_pause();
#endif 

            }        

            // Store the name of the last rom (for resuming later)        
            // Do it in this order in case they passed in the last
            // rom pointer
            char *last = strdup( romfile );
            if( last_rom != NULL )
            {
                free( last_rom );    
            }

            last_rom = last;

            if( top_menu_exit )
            {
                while( wii_pop_menu() != NULL );
            }
        }
    }
    catch( CLynxException& ex )
    {		
        succeeded = FALSE;
        wii_set_status_message( ex.mMsg.str().c_str() );
    }

    if( !succeeded )
    {
        // Reset the last rom that was loaded
        if( last_rom != NULL )
        {
            free( last_rom );
            last_rom = NULL;
        }

        // Remount the SD Card
        wii_remount_sd();
    }

    SDL_PauseAudio(1);
    //SDL_CloseAudio();	
}

/*
 * Starts the emulator for the specified snapshot file.
 *
 * savefile The name of the save file to load. 
 */
static void wii_start_snapshot( char *savefile )
{
    BOOL succeeded = FALSE;
    BOOL seterror = FALSE;

    // Determine the extension
    char ext[WII_MAX_PATH];
    Util_getextension( savefile, ext );

    if( !strcmp( ext, WII_SAVE_GAME_EXT ) )
    {
        char savename[WII_MAX_PATH];

        // Get the save name (without extension)
        Util_splitpath( savefile, NULL, savename );

        int namelen = strlen( savename );
        int extlen = strlen( WII_SAVE_GAME_EXT );

        if( namelen > extlen )
        {
            // build the associated rom name
            savename[namelen - extlen - 1] = '\0';

            char romfile[WII_MAX_PATH];
            snprintf( romfile, WII_MAX_PATH, "%s/%s", WII_ROMS_DIR, savename );

            int exists = Util_fileexists( romfile );

            // Ensure the rom exists
            if( !exists )            
            {
                wii_set_status_message(
                    "Unable to find associated ROM file." );                
                seterror = TRUE;
            }
            else
            {
                // launch the emulator for the save
                wii_start_emulation( romfile, savefile );
                succeeded = TRUE;
            }
        }
    }

    if( !succeeded && !seterror )
    {
        wii_set_status_message( 
            "The file selected is not a a valid snapshot file." );    
    }
}

/*
 * Resumes emulation of the current game
 */
static void wii_resume_emulation()
{
    if( last_rom != NULL )
    {
        char save_loc[WII_MAX_PATH];
        wii_get_app_relative( WII_CURRENT_SAVE, save_loc );

        wii_start_emulation( last_rom, save_loc );
    }
}

/*
 * Resets the current game
 */
static void wii_reset_emulation()
{
    if( last_rom != NULL )
    {
        wii_start_emulation( last_rom, "" /* represents a reset */ );
    }
}

/*
 * Updates the buffer with the header message for the current menu
 *
 * menu     The menu
 * buffer   The buffer to update with the header message for the
 *          current menu.
 */
static void wii_menu_get_header( TREENODE* menu, char *buffer )
{
    switch( menu->node_type )
    {
        case NODETYPE_LOAD_ROM:
            if( !games_read )
            {
                snprintf( buffer, MENU_BUFF_SIZE, "Reading game list..." );                
            }
            break;
        case NODETYPE_LOAD_SNAPSHOT:
            if( !snapshots_read )
            {
                snprintf( buffer, MENU_BUFF_SIZE, "Reading snapshot list..." );                            
            }
            break;
    }

    if( strlen( buffer ) == 0 )
    {
        snprintf( buffer, MENU_BUFF_SIZE, 
            "U/D = Scroll%s, A = Select%s, Home = Exit", 
            ( menu->child_count > MENU_PAGE_SIZE ? ", L/R = Page" : "" ),
            ( menu_stack_head > 0 ? ", B = Back" : "" )            
        );
    }
}

/*
 * Returns the standard list footer for snapshots, games, etc.
 *
 * menu     The current menu
 * listname The name of the list (snapshot/game, etc.)
 * buffer   The buffer to write the footer to
 */
static void wii_get_list_footer( TREENODE* menu, const char *listname, char *buffer )
{
    if( menu->child_count == 0 )
    {
        snprintf( buffer, MENU_BUFF_SIZE, "No %ss found.", listname );                
    }
    else if( menu->child_count == 1 )
    {
        snprintf( buffer, MENU_BUFF_SIZE, "1 %s found.", listname );                
    }
    else
    {
        s16 end_idx = menu_start_idx + MENU_PAGE_SIZE;
        snprintf( buffer, MENU_BUFF_SIZE, 
            "%d %ss found, displaying %d to %d.",
            menu->child_count,
            listname,
            menu_start_idx + 1,
            ( end_idx < menu->child_count ? end_idx : menu->child_count )                            
        );                
    }
}

/*
 * Updates the buffer with the footer message for the current menu
 *
 * menu     The menu
 * buffer   The buffer to update with the footer message for the
 *          current menu.
 */
static void wii_menu_get_footer( TREENODE* menu, char *buffer )
{    
    // Any exceptions? If so display it in the footer
    if( wii_status_message_count > 0 )
    {        
        wii_status_message_count--;
        snprintf( buffer, MENU_BUFF_SIZE, wii_status_message );
    }
    else
    {
        switch( menu->node_type )
        {
            case NODETYPE_LOAD_ROM:
                if( games_read )
                {
                    wii_get_list_footer( menu, "cartridge", buffer );
                }
                break;
            case NODETYPE_LOAD_SNAPSHOT:
                wii_get_list_footer( menu, "snapshot", buffer );
                break;
        }
    }
}

/*
 * Updates the buffer with the name of the specified node
 *
 * node     The node
 * buffer   The name of the specified node
 */
static void wii_menu_get_node_name( TREENODE* node, char *buffer )
{
    switch( node->node_type )
    {
        case NODETYPE_SCALE:
            snprintf( buffer, MENU_BUFF_SIZE, "%s: %dx", node->name, 
                scale );
            break;
        case NODETYPE_ORIENT:
            const char *orient;
            switch( gForceRotate )
            {
                case MIKIE_NO_ROTATE:
                    orient="None";
                    break;
                case MIKIE_ROTATE_L:
                    orient="Left";
                    break;
                case MIKIE_ROTATE_R:
                    orient="Right";
                    break;
                default:
                    orient="(Auto)";
            }
            snprintf( buffer, MENU_BUFF_SIZE, "%s: %s", node->name, orient );
            break;
        case NODETYPE_CONTROLS_ORIENT:
            const char *corient;
            switch( wii_rotate_controls )
            {
                case MIKIE_NO_ROTATE:
                    corient="None";
                    break;
                case MIKIE_ROTATE_L:
                    corient="Left";
                    break;
                case MIKIE_ROTATE_R:
                    corient="Right";
                    break;
                default:
                    corient="(With screen)";
            }
            snprintf( buffer, MENU_BUFF_SIZE, "%s: %s", node->name, corient );
            break;        
        case NODETYPE_MAX_FRAME_RATE:
            if( wii_max_frame_rate == 0 )
            {
                snprintf( buffer, MENU_BUFF_SIZE, "%s: (Unlimited)", node->name );
            }
            else
            {
                snprintf( buffer, MENU_BUFF_SIZE, "%s: %d%s", 
                    node->name, wii_max_frame_rate,
                    ( wii_max_frame_rate == 60 ? " (Default)" : "" ) );
            }
            break;
        case NODETYPE_LCD:
        case NODETYPE_DEBUG_MODE:
        case NODETYPE_TOP_MENU_EXIT:
        case NODETYPE_AUTO_LOAD_SNAPSHOT:
        case NODETYPE_AUTO_SAVE_SNAPSHOT:
            BOOL enabled;
            switch( node->node_type )
            {
                case NODETYPE_LCD:
                    enabled = LynxLCD == 1;
                    break;
                case NODETYPE_DEBUG_MODE:
                    enabled = wii_debug;
                    break;
                case NODETYPE_TOP_MENU_EXIT:
                    enabled = top_menu_exit;
                    break;
                case NODETYPE_AUTO_LOAD_SNAPSHOT:
                    enabled = auto_load_snapshot;
                    break;
                case NODETYPE_AUTO_SAVE_SNAPSHOT:
                    enabled = auto_save_snapshot;
                    break;
            }

            snprintf( buffer, MENU_BUFF_SIZE, "%s: %s", node->name, 
                enabled ? "Enabled" : "Disabled" );
            break;

        default:	
            snprintf( buffer, MENU_BUFF_SIZE, node->name );
    }
}

/*
 * React to the "select" event for the specified node
 *
 * node     The node
 */
static void wii_menu_select_node( TREENODE *node )
{
    switch( node->node_type )
    {
        case NODETYPE_SCALE:
            scale++;
            if( scale > 4 ) 
            {
                scale = 1;
            }			
            break;
        case NODETYPE_ORIENT:
            gForceRotate++;
            if( gForceRotate > 3 ) 
            {
                gForceRotate = 0;
            }
            break;
        case NODETYPE_CONTROLS_ORIENT:
            wii_rotate_controls++;
            if( wii_rotate_controls > 3 ) 
            {
                wii_rotate_controls = 0;
            }
            break;
        case NODETYPE_ROM:
            char buff[WII_MAX_PATH];
            snprintf( buff, sizeof(buff), "%s/%s", WII_ROMS_DIR, node->name );             
            wii_start_emulation( buff );
            break;
        case NODETYPE_RESUME:
            wii_resume_emulation();
            break;
        case NODETYPE_RESET:
            wii_reset_emulation();
            break;
        case NODETYPE_SNAPSHOT:
            snprintf( buff, sizeof(buff), "%s%s", WII_SAVES_DIR, node->name );  
            wii_start_snapshot( buff );
            break;
        case NODETYPE_MAX_FRAME_RATE:
            wii_max_frame_rate += 1;
            if( wii_max_frame_rate > 100 )
            {
                wii_max_frame_rate = 0;
            }
            break;
        case NODETYPE_TOP_MENU_EXIT:
            top_menu_exit ^= 1;
            break;
        case NODETYPE_LCD:
            LynxLCD ^= 1;
            break;
        case NODETYPE_DEBUG_MODE:
            wii_debug ^= 1;
            break;
        case NODETYPE_AUTO_LOAD_SNAPSHOT:
            auto_load_snapshot ^= 1;
            break;
        case NODETYPE_AUTO_SAVE_SNAPSHOT:
            auto_save_snapshot ^= 1;
            break;
        case NODETYPE_SNAPSHOT_MANAGEMENT:
        case NODETYPE_ADVANCED:
        case NODETYPE_LOAD_ROM:        
            wii_push_menu( node );
            break;
        case NODETYPE_LOAD_SNAPSHOT:
            wii_push_menu( node );
            snapshots_read = FALSE;
            break;
        case NODETYPE_SAVE_SNAPSHOT:
            wii_save_snapshot();
            break;
        case NODETYPE_DELETE_SNAPSHOT:
            wii_delete_snapshot();
            break;
        case NODETYPE_WRITE_HEADER:
            wii_write_cart_header();
            break;
        case NODETYPE_EXIT:
            quit = 1;
            break;
    }
}

/*
 * Determines whether the node is currently visible
 *
 * node     The node
 * return   Whether the node is visible
 */
static BOOL wii_menu_is_node_visible( TREENODE *node )
{
    switch( node->node_type )
    {
        case NODETYPE_SAVE_SNAPSHOT:
        case NODETYPE_RESET:
        case NODETYPE_RESUME:
            return last_rom != NULL;
            break;
        case NODETYPE_DELETE_SNAPSHOT:
            if( last_rom != NULL )
            {
                char savename[WII_MAX_PATH];
                wii_get_snapshot_name( last_rom, savename );
                return Util_fileexists( savename );
            }
            return FALSE;
            break;
        case NODETYPE_WRITE_HEADER:
            return last_rom != NULL && gForceRotate != MIKIE_BAD_MODE;
            break;
    }

    return TRUE;
}

/*
 * Determines whether the node is selectable
 *
 * node     The node
 * return   Whether the node is selectable
 */
static BOOL wii_menu_is_node_selectable( TREENODE *node )
{
    if( !wii_menu_is_node_visible( node ) )
    {
        return FALSE;
    }

    switch( node->node_type )
    {
        case NODETYPE_SPACER:
            return FALSE;
            break;
    }

    return TRUE;
}

/*
 * Provides an opportunity for the specified menu to do something during 
 * a cycle of the menu.
 *
 * menu     The menu
 */
static void wii_menu_update( TREENODE *menu )
{
    switch( menu->node_type )
    {
        case NODETYPE_LOAD_ROM:
            if( !games_read )
            {
                wii_read_game_list( menu );       
                wii_menu_move( menu, 1 );
                redraw_menu = 1;
            }
            break;
        case NODETYPE_LOAD_SNAPSHOT:
            if( !snapshots_read )
            {
                wii_read_snapshot_list( menu );    
                wii_menu_reset_indexes();    
                wii_menu_move( menu, 1 );
                redraw_menu = 1;            
            }
            break;
    }
}

/*
 * Renders the specified menu
 */
static void wii_menu_render( TREENODE *menu )
{	
#if 0
    u32 start_time = SDL_GetTicks();
#endif

    // Swap buffers
    cur_xfb ^= 1;
    u32 *fb = xfb[cur_xfb];

    VIDEO_ClearFrameBuffer( display_mode, fb, COLOR_BLACK );
    wii_console_init( fb );

    if( about_buff != NULL )
    {
        wii_draw_image( 
            &about_props, 
            about_buff, 
            fb, 
            ( display_mode->fbWidth - about_props.imgWidth ) >> 1,  
            ABOUT_Y );
    }

    if( menu )
    {	
        char buffer[MENU_BUFF_SIZE];
        char buffer2[MENU_BUFF_SIZE];

        buffer[0] = '\0';
        wii_menu_get_header( menu, buffer );

        snprintf( buffer2, sizeof(buffer2), "\x1b[%d;%dH %s", 
            MENU_HEAD_Y, MENU_HEAD_X, buffer );		

        wii_write_vt( buffer2 );

        int displayed = 0;        
        for( int i = menu_start_idx; 
            i < menu->child_count && displayed < MENU_PAGE_SIZE; 
            i++ )
        {		
            buffer[0] = '\0';

            TREENODE* node = menu->children[i];
            if( wii_menu_is_node_visible( node ) )
            {
                wii_menu_get_node_name( node, buffer );

                snprintf( buffer2, sizeof(buffer2), "\x1b[%d;%dH%s %s %s", 				
                    MENU_START_Y + displayed, 
                    MENU_START_X, 
                    ( menu_cur_idx == i ? "\x1b[47m\x1b[30m" : "" ),
                    buffer,
                    ( menu_cur_idx == i ? "\x1b[40m\x1b[37m" : "" )              
                );

                wii_write_vt( buffer2 );
                ++displayed;
            }
        }

        buffer[0] = '\0';
        wii_menu_get_footer( menu, buffer );

        snprintf( buffer2, sizeof(buffer2), "\x1b[%d;%dH %s", 
            MENU_FOOT_Y, MENU_HEAD_X, buffer );		

        wii_write_vt( buffer2 );

#if 0
        u32 elapsed = SDL_GetTicks() - start_time;
        snprintf( buffer, sizeof(buffer), "\x1b[2;0H%.3f",  
            ( (float)elapsed / 1000.0) );
        wii_write_vt( buffer );
#endif
    }

    VIDEO_SetNextFramebuffer( fb );
    VIDEO_SetBlack(FALSE);
    VIDEO_Flush();	
    //VIDEO_WaitVSync();
}

/*
 * Move the current selection in the menu by the specified step count
 *
 * menu     The current menu
 * steps    The number of steps to move the selection
 */
static void wii_menu_move( TREENODE *menu, s16 steps )
{	
    // TODO: When paging, need to take into consideration non-visible items

    if( !menu ) return;

    s16 new_idx = menu_cur_idx;

    // Attempt to move to the new location
    while( 1 )
    {
        s16 prev_idx = new_idx;
        new_idx += steps;

        if( new_idx >= menu->child_count )
        {
            new_idx = menu->child_count - 1;
        }		

        if( new_idx < 0 )
        {
            new_idx = 0;
        }

        if( new_idx < menu->child_count )
        {
            // Make sure the node can be selected
            TREENODE* curchild = menu->children[new_idx];
            if( ( !wii_menu_is_node_selectable( curchild ) ) &&
                prev_idx != new_idx )
            {
                steps = steps > 0 ? 1 : -1;
            }
            else
            {
                break;
            } 
        }
        else
        {
            break;
        }
    }

    // Is the new location valid? If so make updates.
    if( new_idx >= 0 && 
        new_idx < menu->child_count &&
        wii_menu_is_node_selectable( menu->children[new_idx] ) )
    {
        menu_cur_idx = new_idx;

        if( menu_cur_idx < menu_start_idx )
        {
            menu_start_idx = menu_cur_idx;
        }
        else
        {
            s16 min_start = menu_cur_idx - MENU_PAGE_SIZE + 1;
            if( min_start > menu_start_idx )
            {
                menu_start_idx = min_start;
            }
        }
    }
}

#define DELAY_FRAMES 6
#define DELAY_STEP 1
#define DELAY_MIN 0

/*
 * Displays the menu 
 */
static void wii_show_menu()
{
    // Allows for incremental speed when scrolling the menu 
    // (Scrolls faster the longer the directional pad is held)
    s16 delay_frames = -1;
    s16 delay_factor = -1;

    while( !quit )
    {		
        WPAD_ScanPads();

        u32 down = WPAD_ButtonsDown(0);
        u32 held = WPAD_ButtonsHeld(0);

        TREENODE *menu = 
            menu_stack_head >= 0 ?			
            menu_stack[menu_stack_head] : NULL;

        if( redraw_menu )
        {
            redraw_menu = 0;
            wii_menu_render( menu );
        }

        if( menu )
        {                    
            wii_menu_update( menu );

            if( ( held & (
                    WII_BUTTON_LEFT | WII_BUTTON_RIGHT | 
                    WII_BUTTON_DOWN | WII_BUTTON_UP ) ) == 0 )
            {
                delay_frames = -1;
                delay_factor = -1;
            }
            else
            {
                if( delay_frames < 0 )
                {
                    if( held & WII_BUTTON_LEFT )
                    {
                        wii_menu_move( menu, -MENU_PAGE_SIZE );
                        redraw_menu = 1;
                    }
                    else if( held & WII_BUTTON_RIGHT )
                    {
                        wii_menu_move( menu, MENU_PAGE_SIZE );
                        redraw_menu = 1;
                    }
                    else if( held & WII_BUTTON_DOWN )
                    {
                        wii_menu_move( menu, 1 );
                        redraw_menu = 1;
                    }
                    else if( held & WII_BUTTON_UP  )
                    {
                        if( menu )
                        {
                            wii_menu_move( menu, -1 );						
                            redraw_menu = 1;
                        }
                    }

                    delay_frames = 
                        DELAY_FRAMES - (DELAY_STEP * ++delay_factor);

                    if( delay_frames < DELAY_MIN ) 
                    {
                        delay_frames = DELAY_MIN;
                    }
                }
                else
                {
                    delay_frames--;
                }
            }
           
            if( down & WII_BUTTON_A )
            {	
                wii_menu_select_node( menu->children[menu_cur_idx] );
                redraw_menu = 1;            
            }
            if( down & WII_BUTTON_B )
            {
                wii_pop_menu();
                redraw_menu = 1;
            }
        }
        
        if( down & WII_BUTTON_HOME || wii_hw_button )
        {
            quit = 1;
        }

        VIDEO_WaitVSync();
    }
}

/*
 * Read the configuration file
 *
 * return   Was the read success?
 */
static BOOL wii_read_config()
{
    char buff[512];

    char config_loc[WII_MAX_PATH];
    wii_get_app_relative( WII_CONFIG, config_loc );

    FILE *fp;
    fp = fopen( config_loc, "r" );
    if (fp == NULL) 
    {	
        // Remount the SD Card
        wii_remount_sd();

        return FALSE;		
    }

    while( fgets( buff, sizeof( buff ), fp ) ) 
    {
        char *ptr;
        Util_chomp( buff );
        ptr = strchr( buff, '=' );
        if( ptr != NULL ) 
        {
            *ptr++ = '\0';
            Util_trim( buff );
            Util_trim( ptr );

            if( strcmp( buff, "SCREEN_SIZE" ) == 0 )
            {
                scale = Util_sscandec( ptr );
            }
            else if ( strcmp( buff, "ROTATE_SCREEN" ) == 0 )
            {
                gForceRotate = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "ROTATE_CONTROLS" ) == 0 )
            {
                wii_rotate_controls = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "LCD_EMULATION" ) == 0 )
            {
                LynxLCD = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "DEBUG" ) == 0 )
            {
                wii_debug = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "MAX_FRAME_RATE" ) == 0 )
            {
                wii_max_frame_rate = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "TOP_MENU_EXIT" ) == 0 )
            {
                top_menu_exit = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "AUTO_LOAD_SNAPSHOT" ) == 0 )
            {
                auto_load_snapshot = Util_sscandec( ptr );				
            }
            else if ( strcmp( buff, "AUTO_SAVE_SNAPSHOT" ) == 0 )
            {
                auto_save_snapshot = Util_sscandec( ptr );				
            }
        }
    }

    fclose(fp);

    // Remount the SD Card
    wii_remount_sd();

    return TRUE;
}

/*
 * Write the configuration file
 *
 * return   Was the write success?
 */
static BOOL wii_write_config(void)
{
    char config_loc[WII_MAX_PATH];
    wii_get_app_relative( WII_CONFIG, config_loc );

    FILE *fp;
    fp = fopen( config_loc, "w" );
    if( fp == NULL ) 
    {
        // Remount the SD Card
        wii_remount_sd();

        return FALSE;
    }

    fprintf( fp, "SCREEN_SIZE=%d\n", scale );
    fprintf( fp, "ROTATE_SCREEN=%d\n", gForceRotate );
    fprintf( fp, "LCD_EMULATION=%d\n", LynxLCD );
    fprintf( fp, "ROTATE_CONTROLS=%d\n", wii_rotate_controls );
    fprintf( fp, "DEBUG=%d\n", wii_debug );
    fprintf( fp, "MAX_FRAME_RATE=%d\n", wii_max_frame_rate );
    fprintf( fp, "TOP_MENU_EXIT=%d\n", top_menu_exit );
    fprintf( fp, "AUTO_LOAD_SNAPSHOT=%d\n", auto_load_snapshot );
    fprintf( fp, "AUTO_SAVE_SNAPSHOT=%d\n", auto_save_snapshot );

    fclose(fp);

    // Remount the SD Card
    wii_remount_sd();

    return TRUE;
}

/*
 * Updates the specified result string with the location of the file relative
 * to the appliation root directory.
 * 
 * file     The file name
 * result   The buffer to store the result in
 */
void wii_get_app_relative( const char *file, char *result )
{
    snprintf( result, WII_MAX_PATH, "%s%s", app_path, file );
}

/*
 * Determines and stores the base application path
 *
 * argc     The count of main arguments
 * argv     The array of argument values
 */
static void set_app_path( int argc, char *argv[] )
{    
    snprintf( app_path, WII_MAX_PATH, "%s", WII_BASE_APP_DIR );
    if( argc > 0 && argv[0] != NULL )
    {
        char temp_path[WII_MAX_PATH];
        snprintf( temp_path, WII_MAX_PATH, "%s", argv[0] );

        char *loc;

        // Remove the file name
        loc = strrchr( temp_path, '/' );
        if (loc != NULL)
        {
            *loc = '\0'; 
        }

        // Remove the "fat:"
        loc = strchr( temp_path, '/' ); 
        if (loc != NULL)
        {
            snprintf( app_path, WII_MAX_PATH, "%s%s/", WII_SD_CARD, loc );
        }     
    }
}

/*
 * Callback for the reset button on the Wii.
 */
void wii_reset_pressed()
{
	wii_hw_button = SYS_RETURNTOMENU;
}
 
/*
 * Callback for the power button on the Wii.
 */
void wii_power_pressed()
{
	wii_hw_button = SYS_POWEROFF_STANDBY;
}
 
/*
 * Callback for the power button on the Wiimote.
 *
 * chan The Wiimote that pressed the button
 */
void wii_mote_power_pressed(s32 chan)
{
	wii_hw_button = SYS_POWEROFF_STANDBY;
} 

/*
 * Main for WiiHandy/SDL
 */
int main(int argc, char *argv[])
{
    set_app_path( argc, argv );

#if 0
    wii_set_status_message( app_path );
#endif

    gError = new CWiiErrorHandler();

    // Mount the SD Card
    wii_mount_sd();

    WPAD_Init();

    SYS_SetResetCallback( wii_reset_pressed );
	SYS_SetPowerCallback( wii_power_pressed );
	WPAD_SetPowerButtonCallback( wii_mote_power_pressed );

    LynxLCD = 0;
    filter = 0;	
    gAudioEnabled = TRUE; 

    memset( &menu_stack, 0, sizeof(menu_stack) );

    wii_load_resources();
    wii_read_config();

    if( SDL_Init( SDL_INIT_AUDIO | SDL_INIT_VIDEO ) < 0) 
    {
        fprintf( stderr, "FAILED : Unable to init SDL: %s\n", SDL_GetError() );
        exit(EXIT_FAILURE);
    }

    xfb[0] = frame_buffer;
    xfb[1] = (u32*)MEM_K0_TO_K1(SYS_AllocateFramebuffer(display_mode));	

    wii_show_menu();
    wii_free_resources();
    wii_write_config();

    SDL_Quit();

    if( wii_hw_button )
    {
	    SYS_ResetSystem( wii_hw_button, 0, 0 );
	}
    else if( !!*(u32*)0x80001800 ) 
    {
        exit(1);
    }
    else
    {
        SYS_ResetSystem( SYS_RETURNTOMENU, 0, 0 );
    }
}
