/***************************************************************************

  M.A.M.E.32  -  Multiple Arcade Machine Emulator for Win32
  Win32 Portions Copyright (C) 1997-2001 Michael Soderstrom and Chris Kirmse

  This file is part of MAME32, and may only be used, modified and
  distributed under the terms of the MAME license, in "readme.txt".
  By continuing to use, modify or distribute this file you indicate
  that you have read the license and understand and accept it fully.

 ***************************************************************************/
 
/***************************************************************************

  options.c

  Stores global options and per-game options;

 ***************************************************************************/

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <windowsx.h>
#include <winreg.h>
#include <commctrl.h>
#include <assert.h>
#include <stdio.h>
#include <sys/stat.h>
#include <malloc.h>
#include <math.h>
#include <driver.h>
#include "mame32.h"
#include "m32util.h"
#include "resource.h"

#ifdef JMAME
#include <direct.h>
#endif

/***************************************************************************
    Internal function prototypes
 ***************************************************************************/

static void  LoadOptions(void);
static void  SaveOptions(void);
static void  SaveBackupOptions(void);

static void  LoadIniSettingsOptions(char *szVersion);
static void  SaveIniSettingsOptions(void);

static void  LoadIniGameOptions(options_type *osd_options);
static void  SaveIniGameOptions(options_type *osd_options);

static void  SaveFolderFlags(char *folderName, DWORD dwFlags);

static void  ColumnEncodeString(void *data, char *str);
static void  ColumnDecodeString(const char *str, void *data);

static void  ColumnDecodeWidths(const char *ptr, void *data);

static void  SplitterEncodeString(void *data, char *str);
static void  SplitterDecodeString(const char *str, void *data);

static void  ListEncodeString(void *data, char *str);
static void  ListDecodeString(const char *str, void *data);

static void  FontEncodeString(void *data, char *str);
static void  FontDecodeString(const char *str, void *data);

static BOOL  IniCreate(void);
static void  IniDestroy(void);
static void  IniReset(void);
static BOOL  IniReadDefaultFile(FILE *fp, char *linebuf);
static void  IniReadFile(FILE *fp, char *linebuf);
static void  IniWriteFile(FILE *fp, const char *keyname);
static long  IniQueryValue(const char *name, void *value, DWORD *size);
static void  IniSetValue(const char *name, void *value, DWORD size);

static DWORD GetIniOption(char *name);
static void  PutIniOption(char *name, DWORD value);

static BOOL  GetIniBoolOption(char *name);
static void  PutIniBoolOption(char *name, BOOL value);

static char  *GetIniStringOption(char *name);
static void  PutIniStringOption(char *name, char *option);

static void  GetIniObj(INI_OPTIONS *iniOpt);
static void  PutIniObj(INI_OPTIONS *iniOpt);

INLINE int   GetIndexByName(const char *name);

/***************************************************************************
    Internal defines
 ***************************************************************************/

#ifdef JAPANESE
	#define DEFAULT_INI     "jmame32.ini"
	#else
	#define DEFAULT_INI     "jmame32e.ini"
#endif
#define GAMES_INI       "games.ini"
#define FOLDERS_INI     "folders.ini"
#define GUIBACK_INI     "guiback.ini"
#define GAMEBACK_INI    "gameback.ini"

#define LINEBUF_SIZE  65536

/***************************************************************************
    Internal structures
 ***************************************************************************/

typedef struct ini_t {
	char *name;
	char *value;
} ini_struct;

static ini_struct *ini;

/***************************************************************************
    Internal variables
 ***************************************************************************/

static settings_type settings;

static options_type gOpts;  /* Used when saving/loading from Registry */
static options_type global; /* Global 'default' options */
static options_type backup; /* Backup 'default' options */
static options_type *game;  /* Array of Game specific options */

/* Global UI options */
INI_OPTIONS iniSettings[] =
{
	{"DefaultGame",        IO_STRING,  settings.default_game,      0, 0},
	{"FolderID",           IO_INT,     &settings.folder_id,        0, 0},
	{"ShowScreenShot",     IO_BOOL,    &settings.show_screenshot,  0, 0},
	{"ShowPageTab",        IO_BOOL,    &settings.show_tabctrl,     0, 0},
	{"ShowFlyer",          IO_INT,     &settings.show_pict_type,   0, 0},
	{"ShowToolBar",        IO_BOOL,    &settings.show_toolbar,     0, 0},
	{"ShowStatusBar",      IO_BOOL,    &settings.show_statusbar,   0, 0},
	{"ShowFolderList",     IO_BOOL,    &settings.show_folderlist,  0, 0},
	{"GameCheck",          IO_BOOL,    &settings.game_check,       0, 0},
	{"VersionCheck",       IO_BOOL,    &settings.version_check,    0, 0},
	{"JoyGUI",             IO_BOOL,    &settings.use_joygui,       0, 0},
	{"Broadcast",          IO_BOOL,    &settings.broadcast,        0, 0},
	{"random_bg",          IO_BOOL,    &settings.random_bg,        0, 0},
#ifdef JAPANESE
	{"UseJapaneseList",    IO_BOOL,    &settings.use_japaneselist, 0, 0},
#endif

	{"SortColumn",         IO_INT,     &settings.sort_column,      0, 0},
	{"SortReverse",        IO_BOOL,    &settings.sort_reverse,     0, 0},
	{"X",                  IO_INT,     &settings.area.x,           0, 0},
	{"Y",                  IO_INT,     &settings.area.y,           0, 0},
	{"Width",              IO_INT,     &settings.area.width,       0, 0},
	{"Height",             IO_INT,     &settings.area.height,      0, 0},
	{"State",              IO_INT,     &settings.windowstate,      0, 0},

//	{"Language",           IO_PSTRING, &settings.language,         0, 0},
	{"FlyerDir",           IO_PSTRING, &settings.flyerdir,         0, 0},
	{"CabinetDir",         IO_PSTRING, &settings.cabinetdir,       0, 0},
	{"MarqueeDir",         IO_PSTRING, &settings.marqueedir,       0, 0},
	{"TitleDir",           IO_PSTRING, &settings.titlesdir,        0, 0},
	{"IconDir",            IO_PSTRING, &settings.iconsdir,         0, 0},
	{"BkgroundDir",        IO_PSTRING, &settings.bgdir,            0, 0},

	{"rompath",            IO_PSTRING, &settings.romdirs,          0, 0},
	{"samplepath",         IO_PSTRING, &settings.sampledirs,       0, 0},
	{"inipath",            IO_PSTRING, &settings.inidirs,          0, 0},
	{"cfg_directory",      IO_PSTRING, &settings.cfgdir,           0, 0},
	{"nvram_directory",    IO_PSTRING, &settings.nvramdir,         0, 0},
	{"memcard_directory",  IO_PSTRING, &settings.memcarddir,       0, 0},
	{"input_directory",    IO_PSTRING, &settings.inpdir,           0, 0},
	{"hiscore_directory",  IO_PSTRING, &settings.hidir,            0, 0},
	{"state_directory",    IO_PSTRING, &settings.statedir,         0, 0},
	{"artwork_directory",  IO_PSTRING, &settings.artdir,           0, 0},
	{"snapshot_directory", IO_PSTRING, &settings.imgdir,           0, 0},
	{"diff_directory",     IO_PSTRING, &settings.diffdir,          0, 0},
	{"cheat_directory",    IO_PSTRING, &settings.cheatdir,         0, 0},
	{"cheat_file",         IO_PSTRING, &settings.cheatfile,        0, 0},
	{"history_file",       IO_PSTRING, &settings.history_filename, 0, 0},
	{"mameinfo_file",      IO_PSTRING, &settings.mameinfo_filename,0, 0},
	{"ctrlr_directory",    IO_PSTRING, &settings.ctrlrdir,         0, 0},
#ifdef EXTRA_FOLDER
	{"folder_directory",   IO_PSTRING, &settings.folderdir,        0, 0},
#endif
#ifdef JMAME // COMMAND_FOLDER
	{"command_directory",  IO_PSTRING, &settings.commanddir,       0, 0},
#endif

	/* ListMode needs to be before ColumnWidths settings */
	{"ListMode",           IO_ENCODE,  &settings.view,             ListEncodeString,     ListDecodeString},
	{"Splitters",          IO_ENCODE,  settings.splitter,          SplitterEncodeString, SplitterDecodeString},
	{"ListFont",           IO_ENCODE,  &settings.list_font,        FontEncodeString,     FontDecodeString},
	{"ColumnWidths",       IO_ENCODE,  &settings.column_width,     ColumnEncodeString,   ColumnDecodeWidths},
	{"ColumnOrder",        IO_ENCODE,  &settings.column_order,     ColumnEncodeString,   ColumnDecodeString},
	{"ColumnShown",        IO_ENCODE,  &settings.column_shown,     ColumnEncodeString,   ColumnDecodeString},
};

