Subversion Repositories psp

[/] [trunk/] [vice/] [arch/] [psp/] [c64ui.c] - Rev 519

Compare with Previous | Blame | View Log

/*
 * c64ui.c - Implementation of the C64-specific part of the UI.
 *
 * Written by
 *  Akop Karapetyan <dev@psp.akop.org>
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  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
 *  (at your option) 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.
 *
 */
 
#include "lib/video.h"
#include "lib/ui.h"
#include "lib/pl_menu.h"
#include "lib/pl_file.h"
#include "lib/ctrl.h"
#include "lib/pl_psp.h"
#include "lib/pl_ini.h"
#include "lib/pl_util.h"
#include "lib/pl_gfx.h"
#include "libmz/unzip.h"
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pspkernel.h>
 
#include "autostart.h"
#include "vice.h"
#include "lib.h"
#include "machine.h"
#include "ui.h"
#include "attach.h"
#include "util.h"
#include "sid.h"
#include "log.h"
#include "resources.h"
#include "tape.h"
#include "cartridge.h"
#include "imagecontents.h"
#include "tapecontents.h"
#include "diskcontents.h"
#include "videoarch.h"
 
#define TAB_QUICKLOAD 0
#define TAB_STATE     1
#define TAB_CONTROLS  2
#define TAB_OPTIONS   3
#define TAB_SYSTEM    4
#define TAB_MAX       TAB_SYSTEM
#define TAB_ABOUT     5
 
#define OPTION_DISPLAY_MODE  0x01
#define OPTION_FRAME_LIMITER 0x02
#define OPTION_CLOCK_FREQ    0x03
#define OPTION_SHOW_FPS      0x04
#define OPTION_CONTROL_MODE  0x05
#define OPTION_ANIMATE       0x06
#define OPTION_TOGGLE_VK     0x08
#define OPTION_AUTOLOAD      0x09
#define OPTION_SHOW_OSI      0x0A
#define OPTION_SHOW_BORDER   0x0B
#define OPTION_REFRESH_RATE  0x0C
#define OPTION_VSYNC         0x0D
 
#define SYSTEM_SCRNSHOT     0x11
#define SYSTEM_RESET        0x12
#define SYSTEM_JOYPORT      0x13
#define SYSTEM_SOUND        0x14
#define SYSTEM_SND_ENGINE   0x15
#define SYSTEM_TRUE_DRIVE   0x16
#define SYSTEM_VIDEO_STD    0x17
 
#define SYSTEM_CART         0x26
#define SYSTEM_TAPE         0x27
#define SYSTEM_DRIVE8       0x28
 
#define GET_DRIVE(code) ((code)&0x0F)
#define GET_DRIVE_MENU_ID(code) (((code)&0x0F)|0x20)
 
static const char 
  PresentSlotText[] = "\026\244\020 Save\t\026\001\020 Load\t\026\243\020 Delete",
  EmptySlotText[]   = "\026\244\020 Save",
  ControlHelpText[] = "\026\250\020 Change mapping\t"
                      "\026\244\020 Set as default\t\026\243\020 Load defaults";
 
static const char 
  *QuickloadFilter[] =
     { "ZIP",
       "D64","D71","D80","D81","D82","G64","G41","X64",
       "T64","TAP",
       "PRG","P00",
       "CRT",'\0' },
  *DiskFilter[] =
     { "ZIP", "D64","D71","D80","D81","D82","G64","G41","X64",'\0' },
  *CartFilter[] =
     { "ZIP", "CRT",'\0' },
  *TapeFilter[] =
     { "ZIP", "T64","TAP",'\0' };
 
/* Tab labels */
static const char *TabLabel[] = 
{
  "Game",
  "Save/Load",
  "Controls",
  "Options",
  "System",
  "About"
};
 
/* Menu definitions */
PL_MENU_OPTIONS_BEGIN(ToggleOptions)
  PL_MENU_OPTION("Disabled", 0)
  PL_MENU_OPTION("Enabled", 1)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(ScreenSizeOptions)
  PL_MENU_OPTION("Actual size", DISPLAY_MODE_UNSCALED)
  PL_MENU_OPTION("4:3 scaled (fit height)", DISPLAY_MODE_FIT_HEIGHT)
  PL_MENU_OPTION("16:9 scaled (fit screen)", DISPLAY_MODE_FILL_SCREEN)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(PspClockFreqOptions)
  PL_MENU_OPTION("222 MHz", 222)
  PL_MENU_OPTION("266 MHz", 266)
  PL_MENU_OPTION("300 MHz", 300)
  PL_MENU_OPTION("333 MHz", 333)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(JoyPortOptions)
  PL_MENU_OPTION("Port 1", 1)
  PL_MENU_OPTION("Port 2", 2)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(ControlModeOptions)
  PL_MENU_OPTION("\026\242\020 cancels, \026\241\020 confirms (US)", 0)
  PL_MENU_OPTION("\026\241\020 cancels, \026\242\020 confirms (Japan)", 1)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(VkModeOptions)
  PL_MENU_OPTION("Display when button is held down (classic mode)", 0)
  PL_MENU_OPTION("Toggle display on and off when button is pressed", 1)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(SoundEngineOptions)
  PL_MENU_OPTION("FastSID", SID_ENGINE_FASTSID)
#ifdef HAVE_RESID
  PL_MENU_OPTION("ReSID", SID_ENGINE_RESID)
#endif
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(VideoStandardOptions)
  PL_MENU_OPTION("PAL-G", MACHINE_SYNC_PAL)
  PL_MENU_OPTION("NTSC-M", MACHINE_SYNC_NTSC)
  PL_MENU_OPTION("Old NTSC-M", MACHINE_SYNC_NTSCOLD)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(AutoloadSlots)
  PL_MENU_OPTION("Disabled", -1)
  PL_MENU_OPTION("1", 0)
  PL_MENU_OPTION("2", 1)
  PL_MENU_OPTION("3", 2)
  PL_MENU_OPTION("4", 3)
  PL_MENU_OPTION("5", 4)
  PL_MENU_OPTION("6", 5)
  PL_MENU_OPTION("7", 6)
  PL_MENU_OPTION("8", 7)
  PL_MENU_OPTION("9", 8)
  PL_MENU_OPTION("10",9)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(RefreshRateOptions)
  PL_MENU_OPTION("Automatic", 0)
  PL_MENU_OPTION("Don't skip frames", 1)
  PL_MENU_OPTION("Skip 1 frame", 2)
  PL_MENU_OPTION("Skip 2 frames", 3)
  PL_MENU_OPTION("Skip 3 frames", 4)
  PL_MENU_OPTION("Skip 4 frames", 5)
