/*  RetroArch - A frontend for libretro.
 *  Copyright (C) 2010-2012 - Hans-Kristian Arntzen
 *  Copyright (C) 2011-2012 - Daniel De Matteis
 * 
 *  RetroArch 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 Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  RetroArch 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 RetroArch.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include "../boolean.h"
#include "../compat/strl.h"
#include "../libretro.h"
#include "../general.h"
#include "../compat/strl.h"
#include "retroarch_console.h"
#include "../file.h"

#ifdef HAVE_CONFIGFILE
#include "../conf/config_file.h"
#include "../conf/config_file_macros.h"
#endif

#ifdef HAVE_ZLIB
#include "rzlib/zlib.h"
#define WRITEBUFFERSIZE (1024 * 512)
#endif

#ifdef _WIN32
#include "../compat/posix_string.h"
#endif

#define MAX_ARGS 32

/*============================================================
	ROM EXTENSIONS
============================================================ */

const char *rarch_console_get_rom_ext(void)
{
   const char *retval = NULL;

   struct retro_system_info info;
#ifdef ANDROID
   pretro_get_system_info(&info);
#else
   retro_get_system_info(&info);
#endif

   if (info.valid_extensions)
      retval = info.valid_extensions;
   else
      retval = "ZIP|zip";

   return retval;
}

void rarch_console_name_from_id(char *name, size_t size)
{
   if (size == 0)
      return;

   struct retro_system_info info;
#ifdef ANDROID
   pretro_get_system_info(&info);
#else
   retro_get_system_info(&info);
#endif
   const char *id = info.library_name ? info.library_name : "Unknown";

   if (!id || strlen(id) >= size)
   {
      name[0] = '\0';
      return;
   }

   name[strlen(id)] = '\0';

   for (size_t i = 0; id[i] != '\0'; i++)
   {
      char c = id[i];
      if (isspace(c) || isblank(c))
         name[i] = '_';
      else
         name[i] = tolower(c);
   }
}

#ifdef HAVE_ZLIB
static int rarch_extract_currentfile_in_zip(unzFile uf)
{
   char filename_inzip[PATH_MAX];
   FILE *fout = NULL;

   unz_file_info file_info;
   int err = unzGetCurrentFileInfo(uf,
         &file_info, filename_inzip, sizeof(filename_inzip),
         NULL, 0, NULL, 0);

   if (err != UNZ_OK)
   {
      RARCH_ERR("Error %d with ZIP file in unzGetCurrentFileInfo.\n", err);
      return err;
   }

   size_t size_buf = WRITEBUFFERSIZE;
   void *buf = malloc(size_buf);
   if (!buf)
   {
      RARCH_ERR("Error allocating memory\n");
      return UNZ_INTERNALERROR;
   }

   char write_filename[PATH_MAX];

#ifdef HAVE_HDD_CACHE_PARTITION

#if defined(__CELLOS_LV2__)
   snprintf(write_filename, sizeof(write_filename), "/dev_hdd1/%s", filename_inzip);
#elif defined(_XBOX)
   snprintf(write_filename, sizeof(write_filename), "cache:\\%s", filename_inzip);
#endif

#endif

   err = unzOpenCurrentFile(uf);
   if (err != UNZ_OK)
      RARCH_ERR("Error %d with ZIP file in unzOpenCurrentFile.\n", err);
   else
   {
      /* success */
      fout = fopen(write_filename, "wb");

      if (!fout)
         RARCH_ERR("Error opening %s.\n", write_filename);
   }

   if (fout)
   {
      RARCH_LOG("Extracting: %s\n", write_filename);

      do
      {
         err = unzReadCurrentFile(uf, buf, size_buf);
         if (err < 0)
         {
            RARCH_ERR("error %d with ZIP file in unzReadCurrentFile.\n", err);
            break;
         }

         if (err > 0)
         {
            if (fwrite(buf, err, 1, fout) != 1)
            {
               RARCH_ERR("Error in writing extracted file.\n");
               err = UNZ_ERRNO;
               break;
            }
         }
      } while (err > 0);

      if (fout)
         fclose(fout);
   }

   if (err == UNZ_OK)
   {
      err = unzCloseCurrentFile (uf);
      if (err != UNZ_OK)
         RARCH_ERR("Error %d with ZIP file in unzCloseCurrentFile.\n", err);
   }
   else
      unzCloseCurrentFile(uf); 

   free(buf);
   return err;
}

int rarch_extract_zipfile(const char *zip_path)
{
   unzFile uf = unzOpen(zip_path); 

   unz_global_info gi;
   int err = unzGetGlobalInfo(uf, &gi);
   if (err != UNZ_OK)
      RARCH_ERR("error %d with ZIP file in unzGetGlobalInfo \n",err);

   for (unsigned i = 0; i < gi.number_entry; i++)
   {
      if (rarch_extract_currentfile_in_zip(uf) != UNZ_OK)
         break;

      if ((i + 1) < gi.number_entry)
      {
         err = unzGoToNextFile(uf);
         if (err != UNZ_OK)
         {
            RARCH_ERR("error %d with ZIP file in unzGoToNextFile\n",err);
            break;
         }
      }
   }

#ifdef HAVE_HDD_CACHE_PARTITION
   if(g_console.info_msg_enable)
      rarch_settings_msg(S_MSG_EXTRACTED_ZIPFILE, S_DELAY_180);
#endif

   return 0;
}