/* Game Options */
INI_OPTIONS iniGameOpts[] =
{
	/* video */
	{ "autoframeskip",          IO_BOOL,    &gOpts.autoframeskip,     0, 0},
	{ "frameskip",              IO_INT,     &gOpts.frameskip,         0, 0},
	{ "waitvsync",              IO_BOOL,    &gOpts.wait_vsync,        0, 0},
	{ "triplebuffer",           IO_BOOL,    &gOpts.use_triplebuf,     0, 0},
	{ "window",                 IO_BOOL,    &gOpts.window_mode,       0, 0},
	{ "ddraw",                  IO_BOOL,    &gOpts.use_ddraw,         0, 0},
	{ "hwstretch",              IO_BOOL,    &gOpts.ddraw_stretch,     0, 0},
	{ "resolution",             IO_STRING,  &gOpts.resolution,        0, 0},
	{ "refresh",                IO_INT,     &gOpts.gfx_refresh,       0, 0},
	{ "scanlines",              IO_BOOL,    &gOpts.scanlines,         0, 0},
	{ "switchres",              IO_BOOL,    &gOpts.switchres,         0, 0},
	{ "switchbpp",              IO_BOOL,    &gOpts.switchbpp,         0, 0},
	{ "maximize",               IO_BOOL,    &gOpts.maximize,          0, 0},
	{ "keepaspect",             IO_BOOL,    &gOpts.keepaspect,        0, 0},
	{ "matchrefresh",           IO_BOOL,    &gOpts.matchrefresh,      0, 0},
	{ "syncrefresh",            IO_BOOL,    &gOpts.syncrefresh,       0, 0},
	{ "throttle",               IO_BOOL,    &gOpts.throttle,          0, 0},
//	{ "full_screen_brightness", IO_DOUBLE,  &gOpts.gfx_brightness,    0, 0},
	{ "frames_to_run",          IO_INT,     &gOpts.frames_to_display, 0, 0},
	{ "effect",                 IO_STRING,  &gOpts.effect,            0, 0},
	{ "screen_aspect",          IO_STRING,  &gOpts.aspect,            0, 0},
#ifdef DIRECT3D_EFFECT
	{ "direct3d",               IO_BOOL,    &gOpts.direct3d,          0, 0},
	{ "motionblur",             IO_BOOL,    &gOpts.motionblur,        0, 0},
	{ "bilinear",               IO_BOOL,    &gOpts.bilinear,          0, 0},
#endif

	/* input */
	{ "mouse",                  IO_BOOL,    &gOpts.use_mouse,         0, 0},
	{ "joystick",               IO_BOOL,    &gOpts.use_joystick,      0, 0},
	{ "steadykey",              IO_BOOL,    &gOpts.steadykey,         0, 0},
	{ "lightgun",               IO_BOOL,    &gOpts.lightgun,          0, 0},
	{ "ctrlr",                  IO_STRING,  &gOpts.ctrlr,             0, 0},
	{ "a2d_deadzone",           IO_DOUBLE,  &gOpts.f_a2d,             0, 0},
#ifdef JMAME // JOYSTICK_ID
	{ "joyid1",                 IO_INT,     &gOpts.joyid[0],          0, 0},
	{ "joyid2",                 IO_INT,     &gOpts.joyid[1],          0, 0},
	{ "joyid3",                 IO_INT,     &gOpts.joyid[2],          0, 0},
	{ "joyid4",                 IO_INT,     &gOpts.joyid[3],          0, 0},
#endif

	/* core video */
	{ "brightness",             IO_DOUBLE,  &gOpts.f_bright_correct,  0, 0}, 
	{ "norotate",               IO_BOOL,    &gOpts.norotate,          0, 0},
	{ "ror",                    IO_BOOL,    &gOpts.ror,               0, 0},
	{ "rol",                    IO_BOOL,    &gOpts.rol,               0, 0},
	{ "flipx",                  IO_BOOL,    &gOpts.flipx,             0, 0},
	{ "flipy",                  IO_BOOL,    &gOpts.flipy,             0, 0},
	{ "debug_resolution",       IO_STRING,  &gOpts.debugres,          0, 0}, 
	{ "gamma",                  IO_DOUBLE,  &gOpts.f_gamma_correct,   0, 0},

	/* vector */
	{ "antialias",              IO_BOOL,    &gOpts.antialias,         0, 0},
	{ "translucency",           IO_BOOL,    &gOpts.translucency,      0, 0},
	{ "beam",                   IO_DOUBLE,  &gOpts.f_beam,            0, 0},
	{ "flicker",                IO_DOUBLE,  &gOpts.f_flicker,         0, 0},
	{ "intensity",              IO_DOUBLE,  &gOpts.f_intensity,       0, 0},

	/* sound */
	{ "samplerate",             IO_INT,     &gOpts.samplerate,        0, 0},
	{ "use_samples",            IO_BOOL,    &gOpts.use_samples,       0, 0},
	{ "resamplefilter",         IO_BOOL,    &gOpts.use_filter,        0, 0},
	{ "sound",                  IO_BOOL,    &gOpts.enable_sound,      0, 0},
	{ "volume",                 IO_INT,     &gOpts.attenuation,       0, 0},
#ifdef JMAME // DIRECTSOUND_BUFFER
	{ "dsound_buffer",          IO_INT,     &gOpts.dsound_buffer,     0, 0},
#endif

	/* misc artwork options */
	{ "artwork",                IO_BOOL,    &gOpts.use_artwork,       0, 0},
	{ "backdrops",              IO_BOOL,    &gOpts.backdrops,         0, 0},
	{ "overlays",               IO_BOOL,    &gOpts.overlays,          0, 0},
	{ "bezels",                 IO_BOOL,    &gOpts.bezels,            0, 0},
	{ "artwork_crop",           IO_BOOL,    &gOpts.artwork_crop,      0, 0},
	{ "artres",                 IO_INT,     &gOpts.artres,            0, 0},

	/* misc */
	{ "cheat",                  IO_BOOL,    &gOpts.cheat,             0, 0},
	{ "debug",                  IO_BOOL,    &gOpts.mame_debug,        0, 0},
/*	{ "playback",               IO_STRING,  &gOpts.playbackname,      0, 0},*/
/*	{ "record",                 IO_STRING,  &gOpts.recordname,        0, 0},*/
	{ "log",                    IO_BOOL,    &gOpts.errorlog,          0, 0},
	{ "sleep",                  IO_BOOL,    &gOpts.sleep,             0, 0},
	{ "leds",                   IO_BOOL,    &gOpts.leds,              0, 0}

};

#define NUM_SETTINGS (sizeof(iniSettings) / sizeof(iniSettings[0]))
#define NUM_GAMEOPTS (sizeof(iniGameOpts) / sizeof(iniGameOpts[0]))
#define NUM_EXTRA (64)
#define INI_MAX_ENTRIES (NUM_SETTINGS + NUM_GAMEOPTS + NUM_EXTRA)

static int  num_games = 0;
static BOOL bResetGUI      = FALSE;
static BOOL bResetGameDefs = FALSE;

#ifdef DRIVERS_COLUMN
/* Default sizes based on 8pt font w/sort arrow in that column */
static int default_column_width[] = { 220, 60, 60, 70, 60, 88, 74,140, 50,160, 80 };
static int default_column_shown[] = {   1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1 };
/* Hidden columns need to go at the end of the order array */
static int default_column_order[] = {   0,  2,  3,  4,  5,  6,  7,  8,  9, 10,  1 };
#else
/* Default sizes based on 8pt font w/sort arrow in that column */
static int default_column_width[] = { 220, 60, 60, 70, 60, 88, 74,140, 50,160,};
static int default_column_shown[] = {   1,  0,  1,  1,  1,  1,  1,  1,  1,  1 };
/* Hidden columns need to go at the end of the order array */
static int default_column_order[] = {   0,  2,  3,  4,  5,  6,  7,  8,  9,  1 };
#endif