PL_MENU_OPTIONS_END
PL_MENU_OPTIONS_BEGIN(MappableButtons)
  /* Unmapped */
  PL_MENU_OPTION("None", 0)
  /* Special */
  PL_MENU_OPTION("Special: Open Menu",     (SPC|SPC_MENU))
  PL_MENU_OPTION("Special: Show keyboard", (SPC|SPC_KYBD))
  /* Function keys */
  PL_MENU_OPTION("Joystick Up",    JOY|0x01)
  PL_MENU_OPTION("Joystick Down",  JOY|0x02)
  PL_MENU_OPTION("Joystick Left",  JOY|0x04)
  PL_MENU_OPTION("Joystick Right", JOY|0x08)
  PL_MENU_OPTION("Joystick Fire",  JOY|0x10)
  /* Cursor */
  PL_MENU_OPTION("Up/Down",    CK(0,7,8))
  PL_MENU_OPTION("Left/Right", CK(0,2,8))
  PL_MENU_OPTION("Space",        CK(7,4,8))
  /* Function keys */
  PL_MENU_OPTION("F1/F2", CK(0,4,8))
  PL_MENU_OPTION("F3/F4", CK(0,5,8))
  PL_MENU_OPTION("F5/F6", CK(0,6,8))
  PL_MENU_OPTION("F7/F8", CK(0,3,8))
  /* Etc.. */
  PL_MENU_OPTION("Clr/Home",     CK(6,3,8))
  PL_MENU_OPTION("Ins/Del",      CK(0,0,8))
  PL_MENU_OPTION("Ctrl",         CK(7,2,8))
  PL_MENU_OPTION("Restore",      CK(0xf,0xf,8))
  PL_MENU_OPTION("Run/Stop",     CK(7,7,8))
  PL_MENU_OPTION("Return",       CK(0,1,8))
  PL_MENU_OPTION("C= (CBM)",     CK(7,5,2))
  PL_MENU_OPTION("L. Shift",     CK(1,7,2))
  PL_MENU_OPTION("R. Shift",     CK(6,4,4))
  /* Symbols */
  PL_MENU_OPTION("+", CK(5,0,8))
  PL_MENU_OPTION("-", CK(5,3,8))
  PL_MENU_OPTION("Pound sterling", CK(6,0,8))
  PL_MENU_OPTION("@", CK(5,6,8))
  PL_MENU_OPTION("*", CK(6,1,8))
  PL_MENU_OPTION("^", CK(6,6,8))
  PL_MENU_OPTION("[", CK(5,5,8))
  PL_MENU_OPTION("]", CK(6,2,8))
  PL_MENU_OPTION("=", CK(6,5,8))
  PL_MENU_OPTION("<", CK(5,7,8))
  PL_MENU_OPTION(">", CK(5,4,8))
  PL_MENU_OPTION("?", CK(6,7,8))
  /* Digits */
  PL_MENU_OPTION("<-",CK(7,1,8))
  PL_MENU_OPTION("1", CK(7,0,8))
  PL_MENU_OPTION("2", CK(7,3,8))
  PL_MENU_OPTION("3", CK(1,0,8))
  PL_MENU_OPTION("4", CK(1,3,8))
  PL_MENU_OPTION("5", CK(2,0,8))
  PL_MENU_OPTION("6", CK(2,3,8))
  PL_MENU_OPTION("7", CK(3,0,8))
  PL_MENU_OPTION("8", CK(3,3,8))
  PL_MENU_OPTION("9", CK(4,0,8))
  PL_MENU_OPTION("0", CK(4,3,8))
  /* Alphabet */
  PL_MENU_OPTION("A", CK(1,2,8))
  PL_MENU_OPTION("B", CK(3,4,8))
  PL_MENU_OPTION("C", CK(2,4,8))
  PL_MENU_OPTION("D", CK(2,2,8))
  PL_MENU_OPTION("E", CK(1,6,8))
  PL_MENU_OPTION("F", CK(2,5,8))
  PL_MENU_OPTION("G", CK(3,2,8))
  PL_MENU_OPTION("H", CK(3,5,8))
  PL_MENU_OPTION("I", CK(4,1,8))
  PL_MENU_OPTION("J", CK(4,2,8))
  PL_MENU_OPTION("K", CK(4,5,8))
  PL_MENU_OPTION("L", CK(5,2,8))
  PL_MENU_OPTION("M", CK(4,4,8))
  PL_MENU_OPTION("N", CK(4,7,8))
  PL_MENU_OPTION("O", CK(4,6,8))
  PL_MENU_OPTION("P", CK(5,1,8))
  PL_MENU_OPTION("Q", CK(7,6,8))
  PL_MENU_OPTION("R", CK(2,1,8))
  PL_MENU_OPTION("S", CK(1,5,8))
  PL_MENU_OPTION("T", CK(2,6,8))
  PL_MENU_OPTION("U", CK(3,6,8))
  PL_MENU_OPTION("V", CK(3,7,8))
  PL_MENU_OPTION("W", CK(1,1,8))
  PL_MENU_OPTION("X", CK(2,7,8))
  PL_MENU_OPTION("Y", CK(3,1,8))
  PL_MENU_OPTION("Z", CK(1,4,8))
PL_MENU_OPTIONS_END
 
PL_MENU_ITEMS_BEGIN(SystemMenuDef)
  PL_MENU_HEADER("Video")
  PL_MENU_ITEM("Video standard",SYSTEM_VIDEO_STD,VideoStandardOptions,
               "\026\250\020 Select video standard")
  PL_MENU_HEADER("Sound")
  PL_MENU_ITEM("Playback",SYSTEM_SOUND,ToggleOptions,
               "\026\250\020 Enable/disable sound playback")
  PL_MENU_ITEM("SID engine",SYSTEM_SND_ENGINE,SoundEngineOptions,
               "\026\250\020 Select sound engine")
  PL_MENU_HEADER("Input")
  PL_MENU_ITEM("Joystick port",SYSTEM_JOYPORT,JoyPortOptions,
               "\026\250\020 Select joystick port")
  PL_MENU_HEADER("Peripherals")
  PL_MENU_ITEM("Cartridge",SYSTEM_CART,NULL,
               "\026\001\020 Browse\t\026\243\020 Eject")
  PL_MENU_ITEM("Tape",SYSTEM_TAPE,NULL,
               "\026\001\020 Browse\t\026\250\020 Autoload program (if image present)\t\026\243\020 Eject")
  PL_MENU_ITEM("Drive 8",SYSTEM_DRIVE8,NULL,
               "\026\001\020 Browse\t\026\250\020 Autoload program (if image present)\t\026\243\020 Eject")
  PL_MENU_ITEM("True drive emulation",SYSTEM_TRUE_DRIVE,ToggleOptions,
               "\026\250\020 Enable/disable true drive emulation")
  PL_MENU_HEADER("Options")
  PL_MENU_ITEM("Reset",SYSTEM_RESET,NULL,
               "\026\001\020 Reset system")
  PL_MENU_ITEM("Save screenshot",SYSTEM_SCRNSHOT,NULL,
               "\026\001\020 Save screenshot")
PL_MENU_ITEMS_END
PL_MENU_ITEMS_BEGIN(OptionMenuDef)
  PL_MENU_HEADER("Video")
  PL_MENU_ITEM("Screen size",OPTION_DISPLAY_MODE,ScreenSizeOptions,
               "\026\250\020 Change screen size")
  PL_MENU_ITEM("Border",OPTION_SHOW_BORDER,ToggleOptions,
               "\026\250\020 Show/hide border surrounding the main display area")
  PL_MENU_HEADER("Input")
  PL_MENU_ITEM("Virtual keyboard mode",OPTION_TOGGLE_VK,VkModeOptions,
               "\026\250\020 Select virtual keyboard mode")
  PL_MENU_HEADER("Enhancements")
  PL_MENU_ITEM("Autoload slot",OPTION_AUTOLOAD,AutoloadSlots,
               "\026\250\020 Select save state to be loaded automatically")
  PL_MENU_HEADER("Performance")
  PL_MENU_ITEM("VSync (NTSC only)",OPTION_VSYNC,ToggleOptions,
               "\026\250\020 Enable/disable vertical blanking synchronization")
#if 0
  PL_MENU_ITEM("Frame skipping",OPTION_REFRESH_RATE,RefreshRateOptions,
               "\026\250\020 Set frameskip preferences")
#endif
  PL_MENU_ITEM("PSP clock frequency",OPTION_CLOCK_FREQ,PspClockFreqOptions,
               "\026\250\020 Larger values: faster emulation, faster battery depletion (default: 222MHz)")
  PL_MENU_ITEM("Show FPS counter",OPTION_SHOW_FPS,ToggleOptions,
               "\026\250\020 Show/hide the frames-per-second counter")
  PL_MENU_ITEM("Show system indicators",OPTION_SHOW_OSI,ToggleOptions,
               "\026\250\020 Show/hide system status indicators (LED's, etc...)")
  PL_MENU_HEADER("Menu")
  PL_MENU_ITEM("Button mode",OPTION_CONTROL_MODE,ControlModeOptions,
               "\026\250\020 Change OK and Cancel button mapping")
  PL_MENU_ITEM("Animations",OPTION_ANIMATE,ToggleOptions,
               "\026\250\020 Enable/disable menu animations")
PL_MENU_ITEMS_END
PL_MENU_ITEMS_BEGIN(ControlMenuDef)
  PL_MENU_ITEM(PSP_CHAR_ANALUP,0,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_ANALDOWN,1,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_ANALLEFT,2,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_ANALRIGHT,3,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_UP,4,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_DOWN,5,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LEFT,6,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RIGHT,7,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_SQUARE,8,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_CROSS,9,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_CIRCLE,10,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_TRIANGLE,11,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER,12,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RTRIGGER,13,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_SELECT,14,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_START,15,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER"+"PSP_CHAR_RTRIGGER,16,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_START"+"PSP_CHAR_SELECT,17,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER"+"PSP_CHAR_SELECT,18,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RTRIGGER"+"PSP_CHAR_SELECT,19,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER"+"PSP_CHAR_SQUARE,20,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER"+"PSP_CHAR_CROSS,21,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER"+"PSP_CHAR_CIRCLE,22,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_LTRIGGER"+"PSP_CHAR_TRIANGLE,23,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RTRIGGER"+"PSP_CHAR_SQUARE,24,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RTRIGGER"+"PSP_CHAR_CROSS,25,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RTRIGGER"+"PSP_CHAR_CIRCLE,26,MappableButtons,ControlHelpText)
  PL_MENU_ITEM(PSP_CHAR_RTRIGGER"+"PSP_CHAR_TRIANGLE,27,MappableButtons,ControlHelpText)