#endif


/*============================================================
	INPUT EXTENSIONS
============================================================ */

#include "retroarch_console_input.h"

struct platform_bind
{
   uint64_t joykey;
   const char *label;
};

uint64_t rarch_default_keybind_lut[RARCH_FIRST_META_KEY];

char rarch_default_libretro_keybind_name_lut[RARCH_FIRST_META_KEY][256] = {
   "RetroPad Button B",          /* RETRO_DEVICE_ID_JOYPAD_B      */
   "RetroPad Button Y",          /* RETRO_DEVICE_ID_JOYPAD_Y      */
   "RetroPad Button Select",     /* RETRO_DEVICE_ID_JOYPAD_SELECT */
   "RetroPad Button Start",      /* RETRO_DEVICE_ID_JOYPAD_START  */
   "RetroPad D-Pad Up",          /* RETRO_DEVICE_ID_JOYPAD_UP     */
   "RetroPad D-Pad Down",        /* RETRO_DEVICE_ID_JOYPAD_DOWN   */
   "RetroPad D-Pad Left",        /* RETRO_DEVICE_ID_JOYPAD_LEFT   */
   "RetroPad D-Pad Right",       /* RETRO_DEVICE_ID_JOYPAD_RIGHT  */
   "RetroPad Button A",          /* RETRO_DEVICE_ID_JOYPAD_A      */
   "RetroPad Button X",          /* RETRO_DEVICE_ID_JOYPAD_X      */
   "RetroPad Button L1",          /* RETRO_DEVICE_ID_JOYPAD_L      */
   "RetroPad Button R1",          /* RETRO_DEVICE_ID_JOYPAD_R      */
   "RetroPad Button L2",         /* RETRO_DEVICE_ID_JOYPAD_L2     */
   "RetroPad Button R2",         /* RETRO_DEVICE_ID_JOYPAD_R2     */
   "RetroPad Button L3",         /* RETRO_DEVICE_ID_JOYPAD_L3     */
   "RetroPad Button R3",         /* RETRO_DEVICE_ID_JOYPAD_R3     */
};