static char *view_modes[VIEW_MAX] = { "Large Icons", "Small Icons", "List", "Details" };

static char oldInfoMsg[400] = 
#ifdef JAPANESE
MAME32NAME "は、古い設定情報を検出しました。\n\n"
"検出した情報は、" MAME32NAME " バージョン %s のものです。\n"
"現在のバージョンは %s です。\n"
"現在のバージョンによる新しい標準設定を使用することを推奨します。\n\n"
"新しい設定を使用しますか？";
#else
MAME32NAME " has detected outdated configuration data.\n\n\
The detected configuration data is from Version %s of " MAME32NAME ".\n\
The current version is %s. It is recommended that the\n\
configuration is set to the new defaults.\n\n\
Would you like to use the new configuration?";
#endif

#define DEFAULT_GAME "rezon"

/***************************************************************************
    External functions  
 ***************************************************************************/

void OptionsInit(int total_games)
{
	int i;

	num_games = total_games;

	strcpy(settings.default_game, DEFAULT_GAME);
	settings.folder_id       = 0;
	settings.view            = VIEW_REPORT;
	settings.show_folderlist = TRUE;
	settings.show_toolbar    = TRUE;
	settings.show_statusbar  = TRUE;
	settings.show_screenshot = TRUE;
	settings.show_tabctrl    = TRUE;
	settings.game_check      = TRUE;
	settings.version_check   = TRUE;
	settings.use_joygui      = FALSE;
	settings.broadcast       = FALSE;
	settings.random_bg       = FALSE;
#ifdef JAPANESE
	settings.use_japaneselist = FALSE;
#endif

	for (i = 0; i < COLUMN_MAX; i++)
	{
		settings.column_width[i] = default_column_width[i];
		settings.column_order[i] = default_column_order[i];
		settings.column_shown[i] = default_column_shown[i];
	}

	settings.sort_column = 0;
	settings.sort_reverse= FALSE;
	settings.area.x      = 0;
	settings.area.y      = 0;
	settings.area.width  = 640;
	settings.area.height = 400;
	settings.windowstate = 1;
	settings.splitter[0] = 150;
	settings.splitter[1] = 362;

	settings.language          = strdup("english");
	settings.flyerdir          = strdup("flyers");
	settings.cabinetdir        = strdup("cabinets");
	settings.marqueedir        = strdup("marquees");
	settings.titlesdir         = strdup("titles");

	settings.romdirs           = strdup("roms");
	settings.sampledirs        = strdup("samples");
	settings.inidirs	   = strdup("ini");
	settings.cfgdir            = strdup("cfg");
	settings.nvramdir          = strdup("nvram");
	settings.memcarddir        = strdup("memcard");
	settings.inpdir            = strdup("inp");
	settings.hidir             = strdup("hi");
	settings.statedir          = strdup("sta");
	settings.artdir            = strdup("artwork");
	settings.imgdir            = strdup("snap");
	settings.diffdir           = strdup("diff");
	settings.iconsdir          = strdup("icons");
	settings.bgdir             = strdup("bkground");
	settings.cheatdir          = strdup("cheat");
	settings.cheatfile         = strdup("cheat.dat");
	settings.history_filename  = strdup("history.dat");
	settings.mameinfo_filename = strdup("mameinfo.dat");
	settings.ctrlrdir          = strdup("ctrlr");
#ifdef EXTRA_FOLDER
	settings.folderdir         = strdup("folders");
#endif
#ifdef JMAME // COMMAND_FOLDER
	settings.commanddir        = strdup("command");
#endif

#ifdef JAPANESE
	settings.list_font.lfHeight         = -12;
#else
	settings.list_font.lfHeight         = -8;
#endif
	settings.list_font.lfWidth          = 0;
	settings.list_font.lfEscapement     = 0;
	settings.list_font.lfOrientation    = 0;
	settings.list_font.lfWeight         = FW_NORMAL;
	settings.list_font.lfItalic         = FALSE;
	settings.list_font.lfUnderline      = FALSE;
	settings.list_font.lfStrikeOut      = FALSE;
#ifdef JAPANESE
	settings.list_font.lfCharSet        = SHIFTJIS_CHARSET;
#else
	settings.list_font.lfCharSet        = ANSI_CHARSET;
#endif
	settings.list_font.lfOutPrecision   = OUT_DEFAULT_PRECIS;
	settings.list_font.lfClipPrecision  = CLIP_DEFAULT_PRECIS;
	settings.list_font.lfQuality        = DEFAULT_QUALITY;
	settings.list_font.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;

#ifdef JAPANESE
	strcpy(settings.list_font.lfFaceName, "MS UI Gothic");
#else
	strcpy(settings.list_font.lfFaceName, "MS Sans Serif");
#endif

	settings.list_font_color = (COLORREF)-1;

	global.use_default = FALSE;

	global.play_count   = 0;
	global.has_roms     = UNKNOWN;
	global.has_samples  = UNKNOWN;
	global.is_favorite  = FALSE;

	/* video */
#ifdef JMAME
	global.autoframeskip     = FALSE;
#else
	global.autoframeskip     = TRUE;
#endif
	global.frameskip         = 0;
	global.wait_vsync        = FALSE;
#ifdef JMAME
	global.use_triplebuf     = TRUE;
#else
	global.use_triplebuf     = FALSE;
#endif
	global.window_mode       = FALSE;
	global.use_ddraw         = TRUE;
	global.ddraw_stretch     = TRUE;
	strcpy(global.resolution, "auto");
	global.gfx_refresh       = 0;
	global.scanlines         = FALSE;
	global.switchres         = TRUE;
	global.switchbpp         = TRUE;
	global.maximize          = TRUE;
	global.keepaspect        = TRUE;
	global.matchrefresh      = FALSE;
	global.syncrefresh       = FALSE;
	global.throttle          = TRUE;
	global.gfx_brightness    = 1.0;
	global.frames_to_display = 0;
	strcpy(global.effect,    "none");
	strcpy(global.aspect,    "4:3");
#ifdef DIRECT3D_EFFECT
	global.direct3d          = TRUE;
	global.motionblur        = TRUE;
	global.bilinear          = TRUE;
#endif

	/* input */
	global.use_mouse         = FALSE;
	global.use_joystick      = FALSE;
	global.f_a2d             = 0.3;
	global.steadykey         = FALSE;
	global.lightgun          = FALSE;
	strcpy(global.ctrlr,     "Standard");
#ifdef JMAME // JOYSTICK_ID
	global.joyid[0]          = 0;
	global.joyid[1]          = 1;
	global.joyid[2]          = 2;
	global.joyid[3]          = 3;
#endif

	/* Core video */
	global.f_bright_correct  = 1.0;
	global.norotate          = FALSE;
	global.ror               = FALSE;
	global.rol               = FALSE;
	global.flipx             = FALSE;
	global.flipy             = FALSE;
	strcpy(global.debugres, "auto");
	global.f_gamma_correct   = 1.0;

	/* Core vector */
	global.antialias         = TRUE;
	global.translucency      = TRUE;
	global.f_beam            = 1.0;
	global.f_flicker         = 0.0;
	global.f_intensity		 = 1.5;

	/* Sound */
	global.samplerate        = 44100;
	global.use_samples       = TRUE;
	global.use_filter        = TRUE;
	global.enable_sound      = TRUE;
	global.attenuation       = 0;
#ifdef JMAME // DIRECTSOUND_BUFFER
	global.dsound_buffer     = 100;
#endif

	/* misc artwork options */
	global.use_artwork       = TRUE;
	global.backdrops         = TRUE;
	global.overlays          = TRUE;
	global.bezels            = TRUE;
	global.artwork_crop      = FALSE;
	global.artres            = 0; /* auto */

	/* misc */
#ifdef JMAME
	global.cheat             = TRUE;
#else
	global.cheat             = FALSE;
#endif
	global.mame_debug        = FALSE;
	global.playbackname      = NULL;
	global.recordname        = NULL;
	global.errorlog          = FALSE;
#ifdef JMAME
	global.sleep             = FALSE;
#else
	global.sleep             = TRUE;
#endif
	global.leds				 = TRUE;



    backup = global;

	/* Make directorys */
	mkdir("roms");
	mkdir("samples");
	mkdir("ini");
	mkdir("cfg");
	mkdir("hi");
	mkdir("snap");
	mkdir("inp");
	mkdir("sta");
	mkdir("artwork");
	mkdir("memcard");
	mkdir("flyers");
	mkdir("cabinets");
	mkdir("marquees");
	mkdir("titles");
	mkdir("nvram");
	mkdir("ctrlr");
	mkdir("diff");
	mkdir("icons");
#ifdef EXTRA_FOLDER
	mkdir("folders");
#endif
#ifdef JMAME // COMMAND_FOLDER
	mkdir("command");
#endif
	mkdir("bkground");
	mkdir("docs");



	/* This allocation should be checked */
	game = (options_type *)malloc(num_games * sizeof(options_type));

	for (i = 0; i < num_games; i++)
	{
		game[i] = global;
		game[i].use_default = TRUE;
	}

	IniCreate();
	SaveBackupOptions();
	LoadOptions();
}