PL_MENU_ITEMS_END
 
/* Default configuration */
static psp_ctrl_map_t default_map =
{
  {
    JOY|0x01,  /* Analog Up    */
    JOY|0x02,  /* Analog Down  */
    JOY|0x04,  /* Analog Left  */
    JOY|0x08,  /* Analog Right */
    JOY|0x01,  /* D-pad Up     */
    JOY|0x02,  /* D-pad Down   */
    JOY|0x04,  /* D-pad Left   */
    JOY|0x08,  /* D-pad Right  */
    0,         /* Square       */
    JOY|0x10,  /* Cross        */
    CK(7,4,8), /* Circle       */
    0,         /* Triangle     */
    0,         /* L Trigger    */
    SPC|SPC_KYBD,          /* R Trigger    */
    0,         /* Select       */
    CK(7,7,8), /* Start        */
    SPC|SPC_MENU,          /* L+R Triggers */
    0,                     /* Start+Select */
    0,                     /* L + Select   */
    0,                     /* R + Select   */
    0,                     /* L + Square   */
    0,                     /* L + Cross    */
    0,                     /* L + Circle   */
    0,                     /* L + Triangle */
    0,                     /* R + Square   */
    0,                     /* R + Cross    */
    0,                     /* R + Circle   */
    0,                     /* R + Triangle */
  }
};
 
/* Menu callbacks */
static int         OnSplashButtonPress(const struct PspUiSplash *splash,
                                       u32 button_mask);
static void        OnSplashRender(const void *uiobject, const void *null);
static const char* OnSplashGetStatusBarText(const struct PspUiSplash *splash);
 
static int  OnGenericCancel(const void *uiobject, const void* param);
static void OnGenericRender(const void *uiobject, const void *item_obj);
static int  OnGenericButtonPress(const PspUiFileBrowser *browser, const char *path,
                                 u32 button_mask);
 
static int OnMenuOk(const void *menu, const void *item);
static int OnMenuButtonPress(const struct PspUiMenu *uimenu, pl_menu_item* item,
                             u32 button_mask);
static int OnMenuItemChanged(const struct PspUiMenu *uimenu, pl_menu_item* item,
                             const pl_menu_option* option);
 
static void OnSystemRender(const void *uiobject, const void *item_obj);
 
static int OnQuickloadOk(const void *browser, const void *path);
static int OnFileOk(const void *browser, const void *path);
 
static int OnSaveStateOk(const void *gallery, const void *item);
static int OnSaveStateButtonPress(const PspUiGallery *gallery,
                                  pl_menu_item *sel,
                                  u32 button_mask);
 
PspUiSplash SplashScreen = 
{
  OnSplashRender,
  OnGenericCancel,
  OnSplashButtonPress,
  OnSplashGetStatusBarText
};
PspUiFileBrowser
  QuickloadBrowser = 
{
  OnGenericRender,
  OnQuickloadOk,
  OnGenericCancel,
  OnGenericButtonPress,
  QuickloadFilter,
  0
},
  FileBrowser = 
{
  OnGenericRender,
  OnFileOk,
  NULL,
  NULL,
  NULL,
  NULL
};
 
PspUiMenu
  OptionUiMenu =
  {
    OnGenericRender,       /* OnRender() */
    OnMenuOk,              /* OnOk() */
    OnGenericCancel,       /* OnCancel() */
    OnMenuButtonPress,     /* OnButtonPress() */
    OnMenuItemChanged,     /* OnItemChanged() */
  },
  SystemUiMenu =
  {
    OnSystemRender,        /* OnRender() */
    OnMenuOk,              /* OnOk() */
    OnGenericCancel,       /* OnCancel() */
    OnMenuButtonPress,     /* OnButtonPress() */
    OnMenuItemChanged,     /* OnItemChanged() */
  },
  ControlUiMenu =
  {
    OnGenericRender,       /* OnRender() */
    OnMenuOk,              /* OnOk() */
    OnGenericCancel,       /* OnCancel() */
    OnMenuButtonPress,     /* OnButtonPress() */
    OnMenuItemChanged,     /* OnItemChanged() */
  };
PspUiGallery SaveStateGallery = 
{
  OnGenericRender,             /* OnRender() */
  OnSaveStateOk,               /* OnOk() */
  OnGenericCancel,             /* OnCancel() */
  OnSaveStateButtonPress,      /* OnButtonPress() */
  NULL                         /* Userdata */
};
 
pl_file_path psp_current_game = {'\0'},
             psp_game_path = {'\0'},
             psp_save_state_path,
             psp_screenshot_path,
             psp_config_path,
             psp_temp_path,
             psp_tmp_file[] = { "","","","" };
psp_options_t psp_options;
 
#define CURRENT_GAME (psp_current_game)
#define GAME_LOADED (psp_current_game[0] != '\0')
#define SET_AS_CURRENT_GAME(filename) \
  strncpy(psp_current_game, filename, sizeof(psp_current_game) - 1)
 
static int psp_controls_changed;
static int psp_tab_index;
static PspImage *psp_menu_bg;
static PspImage *psp_blank_ss_icon;
static int psp_exit_menu;
static int psp_options_loaded = 0;
extern PspImage *Screen;
psp_ctrl_map_t current_map;
 
static void psp_load_options();
static int  psp_save_options();
 
static void psp_init_controls(psp_ctrl_map_t *config);
static int  psp_load_controls(const char *filename, psp_ctrl_map_t *config);
static int  psp_save_controls(const char *filename, const psp_ctrl_map_t *config);
 
static PspImage* psp_load_state_icon(const char *path);
static int psp_load_state(const char *path);
static PspImage* psp_save_state(const char *path);
 
static void psp_display_state_tab();
static void psp_display_control_tab();
static void psp_display_system_tab();
static void psp_refresh_devices();
 