#if defined(__CELLOS_LV2__)
static const struct platform_bind platform_keys[] = {
   { CTRL_CIRCLE_MASK, "Circle button" },
   { CTRL_CROSS_MASK, "Cross button" },
   { CTRL_TRIANGLE_MASK, "Triangle button" },
   { CTRL_SQUARE_MASK, "Square button" },
   { CTRL_UP_MASK, "D-Pad Up" },
   { CTRL_DOWN_MASK, "D-Pad Down" },
   { CTRL_LEFT_MASK, "D-Pad Left" },
   { CTRL_RIGHT_MASK, "D-Pad Right" },
   { CTRL_SELECT_MASK, "Select button" },
   { CTRL_START_MASK, "Start button" },
   { CTRL_L1_MASK, "L1 button" },
   { CTRL_L2_MASK, "L2 button" },
   { CTRL_L3_MASK, "L3 button" },
   { CTRL_R1_MASK, "R1 button" },
   { CTRL_R2_MASK, "R2 button" },
   { CTRL_R3_MASK, "R3 button" },
   { CTRL_LSTICK_LEFT_MASK, "LStick Left" },
   { CTRL_LSTICK_RIGHT_MASK, "LStick Right" },
   { CTRL_LSTICK_UP_MASK, "LStick Up" },
   { CTRL_LSTICK_DOWN_MASK, "LStick Down" },
   { CTRL_LEFT_MASK | CTRL_LSTICK_LEFT_MASK, "LStick D-Pad Left" },
   { CTRL_RIGHT_MASK | CTRL_LSTICK_RIGHT_MASK, "LStick D-Pad Right" },
   { CTRL_UP_MASK | CTRL_LSTICK_UP_MASK, "LStick D-Pad Up" },
   { CTRL_DOWN_MASK | CTRL_LSTICK_DOWN_MASK, "LStick D-Pad Down" },
   { CTRL_RSTICK_LEFT_MASK, "RStick Left" },
   { CTRL_RSTICK_RIGHT_MASK, "RStick Right" },
   { CTRL_RSTICK_UP_MASK, "RStick Up" },
   { CTRL_RSTICK_DOWN_MASK, "RStick Down" },
   { CTRL_LEFT_MASK | CTRL_RSTICK_LEFT_MASK, "RStick D-Pad Left" },
   { CTRL_RIGHT_MASK | CTRL_RSTICK_RIGHT_MASK, "RStick D-Pad Right" },
   { CTRL_UP_MASK | CTRL_RSTICK_UP_MASK, "RStick D-Pad Up" },
   { CTRL_DOWN_MASK | CTRL_RSTICK_DOWN_MASK, "RStick D-Pad Down" },
};
#elif defined(_XBOX360)
static const struct platform_bind platform_keys[] = {
   { XINPUT_GAMEPAD_B, "B button" },
   { XINPUT_GAMEPAD_A, "A button" },
   { XINPUT_GAMEPAD_Y, "Y button" },
   { XINPUT_GAMEPAD_X, "X button" },
   { XINPUT_GAMEPAD_DPAD_UP, "D-Pad Up" },
   { XINPUT_GAMEPAD_DPAD_DOWN, "D-Pad Down" },
   { XINPUT_GAMEPAD_DPAD_LEFT, "D-Pad Left" },
   { XINPUT_GAMEPAD_DPAD_RIGHT, "D-Pad Right" },
   { XINPUT_GAMEPAD_BACK, "Back button" },
   { XINPUT_GAMEPAD_START, "Start button" },
   { XINPUT_GAMEPAD_LEFT_SHOULDER, "Left Shoulder" },
   { XINPUT_GAMEPAD_LEFT_TRIGGER, "Left Trigger" },
   { XINPUT_GAMEPAD_LEFT_THUMB, "Left Thumb" },
   { XINPUT_GAMEPAD_RIGHT_SHOULDER, "Right Shoulder" },
   { XINPUT_GAMEPAD_RIGHT_TRIGGER, "Right Trigger" },
   { XINPUT_GAMEPAD_RIGHT_THUMB, "Right Thumb" },
   { XINPUT_GAMEPAD_LSTICK_LEFT_MASK, "LStick Left" },
   { XINPUT_GAMEPAD_LSTICK_RIGHT_MASK, "LStick Right" },
   { XINPUT_GAMEPAD_LSTICK_UP_MASK, "LStick Up" },
   { XINPUT_GAMEPAD_LSTICK_DOWN_MASK, "LStick Down" },
   { XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_LSTICK_LEFT_MASK, "LStick D-Pad Left" },
   { XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_LSTICK_RIGHT_MASK, "LStick D-Pad Right" },
   { XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_LSTICK_UP_MASK, "LStick D-Pad Up" },
   { XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_LSTICK_DOWN_MASK, "LStick D-Pad Down" },
   { XINPUT_GAMEPAD_RSTICK_LEFT_MASK, "RStick Left" },
   { XINPUT_GAMEPAD_RSTICK_RIGHT_MASK, "RStick Right" },
   { XINPUT_GAMEPAD_RSTICK_UP_MASK, "RStick Up" },
   { XINPUT_GAMEPAD_RSTICK_DOWN_MASK, "RStick Down" },
   { XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_RSTICK_LEFT_MASK, "RStick D-Pad Left" },
   { XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_RSTICK_RIGHT_MASK, "RStick D-Pad Right" },
   { XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_RSTICK_UP_MASK, "RStick D-Pad Up" },
   { XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_RSTICK_DOWN_MASK, "RStick D-Pad Down" },
};
#elif defined(_XBOX1)
static const struct platform_bind platform_keys[] = {
   { XINPUT_GAMEPAD_B, "B button" },
   { XINPUT_GAMEPAD_A, "A button" },
   { XINPUT_GAMEPAD_Y, "Y button" },
   { XINPUT_GAMEPAD_X, "X button" },
   { XINPUT_GAMEPAD_DPAD_UP, "D-Pad Up" },
   { XINPUT_GAMEPAD_DPAD_DOWN, "D-Pad Down" },
   { XINPUT_GAMEPAD_DPAD_LEFT, "D-Pad Left" },
   { XINPUT_GAMEPAD_DPAD_RIGHT, "D-Pad Right" },
   { XINPUT_GAMEPAD_BACK, "Back button" },
   { XINPUT_GAMEPAD_START, "Start button" },
   { XINPUT_GAMEPAD_WHITE, "Left Shoulder" },
   { XINPUT_GAMEPAD_LEFT_TRIGGER, "Left Trigger" },
   { XINPUT_GAMEPAD_LEFT_THUMB, "Left Thumb" },
   { XINPUT_GAMEPAD_BLACK, "Right Shoulder" },
   { XINPUT_GAMEPAD_RIGHT_TRIGGER, "Right Trigger" },
   { XINPUT_GAMEPAD_RIGHT_THUMB, "Right Thumb" },
   { XINPUT_GAMEPAD_LSTICK_LEFT_MASK, "LStick Left" },
   { XINPUT_GAMEPAD_LSTICK_RIGHT_MASK, "LStick Right" },
   { XINPUT_GAMEPAD_LSTICK_UP_MASK, "LStick Up" },
   { XINPUT_GAMEPAD_LSTICK_DOWN_MASK, "LStick Down" },
   { XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_LSTICK_LEFT_MASK, "LStick D-Pad Left" },
   { XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_LSTICK_RIGHT_MASK, "LStick D-Pad Right" },
   { XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_LSTICK_UP_MASK, "LStick D-Pad Up" },
   { XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_LSTICK_DOWN_MASK, "LStick D-Pad Down" },
   { XINPUT_GAMEPAD_RSTICK_LEFT_MASK, "RStick Left" },
   { XINPUT_GAMEPAD_RSTICK_RIGHT_MASK, "RStick Right" },
   { XINPUT_GAMEPAD_RSTICK_UP_MASK, "RStick Up" },
   { XINPUT_GAMEPAD_RSTICK_DOWN_MASK, "RStick Down" },
   { XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_RSTICK_LEFT_MASK, "RStick D-Pad Left" },
   { XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_RSTICK_RIGHT_MASK, "RStick D-Pad Right" },
   { XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_RSTICK_UP_MASK, "RStick D-Pad Up" },
   { XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_RSTICK_DOWN_MASK, "RStick D-Pad Down" },
};
#elif defined(GEKKO)
static const struct platform_bind platform_keys[] = {
   { PAD_BUTTON_B, "(NGC) B button" },
   { PAD_BUTTON_A, "(NGC) A button" },
   { PAD_BUTTON_Y, "(NGC) Y button" },
   { PAD_BUTTON_X, "(NGC) X button" },
   { PAD_BUTTON_UP, "(NGC) D-Pad Up" },
   { PAD_BUTTON_DOWN, "(NGC) D-Pad Down" },
   { PAD_BUTTON_LEFT, "(NGC) D-Pad Left" },
   { PAD_BUTTON_RIGHT, "(NGC) D-Pad Right" },
   { PAD_TRIGGER_Z, "(NGC) Z trigger" },
   { PAD_BUTTON_START, "(NGC) Start button" },
   { PAD_TRIGGER_L, "(NGC) Left Trigger" },
   { PAD_TRIGGER_R, "(NGC) Right Trigger" },
#ifdef HW_RVL
   { WPAD_CLASSIC_BUTTON_B, "(Wii Classici) B button" },
   { WPAD_CLASSIC_BUTTON_A, "(Wii Classic) A button" },
   { WPAD_CLASSIC_BUTTON_Y, "(Wii Classic) Y button" },
   { WPAD_CLASSIC_BUTTON_X, "(Wii Classic) X button" },
   { WPAD_CLASSIC_BUTTON_UP, "(Wii Classic) D-Pad Up" },
   { WPAD_CLASSIC_BUTTON_DOWN, "(Wii Classic) D-Pad Down" },
   { WPAD_CLASSIC_BUTTON_LEFT, "(Wii Classic) D-Pad Left" },
   { WPAD_CLASSIC_BUTTON_RIGHT, "(Wii Classic) D-Pad Right" },
   { WPAD_CLASSIC_BUTTON_MINUS, "(Wii Classic) Select/Minus button" },
   { WPAD_CLASSIC_BUTTON_PLUS, "(Wii Classic) Start/Plus button" },
   { WPAD_CLASSIC_BUTTON_HOME, "(Wii Classic) Home button" },
   { WPAD_CLASSIC_BUTTON_FULL_L, "(Wii Classic) Left Trigger" },
   { WPAD_CLASSIC_BUTTON_FULL_R, "(Wii Classic) Right Trigger" },
   { WPAD_CLASSIC_BUTTON_ZL, "(Wii Classic) ZL button" },
   { WPAD_CLASSIC_BUTTON_ZR, "(Wii Classic) ZR button" },
#endif
};
#endif