void OptionsExit(void)
{
    SaveOptions();
    free(game);
    free(settings.language);
    free(settings.romdirs);
    free(settings.sampledirs);
	free(settings.inidirs);
    free(settings.cfgdir);
    free(settings.hidir);
    free(settings.inpdir);
    free(settings.imgdir);
    free(settings.statedir);
    free(settings.artdir);
    free(settings.memcarddir);
    free(settings.flyerdir);
    free(settings.cabinetdir);
    free(settings.marqueedir);
    free(settings.titlesdir);
    free(settings.nvramdir);
    free(settings.diffdir);
    free(settings.iconsdir);
	free(settings.cheatdir);
	free(settings.cheatfile);
	free(settings.history_filename);
	free(settings.mameinfo_filename);
    free(settings.ctrlrdir);
#ifdef EXTRA_FOLDER
	free(settings.folderdir);
#endif
#ifdef JMAME // COMMAND_FOLDER
	free(settings.commanddir);
#endif
    IniDestroy();
}

options_type * GetDefaultOptions(void)
{
	return &global;
}

options_type * GetGameOptions(int num_game)
{
	int play_count;
	int has_roms;
	int has_samples;
	BOOL is_favorite;

	assert(0 <= num_game && num_game < num_games);

	play_count	= game[num_game].play_count;
	has_roms	= game[num_game].has_roms;
	has_samples = game[num_game].has_samples;
	is_favorite = game[num_game].is_favorite;

	if (game[num_game].use_default)
	{
		game[num_game]				= global;
		game[num_game].use_default	= TRUE;
		game[num_game].play_count	= play_count;
		game[num_game].has_roms 	= has_roms;
		game[num_game].has_samples	= has_samples;
		game[num_game].is_favorite	= is_favorite;

	}
	return &game[num_game];
}

void ResetGUI(void)
{
	bResetGUI = TRUE;
}

void SetViewMode(int val)
{
	settings.view = val;
}

int GetViewMode(void)
{
	return settings.view;
}

void SetGameCheck(BOOL game_check)
{
	settings.game_check = game_check;
}

BOOL GetGameCheck(void)
{
	return settings.game_check;
}

void SetVersionCheck(BOOL version_check)
{
	settings.version_check = version_check;
}

BOOL GetVersionCheck(void)
{
	return settings.version_check;
}

void SetJoyGUI(BOOL use_joygui)
{
	settings.use_joygui = use_joygui;
}

BOOL GetJoyGUI(void)
{
	return settings.use_joygui;
}

void SetBroadcast(BOOL broadcast)
{
	settings.broadcast = broadcast;
}

BOOL GetBroadcast(void)
{
	return settings.broadcast;
}

void SetRandomBg (BOOL random_bg)
{
	settings.random_bg = random_bg;
}

BOOL GetRandomBg (void)
{
	return settings.random_bg;
}

void SetSavedFolderID(UINT val)
{
	settings.folder_id = val;
}

UINT GetSavedFolderID(void)
{
	return settings.folder_id;
}

void SetShowScreenShot(BOOL val)
{
	settings.show_screenshot = val;
}

BOOL GetShowScreenShot(void)
{
	return settings.show_screenshot;
}

void SetShowTabCtrl(BOOL val)
{
	settings.show_tabctrl = val;
}

BOOL GetShowTabCtrl(void)
{
	return settings.show_tabctrl;
}

void SetShowFolderList(BOOL val)
{
	settings.show_folderlist = val;
}

BOOL GetShowFolderList(void)
{
	return settings.show_folderlist;
}

void SetShowStatusBar(BOOL val)
{
	settings.show_statusbar = val;
}

BOOL GetShowStatusBar(void)
{
	return settings.show_statusbar;
}

void SetShowToolBar(BOOL val)
{
	settings.show_toolbar = val;
}

BOOL GetShowToolBar(void)
{
	return settings.show_toolbar;
}

void SetShowPictType(int val)
{
	settings.show_pict_type = val;
}

int GetShowPictType(void)
{
	return settings.show_pict_type;
}

void SetDefaultGame(const char *name)
{
	strcpy(settings.default_game,name);
}

const char *GetDefaultGame(void)
{
	return settings.default_game;
}

void SetWindowArea(AREA *area)
{
	memcpy(&settings.area, area, sizeof(AREA));
}

void GetWindowArea(AREA *area)
{
	memcpy(area, &settings.area, sizeof(AREA));
}

void SetWindowState(UINT state)
{
	settings.windowstate = state;
}

UINT GetWindowState(void)
{
	return settings.windowstate;
}

void SetListFont(LOGFONT *font)
{
	memcpy(&settings.list_font, font, sizeof(LOGFONT));
}

void GetListFont(LOGFONT *font)
{
	memcpy(font, &settings.list_font, sizeof(LOGFONT));
}

void SetListFontColor(COLORREF uColor)
{
	if (settings.list_font_color == GetSysColor(COLOR_WINDOWTEXT))
		settings.list_font_color = (COLORREF)-1;
	else
		settings.list_font_color = uColor;
}

COLORREF GetListFontColor(void)
{
	if (settings.list_font_color == (COLORREF)-1)
		return (GetSysColor(COLOR_WINDOWTEXT));

	return settings.list_font_color;
}

void SetColumnWidths(int width[])
{
	int i;

	for (i = 0; i < COLUMN_MAX; i++)
		settings.column_width[i] = width[i];
}

void GetColumnWidths(int width[])
{
	int i;

	for (i = 0; i < COLUMN_MAX; i++)
		width[i] = settings.column_width[i];
}

void SetSplitterPos(int splitterId, int pos)
{
	if (splitterId < SPLITTER_MAX)
		settings.splitter[splitterId] = pos;
}

int  GetSplitterPos(int splitterId)
{
	if (splitterId < SPLITTER_MAX)
		return settings.splitter[splitterId];

	return -1; /* Error */
}

void SetColumnOrder(int order[])
{
	int i;

	for (i = 0; i < COLUMN_MAX; i++)
		settings.column_order[i] = order[i];
}

void GetColumnOrder(int order[])
{
	int i;

	for (i = 0; i < COLUMN_MAX; i++)
		order[i] = settings.column_order[i];
}

void SetColumnShown(int shown[])
{
	int i;

	for (i = 0; i < COLUMN_MAX; i++)
		settings.column_shown[i] = shown[i];
}

void GetColumnShown(int shown[])
{
	int i;

	for (i = 0; i < COLUMN_MAX; i++)
		shown[i] = settings.column_shown[i];
}

void SetSortColumn(int column)
{
	settings.sort_reverse = (column < 0) ? TRUE : FALSE;
	settings.sort_column  = abs(column) - 1;
}

int GetSortColumn(void)
{
	int column = settings.sort_column + 1;

	if (settings.sort_reverse)
		column = -(column);

	return column;
}

const char* GetLanguage(void)
{
	return settings.language;
}

void SetLanguage(const char* lang)
{
	if (settings.language != NULL)
	{
		free(settings.language);
		settings.language = NULL;
	}

	if (lang != NULL)
		settings.language = strdup(lang);
}

const char* GetRomDirs(void)
{
	return settings.romdirs;
}