int c64ui_init(int *argc, char **argv)
{
  /* Initialize paths */
  sprintf(psp_save_state_path, "%sstates/", pl_psp_get_app_directory());
  sprintf(psp_screenshot_path, "ms0:/PSP/PHOTO/%s/", PSP_APP_NAME);
  sprintf(psp_config_path, "%sconfig/", pl_psp_get_app_directory());
  sprintf(psp_temp_path, "%stemp/", pl_psp_get_app_directory());
 
  /* Create directories, if necessary */
  if (!pl_file_exists(psp_save_state_path))
    pl_file_mkdir_recursive(psp_save_state_path);
  if (!pl_file_exists(psp_config_path))
    pl_file_mkdir_recursive(psp_config_path);
  if (!pl_file_exists(psp_temp_path))
    pl_file_mkdir_recursive(psp_temp_path);
 
  /* Initialize menus */
  pl_menu_create(&OptionUiMenu.Menu, OptionMenuDef);
  pl_menu_create(&SystemUiMenu.Menu, SystemMenuDef);
  pl_menu_create(&ControlUiMenu.Menu, ControlMenuDef);
 
  /* Init NoSaveState icon image */
  psp_blank_ss_icon = pspImageCreate(160, 100, PSP_IMAGE_16BPP);
  pspImageClear(psp_blank_ss_icon, RGB(0x3e,0x31,0xa2));
 
  /* Initialize state menu */
  int i;
  pl_menu_item *item;
  for (i = 0; i < 10; i++)
  {
    item = pl_menu_append_item(&SaveStateGallery.Menu, i, NULL);
    pl_menu_set_item_help_text(item, EmptySlotText);
  }
 
  /* Load the background image */
  psp_menu_bg = pspImageLoadPng("background.png");
 
  /* Initialize UI components */
  UiMetric.Background = psp_menu_bg;
  UiMetric.Font = &PspStockFont;
  UiMetric.Left = 8;
  UiMetric.Top = 24;
  UiMetric.Right = 472;
  UiMetric.Bottom = 240;
  UiMetric.ScrollbarColor = PSP_COLOR_GRAY;
  UiMetric.ScrollbarBgColor = 0x44ffffff;
  UiMetric.ScrollbarWidth = 10;
  UiMetric.TextColor = PSP_COLOR_GRAY;
  UiMetric.SelectedColor = COLOR(0xf7,0xc2,0x50,0xFF);
  UiMetric.SelectedBgColor = COLOR(0xff,0xff,0xff,0x99);
  UiMetric.StatusBarColor = PSP_COLOR_WHITE;
  UiMetric.BrowserFileColor = PSP_COLOR_GRAY;
  UiMetric.BrowserDirectoryColor = PSP_COLOR_YELLOW;
  UiMetric.GalleryIconsPerRow = 5;
  UiMetric.GalleryIconMarginWidth = 8;
  UiMetric.MenuItemMargin = 20;
  UiMetric.MenuSelOptionBg = PSP_COLOR_BLACK;
  UiMetric.MenuOptionBoxColor = PSP_COLOR_GRAY;
  UiMetric.MenuOptionBoxBg = COLOR(0x3e,0x31,0xa2,0xCC);
  UiMetric.MenuDecorColor = UiMetric.SelectedColor;
  UiMetric.DialogFogColor = COLOR(0x3e,0x31,0xa2,0x66);
  UiMetric.TitlePadding = 4;
  UiMetric.TitleColor = PSP_COLOR_WHITE;
  UiMetric.MenuFps = 30;
  UiMetric.TabBgColor = PSP_COLOR_WHITE;
  UiMetric.BrowserScreenshotPath = psp_screenshot_path;
  UiMetric.BrowserScreenshotDelay = 30;
 
  psp_tab_index = TAB_ABOUT;
  psp_options_loaded = 0;
 
  /* Load default configuration */
  psp_load_controls("DEFAULT", &default_map);
  psp_init_controls(&current_map);
 
  return 0;
}
 
void c64ui_shutdown()
{
  if (psp_menu_bg) pspImageDestroy(psp_menu_bg);
 
  pl_menu_destroy(&ControlUiMenu.Menu);
  pl_menu_destroy(&OptionUiMenu.Menu);
  pl_menu_destroy(&SystemUiMenu.Menu);
  pl_menu_destroy(&SaveStateGallery.Menu);
 
  /* Remove temp. files (if any) */
  int i;
  for (i = 0; i < 4; i++)
    if (*psp_tmp_file[i] && pl_file_exists(psp_tmp_file[i]))
      pl_file_rm(psp_tmp_file[i]);
 
  psp_save_options();
}
 
/**************************/
/* Helper functions       */
/**************************/
 
static void psp_refresh_devices()
{
  int unit;
  pl_menu_item *item;
  const char *name;
 
  /* Refresh tape contents */
  do /* For flow control - not a loop */
  {
    name = tape_image_dev1->name; /* Filename */
    item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_TAPE);
    pl_menu_clear_options(item); /* Clear current names */
 
    if (!name) break;
 
    image_contents_t *listing = tapecontents_read(name);
 
    if (listing == NULL)
      break;
 
    image_contents_file_list_t *element = listing->file_list;
 
    do 
    {
      char *string = image_contents_file_to_string(element, 1);
      pl_menu_append_option(item, string, NULL, 0);
      lib_free(string);
    } while ( (element = element->next) != NULL);
 
    /* Select first option */
    pl_menu_select_option_by_index(item, 0);
  } while (0);
 
  /* Refresh drive contents */  
  for (unit = 8; unit <= 8; unit++)
  {
    name = file_system_get_disk_name(unit); /* Filename */
    item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, GET_DRIVE_MENU_ID(unit));
    pl_menu_clear_options(item); /* Clear current names */
 
    if (!name) continue;
 
    image_contents_t *listing = diskcontents_read(name, unit);
 
    if (listing == NULL)
      continue;
 
    image_contents_file_list_t *element = listing->file_list;
 
    do 
    {
      char *string = image_contents_file_to_string(element, 1);
      pl_menu_append_option(item, string, NULL, 0);
      lib_free(string);
    } while ( (element = element->next) != NULL);
 
    /* Select first option */
    pl_menu_select_option_by_index(item, 0);
  }
 
  /* Cart name */
  name = cartridge_get_file_name(0);
  item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_CART);
  pl_menu_clear_options(item); /* Clear current names */
  if (name)
    pl_menu_append_option(item, pl_file_get_filename(name), NULL, 1);
}
 
static void psp_display_system_tab()
{
  pl_menu_item *item;
 
  item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_JOYPORT);
  pl_menu_select_option_by_value(item, (void*)psp_options.joyport);
 
  int setting;
  resources_get_int("SidEngine", &setting);
  item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_SND_ENGINE);
  pl_menu_select_option_by_value(item, (void*)setting);
  resources_get_int("Sound", &setting);
  item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_SOUND);
  pl_menu_select_option_by_value(item, (void*)setting);
  resources_get_int("DriveTrueEmulation", &setting);
  item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_TRUE_DRIVE);
  pl_menu_select_option_by_value(item, (void*)setting);
  resources_get_int("MachineVideoStandard", &setting);
  item = pl_menu_find_item_by_id(&SystemUiMenu.Menu, SYSTEM_VIDEO_STD);
  pl_menu_select_option_by_value(item, (void*)setting);
 
  psp_refresh_devices();
  pspUiOpenMenu(&SystemUiMenu, NULL);
}
 
static void psp_display_control_tab()
{
  pl_menu_item *item;
  const char *config_name = (GAME_LOADED)
    ? pl_file_get_filename(psp_current_game) : "BASIC";
  char *game_name = strdup(config_name);
  char *dot = strrchr(game_name, '.');
  int i;
  if (dot) *dot='\0';
 
  /* Load current button mappings */
  for (item = ControlUiMenu.Menu.items, i = 0; item; item = item->next, i++)
    pl_menu_select_option_by_value(item, (void*)current_map.button_map[i]);
 
  psp_controls_changed = 0;
 
  pspUiOpenMenu(&ControlUiMenu, game_name);
  free(game_name);
 
  /* Save to MS, if configuration changed */
  if (psp_controls_changed)
  {
    pspUiFlashMessage("Saving configuration, please wait...");
    if (!psp_save_controls(config_name, &current_map))
      pspUiAlert("ERROR: Changes not saved");
  }
}
 
static void psp_init_controls(psp_ctrl_map_t *config)
{
  /* Initialize to default configuration */
  if (config != &default_map)
    memcpy(config, &default_map, sizeof(psp_ctrl_map_t));
}
 
static int psp_load_controls(const char *filename, psp_ctrl_map_t *config)
{
  pl_file_path path;
  snprintf(path, sizeof(path), "%s%s.cnf", psp_config_path, filename);
 
  /* Initialize default controls */
  psp_init_controls(config);
 
  /* No configuration; defaults are fine */
  if (!pl_file_exists(path))
    return 1;
 
  /* Open file for reading */
  FILE *file = fopen(path, "r");
  if (!file) return 0;
 
  /* Load defaults; attempt to read controls from file */
  psp_init_controls(config);
  int nread = fread(config, sizeof(uint32_t), MAP_BUTTONS, file);
 
  fclose(file);
 
  /* Reading less than MAP_BUTTONS is ok; may be an older config file */
  if (nread < 1)
  {
    psp_init_controls(config);
    return 0;
  }
 
  return 1;
}
 
static int psp_save_controls(const char *filename, const psp_ctrl_map_t *config)
{
  pl_file_path path;
  snprintf(path, sizeof(path)-1, "%s%s.cnf", psp_config_path, filename);
 
  /* Open file for writing */
  FILE *file = fopen(path, "w");
  if (!file) return 0;
 
  /* Write contents of struct */
  int nwritten = fwrite(config, sizeof(psp_ctrl_map_t), 1, file);
  fclose(file);
 
  return (nwritten == 1);
}
 
/* Load state icon */
static PspImage* psp_load_state_icon(const char *path)
{
  FILE *f = fopen(path, "r");
  if (!f) return NULL;
 
  /* Load image */
  PspImage *image = pspImageLoadPngFd(f);
  fclose(f);
 
  return image;
}
 