#ifdef HAVE_DEFAULT_RETROPAD_INPUT

uint64_t rarch_input_find_previous_platform_key(uint64_t joykey)
{
   size_t arr_size = sizeof(platform_keys) / sizeof(platform_keys[0]);

   if (platform_keys[0].joykey == joykey)
      return joykey;

   for (size_t i = 1; i < arr_size; i++)
   {
      if (platform_keys[i].joykey == joykey)
         return platform_keys[i - 1].joykey;
   }

   return NO_BTN;
}

uint64_t rarch_input_find_next_platform_key(uint64_t joykey)
{
   size_t arr_size = sizeof(platform_keys) / sizeof(platform_keys[0]);
   if (platform_keys[arr_size - 1].joykey == joykey)
      return joykey;

   for (size_t i = 0; i < arr_size - 1; i++)
   {
      if (platform_keys[i].joykey == joykey)
         return platform_keys[i + 1].joykey;
   }

   return NO_BTN;
}

const char *rarch_input_find_platform_key_label(uint64_t joykey)
{
   if (joykey == NO_BTN)
      return "No button";

   size_t arr_size = sizeof(platform_keys) / sizeof(platform_keys[0]);
   for (size_t i = 0; i < arr_size; i++)
   {
      if (platform_keys[i].joykey == joykey)
         return platform_keys[i].label;
   }

   return "Unknown";
}


void rarch_input_set_keybind(unsigned player, unsigned keybind_action, uint64_t default_retro_joypad_id)
{
   uint64_t *key = &g_settings.input.binds[player][default_retro_joypad_id].joykey;

   switch (keybind_action)
   {
      case KEYBIND_DECREMENT:
         *key = rarch_input_find_previous_platform_key(*key);
         break;

      case KEYBIND_INCREMENT:
         *key = rarch_input_find_next_platform_key(*key);
         break;

      case KEYBIND_DEFAULT:
         *key = rarch_default_keybind_lut[default_retro_joypad_id];
         break;

      default:
         break;
   }
}

void rarch_input_set_default_keybinds(unsigned player)
{
   for (unsigned i = 0; i < RARCH_FIRST_META_KEY; i++)
   {
      g_settings.input.binds[player][i].id = i;
      g_settings.input.binds[player][i].joykey = rarch_default_keybind_lut[i];
   }
   g_settings.input.dpad_emulation[player] = DPAD_EMULATION_LSTICK;
}