void SetRomDirs(const char* paths)
{
	if (settings.romdirs != NULL)
	{
		free(settings.romdirs);
		settings.romdirs = NULL;
	}

	if (paths != NULL)
		settings.romdirs = strdup(paths);
}

const char* GetSampleDirs(void)
{
	return settings.sampledirs;
}

void SetSampleDirs(const char* paths)
{
	if (settings.sampledirs != NULL)
	{
		free(settings.sampledirs);
		settings.sampledirs = NULL;
	}

	if (paths != NULL)
		settings.sampledirs = strdup(paths);
}

const char* GetIniDirs(void)
{
	return settings.inidirs;
}

void SetIniDirs(const char* paths)
{
	if (settings.inidirs != NULL)
	{
		free(settings.inidirs);
		settings.inidirs = NULL;
	}

	if (paths != NULL)
		settings.inidirs = strdup(paths);
}

const char* GetCtrlrDir(void)
{
	return settings.ctrlrdir;
}

void SetCtrlrDir(const char* path)
{
	if (settings.ctrlrdir != NULL)
	{
		free(settings.ctrlrdir);
		settings.ctrlrdir = NULL;
	}

	if (path != NULL)
		settings.ctrlrdir = strdup(path);
}

const char* GetCfgDir(void)
{
	return settings.cfgdir;
}

void SetCfgDir(const char* path)
{
	if (settings.cfgdir != NULL)
	{
		free(settings.cfgdir);
		settings.cfgdir = NULL;
	}

	if (path != NULL)
		settings.cfgdir = strdup(path);
}

const char* GetHiDir(void)
{
	return settings.hidir;
}

void SetHiDir(const char* path)
{
	if (settings.hidir != NULL)
	{
		free(settings.hidir);
		settings.hidir = NULL;
	}

	if (path != NULL)
		settings.hidir = strdup(path);
}

const char* GetNvramDir(void)
{
	return settings.nvramdir;
}

void SetNvramDir(const char* path)
{
	if (settings.nvramdir != NULL)
	{
		free(settings.nvramdir);
		settings.nvramdir = NULL;
	}

	if (path != NULL)
		settings.nvramdir = strdup(path);
}

const char* GetInpDir(void)
{
	return settings.inpdir;
}

void SetInpDir(const char* path)
{
	if (settings.inpdir != NULL)
	{
		free(settings.inpdir);
		settings.inpdir = NULL;
	}

	if (path != NULL)
		settings.inpdir = strdup(path);
}

const char* GetImgDir(void)
{
	return settings.imgdir;
}

void SetImgDir(const char* path)
{
	if (settings.imgdir != NULL)
	{
		free(settings.imgdir);
		settings.imgdir = NULL;
	}

	if (path != NULL)
		settings.imgdir = strdup(path);
}

const char* GetStateDir(void)
{
	return settings.statedir;
}

void SetStateDir(const char* path)
{
	if (settings.statedir != NULL)
	{
		free(settings.statedir);
		settings.statedir = NULL;
	}

	if (path != NULL)
		settings.statedir = strdup(path);
}

const char* GetArtDir(void)
{
	return settings.artdir;
}

void SetArtDir(const char* path)
{
	if (settings.artdir != NULL)
	{
		free(settings.artdir);
		settings.artdir = NULL;
	}

	if (path != NULL)
		settings.artdir = strdup(path);
}

const char* GetMemcardDir(void)
{
	return settings.memcarddir;
}

void SetMemcardDir(const char* path)
{
	if (settings.memcarddir != NULL)
	{
		free(settings.memcarddir);
		settings.memcarddir = NULL;
	}

	if (path != NULL)
		settings.memcarddir = strdup(path);
}

const char* GetFlyerDir(void)
{
	return settings.flyerdir;
}

void SetFlyerDir(const char* path)
{
	if (settings.flyerdir != NULL)
	{
		free(settings.flyerdir);
		settings.flyerdir = NULL;
	}

	if (path != NULL)
		settings.flyerdir = strdup(path);
}

const char* GetCabinetDir(void)
{
	return settings.cabinetdir;
}

void SetCabinetDir(const char* path)
{
	if (settings.cabinetdir != NULL)
	{
		free(settings.cabinetdir);
		settings.cabinetdir = NULL;
	}

	if (path != NULL)
		settings.cabinetdir = strdup(path);
}

const char* GetMarqueeDir(void)
{
	return settings.marqueedir;
}

void SetMarqueeDir(const char* path)
{
	if (settings.marqueedir != NULL)
	{
		free(settings.marqueedir);
		settings.marqueedir = NULL;
	}

	if (path != NULL)
		settings.marqueedir = strdup(path);
}

const char* GetTitlesDir(void)
{
	return settings.titlesdir;
}

void SetTitlesDir(const char* path)
{
	if (settings.titlesdir != NULL)
	{
		free(settings.titlesdir);
		settings.titlesdir = NULL;
	}

	if (path != NULL)
		settings.titlesdir = strdup(path);
}

const char* GetDiffDir(void)
{
	return settings.diffdir;
}

void SetDiffDir(const char* path)
{
	if (settings.diffdir != NULL)
	{
		free(settings.diffdir);
		settings.diffdir = NULL;
	}

	if (path != NULL)
		settings.diffdir = strdup(path);
}

const char* GetIconsDir(void)
{
	return settings.iconsdir;
}

void SetIconsDir(const char* path)
{
	if (settings.iconsdir != NULL)
	{
		free(settings.iconsdir);
		settings.iconsdir = NULL;
	}

	if (path != NULL)
		settings.iconsdir = strdup(path);
}

const char* GetBgDir (void)
{
	return settings.bgdir;
}

void SetBgDir (const char* path)
{
	free(settings.bgdir);

	if (path != NULL)
		settings.bgdir = strdup (path);
}

#ifdef EXTRA_FOLDER
const char* GetFolderDir(void)
{
	return settings.folderdir;
}

void SetFolderDir(const char* path)
{
	if (settings.folderdir != NULL)
	{
		free(settings.folderdir);
		settings.folderdir = NULL;
	}

	if (path != NULL)
		settings.folderdir = strdup(path);
}
#endif

#ifdef JMAME // COMMAND_FOLDER
const char* GetCommandDir(void)
{
	return settings.commanddir;
}

void SetCommandDir(const char* path)
{
	if (settings.commanddir != NULL)
	{
		free(settings.commanddir);
		settings.commanddir = NULL;
	}

	if (path != NULL)
		settings.commanddir = strdup(path);
}
#endif

const char* GetCheatDir(void)
{
	return settings.cheatdir;
}

void SetCheatDir(const char* path)
{
	if (settings.cheatdir != NULL)
	{
		free(settings.cheatdir);
		settings.cheatdir = NULL;
	}

	if (path != NULL)
		settings.cheatdir = strdup(path);
}

const char* GetCheatFileName(void)
{
	return settings.cheatfile;
}

void SetCheatFileName(const char* path)
{
	if (settings.cheatfile != NULL)
	{
		free(settings.cheatfile);
		settings.cheatfile = NULL;
	}

	if (path != NULL)
		settings.cheatfile = strdup(path);
}

const char* GetHistoryFileName(void)
{
	return settings.history_filename;
}

void SetHistoryFileName(const char* path)
{
	if (settings.history_filename != NULL)
	{
		free(settings.history_filename);
		settings.history_filename = NULL;
	}

	if (path != NULL)
		settings.history_filename = strdup(path);
}


const char* GetMAMEInfoFileName(void)
{
	return settings.mameinfo_filename;
}

void SetMAMEInfoFileName(const char* path)
{
	if (settings.mameinfo_filename != NULL)
	{
		free(settings.mameinfo_filename);
		settings.mameinfo_filename = NULL;
	}

	if (path != NULL)
		settings.mameinfo_filename = strdup(path);
}

void ResetGameOptions(int num_game)
{
	int play_count;
	int has_roms;
	int has_samples;

	assert(0 <= num_game && num_game < num_games);

	play_count	= game[num_game].play_count;
	has_roms	= game[num_game].has_roms;
	has_samples = game[num_game].has_samples;

	game[num_game]				= global;
	game[num_game].use_default	= TRUE;
	game[num_game].play_count	= play_count;
	game[num_game].has_roms 	= has_roms;
	game[num_game].has_samples	= has_samples;
}