/* Load state */
static int psp_load_state(const char *path)
{
  /* Open file for reading */
  FILE *f = fopen(path, "r");
  if (!f) return 0;
 
  pspUiFlashMessage("Loading state, please wait...");
 
  /* Load image into temporary object */
  PspImage *image = pspImageLoadPngFd(f);
  pspImageDestroy(image);
 
  /* Load the state data */
  /* HACK: snapshot saving overridden in snapshot.c */
  int status = machine_read_snapshot((char*)f, 0);
  fclose(f);
 
  return status == 0;
}
 
/* Save state */
static PspImage* psp_save_state(const char *path)
{
  /* Create copy of the screen */
  PspImage *copy = pspImageCreateCopy(Screen);
  if (!copy) return NULL;
 
  /* Reset viewport to 320x200 */
  psp_reset_viewport(&copy->Viewport, 0);
 
  /* Create thumbnail, destroy copy */
  PspImage *thumb = pspImageCreateThumbnail(copy);
  pspImageDestroy(copy);
  if (!thumb) return NULL;
 
  /* Open file for writing */
  FILE *f;
  if (!(f = fopen(path, "w")))
  {
    pspImageDestroy(thumb);
    return NULL;
  }
 
  /* Write the thumbnail */
  if (!pspImageSavePngFd(f, thumb))
  {
    pspImageDestroy(thumb);
    fclose(f);
    return NULL;
  }
 
  /* Write the state */
  /* HACK: snapshot saving overridden in snapshot.c */
  if (machine_write_snapshot((char*)f, 0, 0, 0) < 0)
  {
    pspImageDestroy(thumb);
    thumb = NULL;
  }
 
  fclose(f);
  return thumb;
}
 
static void psp_load_options()
{
  pl_file_path path;
  snprintf(path, sizeof(path) - 1, "%s%s", pl_psp_get_app_directory(), "options.ini");
 
  /* Load INI */
  pl_ini_file file;
  pl_ini_load(&file, path);
 
  psp_options.autoload_slot = pl_ini_get_int(&file, "System", "AutoloadSlot", 9);
  psp_options.joyport = pl_ini_get_int(&file, "System", "JoystickPort", 2);
  psp_options.show_border = pl_ini_get_int(&file, "Video", "ShowBorder", 1);
  psp_options.display_mode = pl_ini_get_int(&file, "Video", "DisplayMode", 
                                            DISPLAY_MODE_UNSCALED);
  psp_options.clock_freq = pl_ini_get_int(&file, "Video", "PSPClockFrequency", 300);
  psp_options.show_fps = pl_ini_get_int(&file, "Video", "ShowFPS", 0);
  psp_options.show_osi = pl_ini_get_int(&file, "Video", "ShowOSI", 0);
  psp_options.vsync = pl_ini_get_int(&file, "Video", "VSync", 0);
  psp_options.control_mode = pl_ini_get_int(&file, "Menu", "ControlMode", 0);
  psp_options.animate_menu = pl_ini_get_int(&file, "Menu", "Animate", 1);
  psp_options.toggle_vk = pl_ini_get_int(&file, "Input", "VKMode", 0);
  pl_ini_get_string(&file, "File", "GamePath", NULL, psp_game_path, sizeof(psp_game_path));
 
  /* VICE settings */
  int vice_setting;
  vice_setting = pl_ini_get_int(&file, "VICE", "SidEngine", SID_ENGINE_FASTSID);
  resources_set_int("SidEngine", vice_setting);
  vice_setting = pl_ini_get_int(&file, "VICE", "DriveTrueEmulation", 1);
  resources_set_int("DriveTrueEmulation", vice_setting);
  vice_setting = pl_ini_get_int(&file, "VICE", "Sound", 1);
  resources_set_int("Sound", vice_setting);
  vice_setting = pl_ini_get_int(&file, "VICE", "MachineVideoStandard", MACHINE_SYNC_PAL);
  resources_set_int("MachineVideoStandard", vice_setting);
  vice_setting = pl_ini_get_int(&file, "VICE", "RefreshRate", MACHINE_SYNC_PAL);
  resources_set_int("RefreshRate", vice_setting);
 
  /* Clean up */
  pl_ini_destroy(&file);
 
  /* Reset menu prefs */
  UiMetric.OkButton = (!psp_options.control_mode)
    ? PSP_CTRL_CROSS : PSP_CTRL_CIRCLE;
  UiMetric.CancelButton = (!psp_options.control_mode)
    ? PSP_CTRL_CIRCLE : PSP_CTRL_CROSS;
  UiMetric.Animate = psp_options.animate_menu;
}
 
static int psp_save_options()
{
  pl_file_path path;
  snprintf(path, sizeof(path)-1, "%s%s", pl_psp_get_app_directory(), "options.ini");
 
  /* Initialize INI structure */
  pl_ini_file file;
  pl_ini_create(&file);
  pl_ini_set_int(&file, "System", "AutoloadSlot", psp_options.autoload_slot);
  pl_ini_set_int(&file, "System", "JoystickPort", psp_options.joyport);
  pl_ini_set_int(&file, "Video", "ShowBorder", psp_options.show_border);
  pl_ini_set_int(&file, "Video", "DisplayMode", psp_options.display_mode);
  pl_ini_set_int(&file, "Video", "PSPClockFrequency", psp_options.clock_freq);
  pl_ini_set_int(&file, "Video", "ShowFPS", psp_options.show_fps);
  pl_ini_set_int(&file, "Video", "ShowOSI", psp_options.show_osi);
  pl_ini_set_int(&file, "Video", "VSync", psp_options.vsync);
  pl_ini_set_int(&file, "Menu", "ControlMode", psp_options.control_mode);
  pl_ini_set_int(&file, "Menu", "Animate", psp_options.animate_menu);
  pl_ini_set_int(&file, "Input", "VKMode", psp_options.toggle_vk);
  pl_ini_set_string(&file, "File", "GamePath", psp_game_path);
 
  /* VICE settings */
  int vice_setting;
  resources_get_int("SidEngine", &vice_setting);
  pl_ini_set_int(&file, "VICE", "SidEngine", vice_setting);
  resources_get_int("DriveTrueEmulation", &vice_setting);
  pl_ini_set_int(&file, "VICE", "DriveTrueEmulation", vice_setting);
  resources_get_int("Sound", &vice_setting);
  pl_ini_set_int(&file, "VICE", "Sound", vice_setting);
  resources_get_int("MachineVideoStandard", &vice_setting);
  pl_ini_set_int(&file, "VICE", "MachineVideoStandard", vice_setting);
  resources_get_int("RefreshRate", &vice_setting);
  pl_ini_set_int(&file, "VICE", "RefreshRate", vice_setting);
 
  int status = pl_ini_save(&file, path);
  pl_ini_destroy(&file);
 
  return status;
}
 
static const char *prepare_file(const char *path, int slot)
{
  const char *game_path = path;
  void *file_buffer = NULL;
  int file_size = 0;
 
  if (pl_file_is_of_type(path, "ZIP"))
  {
    pspUiFlashMessage("Loading compressed file, please wait...");
 
    char archived_file[512];
    unzFile zipfile = NULL;
    unz_global_info gi;
    unz_file_info fi;
 
    /* Open archive for reading */
    if (!(zipfile = unzOpen(path)))
      return NULL;
 
    /* Get global ZIP file information */
    if (unzGetGlobalInfo(zipfile, &gi) != UNZ_OK)
    {
      unzClose(zipfile);
      return NULL;
    }
 
    const char *extension;
    int i, j;
 
    for (i = 0; i < (int)gi.number_entry; i++)
    {
      /* Get name of the archived file */
      if (unzGetCurrentFileInfo(zipfile, &fi, archived_file, 
          sizeof(archived_file), NULL, 0, NULL, 0) != UNZ_OK)
      {
        unzClose(zipfile);
        return NULL;
      }
 
      extension = pl_file_get_extension(archived_file);
      for (j = 1; QuickloadFilter[j]; j++)
      {
        if (strcasecmp(QuickloadFilter[j], extension) == 0)
        {
          file_size = fi.uncompressed_size;
 
          /* Open archived file for reading */
          if(unzOpenCurrentFile(zipfile) != UNZ_OK)
          {
            unzClose(zipfile); 
            return NULL;
          }
 
          if (!(file_buffer = malloc(file_size)))
          {
            unzCloseCurrentFile(zipfile);
            unzClose(zipfile); 
            return NULL;
          }
 
          unzReadCurrentFile(zipfile, file_buffer, file_size);
          unzCloseCurrentFile(zipfile);
 
          goto close_archive;
        }
      }
 
      /* Go to the next file in the archive */
      if (i + 1 < (int)gi.number_entry)
      {
        if (unzGoToNextFile(zipfile) != UNZ_OK)
        {
          unzClose(zipfile);
          return NULL;
        }
      }
    }
 
    /* No eligible files */
    return NULL;
 
close_archive:
    unzClose(zipfile);
 
    /* Remove temp. file (if any) */
    if (*psp_tmp_file[slot] && pl_file_exists(psp_tmp_file[slot]))
      pl_file_rm(psp_tmp_file[slot]);
 
    /* Define temp filename */
    sprintf(psp_tmp_file[slot], "%s%s", psp_temp_path, archived_file);
 
    /* Write file to stick */
    FILE *file = fopen(psp_tmp_file[slot], "w");
    if (!file)
    {
      *psp_tmp_file[slot] = '\0';
      return NULL;
    }
    if (fwrite(file_buffer, 1, file_size, file) < file_size)
    {
      fclose(file);
      *psp_tmp_file[slot] = '\0';
      return NULL;
    }
    fclose(file);
 
    game_path = psp_tmp_file[slot];
  }
 
  return game_path;
}
 