void rarch_input_set_controls_default (void)
{
#if defined(__CELLOS_LV2__)
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_B]		= platform_keys[PS3_DEVICE_ID_JOYPAD_CROSS].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_Y]		= platform_keys[PS3_DEVICE_ID_JOYPAD_SQUARE].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_SELECT]	= platform_keys[PS3_DEVICE_ID_JOYPAD_SELECT].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_START]	= platform_keys[PS3_DEVICE_ID_JOYPAD_START].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_UP]		= platform_keys[PS3_DEVICE_ID_JOYPAD_UP].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_DOWN]	= platform_keys[PS3_DEVICE_ID_JOYPAD_DOWN].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_LEFT]	= platform_keys[PS3_DEVICE_ID_JOYPAD_LEFT].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_RIGHT]	= platform_keys[PS3_DEVICE_ID_JOYPAD_RIGHT].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_A]		= platform_keys[PS3_DEVICE_ID_JOYPAD_CIRCLE].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_X]		= platform_keys[PS3_DEVICE_ID_JOYPAD_TRIANGLE].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_L]		= platform_keys[PS3_DEVICE_ID_JOYPAD_L1].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_R]		= platform_keys[PS3_DEVICE_ID_JOYPAD_R1].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_R2]		= platform_keys[PS3_DEVICE_ID_JOYPAD_R2].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_R3]		= platform_keys[PS3_DEVICE_ID_JOYPAD_R3].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_L2]		= platform_keys[PS3_DEVICE_ID_JOYPAD_L2].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_L3]		= platform_keys[PS3_DEVICE_ID_JOYPAD_L3].joykey;
#elif defined(_XBOX360)
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_B]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_A].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_Y]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_X].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_SELECT]	= platform_keys[XDK360_DEVICE_ID_JOYPAD_BACK].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_START]	= platform_keys[XDK360_DEVICE_ID_JOYPAD_START].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_UP]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_UP].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_DOWN]	= platform_keys[XDK360_DEVICE_ID_JOYPAD_DOWN].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_LEFT]	= platform_keys[XDK360_DEVICE_ID_JOYPAD_LEFT].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_RIGHT]	= platform_keys[XDK360_DEVICE_ID_JOYPAD_RIGHT].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_A]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_B].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_X]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_Y].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_L]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_LB].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_R]		= platform_keys[XDK360_DEVICE_ID_JOYPAD_RB].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_L2]     = platform_keys[XDK360_DEVICE_ID_JOYPAD_LEFT_TRIGGER].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_R2]     = platform_keys[XDK360_DEVICE_ID_JOYPAD_RIGHT_TRIGGER].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_L3]     = platform_keys[XDK360_DEVICE_ID_LSTICK_THUMB].joykey;
   rarch_default_keybind_lut[RETRO_DEVICE_ID_JOYPAD_R3]     = platform_keys[XDK360_DEVICE_ID_RSTICK_THUMB].joykey;
#endif
   for(uint32_t x = 0; x < MAX_PLAYERS; x++)
      rarch_input_set_default_keybinds(x);
}

const char *rarch_input_get_default_keybind_name(unsigned id)
{
   return rarch_default_libretro_keybind_name_lut[id];
}

#endif

void rarch_input_set_default_keybind_names_for_emulator(void)
{
   struct retro_system_info info;
#ifdef ANDROID
   pretro_get_system_info(&info);
#else
   retro_get_system_info(&info);
#endif
   const char *id = info.library_name ? info.library_name : "Unknown";

   // Genesis Plus GX/Next
   if (strstr(id, "Genesis Plus GX"))
   {
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_B],
            "B button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_B]));
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_A],
            "C button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_A]));
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_X],
            "Y button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_X]));
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_Y],
            "A button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_Y]));
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_L],
            "X button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_L]));
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_R],
            "Z button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_R]));
      strlcpy(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_SELECT],
            "Mode button", sizeof(rarch_default_libretro_keybind_name_lut[RETRO_DEVICE_ID_JOYPAD_SELECT]));
   }
}


/*============================================================
  VIDEO EXTENSIONS
  ============================================================ */

#if defined(HAVE_HLSL)
#include "../gfx/shader_hlsl.h"
#elif defined(HAVE_CG) && defined(HAVE_OPENGL)
#include "../gfx/shader_cg.h"
#endif

struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = {
   { "1:1",    1.0f },
   { "2:1",    2.0f },
   { "3:2",    1.5f },
   { "3:4",    0.75f },
   { "4:1",    4.0f },
   { "4:3",    1.3333f },
   { "4:4",    1.0f },
   { "5:4",    1.25f },
   { "6:5",    1.2f },
   { "7:9",    0.7777f },
   { "8:3",    2.6666f },
   { "8:7",    1.1428f },
   { "16:9",   1.7778f },
   { "16:10",  1.6f },
   { "16:15",  3.2f },
   { "19:12",  1.5833f },
   { "19:14",  1.3571f },
   { "30:17",  1.7647f },
   { "32:9",   3.5555f },
   { "Auto",   1.0f },
   { "Custom", 0.0f }
};

char rotation_lut[ASPECT_RATIO_END][PATH_MAX] =
{
	"Normal",
    "Vertical",
	"Flipped",
    "Flipped Rotated"
};