void ResetGameDefaults(void)
{
	bResetGameDefs = TRUE;
}

void ResetAllGameOptions(void)
{
	int i;

	for (i = 0; i < num_games; i++)
		ResetGameOptions(i);
}

int  GetHasRoms(int num_game)
{
	assert(0 <= num_game && num_game < num_games);

	return game[num_game].has_roms;
}

void SetHasRoms(int num_game, int has_roms)
{
	assert(0 <= num_game && num_game < num_games);

	game[num_game].has_roms = has_roms;
}

int  GetHasSamples(int num_game)
{
	assert(0 <= num_game && num_game < num_games);

	return game[num_game].has_samples;
}

void SetHasSamples(int num_game, int has_samples)
{
	assert(0 <= num_game && num_game < num_games);

	game[num_game].has_samples = has_samples;
}

int  GetIsFavorite(int num_game)
{
	assert(0 <= num_game && num_game < num_games);

	return game[num_game].is_favorite;
}

void SetIsFavorite(int num_game, int is_favorite)
{
	assert(0 <= num_game && num_game < num_games);

	game[num_game].is_favorite = is_favorite;
}

void IncrementPlayCount(int num_game)
{
	assert(0 <= num_game && num_game < num_games);

	game[num_game].play_count++;

//	SavePlayCount(num_game);
}

void SetFolderFlags(char *folderName, DWORD dwFlags)
{
	SaveFolderFlags(folderName, dwFlags);
}

int GetPlayCount(int num_game)
{
	assert(0 <= num_game && num_game < num_games);

	return game[num_game].play_count;
}

/***************************************************************************
    Internal functions
 ***************************************************************************/

static void ColumnEncodeString(void* data, char *str)
{
	int* value = (int*)data;
	int  i;
	char tmpStr[100];

	sprintf(tmpStr, "%d", value[0]);
	
	strcpy(str, tmpStr);

	for (i = 1; i < COLUMN_MAX; i++)
	{
		sprintf(tmpStr, ",%d", value[i]);
		strcat(str, tmpStr);
	}
}

static void ColumnDecodeString(const char* str, void* data)
{
	int* value = (int*)data;
	int  i;
	char *s, *p;
	char tmpStr[100];

	if (str == NULL)
		return;

	strcpy(tmpStr, str);
	p = tmpStr;
	
	for (i = 0; p && i < COLUMN_MAX; i++)
	{
		s = p;
		
		if ((p = strchr(s,',')) != NULL && *p == ',')
		{
			*p = '\0';
			p++;
		}
		value[i] = atoi(s);
	}
}

static void ColumnDecodeWidths(const char* str, void* data)
{
	if (settings.view == VIEW_REPORT)
		ColumnDecodeString(str, data);
}

static void SplitterEncodeString(void* data, char* str)
{
	int* value = (int*)data;
	int  i;
	char tmpStr[100];

	sprintf(tmpStr, "%d", value[0]);
	
	strcpy(str, tmpStr);

	for (i = 1; i < SPLITTER_MAX; i++)
	{
		sprintf(tmpStr, ",%d", value[i]);
		strcat(str, tmpStr);
	}
}

static void SplitterDecodeString(const char* str, void* data)
{
	int* value = (int*)data;
	int  i;
	char *s, *p;
	char tmpStr[100];

	if (str == NULL)
		return;

	strcpy(tmpStr, str);
	p = tmpStr;
	
	for (i = 0; p && i < SPLITTER_MAX; i++)
	{
		s = p;
		
		if ((p = strchr(s,',')) != NULL && *p == ',')
		{
			*p = '\0';
			p++;
		}
		value[i] = atoi(s);
	}
}

static void ListDecodeString(const char* str, void* data)
{
	int* value = (int*)data;
	int i;

	*value = VIEW_REPORT;

	for (i = VIEW_LARGE_ICONS; i < VIEW_MAX; i++)
	{
		if (strcmp(str, view_modes[i]) == 0)
		{
			*value = i;
			return;
		}
	}
}

static void ListEncodeString(void* data, char *str)
{
	int* value = (int*)data;

	strcpy(str, view_modes[*value]);
}

/***************************************************************************/
/* Parse the given comma-delimited string into a LOGFONT structure */
static void FontDecodeString(const char* str, void* data)
{
	LOGFONT* f = (LOGFONT*)data;
	char*	 ptr;
	int n[8];

	sscanf(str, "%ld,%ld,%ld,%ld,%ld,%i,%i,%i,%i,%i,%i,%i,%i",
		&f->lfHeight,
		&f->lfWidth,
		&f->lfEscapement,
		&f->lfOrientation,
		&f->lfWeight,
		&n[0], &n[1], &n[2], &n[3],
		&n[4], &n[5], &n[6], &n[7]);

		f->lfItalic         = (BYTE)n[0];
		f->lfUnderline      = (BYTE)n[1];
		f->lfStrikeOut      = (BYTE)n[2];
		f->lfCharSet        = (BYTE)n[3];
		f->lfOutPrecision   = (BYTE)n[4];
		f->lfClipPrecision  = (BYTE)n[5];
		f->lfQuality        = (BYTE)n[6];
		f->lfPitchAndFamily = (BYTE)n[7];

	ptr = strrchr(str, ',');
	if (ptr != NULL)
		strcpy(f->lfFaceName, ptr + 1);
}

/* Encode the given LOGFONT structure into a comma-delimited string */
static void FontEncodeString(void* data, char *str)
{
	LOGFONT* f = (LOGFONT*)data;

	sprintf(str, "%ld,%ld,%ld,%ld,%ld,%i,%i,%i,%i,%i,%i,%i,%i,%s",
			f->lfHeight,
			f->lfWidth,
			f->lfEscapement,
			f->lfOrientation,
			f->lfWeight,
			(int)f->lfItalic,
			(int)f->lfUnderline,
			(int)f->lfStrikeOut,
			(int)f->lfCharSet,
			(int)f->lfOutPrecision,
			(int)f->lfClipPrecision,
			(int)f->lfQuality,
			(int)f->lfPitchAndFamily,
			f->lfFaceName);
}

/***************************************************************************/
/* Register access functions below */
static void LoadOptions(void)
{
	char temp[80], filename[80];
	char *linebuf;
	FILE *fp;

	linebuf = malloc(LINEBUF_SIZE);
	if (linebuf == NULL)
	return;

#if 0
	sprintf(filename, "%s/%s", GetIniDirs(), DEFAULT_INI);
	fp = fopen(filename, "r");
#else
	fp = fopen(DEFAULT_INI, "r");
#endif
	if (fp == NULL)
	{
		free(linebuf);
		return;
	}

	IniReset();
	if (IniReadDefaultFile(fp, linebuf) == TRUE)
	{
		LoadIniSettingsOptions(temp);

		if (!bResetGUI && settings.version_check)
		{
			if (strcmp(temp, build_version) != 0)
			{
				char msg[400];
				sprintf(msg, oldInfoMsg, temp, build_version);
#ifdef JAPANESE
				if (MessageBox(0, msg, MAME32NAME " - バージョンの相違", MB_YESNO | MB_ICONQUESTION) == IDYES)
#else
				if (MessageBox(0, msg, MAME32NAME " - Version Mismatch", MB_YESNO | MB_ICONQUESTION) == IDYES)
#endif
				{
					bResetGUI = TRUE;
					bResetGameDefs = TRUE;
				}
			}
		}

		if (bResetGUI)
		{
			fclose(fp);

			sprintf(filename, "%s/%s", GetIniDirs(), GUIBACK_INI);
			fp = fopen(filename, "r");
			if (fp)
			{
				IniReset();
				if (IniReadDefaultFile(fp, linebuf) == TRUE)
				LoadIniSettingsOptions(temp);
			}
		}

		if (settings.list_font_color != -1)
		{
			if (settings.list_font_color == GetSysColor(COLOR_WINDOWTEXT))
			settings.list_font_color = (COLORREF)-1;
		}

		LoadIniGameOptions(&global);
	}
	fclose(fp);

	sprintf(filename, "%s/%s", GetIniDirs(), GAMES_INI);
	fp = fopen(filename, "r");
	if (fp == NULL)
	{
		free(linebuf);
		return;
	}

	IniReadFile(fp, linebuf);
	fclose(fp);

	free(linebuf);
}