static int psp_load_game(const char *path)
{
  /* Eject all cartridges, tapes & disks */
  cartridge_detach_image();
  tape_image_detach(1);
  file_system_detach_disk(GET_DRIVE(8));
 
  const char *game_path = prepare_file(path, 0);
 
  if (!game_path)
  {
    pspUiAlert("Error loading compressed file");
    return 0;
  }
 
  if (pl_file_is_of_type(game_path, "CRT"))
    return !cartridge_attach_image(CARTRIDGE_CRT, game_path);
 
  return !autostart_autodetect(game_path, NULL, 0, AUTOSTART_MODE_RUN);
}
 
void psp_display_menu()
{
  /* Load the options. Loading them in c64_init crashes the emulator */
  if (!psp_options_loaded)
  {
    psp_load_options();
    psp_options_loaded = 1;
  }
 
  int setting = 0;
  pl_menu_item *item;
  psp_exit_menu = 0;
 
  /* Set normal clock frequency */
  pl_psp_set_clock_freq(222);
  /* Set buttons to autorepeat */
  pspCtrlSetPollingMode(PSP_CTRL_AUTOREPEAT);
 
  /* Menu loop */
  while (!ExitPSP && !psp_exit_menu)
  {
    /* Display appropriate tab */
    switch (psp_tab_index)
    {
    case TAB_QUICKLOAD:
      pspUiOpenBrowser(&QuickloadBrowser, 
        (GAME_LOADED) ? psp_current_game 
          : ((psp_game_path[0]) ? psp_game_path : NULL));
      break;
    case TAB_CONTROLS:
      psp_display_control_tab();
      break;
    case TAB_OPTIONS:
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_DISPLAY_MODE);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.display_mode);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_CLOCK_FREQ);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.clock_freq);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_SHOW_FPS);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.show_fps);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_SHOW_OSI);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.show_osi);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_CONTROL_MODE);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.control_mode);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_ANIMATE);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.animate_menu);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_TOGGLE_VK);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.toggle_vk);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_AUTOLOAD);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.autoload_slot);
      item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_SHOW_BORDER);
      pl_menu_select_option_by_value(item, (void*)(int)psp_options.show_border);
      if ((item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_VSYNC)))
        pl_menu_select_option_by_value(item, (void*)(int)psp_options.vsync);
      resources_get_int("RefreshRate", &setting);
      if ((item = pl_menu_find_item_by_id(&OptionUiMenu.Menu, OPTION_REFRESH_RATE)))
        pl_menu_select_option_by_value(item, (void*)setting);
 
      pspUiOpenMenu(&OptionUiMenu, NULL);
      break;
    case TAB_STATE:
      psp_display_state_tab();
      break;
    case TAB_SYSTEM:
      psp_display_system_tab();
      break;
    case TAB_ABOUT:
      pspUiSplashScreen(&SplashScreen);
      break;
    }
  }
 
  if (!ExitPSP)
  {
    /* Set clock frequency during emulation */
    pl_psp_set_clock_freq(psp_options.clock_freq);
    /* Set buttons to normal mode */
    pspCtrlSetPollingMode(PSP_CTRL_NORMAL);
 
    if (psp_options.animate_menu)
      pspUiFadeout();
  }
}
 
static void psp_display_state_tab()
{
  pl_menu_item *item, *sel = NULL;
  SceIoStat stat;
  ScePspDateTime latest;
  char caption[32];
  const char *config_name = (GAME_LOADED)
    ? pl_file_get_filename(psp_current_game) : "BASIC";
  char *path = (char*)malloc(strlen(psp_save_state_path) + strlen(config_name) + 8);
  char *game_name = strdup(config_name);
  char *dot = strrchr(game_name, '.');
  if (dot) *dot='\0';
 
  memset(&latest,0,sizeof(latest));
 
  /* Initialize icons */
  for (item = SaveStateGallery.Menu.items; item; item = item->next)
  {
    sprintf(path, "%s%s_%02i.sna", psp_save_state_path, config_name, item->id);
 
    if (pl_file_exists(path))
    {
      if (sceIoGetstat(path, &stat) < 0)
        sprintf(caption, "ERROR");
      else
      {
        /* Determine the latest save state */
        if (pl_util_date_compare(&latest, &stat.st_mtime) < 0)
        {
          sel = item;
          latest = stat.st_mtime;
        }
 
        sprintf(caption, "%02i/%02i/%02i %02i:%02i%s", 
          stat.st_mtime.month,
          stat.st_mtime.day,
          stat.st_mtime.year - (stat.st_mtime.year / 100) * 100,
          stat.st_mtime.hour,
          stat.st_mtime.minute,
          ((int)item->id == psp_options.autoload_slot) ? "*" : "");
      }
 
      pl_menu_set_item_caption(item, caption);
      item->param = psp_load_state_icon(path);
      pl_menu_set_item_help_text(item, PresentSlotText);
    }
    else
    {
      pl_menu_set_item_caption(item, ((int)item->id == psp_options.autoload_slot)
          ? "Autoload" : "Empty");
      item->param = psp_blank_ss_icon;
      pl_menu_set_item_help_text(item, EmptySlotText);
    }
  }
 
  free(path);
 
  /* Highlight the latest save state if none are selected */
  if (SaveStateGallery.Menu.selected == NULL)
    SaveStateGallery.Menu.selected = sel;
 
  pspUiOpenGallery(&SaveStateGallery, game_name);
  free(game_name);
 
  /* Destroy any icons */
  for (item = SaveStateGallery.Menu.items; item; item = item->next)
    if (item->param != NULL && item->param != psp_blank_ss_icon)
      pspImageDestroy((PspImage*)item->param);
}
 
/**************************/
/* psplib event callbacks */
/**************************/
static int OnGenericCancel(const void *uiobject, const void* param)
{
  psp_exit_menu = 1;
  return 1;
}
 
static void OnGenericRender(const void *uiobject, const void *item_obj)
{
  int height = pspFontGetLineHeight(UiMetric.Font);
  int width;
 
  /* Draw tabs */
  int i, x;
  for (i = 0, x = 5; i <= TAB_MAX; i++, x += width + 10)
  {
    /* Determine width of text */
    width = pspFontGetTextWidth(UiMetric.Font, TabLabel[i]);
 
    /* Draw background of active tab */
    if (i == psp_tab_index) 
      pspVideoFillRect(x - 5, 0, x + width + 5, height + 1, 
        UiMetric.TabBgColor);
 
    /* Draw name of tab */
    pspVideoPrint(UiMetric.Font, x, 0, TabLabel[i], PSP_COLOR_WHITE);
  }
}
 