void rarch_set_auto_viewport(unsigned width, unsigned height)
{
   if(width == 0 || height == 0)
      return;

   unsigned aspect_x, aspect_y, len, highest, i;

   len = width < height ? width : height;
   highest = 1;
   for (i = 1; i < len; i++)
   {
      if ((width % i) == 0 && (height % i) == 0)
         highest = i;
   }

   aspect_x = width / highest;
   aspect_y = height / highest;

   snprintf(aspectratio_lut[ASPECT_RATIO_AUTO].name, sizeof(aspectratio_lut[ASPECT_RATIO_AUTO].name), "%d:%d (Auto)", aspect_x, aspect_y);
   aspectratio_lut[ASPECT_RATIO_AUTO].value = (int)aspect_x / (int)aspect_y;
}

void rarch_load_shader(unsigned slot, const char *path)
{
#if defined(HAVE_HLSL)
   hlsl_load_shader(slot, path);
#elif defined(HAVE_CG) && defined(HAVE_OPENGL)
   gl_cg_load_shader(slot, path);
#else
RARCH_WARN("Shader support is not implemented for this build.\n");
#endif

#if defined(HAVE_HLSL) || defined(HAVE_CG)
   if (g_console.info_msg_enable)
      rarch_settings_msg(S_MSG_SHADER_LOADING_SUCCEEDED, S_DELAY_180);
#endif
}

/*============================================================
  RetroArch MAIN WRAP
  ============================================================ */

#ifdef HAVE_RARCH_MAIN_WRAP

bool rarch_startup (const char * config_path)
{
   bool retval = false;

   if(g_console.initialize_rarch_enable)
   {
      if(g_console.emulator_initialized)
         rarch_main_deinit();

      struct rarch_main_wrap args = {0};

      args.verbose = g_extern.verbose;
      args.config_path = config_path;
      args.sram_path = g_console.default_sram_dir_enable ? g_console.default_sram_dir : NULL,
      args.state_path = g_console.default_savestate_dir_enable ? g_console.default_savestate_dir : NULL,
      args.rom_path = g_console.rom_path;

      int init_ret = rarch_main_init_wrap(&args);
      (void)init_ret;

      if(init_ret == 0)
      {
         g_console.emulator_initialized = 1;
         g_console.initialize_rarch_enable = 0;
         retval = true;
      }
      else
      {
         //failed to load the ROM for whatever reason
         g_console.emulator_initialized = 0;
         g_console.mode_switch = MODE_MENU;
         rarch_settings_msg(S_MSG_ROM_LOADING_ERROR, S_DELAY_180);
      }
   }

   return retval;
}

int rarch_main_init_wrap(const struct rarch_main_wrap *args)
{
   int argc = 0;
   char *argv[MAX_ARGS] = {NULL};

   argv[argc++] = strdup("retroarch");
   
   if (args->rom_path)
      argv[argc++] = strdup(args->rom_path);

   if (args->sram_path)
   {
      argv[argc++] = strdup("-s");
      argv[argc++] = strdup(args->sram_path);
   }

   if (args->state_path)
   {
      argv[argc++] = strdup("-S");
      argv[argc++] = strdup(args->state_path);
   }

   if (args->config_path)
   {
      argv[argc++] = strdup("-c");
      argv[argc++] = strdup(args->config_path);
   }

   if (args->verbose)
      argv[argc++] = strdup("-v");

#ifdef HAVE_FILE_LOGGER
   RARCH_LOG("foo\n");
   for(int i = 0; i < argc; i++)
      RARCH_LOG("arg #%d: %s\n", i, argv[i]);
   RARCH_LOG("bar\n");
#endif

   int ret = rarch_main_init(argc, argv);

   char **tmp = argv;
   while (*tmp)
   {
      free(*tmp);
      tmp++;
   }


   return ret;
}

#endif

#ifdef HAVE_RARCH_EXEC

#ifdef __CELLOS_LV2__
#include <cell/sysmodule.h>
#include <sys/process.h>
#include <sysutil/sysutil_common.h>
#include <netex/net.h>
#include <np.h>
#include <np/drm.h>
#endif

void rarch_exec (void)
{
   if(g_console.return_to_launcher)
   {
      RARCH_LOG("Attempt to load executable: [%s].\n", g_console.launch_app_on_exit);
#if defined(_XBOX)
      XLaunchNewImage(g_console.launch_app_on_exit, NULL);
#elif defined(__CELLOS_LV2__)
      char spawn_data[256];
      for(unsigned int i = 0; i < sizeof(spawn_data); ++i)
         spawn_data[i] = i & 0xff;

      char spawn_data_size[16];
      snprintf(spawn_data_size, sizeof(spawn_data_size), "%d", 256);

      const char * const spawn_argv[] = {
         spawn_data_size,
         "test argv for",
         "sceNpDrmProcessExitSpawn2()",
         NULL
      };

      SceNpDrmKey * k_licensee = NULL;
      int ret = sceNpDrmProcessExitSpawn2(k_licensee, g_console.launch_app_on_exit, (const char** const)spawn_argv, NULL, (sys_addr_t)spawn_data, 256, 1000, SYS_PROCESS_PRIMARY_STACK_SIZE_1M);
      if(ret <  0)
      {
         RARCH_WARN("SELF file is not of NPDRM type, trying another approach to boot it...\n");
	 sys_game_process_exitspawn(g_console.launch_app_on_exit, NULL, NULL, NULL, 0, 1000, SYS_PROCESS_PRIMARY_STACK_SIZE_1M);
      }
      sceNpTerm();
      cellSysmoduleUnloadModule(CELL_SYSMODULE_SYSUTIL_NP);
      cellSysmoduleUnloadModule(CELL_SYSMODULE_NET);
#endif
   }
}