static void SaveOptions(void)
{
	FILE  *fp;
	int   i;
	char  filename[80];

#if 0
	sprintf(filename, "%s/%s", GetIniDirs(), DEFAULT_INI);
	fp = fopen(filename, "w");
#else
	fp = fopen(DEFAULT_INI, "w");
#endif
	if (fp)
	{
		IniReset();
		SaveIniSettingsOptions();
		global.use_default = FALSE;
		SaveIniGameOptions(&global);
		IniWriteFile(fp, "default");
		fclose(fp);
	}

	sprintf(filename, "%s/%s", GetIniDirs(), GAMES_INI);
	fp = fopen(filename, "w");
	if (fp)
	{
		for (i = 0; drivers[i]; i++)
		{
			IniReset();
			SaveIniGameOptions(&game[i]);
			IniWriteFile(fp, drivers[i]->name);
		}
		fclose(fp);
	}
}

static void SaveBackupOptions(void)
{
	FILE  *fp;
	char  filename[80];

	sprintf(filename, "%s/%s", GetIniDirs(), GUIBACK_INI);
	fp = fopen(filename, "w");
	if (fp)
	{
		IniReset();
		SaveIniSettingsOptions();
		IniWriteFile(fp, "default");
		fclose(fp);
	}

	sprintf(filename, "%s/%s", GetIniDirs(), GAMEBACK_INI);
	fp = fopen(filename, "w");
	if (fp)
	{
		global.use_default = FALSE;
		IniReset();
		SaveIniGameOptions(&global);
		IniWriteFile(fp, "default");
		fclose(fp);
	}
}

DWORD GetFolderFlags(char *folderName)
{
	FILE  *fp;
	char  *linebuf;
	char  value[16];
	char  filename[80];
	DWORD size;
	DWORD flags = 0;

	sprintf(filename, "%s/%s", GetIniDirs(), FOLDERS_INI);
	fp = fopen(filename, "r");
	if (fp)
	{
		linebuf = malloc(LINEBUF_SIZE);
		if (linebuf)
		{
			IniReset();
			IniReadFile(fp, linebuf);
			free(linebuf);

			if (IniQueryValue(folderName, NULL, &size) == ERROR_SUCCESS)
			{
				if (IniQueryValue(folderName, value, &size) == ERROR_SUCCESS)
				sscanf(value, "%08lX", &flags);
			}
		}
		fclose(fp);
	}

	return flags;
}

static void SaveFolderFlags(char *folderName, DWORD dwFlags)
{
	FILE *fp;
	char  *linebuf;
	char  value[16];
	char  filename[80];
	int   i, check;

	sprintf(value, "%08lX", dwFlags);

	IniReset();

	sprintf(filename, "%s/%s", GetIniDirs(), FOLDERS_INI);
	fp = fopen(filename, "r");
	if (fp)
	{
		linebuf = malloc(LINEBUF_SIZE);
		if (linebuf)
		{
			IniReadFile(fp, linebuf);
			free(linebuf);
		}
		fclose(fp);
	}

	fp = fopen(filename, "w");
	if (fp)
	{
		IniSetValue(folderName, value, strlen(value));
		IniWriteFile(fp, "folders");
		fclose(fp);
	}

	check = 0;
	for (i = 0; ini[i].value; i++)
	check |= atoi(ini[i].value) ? 1 : 0;

	if (check == 0)
	remove(filename);
}

void SaveGameOptions(int game_num)
{
	FILE  *fp;
	int   i;
	char  filename[80];

	sprintf(filename, "%s/%s", GetIniDirs(), GAMES_INI);
	fp = fopen(filename, "w");
	if (fp)
	{
		for (i = 0; drivers[i]; i++)
		{
			IniReset();
			SaveIniGameOptions(&game[i]);
			IniWriteFile(fp, drivers[i]->name);
		}
		fclose(fp);
	}
}

void SaveDefaultOptions(void)
{
	FILE  *fp;
#if 0
	char  filename[80];

	sprintf(filename, "%s/%s", GetIniDirs(), DEFAULT_INI);
	fp = fopen(filename, "w");
#else
	fp = fopen(DEFAULT_INI, "w");
#endif
	if (fp)
	{
		IniReset();
		SaveIniSettingsOptions();
		global.use_default = FALSE;
		SaveIniGameOptions(&global);
		IniWriteFile(fp, "default");
		fclose(fp);
	}
}

static void SaveIniSettingsOptions(void)
{
	int i;

	PutIniStringOption("SaveVersion", GetVersionString());
	PutIniBoolOption("VersionCheck", settings.version_check);
	PutIniOption("FontColor", settings.list_font_color);
	PutIniBoolOption("ResetGUI", bResetGUI);
	PutIniBoolOption("ResetGameDefaults", bResetGameDefs);

	for (i = 0; i < NUM_SETTINGS; i++)
	PutIniObj(&iniSettings[i]);
}

static void LoadIniSettingsOptions(char *szVersion)
{
	int i;

	strcpy(szVersion, GetIniStringOption("SaveVersion"));
	settings.version_check   = GetIniBoolOption("VersionCheck");
	bResetGUI                = GetIniBoolOption("ResetGUI");
	bResetGameDefs           = GetIniBoolOption("ResetGameDefaults");
	settings.list_font_color = GetIniOption("FontColor");

	for (i = 0; i < NUM_SETTINGS; i++)
	GetIniObj(&iniSettings[i]);
}

static void SaveIniGameOptions(options_type *o)
{
	int i;

	if (o->use_default == TRUE)
	IniReset();

	PutIniOption("PlayCount", o->play_count);
	PutIniOption("HasRoms", o->has_roms);
	PutIniOption("HasSamples", o->has_samples);
	PutIniBoolOption("Favorite", o->is_favorite);

	if (o->use_default == TRUE)
	return;

	gOpts = *o;

	for (i = 0; i < NUM_GAMEOPTS; i++)
	PutIniObj(&iniGameOpts[i]);
}

static void LoadIniGameOptions(options_type *o)
{
	int   i;
	DWORD value;
	DWORD size;

	if ((value = GetIniOption("PlayCount")) != -1)
        	o->play_count = value;
	else
        	o->play_count = 0;

	if ((value = GetIniOption("HasRoms")) != -1)
		o->has_roms = value;
	else
		o->has_roms = 0;

	if ((value = GetIniOption("HasSamples")) != -1)
		o->has_samples = value;
	else
		o->has_samples = 0;

	o->is_favorite = GetIniBoolOption("Favorite");

	if (IniQueryValue("autoframeskip", &value, &size) != ERROR_SUCCESS)
	return;

	o->use_default = FALSE;

	gOpts = *o;

	for (i = 0; i < NUM_GAMEOPTS; i++)
	GetIniObj(&iniGameOpts[i]);

	/* copy options back out */
	*o = gOpts;
}

static DWORD GetIniOption(char *name)
{
	char *ptr;
	DWORD value = -1;

	ptr = GetIniStringOption(name);
	if (ptr != NULL)
	value = atoi(ptr);

	return value;
}

static BOOL GetIniBoolOption(char *name)
{
	char *ptr;
	BOOL value = FALSE;

	ptr = GetIniStringOption(name);
	if (ptr != NULL)
	value = (*ptr == '0') ? FALSE : TRUE;

	return value;
}

static char *GetIniStringOption(char *name)
{
	DWORD dwSize;
	static char str[LINEBUF_SIZE];

	if (IniQueryValue(name, NULL, &dwSize) != ERROR_SUCCESS)
	return NULL;

	IniQueryValue(name, str, &dwSize);

	return str;
}

static void PutIniOption(char *name, DWORD value)
{
	char str[16];

	sprintf(str, "%ld", value);

	IniSetValue(name, (void *)&str, strlen(str));
}

static void PutIniBoolOption(char *name, BOOL value)
{
	char str[2];

	str[0] = (value) ? '1' : '0';
	str[1] = '\0';

	IniSetValue(name, (void *)str, 2);
}

static void PutIniStringOption(char *name, char *option)
{
	IniSetValue(name, (void *)option, strlen(option));
}