static int OnGenericButtonPress(const PspUiFileBrowser *browser,
  const char *path, u32 button_mask)
{
  /* If L or R are pressed, switch tabs */
  if (button_mask & PSP_CTRL_LTRIGGER)
  { if (--psp_tab_index < 0) psp_tab_index = TAB_MAX; }
  else if (button_mask & PSP_CTRL_RTRIGGER)
  { if (++psp_tab_index > TAB_MAX) psp_tab_index = 0; }
  else if ((button_mask & (PSP_CTRL_START | PSP_CTRL_SELECT)) 
    == (PSP_CTRL_START | PSP_CTRL_SELECT))
  {
    if (pl_util_save_vram_seq(psp_screenshot_path, "UI"))
      pspUiAlert("Saved successfully");
    else
      pspUiAlert("ERROR: Not saved");
    return 0;
  }
  else return 0;
 
  return 1;
}
 
static void OnSplashRender(const void *splash, const void *null)
{
  int fh, i, x, y, height;
  const char *lines[] = 
  { 
    PSP_APP_NAME" version "PSP_APP_VER" ("__DATE__")",
    "\026http://psp.akop.org/vice",
    " ",
    "2009 Akop Karapetyan",
    "1998-2009 VICE team",
    NULL
  };
 
  fh = pspFontGetLineHeight(UiMetric.Font);
 
  for (i = 0; lines[i]; i++);
  height = fh * (i - 1);
 
  /* Render lines */
  for (i = 0, y = SCR_HEIGHT / 2 - height / 2; lines[i]; i++, y += fh)
  {
    x = SCR_WIDTH / 2 - pspFontGetTextWidth(UiMetric.Font, lines[i]) / 2;
    pspVideoPrint(UiMetric.Font, x, y, lines[i], PSP_COLOR_GRAY);
  }
 
  /* Render PSP status */
  OnGenericRender(splash, null);
}
 
static int OnSplashButtonPress(const struct PspUiSplash *splash, 
                               u32 button_mask)
{
  return OnGenericButtonPress(NULL, NULL, button_mask);
}
 
static const char* OnSplashGetStatusBarText(const struct PspUiSplash *splash)
{
  return "\026\255\020/\026\256\020 Switch tabs";
}
 
static int OnMenuOk(const void *uimenu, const void* sel_item)
{
  switch (((const pl_menu_item*)sel_item)->id)
  {
  case SYSTEM_CART:
    FileBrowser.Userdata = (void*)0; /* Cartridge */
    FileBrowser.Filter = CartFilter;
    pspUiOpenBrowser(&FileBrowser, (*psp_game_path) ? psp_game_path : NULL);
    psp_refresh_devices();
    break;
  case SYSTEM_TAPE:
    FileBrowser.Userdata = (void*)1; /* Tape */
    FileBrowser.Filter = TapeFilter;
    pspUiOpenBrowser(&FileBrowser, (*psp_game_path) ? psp_game_path : NULL);
    psp_refresh_devices();
    break;
  case SYSTEM_DRIVE8:
    FileBrowser.Userdata = (void*)GET_DRIVE(((const pl_menu_item*)sel_item)->id);
    FileBrowser.Filter = DiskFilter;
    pspUiOpenBrowser(&FileBrowser, (*psp_game_path) ? psp_game_path : NULL);
    psp_refresh_devices();
    break;
  case SYSTEM_RESET:
    if (pspUiConfirm("Reset the system?"))
    {
      psp_exit_menu = 1;
      machine_trigger_reset(MACHINE_RESET_MODE_SOFT);
      return 1;
    }
    break;
  case SYSTEM_SCRNSHOT:
    /* Save screenshot */
    if (!pl_util_save_image_seq(psp_screenshot_path, (GAME_LOADED)
                                ? pl_file_get_filename(psp_current_game) : "BASIC",
                                Screen))
      pspUiAlert("ERROR: Screenshot not saved");
    else
      pspUiAlert("Screenshot saved successfully");
    break;
  }
 
  return 0;
}
 
static int OnMenuButtonPress(const struct PspUiMenu *uimenu,
                             pl_menu_item* sel_item,
                             u32 button_mask)
{
  if (uimenu == &ControlUiMenu)
  {
    if (button_mask & PSP_CTRL_SQUARE)
    {
      /* Save to MS as default mapping */
      if (psp_save_controls("DEFAULT", &current_map))
      {
        /* Modify in-memory defaults */
        memcpy(&default_map, &current_map, sizeof(psp_ctrl_map_t));
        pspUiAlert("Changes saved");
      }
      else
        pspUiAlert("ERROR: Changes not saved");
 
      return 0;
    }
    else if (button_mask & PSP_CTRL_TRIANGLE)
    {
      pl_menu_item *item;
      int i;
 
      /* Load default mapping */
      memcpy(&current_map, &default_map, sizeof(psp_ctrl_map_t));
 
      /* Modify the menu */
      for (item = ControlUiMenu.Menu.items, i = 0; item; item = item->next, i++)
        pl_menu_select_option_by_value(item, (void*)default_map.button_map[i]);
 
      return 0;
    }
  }
  else
  {
    int id = ((const pl_menu_item*)sel_item)->id;
    if (button_mask & PSP_CTRL_TRIANGLE)
    {
      switch (id)
      {
      case SYSTEM_CART:
        cartridge_detach_image();
        psp_refresh_devices();
        break;
      case SYSTEM_TAPE:
        tape_image_detach(1);
        psp_refresh_devices();
        break;
      case SYSTEM_DRIVE8:
        file_system_detach_disk(GET_DRIVE(id));
        psp_refresh_devices();
        break;
      }
    }
  }
 
  return OnGenericButtonPress(NULL, NULL, button_mask);
}
 
static int OnMenuItemChanged(const struct PspUiMenu *uimenu,
                             pl_menu_item* item,
                             const pl_menu_option* option)
{
  if (uimenu == &ControlUiMenu)
  {
    current_map.button_map[item->id] = (unsigned int)option->value;
    psp_controls_changed = 1;
  }
  else
  {
    switch((int)item->id)
    {
    case OPTION_DISPLAY_MODE:
      psp_options.display_mode = (int)option->value;
      break;
    case OPTION_CLOCK_FREQ:
      psp_options.clock_freq = (int)option->value;
      break;
    case OPTION_SHOW_FPS:
      psp_options.show_fps = (int)option->value;
      break;
    case OPTION_SHOW_OSI:
      psp_options.show_osi = (int)option->value;
      break;
    case OPTION_TOGGLE_VK:
      psp_options.toggle_vk = (int)option->value;
      break;
    case OPTION_CONTROL_MODE:
      psp_options.control_mode = (int)option->value;
      UiMetric.OkButton = (!psp_options.control_mode) 
                          ? PSP_CTRL_CROSS : PSP_CTRL_CIRCLE;
      UiMetric.CancelButton = (!psp_options.control_mode) 
                              ? PSP_CTRL_CIRCLE : PSP_CTRL_CROSS;
      break;
    case OPTION_ANIMATE:
      psp_options.animate_menu = (int)option->value;
      UiMetric.Animate = psp_options.animate_menu;
      break;
    case OPTION_AUTOLOAD:
      psp_options.autoload_slot = (int)option->value;
      break;
    case OPTION_SHOW_BORDER:
      psp_options.show_border = (int)option->value;
      break;
    case OPTION_REFRESH_RATE:
      resources_set_int("RefreshRate", (int)option->value);
      break;
    case OPTION_VSYNC:
      psp_options.vsync = (int)option->value;
      resources_set_int("VBLANKSync", (int)option->value);
      break;
    case SYSTEM_SND_ENGINE:
      resources_set_int("SidEngine", (int)option->value);
      break;
    case SYSTEM_SOUND:
      resources_set_int("Sound", (int)option->value);
      break;
    case SYSTEM_TRUE_DRIVE:
      resources_set_int("DriveTrueEmulation", (int)option->value);
      break;
    case SYSTEM_VIDEO_STD:
      resources_set_int("MachineVideoStandard", (int)option->value);
      break;
    case SYSTEM_TAPE:
      {
        int index = pl_menu_get_option_index(item, option);
        const char *name = tape_image_dev1->name; /* Filename */
 
        pspUiFlashMessage("Autoloading, please wait...");
 
        if (autostart_autodetect(name, NULL, index, AUTOSTART_MODE_RUN))
        {
          pspUiAlert("Error loading program");
          return 0;
        }
      }
      break;
    case SYSTEM_DRIVE8:
      {
        int index = pl_menu_get_option_index(item, option);
        const char *name = file_system_get_disk_name(GET_DRIVE((int)item->id));
 
        pspUiFlashMessage("Autoloading, please wait...");
 
        if (autostart_autodetect(name, NULL, index, AUTOSTART_MODE_RUN))
        {
          pspUiAlert("Error loading program");
          return 0;
        }
      }
      break;
    case SYSTEM_JOYPORT:
      psp_options.joyport = (int)option->value;
      break;
    }
  }
 
  return 1;
}
 