#endif

#ifdef HAVE_RSOUND
bool rarch_console_rsound_start(const char *ip)
{
   strlcpy(g_settings.audio.driver, "rsound", sizeof(g_settings.audio.driver));
   strlcpy(g_settings.audio.device, ip, sizeof(g_settings.audio.device));
   driver.audio_data = NULL;

   // If driver already has started, it must be reinited.
   if (driver.audio_data)
   {
      uninit_audio();
      driver.audio_data = NULL;
      init_drivers_pre();
      init_audio();
   }
   return g_extern.audio_active;
}

void rarch_console_rsound_stop(void)
{
   strlcpy(g_settings.audio.driver, config_get_default_audio(), sizeof(g_settings.audio.driver));

   // If driver already has started, it must be reinited.
   if (driver.audio_data)
   {
      uninit_audio();
      driver.audio_data = NULL;
      init_drivers_pre();
      init_audio();
   }
}
#endif

/*============================================================
  STRING HANDLING
  ============================================================ */

void rarch_convert_char_to_wchar(wchar_t *buf, const char * str, size_t size)
{
   mbstowcs(buf, str, size / sizeof(wchar_t));
}

const char * rarch_convert_wchar_to_const_char(const wchar_t * wstr)
{
   static char str[256];
   wcstombs(str, wstr, sizeof(str));
   return str;
}

/*============================================================
  CONFIG
  ============================================================ */

#ifdef HAVE_CONFIGFILE
void rarch_config_create_default(const char * conf_name)
{
   FILE * f;
   RARCH_WARN("Config file \"%s\" doesn't exist. Creating...\n", conf_name);
   f = fopen(conf_name, "w");
   fclose(f);
}

void rarch_config_load(const char * conf_name, const char * libretro_dir_path, const char * exe_ext, bool find_libretro_path)
{
   if(!path_file_exists(conf_name))
      rarch_config_create_default(conf_name);
   else
   {
      config_file_t * conf = config_file_new(conf_name);

      // g_settings

#ifdef HAVE_LIBRETRO_MANAGEMENT
      if(find_libretro_path)
      {
         CONFIG_GET_STRING(libretro, "libretro_path");

         if(strcmp(g_settings.libretro, "") == 0)
         {
            char first_file[PATH_MAX];
            rarch_manage_libretro_set_first_file(first_file, sizeof(first_file), libretro_dir_path, exe_ext);
            if(first_file != NULL)
               strlcpy(g_settings.libretro, first_file, sizeof(g_settings.libretro));
         }
      }
#endif

      CONFIG_GET_STRING(cheat_database, "cheat_database");
      CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
      CONFIG_GET_STRING(video.cg_shader_path, "video_cg_shader");
#ifdef HAVE_FBO
      CONFIG_GET_STRING(video.second_pass_shader, "video_second_pass_shader");
      CONFIG_GET_FLOAT(video.fbo_scale_x, "video_fbo_scale_x");
      CONFIG_GET_FLOAT(video.fbo_scale_y, "video_fbo_scale_y");
      CONFIG_GET_BOOL(video.render_to_texture, "video_render_to_texture");
      CONFIG_GET_BOOL(video.second_pass_smooth, "video_second_pass_smooth");
#endif
#ifdef _XBOX
      CONFIG_GET_BOOL_CONSOLE(gamma_correction_enable, "gamma_correction_enable");
      CONFIG_GET_INT_CONSOLE(color_format, "color_format");
#endif
      CONFIG_GET_BOOL(video.smooth, "video_smooth");
      CONFIG_GET_BOOL(video.vsync, "video_vsync");
      CONFIG_GET_FLOAT(video.aspect_ratio, "video_aspect_ratio");
      CONFIG_GET_STRING(audio.device, "audio_device");

      for (unsigned i = 0; i < 7; i++)
      {
         char cfg[64];
	 snprintf(cfg, sizeof(cfg), "input_dpad_emulation_p%u", i + 1);
	 CONFIG_GET_INT(input.dpad_emulation[i], cfg);
      }

      // g_console

#ifdef HAVE_FBO
      CONFIG_GET_BOOL_CONSOLE(fbo_enabled, "fbo_enabled");
#endif
#ifdef __CELLOS_LV2__
      CONFIG_GET_BOOL_CONSOLE(custom_bgm_enable, "custom_bgm_enable");
#endif
      CONFIG_GET_BOOL_CONSOLE(overscan_enable, "overscan_enable");
      CONFIG_GET_BOOL_CONSOLE(screenshots_enable, "screenshots_enable");
      CONFIG_GET_BOOL_CONSOLE(throttle_enable, "throttle_enable");
      CONFIG_GET_BOOL_CONSOLE(triple_buffering_enable, "triple_buffering_enable");
      CONFIG_GET_BOOL_CONSOLE(info_msg_enable, "info_msg_enable");
      CONFIG_GET_INT_CONSOLE(aspect_ratio_index, "aspect_ratio_index");
      CONFIG_GET_INT_CONSOLE(current_resolution_id, "current_resolution_id");
      CONFIG_GET_INT_CONSOLE(viewports.custom_vp.x, "custom_viewport_x");
      CONFIG_GET_INT_CONSOLE(viewports.custom_vp.y, "custom_viewport_y");
      CONFIG_GET_INT_CONSOLE(viewports.custom_vp.width, "custom_viewport_width");
      CONFIG_GET_INT_CONSOLE(viewports.custom_vp.height, "custom_viewport_height");
      CONFIG_GET_INT_CONSOLE(screen_orientation, "screen_orientation");
      CONFIG_GET_INT_CONSOLE(sound_mode, "sound_mode");
      CONFIG_GET_STRING_CONSOLE(default_rom_startup_dir, "default_rom_startup_dir");
      CONFIG_GET_FLOAT_CONSOLE(menu_font_size, "menu_font_size");
      CONFIG_GET_FLOAT_CONSOLE(overscan_amount, "overscan_amount");

      // g_extern
      CONFIG_GET_INT_EXTERN(state_slot, "state_slot");
      CONFIG_GET_INT_EXTERN(audio_data.mute, "audio_mute");
   }
}