static void PutIniObj(INI_OPTIONS *iniOpt)
{
	char    *pString;
	double  *pDouble;
	char    *cName = iniOpt->m_cName;
	char    cTemp[80];

	switch (iniOpt->m_iType)
	{
	case IO_DOUBLE:
		pDouble = (double *)iniOpt->m_vpData;
		sprintf(cTemp, "%03.02f", *pDouble);
		PutIniStringOption(cName, cTemp);
		break;

	case IO_BOOL:
		PutIniBoolOption(cName, *(BOOL *)iniOpt->m_vpData);
		break;

	case IO_INT:
        	PutIniOption(cName, *(int *)iniOpt->m_vpData);
        	break;

	case IO_STRING:
		pString = (char *)iniOpt->m_vpData;
		if (pString)
		PutIniStringOption(cName, pString);
		break;

	case IO_PSTRING:
		pString = *(char **)iniOpt->m_vpData;
		if (pString)
		PutIniStringOption(cName, pString);
		break;

	case IO_ENCODE:
		iniOpt->encode(iniOpt->m_vpData, cTemp);
		PutIniStringOption(cName, cTemp);
		break;

	default:
	break;
	}
}

static void GetIniObj(INI_OPTIONS *iniOpts)
{
	char    *cName = iniOpts->m_cName;
	char    *pString;
	double  *pDouble;

	switch(iniOpts->m_iType)
	{
	case IO_DOUBLE:
		pDouble = (double *)iniOpts->m_vpData;
		if ((pString = GetIniStringOption(cName)) != NULL)
		sscanf(pString, "%lf", pDouble);
		break;

	case IO_BOOL:
		*(BOOL *)iniOpts->m_vpData = GetIniBoolOption(cName);
		break;

	case IO_INT:
		*(int *)iniOpts->m_vpData = GetIniOption(cName);
		break;

	case IO_STRING:
		if ((pString = GetIniStringOption(cName)) != NULL)
		strcpy((char *)iniOpts->m_vpData, pString);
		break;

	case IO_PSTRING:
		if ((pString = GetIniStringOption(cName)) != NULL)
		{
			if (*(char **)iniOpts->m_vpData != NULL)
			free(*(char **)iniOpts->m_vpData);
			*(char **)iniOpts->m_vpData = strdup(pString);
		}
		break;

	case IO_ENCODE:
		if ((pString = GetIniStringOption(cName)) != NULL)
		iniOpts->decode(pString, iniOpts->m_vpData);
		break;

	default:
	break;
	}
}

/***************************************************************************/

static BOOL IniCreate(void)
{
	int i;

	ini = (ini_struct *)malloc(sizeof(ini_struct) * INI_MAX_ENTRIES);
	if (ini == NULL)
	return FALSE;

	for (i = 0; i < INI_MAX_ENTRIES; i++)
	{
		ini[i].name  = NULL;
		ini[i].value = NULL;
	}

	return TRUE;
}

static void IniDestroy(void)
{
	int i;

	if (ini == NULL)
	return;

	for (i = 0; i < INI_MAX_ENTRIES; i++)
	{
		if (ini[i].name)  free(ini[i].name);
		if (ini[i].value) free(ini[i].value);
	}

	free(ini);
	ini = NULL;
}

static void IniReset(void)
{
	int i;

	if (ini == NULL)
	return;

	for (i = 0; i < INI_MAX_ENTRIES; i++)
	{
		if (ini[i].name)  free(ini[i].name);
		if (ini[i].value) free(ini[i].value);

		ini[i].name  = NULL;
		ini[i].value = NULL;
	}
}

static BOOL IniReadDefaultFile(FILE *fp, char *linebuf)
{
	char *name;
	char *value;
	BOOL found = FALSE;

	while (fgets(linebuf, LINEBUF_SIZE, fp))
	{
		if (linebuf[0] == '[')
		{
			char *p = strchr(linebuf, ']');
			if (p != NULL) *p = '\0';
			if (strcmp("default", &linebuf[1]) == 0)
			{
				found = TRUE;
				break;
			}
			if (strcmp("directories", &linebuf[1]) == 0)
			{
				found = TRUE;
				break;
			}
		}
	}

	if (found == FALSE)
        return FALSE;

	while (fgets(linebuf, LINEBUF_SIZE, fp))
	{
		if (linebuf[0] == '[')
		break;

		if (linebuf[0] == ';')
		continue;

		name = strtok(linebuf, "=\r\n");
		if (name == NULL)
		continue;

		value = strtok(NULL, "=\r\n");

		IniSetValue(name, value, strlen(value));
	}

	return TRUE;
}


static void IniReadFile(FILE *fp, char *linebuf)
{
	char *name;
	char *value;
	char *p;
	int  iniindex = -1;

	while (fgets(linebuf, LINEBUF_SIZE, fp))
	{
		if (linebuf[0] == '[')
		{
			if (iniindex != -1)
			LoadIniGameOptions(&game[iniindex]);

			p = strchr(linebuf, ']');
			if (p == NULL)
			continue;

			*p = '\0';
			iniindex = GetIndexByName(&linebuf[1]);
			IniReset();
			continue;
		}

		if (linebuf[0] == ';')
		continue;

		name = strtok(linebuf, "=\r\n");
		if (name == NULL)
		continue;

		value = strtok(NULL, "=\r\n");

		IniSetValue(name, value, strlen(value));
	}

	if (iniindex != -1)
	LoadIniGameOptions(&game[iniindex]);
}


static void IniWriteFile(FILE *fp, const char *keyname)
{
	int   i, j;
	DWORD size;
	char  saved[INI_MAX_ENTRIES];

	memset(saved, 0, INI_MAX_ENTRIES);

	fprintf(fp, "[%s]\n", keyname);

	if (IniQueryValue("autoframeskip", NULL, &size) == ERROR_SUCCESS)
	{
		for (i = 0; i < NUM_GAMEOPTS; i++)
		{
			for (j = 0; ini[j].name; j++)
			{
				if (saved[j]) continue;

				if (strcmp(iniGameOpts[i].m_cName, ini[j].name) == 0)
				{
					fprintf(fp, "%s=%s\n", ini[j].name, ini[j].value);
					saved[j] = 1;
				}
			}
		}
	}

	if (IniQueryValue("DefaultGame", NULL, &size) == ERROR_SUCCESS)
	{
		for (i = 0; i < NUM_SETTINGS; i++)
		{
			for (j = 0; ini[j].name; j++)
			{
				if (saved[j]) continue;

				if (strcmp(iniSettings[i].m_cName, ini[j].name) == 0)
				{
					fprintf(fp, "%s=%s\n", ini[j].name, ini[j].value);
					 saved[j] = 1;
				}
			}
		}
	}

	for (i = 0; ini[i].name; i++)
	{
		if (!saved[i])
		fprintf(fp, "%s=%s\n", ini[i].name, ini[i].value);
	}

	fprintf(fp, "\n");
}


static long IniQueryValue(const char *name, void *value, DWORD *size)
{
	int i;

	for (i = 0; ini[i].name; i++)
	{
		if (strcmp(name, ini[i].name) == 0)
		{
			if (value == NULL)
			{
				*size = strlen(ini[i].value);
				return ERROR_SUCCESS;
			}
			else
			{
				if (ini[i].value)
				{
					strcpy(value, ini[i].value);
					return ERROR_SUCCESS;
				}
				else
					return !ERROR_SUCCESS;
 			}
		}
	}

	return !ERROR_SUCCESS;
}


static void IniSetValue(const char *name, void *value, DWORD size)
{
	int i;
	BOOL found = FALSE;

	for (i = 0; ini[i].name; i++)
	{
		if (strcmp(name, ini[i].name) == 0)
		{
			found = TRUE;
			break;
		}
	}

	if (!found)
	{
		ini[i].name = malloc(strlen(name) + 1);
		strcpy(ini[i].name, name);
	}

	ini[i].value = realloc(ini[i].value, size + 1);
	strcpy(ini[i].value, value);
}


INLINE int GetIndexByName(const char *name)
{
	int i;

	for (i = 0; drivers[i]; i++)
	{
		if (strcmp(name, drivers[i]->name) == 0)
		return i;
	}
	return -1;
}

/***************************************************************************/

#ifdef JAPANESE
BOOL UseJapaneseList(void)
{
	return settings.use_japaneselist;
}

void SetUseJapaneseList(BOOL bUse)
{
	settings.use_japaneselist = bUse;
}
#endif

char* GetVersionString(void)
{
	return build_version;
}

/* End of options.c */