static void OnSystemRender(const void *uiobject, const void *item_obj)
{
  int w, h, x, y;
  w = Screen->Viewport.Width >> 1;
  h = Screen->Viewport.Height >> 1;
  x = UiMetric.Right - w - UiMetric.ScrollbarWidth;
  y = SCR_HEIGHT - h - 80;
 
  /* Draw a small representation of the screen */
  pspVideoShadowRect(x, y, x + w - 1, y + h - 1, PSP_COLOR_BLACK, 3);
  pl_gfx_put_image(Screen, x, y, w, h);
  pspVideoDrawRect(x, y, x + w - 1, y + h - 1, PSP_COLOR_GRAY);
 
  OnGenericRender(uiobject, item_obj);
}
 
static int OnFileOk(const void *browser, const void *path)
{
  const char *game_path;
  int set_as_current = 0, 
      slot,
      drive_num = (int)((const PspUiFileBrowser*)browser)->Userdata;
 
  /* Detach */
  switch (drive_num)
  {
  case 0:
    cartridge_detach_image();
    slot = 1;
    break;
  case 1:
    tape_image_detach(1);
    slot = 2;
    break;
  default:
    file_system_detach_disk(drive_num);
    slot = 3;
    break;
  }
 
  if (!(game_path = prepare_file(path, slot)))
  {
    pspUiAlert("Error loading file");
    return 0;
  }
 
  /* Attach */
  if (drive_num == 0) /* Cart */
  {
    if (cartridge_attach_image(CARTRIDGE_CRT, game_path) < 0)
    {
      pspUiAlert("Error loading cartridge");
      return 0;
    }
 
    set_as_current = 1;
  }
  else if (drive_num == 1) /* Tape */
  {
    if (tape_image_attach(1, game_path) < 0)
    {
      pspUiAlert("Error loading tape image");
      return 0;
    }
 
    set_as_current = 1;
  }
  else /* Disk */
  {
    if (file_system_attach_disk(drive_num, game_path) < 0)
    {
      pspUiAlert("Error loading disk image");
      return 0;
    }
 
    /* If disk 8, set as currently loaded game */
    set_as_current = (drive_num == 8);
  }
 
  if (set_as_current)
  {
    /* Set as currently loaded game */
    SET_AS_CURRENT_GAME(path);
    pl_file_get_parent_directory(path, psp_game_path, sizeof(psp_game_path));
    psp_load_controls(pl_file_get_filename(psp_current_game), &current_map);
 
    /* Reset selected state */
    SaveStateGallery.Menu.selected = NULL;
  }
 
  return 1;
}
 
static int OnQuickloadOk(const void *browser, const void *path)
{
  if (!psp_load_game(path))
  {
    pspUiAlert("Error loading file");
    return 0;
  }
 
  psp_exit_menu = 1;
 
  SET_AS_CURRENT_GAME(path);
  pl_file_get_parent_directory(path,
                               psp_game_path,
                               sizeof(psp_game_path));
  if (!psp_load_controls((GAME_LOADED)
                           ? pl_file_get_filename(psp_current_game) : "BASIC",
                         &current_map));
 
  /* Autoload saved state */
  if (psp_options.autoload_slot >= 0)
  {
    const char *config_name = (GAME_LOADED)
                              ? pl_file_get_filename(psp_current_game) : "BASIC";
    pl_file_path state_file;
    snprintf(state_file, sizeof(state_file) - 1, 
             "%s%s_%02i.sna", psp_save_state_path, config_name, 
             psp_options.autoload_slot);
 
    /* Attempt loading saved state (don't care if fails) */
    psp_load_state(state_file);
  }
 
  /* Reset selected state */
  SaveStateGallery.Menu.selected = NULL;
 
  return 1;
}
 
static int OnSaveStateOk(const void *gallery, const void *item)
{
  char *path;
  const char *config_name = (GAME_LOADED) 
    ? pl_file_get_filename(psp_current_game) : "BASIC";
 
  path = (char*)malloc(strlen(psp_save_state_path) + strlen(config_name) + 8);
  sprintf(path, "%s%s_%02i.sna", psp_save_state_path, config_name,
    ((const pl_menu_item*)item)->id);
 
  if (pl_file_exists(path) && pspUiConfirm("Load state?"))
  {
    if (psp_load_state(path))
    {
      psp_exit_menu = 1;
      pl_menu_find_item_by_id(&((PspUiGallery*)gallery)->Menu,
        ((pl_menu_item*)item)->id);
      free(path);
 
      return 1;
    }
 
    pspUiAlert("ERROR: State failed to load\nSee documentation for possible reasons");
  }
 
  free(path);
  return 0;
}
 
static int OnSaveStateButtonPress(const PspUiGallery *gallery, 
                                  pl_menu_item *sel,
                                  u32 button_mask)
{
  if (button_mask & PSP_CTRL_SQUARE 
    || button_mask & PSP_CTRL_TRIANGLE
    || button_mask & PSP_CTRL_START)
  {
    char *path;
    char caption[32];
    const char *config_name = (GAME_LOADED) 
      ? pl_file_get_filename(psp_current_game) : "BASIC";
    pl_menu_item *item = pl_menu_find_item_by_id(&gallery->Menu, sel->id);
 
    path = (char*)malloc(strlen(psp_save_state_path) + strlen(config_name) + 8);
    sprintf(path, "%s%s_%02i.sna", psp_save_state_path, config_name, item->id);
 
    do /* not a real loop; flow control construct */
    {
      if (button_mask & PSP_CTRL_SQUARE)
      {
        if (pl_file_exists(path) && !pspUiConfirm("Overwrite existing state?"))
          break;
 
        pspUiFlashMessage("Saving, please wait ...");
 
        PspImage *icon;
        if (!(icon = psp_save_state(path)))
        {
          pspUiAlert("ERROR: State not saved");
          break;
        }
 
        SceIoStat stat;
 
        /* Trash the old icon (if any) */
        if (item->param && item->param != psp_blank_ss_icon)
          pspImageDestroy((PspImage*)item->param);
 
        /* Update icon, help text */
        item->param = icon;
        pl_menu_set_item_help_text(item, PresentSlotText);
 
        /* Get file modification time/date */
        if (sceIoGetstat(path, &stat) < 0)
          sprintf(caption, "ERROR");
        else
          sprintf(caption, "%02i/%02i/%02i %02i:%02i", 
            stat.st_mtime.month,
            stat.st_mtime.day,
            stat.st_mtime.year - (stat.st_mtime.year / 100) * 100,
            stat.st_mtime.hour,
            stat.st_mtime.minute);
 
        pl_menu_set_item_caption(item, caption);
      }
      else if (button_mask & PSP_CTRL_TRIANGLE)
      {
        if (!pl_file_exists(path) || !pspUiConfirm("Delete state?"))
          break;
 
        if (!pl_file_rm(path))
        {
          pspUiAlert("ERROR: State not deleted");
          break;
        }
 
        /* Trash the old icon (if any) */
        if (item->param && item->param != psp_blank_ss_icon)
          pspImageDestroy((PspImage*)item->param);
 
        /* Update icon, caption */
        item->param = psp_blank_ss_icon;
        pl_menu_set_item_help_text(item, EmptySlotText);
        pl_menu_set_item_caption(item, ((int)item->id == psp_options.autoload_slot)
            ? "Autoload" : "Empty");
      }
    } while (0);
 
    if (path) free(path);
    return 0;
  }
 
  return OnGenericButtonPress(NULL, NULL, button_mask);
}
 
/* Show a CPU JAM dialog.  */
ui_jam_action_t ui_jam_dialog(const char *format, ...)
{
  static char message[512];
 
  va_list ap;
  va_start (ap, format);
  vsnprintf(message, sizeof(message) - 1, format, ap);
  va_end (ap);
 
  pspUiAlert(message);
  machine_trigger_reset(MACHINE_RESET_MODE_HARD);
 
  return UI_JAM_NONE;
}
 
 

Compare with Previous | Blame | View Log