void rarch_config_save(const char * conf_name)
{
   if(!path_file_exists(conf_name))
      rarch_config_create_default(conf_name);
   else
   {
      config_file_t * conf = config_file_new(conf_name);

      if(conf == NULL)
         conf = config_file_new(NULL);

      // g_settings
      config_set_string(conf, "libretro_path", g_settings.libretro);
#ifdef HAVE_XML
      config_set_string(conf, "cheat_database_path", g_settings.cheat_database);
#endif
      config_set_bool(conf, "rewind_enable", g_settings.rewind_enable);
      config_set_string(conf, "video_cg_shader", g_settings.video.cg_shader_path);
      config_set_float(conf, "video_aspect_ratio", g_settings.video.aspect_ratio);
#ifdef HAVE_FBO
      config_set_float(conf, "video_fbo_scale_x", g_settings.video.fbo_scale_x);
      config_set_float(conf, "video_fbo_scale_y", g_settings.video.fbo_scale_y);
      config_set_string(conf, "video_second_pass_shader", g_settings.video.second_pass_shader);
      config_set_bool(conf, "video_render_to_texture", g_settings.video.render_to_texture);
      config_set_bool(conf, "video_second_pass_smooth", g_settings.video.second_pass_smooth);
#endif
      config_set_bool(conf, "video_smooth", g_settings.video.smooth);
      config_set_bool(conf, "video_vsync", g_settings.video.vsync);
      config_set_string(conf, "audio_device", g_settings.audio.device);

      for (unsigned i = 0; i < 7; i++)
      {
         char cfg[64];
	 snprintf(cfg, sizeof(cfg), "input_dpad_emulation_p%u", i + 1);
	 config_set_int(conf, cfg, g_settings.input.dpad_emulation[i]);
      }

#ifdef RARCH_CONSOLE
      config_set_bool(conf, "fbo_enabled", g_console.fbo_enabled);
#ifdef __CELLOS_LV2__
      config_set_bool(conf, "custom_bgm_enable", g_console.custom_bgm_enable);
#endif
      config_set_bool(conf, "overscan_enable", g_console.overscan_enable);
      config_set_bool(conf, "screenshots_enable", g_console.screenshots_enable);
#ifdef _XBOX
      config_set_bool(conf, "gamma_correction_enable", g_console.gamma_correction_enable);
      config_set_int(conf, "color_format", g_console.color_format);
#endif
      config_set_bool(conf, "throttle_enable", g_console.throttle_enable);
      config_set_bool(conf, "triple_buffering_enable", g_console.triple_buffering_enable);
      config_set_bool(conf, "info_msg_enable", g_console.info_msg_enable);
      config_set_int(conf, "sound_mode", g_console.sound_mode);
      config_set_int(conf, "aspect_ratio_index", g_console.aspect_ratio_index);
      config_set_int(conf, "current_resolution_id", g_console.current_resolution_id);
      config_set_int(conf, "custom_viewport_width", g_console.viewports.custom_vp.width);
      config_set_int(conf, "custom_viewport_height", g_console.viewports.custom_vp.height);
      config_set_int(conf, "custom_viewport_x", g_console.viewports.custom_vp.x);
      config_set_int(conf, "custom_viewport_y", g_console.viewports.custom_vp.y);
      config_set_int(conf, "screen_orientation", g_console.screen_orientation);
      config_set_string(conf, "default_rom_startup_dir", g_console.default_rom_startup_dir);
      config_set_float(conf, "menu_font_size", g_console.menu_font_size);
      config_set_float(conf, "overscan_amount", g_console.overscan_amount);
#endif

      // g_extern
      config_set_int(conf, "state_slot", g_extern.state_slot);
      config_set_int(conf, "audio_mute", g_extern.audio_data.mute);

      if (!config_file_write(conf, conf_name))
         RARCH_ERR("Failed to write config file to \"%s\". Check permissions.\n", conf_name);

      free(conf);
   }
}
#endif