//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2014 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: control functions main
//          and global variable definition/initialization

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT NULL // stdout
#define MPXPLAY_DEBUGOUT_REG stdout

#include <mpxplay.h>
#include "control.h"
#include "cntfuncs.h"
#include "mpxinbuf.h"
#include <au_mixer\au_mixer.h>
#include <diskdriv\diskdriv.h>
#include <display\display.h>
#include <display\visualpi.h>
#include <videoout\videoout.h>
#include <stdlib.h>
#ifdef MPXPLAY_WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#endif

#ifdef MPXPLAY_LINK_INSTALLER
#define MPXPLAY_LINK_WINREGCONFIG 1 // mpxplay.ini has higher priority
#endif

#if defined(MPXPLAY_LINK_ORIGINAL_FFMPEG)
#define MPXPLAY_CONFIG_VIDEOPLAYERTYPE_DEFAULT MPXPLAY_VIDEOPLAYERTYPE_FFMPEG
#elif defined(MPXPLAY_LINK_QTAV)
#define MPXPLAY_CONFIG_VIDEOPLAYERTYPE_DEFAULT MPXPLAY_VIDEOPLAYERTYPE_QTAV
#elif defined(MPXPLAY_LINK_QTMEDIA)
#define MPXPLAY_CONFIG_VIDEOPLAYERTYPE_DEFAULT MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA
#else
#define MPXPLAY_CONFIG_VIDEOPLAYERTYPE_DEFAULT MPXPLAY_VIDEOPLAYERTYPE_NONE
#endif

#define MPXPLAY_CMDLG_INITSIZE 16

static unsigned int cmdlg_alloc(unsigned int newsize);
#ifdef MPXPLAY_GUI_CONSOLE
static void mpxplay_printhelp(void);
#endif

#ifdef MPXPLAY_LINK_WINREGCONFIG
static void mpxplay_control_configreg_load(void);
static void mpxplay_control_configreg_save(void);
#endif

struct mpxpframe_s fr[3];
struct mainvars mvps;
struct mpxplay_audioout_info_s au_infos;
static crossfade_info cf_infos;
#ifdef MPXPLAY_GUI_CONSOLE
static mpxplay_videoout_info_s videoout_infos;
static display_visual_data_s visual_datas;
struct desktoppos dtp;
#endif

//au_mixer
static int MIXER_varcfg_surround,MIXER_varcfg_speed;

//display\visualpi.c
#ifdef MPXPLAY_GUI_CONSOLE
extern char *display_visual_plugin_selectname;
extern unsigned int display_visual_plugin_starttime;
#endif
//newfunc\drivehnd.c
extern unsigned int uselfn;
//playlist\textconv.c
extern char *textconv_codepage_sourcename,*textconv_codepage_targetname;
//diskdriv\drv_ftp.c
extern unsigned long mpxplay_diskdrive_drvftp_config;//, mpxplay_diskdrive_drvhttp_config;

//global
char *id3loadname="!FILES",*id3savename="!FILESS";
char *m3usavename="MPXPLAY.M3U",*mxusavename="MPXPLAY.MXU",*cuesavename="MPXPLAY.CUE";
char *dosshellprg,*drivescanletters,*id3filterkeyword;
char mpxplay_highlitescan_starttime[MPXINI_MAX_CHARDATA_LEN]=PLAYC_HS_STARTTIME;
char mpxplay_highlitescan_timecount[MPXINI_MAX_CHARDATA_LEN]=PLAYC_HS_TIMECOUNT;
char *id3tagset[I3I_MAX+1],*freeopts[MAXFREEOPTS];
char mpxplay_extrafiletypes_config[MPXINI_MAX_CHARDATA_LEN], *mpxplay_extrafiletype_extptrs[MPXINI_MAX_EXTRA_FILETYPES];
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
char mpxplay_config_video_preffered_audiotypes[MPXINI_MAX_CHARDATA_LEN], *mpxplay_config_video_preffered_audiotype_ptrs[MPXINI_MAX_EXTRA_FILETYPES];
char mpxplay_config_video_preferred_language[MPXPLAY_STREAMTYPEINDEX_NUM][MPXINI_MAX_CHARDATA_LEN];
#endif
#ifdef MPXPLAY_UTF8
static char **utf8_argv,commandline_utf8[1024];
#else
unsigned int id3textconv;
char cp_winchars[MPXINI_MAX_CHARDATA_LEN],cp_doschars[MPXINI_MAX_CHARDATA_LEN];
char sortorder_string[MPXINI_MAX_CHARDATA_LEN];
#endif
unsigned int playlist_orderkeys[PLAYLIST_MAX_ORDERKEYS];
#ifdef MPXPLAY_GUI_CONSOLE
unsigned int playlist_max_filenum_list, playcontrol_mpxpini;
#else
unsigned int loadid3tag_mpxpini, outmode_mpxpini, mpxplay_playlistcontrol_mpxpini, preloadinfo_mpxpini;
#ifdef MPXPLAY_LINK_INSTALLER
static char *mpxplay_application_publicname = "MpxplayMMC";
#endif
#endif
static char *mpxplay_main_ini_name = "MPXPLAY.INI";

mpxp_uint32_t mpxplay_programcontrol,mpxplay_inside_programcontrol,mpxplay_playlistcontrol;
unsigned int playlistload,playlistsave,loadid3tag,id3savefields;
unsigned int preloadinfo,saveid3list,writeid3tag,mpxplay_sortcontrol;
unsigned int crossfadepart,intsoundconfig,intsoundcontrol;
unsigned int prebuffertype,prebufferblocks,prebuffermegabytes;
unsigned int outmode,cdw_control_flags,cdw_control_speed;
unsigned int playcontrol,playreplay,playrand;
unsigned int playstartsong,playcountsong;
	 int playstartlist,playstartframe,playstartpercent,playstartmsec;
         int control_startup_type_override, mpxplay_control_startup_programid_select;
        char *playstarttime,*playcounttime,*playendtime;
unsigned int playcountframe,playcountpercent,fullelapstime,channelmode;
unsigned int SOUNDLIMITvol,SOUNDLIMITbegin,SOUNDLIMITlen;
unsigned int displaymode,desktopmode,desktopmod_player,desktopmod_commander;
unsigned int refdisp,editorsideborder,analtabnum,timemode,mouse_on;
unsigned int su_startupenabled,useglvariables;
unsigned int stream_select_audio;
unsigned long allcpuusage,allcputime;
unsigned long mpxplay_config_videoplayer_type, mpxplay_config_videoplayer_control, mpxplay_config_video_audiovisualization_type;

unsigned long mpxplay_progtimebegin,mpxplay_playtimestart,mpxplay_progtimelen,mpxplay_progtimeexit;
static char *p_playtimestart,*p_progtimelen,*p_progtimeexit;
static char mpxplay_extrafiletypes_str[MPXINI_MAX_CHARDATA_LEN];
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
static char mpxplay_preferedaudiotypes_str[MPXINI_MAX_CHARDATA_LEN];
#endif
static unsigned int playendframe,backbuffermegabytes,dispmode_mpxpini;
static unsigned int do_printhelp,do_listdlls;
#ifdef MPXPLAY_GUI_CONSOLE
static unsigned int do_listvideooutmodes;
#endif

static topt **cmdl_groups;
static unsigned int cmdlg_size,cmdlg_entries;
static unsigned int cmdl_argc;
static char **cmdl_argv;

//note: ARG_CHAR is allways pointer in opts[]

static topt main_opts[] = {
{"@"  ,ARG2|ARG_CHAR|ARG_OR,&playlistload, PLL_LOADLIST,&freeopts[OPT_INPUTFILE]},
{"@i" ,ARG_OR,              &playlistload, PLL_LOADLIST|PLL_STDIN,0},
{"@s" ,ARG2|ARG_CHAR,       &playlistsave, PLST_MANUAL,&m3usavename},
{"@sx",ARG2|ARG_CHAR,       &playlistsave, PLST_MANUAL|PLST_MXU,&mxusavename},
{"@sw",ARG2|ARG_CHAR,       &playlistsave, PLST_MANUAL|PLST_EXTM3U,&m3usavename},
{"@sc",ARG2|ARG_CHAR,       &playlistsave, PLST_MANUAL|PLST_CUE,&cuesavename},
{"t"  ,ARG_OR,              &outmode,      OUTMODE_TYPE_TEST,0},
{"o"  ,ARG_OR,              &outmode,      OUTMODE_TYPE_FILE,0},
{"of" ,ARG1|ARG_NUM,        &au_infos.freq_set,0,0},
{"oc" ,ARG1|ARG_NUM,        &au_infos.chan_set,0,0},
{"ob" ,ARG1|ARG_NUM,        &au_infos.bits_set,0,0},
{"obs",ARG_OR,              &outmode,      OUTMODE_CONTROL_FILE_BITSTREAMOUT,0},
{"obf",ARG_OR,              &outmode,      OUTMODE_CONTROL_FILE_FLOATOUT,0},
{"oni",ARG_OR,              &outmode,      OUTMODE_CONTROL_FILE_TAGLFN,0},
{"oat",ARG_OR,              &au_infos.card_controlbits,AUINFOS_CARDCNTRLBIT_AUTOTAGGING,0},
{"db" ,ARG_OR,              &mpxplay_playlistcontrol,MPXPLAY_PLAYLISTC_DIRBROWSER1,0},
{"db2",ARG_OR,              &mpxplay_playlistcontrol,MPXPLAY_PLAYLISTC_DIRBROWSER2,0},
{"ds" ,ARG2|ARG_CHAR|ARG_OR,&playlistload, PLL_DRIVESCAN,&drivescanletters},
{"drfc",ARG1|ARG_NUM,       &mpxplay_diskdrive_drvftp_config,0,0},
{"bn" ,0,                   &prebuffertype,PREBUFTYPE_NONE,0},
{"bs" ,0,                   &prebuffertype,PREBUFTYPE_SHORTRING,0},
{"bp" ,0,                   &prebuffertype,PREBUFTYPE_LONGRING|PREBUFTYPE_INT,0},
{"bb" ,ARG_OR,              &prebuffertype,PREBUFTYPE_LONGRING|PREBUFTYPE_BACK|PREBUFTYPE_INT,0},
{"bpn",ARG_OR,              &prebuffertype,PREBUFTYPE_LONGRING|PREBUFTYPE_INT|PREBUFTYPE_PRELOADNEXT,0},
{"bl" ,0,                   &prebuffertype,PREBUFTYPE_FULL|PREBUFTYPE_INT,0},
{"bni",ARG_OR,              &intsoundconfig,INTSOUND_NOINTDEC,0},
{"bn8",ARG_OR,              &intsoundconfig,INTSOUND_NOINT08,0},
{"bpb",ARG1|ARG_NUM,        &prebufferblocks,0,0},
{"bpm",ARG1|ARG_NUM,        &prebuffermegabytes,0,0},
{"bbm",ARG2|ARG_NUM|ARG_OR, &prebuffertype,PREBUFTYPE_LONGRING|PREBUFTYPE_BACK|PREBUFTYPE_INT,&backbuffermegabytes},
{"f0" ,0,                   &displaymode,  0,0},
{"ff" ,0,                   &displaymode,  DISP_FRAMES,0},
{"fl" ,0,                   &displaymode,  DISP_TIMEPOS,0},
{"fe" ,ARG_AND,             &displaymode,  ~DISP_NOFULLEDIT,0},
{"fs" ,ARG_AND,             &displaymode,  ~DISP_50LINES,0},
{"v"  ,ARG_OR,              &displaymode,  DISP_VERBOSE,0},
{"inl",0,                   &preloadinfo  ,PLI_NOTLOAD,0},
{"irl",0,                   &preloadinfo  ,PLI_PRELOAD,0},
{"ipl",0,                   &preloadinfo  ,PLI_PLAYLOAD,0},
{"idl",0,                   &preloadinfo  ,PLI_DISPLOAD,0},
{"ihl",0,                   &preloadinfo  ,PLI_EHLINELOAD,0},
{"in" ,0,                   &loadid3tag   ,ID3LOADMODE_NONE,0},
{"inf",0,                   &loadid3tag   ,ID3LOADMODE_NOFILE,0},
{"if" ,ARG1|ARG_CHAR,       &id3filterkeyword, 0,0},
{"io" ,ARG1|ARG_NUM,        &(playlist_orderkeys[0]), 0,0},
{"io2",ARG1|ARG_NUM,        &(playlist_orderkeys[1]), 0,0},
{"io3",ARG1|ARG_NUM,        &(playlist_orderkeys[2]), 0,0},
{"io4",ARG1|ARG_NUM,        &(playlist_orderkeys[3]), 0,0},
{"iod",ARG_OR,              &mpxplay_sortcontrol,  PLAYLIST_SORTCONTROL_DESCENDING,0},
{"il" ,ARG2|ARG_CHAR|ARG_OR,&mpxplay_playlistcontrol,MPXPLAY_PLAYLISTC_ID3LIST_LOCAL,&id3loadname},
{"ig" ,ARG2|ARG_CHAR|ARG_OR,&mpxplay_playlistcontrol,MPXPLAY_PLAYLISTC_ID3LIST_GLOBAL,&id3loadname},
{"is" ,ARG2|ARG_CHAR,       &saveid3list,  1,&id3savename},
{"ist",ARG2|ARG_NUM,        &id3savefields,2,&id3savefields},
{"iw" ,0,                   &writeid3tag,  1,0},
{"ita",ARG1|ARG_CHAR,       &id3tagset[I3I_ARTIST]  ,0,0},
{"itt",ARG1|ARG_CHAR,       &id3tagset[I3I_TITLE]   ,0,0},
{"itl",ARG1|ARG_CHAR,       &id3tagset[I3I_ALBUM]   ,0,0},
{"ity",ARG1|ARG_CHAR,       &id3tagset[I3I_YEAR]    ,0,0},
{"itc",ARG1|ARG_CHAR,       &id3tagset[I3I_COMMENT] ,0,0},
{"itg",ARG1|ARG_CHAR,       &id3tagset[I3I_GENRE]   ,0,0},
{"itn",ARG1|ARG_CHAR,       &id3tagset[I3I_TRACKNUM],0,0},
{"scs",ARG1|ARG_CHAR,       &au_infos.card_selectname,0,0},
{"scd",ARG1|ARG_NUM,        &au_infos.card_select_devicenum,0,0},
{"scc",ARG1|ARG_NUM,        &au_infos.card_select_config,0,0},
{"sct",ARG2|ARG_CHAR|ARG_OR,&au_infos.card_controlbits,AUINFOS_CARDCNTRLBIT_TESTCARD,&au_infos.card_selectname},
{"ddma",ARG_OR,             &au_infos.card_controlbits,AUINFOS_CARDCNTRLBIT_DOUBLEDMA,0},
{"scm",ARG_OR,              &au_infos.card_controlbits,AUINFOS_CARDCNTRLBIT_MIDASMANUALCFG,0},
{"scv",ARG1|ARG_NUM,        &au_infos.card_master_volume,0,0},
{"scvom",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_MASTER],0,0},
{"scvop",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_PCM],0,0},
{"scvoh",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_HEADPHONE],0,0},
{"scvos",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_SPDIFOUT],0,0},
{"scvim",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_MICIN],0,0},
{"scvil",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_LINEIN],0,0},
{"scvic",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_CDIN],0,0},
{"scvia",ARG1|ARG_NUM,      &au_infos.card_mixer_values[AU_MIXCHAN_AUXIN],0,0},
{"sctr",ARG1|ARG_NUM,       &au_infos.card_mixer_values[AU_MIXCHAN_TREBLE],0,0},
{"scbs",ARG1|ARG_NUM,       &au_infos.card_mixer_values[AU_MIXCHAN_BASS],0,0},
{"cl" ,0,                   &channelmode,  CHM_LEFT,0},
{"cm" ,0,                   &channelmode,  CHM_DOWNMIX,0},
{"csa",ARG1|ARG_NUM,        &stream_select_audio,0,0},
{"sl" ,ARG1|ARG_NUM,        &SOUNDLIMITvol,0,0},
{"sv" ,ARG1|ARG_NUM,        &MIXER_var_volume,    0,0},
{"sva",0,                   &MIXER_var_autovolume,1,0},
{"sr" ,ARG1|ARG_NUM,        &MIXER_varcfg_surround,0,0},
{"sp" ,ARG1|ARG_NUM,        &MIXER_varcfg_speed,   0,0},
{"mxlo",ARG1|ARG_NUM,       &MIXER_var_limiter_overflow,0,0},
{"mxmd",ARG1|ARG_NUM,       &MIXER_var_mute_voldiv,0,0},
{"psu",ARG1|ARG_NUM,        &control_startup_type_override, 0,0},
{"pss",ARG2|ARG_NUM|ARG_OR, &playcontrol,  PLAYC_STARTNEXT,&playstartsong},
{"psf",ARG1|ARG_NUM,        &playstartframe,  0,0},
{"pst",ARG1|ARG_CHAR,       &playstarttime,   0,0},
{"psp",ARG1|ARG_NUM,        &playstartpercent,0,0},
{"pcs",ARG1|ARG_NUM,        &playcountsong,   0,0},
{"pcf",ARG1|ARG_NUM,        &playcountframe,  0,0},
{"pct",ARG1|ARG_CHAR,       &playcounttime,   0,0},
{"pcp",ARG1|ARG_NUM,        &playcountpercent,0,0},
{"pef",ARG1|ARG_NUM,        &playendframe,    0,0},
{"pet",ARG1|ARG_CHAR,       &playendtime,     0,0},
{"pslf",ARG2|ARG_NUM|ARG_OR,&playlistload, PLL_FASTLIST,&playstartlist},
{"ppa",ARG_OR,              &playcontrol,  PLAYC_PAUSEALL,0},
{"ppn",ARG_OR,              &playcontrol,  PLAYC_PAUSENEXT,0},
{"phs",ARG_OR,              &playcontrol,  (PLAYC_HIGHSCAN|PLAYC_STARTNEXT),0},
{"phsh",ARG_OR,             &playcontrol,  (PLAYC_HIGHSCAN|PLAYC_HSSTARTPOSHALF|PLAYC_STARTNEXT),0},
{"prn",0,                   &playrand,     1,0},
{"prn2",0,                  &playrand,     2,0},
{"pre",0,                   &playreplay,   REPLAY_LIST,0},
{"pre1",0,                  &playreplay,   REPLAY_SONG,0},
{"cf" ,0,                   &cf_infos.usecrossfade,     1,0},
{"cft",ARG1|ARG_NUM,        &cf_infos.crossfadetype,    0,0},
{"cfo",ARG1|ARG_NUM,        &cf_infos.crossfade_out_len,0,0},
{"cfp",ARG1|ARG_NUM,        &cf_infos.crossfadepoint,   0,0},
{"cfi",ARG1|ARG_NUM,        &cf_infos.crossfade_in_len, 0,0},
{"cfl",ARG1|ARG_NUM,        &cf_infos.crossfadelimit,   0,0},
#ifndef MPXPLAY_UTF8
{"8"  ,ARG_OR,              &id3textconv, ID3TEXTCONV_CODEPAGE,0},
{"8f" ,ARG_OR,              &id3textconv, ID3TEXTCONV_FILENAME,0},
{"8u" ,ARG_OR,              &id3textconv, ID3TEXTCONV_UTF8,0},
{"8ua",ARG_OR,              &id3textconv, ID3TEXTCONV_UTF_AUTO,0},
{"8uv",ARG_OR,              &id3textconv, (ID3TEXTCONV_VALIDATE|ID3TEXTCONV_UTF_AUTO),0},
#ifdef MPXPLAY_WIN32
{"8w" ,ARG_OR,              &id3textconv, ID3TEXTCONV_GET_WINCP,0},
#endif
{"8b" ,ARG_OR,              &id3textconv, ID3TEXTCONV_CP_BACK,0},
{"8ucp",ARG2|ARG_CHAR|ARG_OR,&id3textconv, ID3TEXTCONV_UTF_AUTO,&textconv_codepage_sourcename},
#else
{"8ucp",ARG1|ARG_CHAR,      &textconv_codepage_sourcename,0,0},
#endif
{"8ccp",ARG1|ARG_CHAR,      &textconv_codepage_targetname,0,0},
{"ebs",ARG1|ARG_NUM,        &editorsideborder,0,0},
{"x"  ,ARG1|ARG_CHAR,       &dosshellprg, 0,0},
{"xs" ,ARG_OR,              &intsoundconfig,INTSOUND_DOSSHELL,0},
{"xr" ,ARG_OR,              &intsoundconfig,INTSOUND_TSR,0},
{"xas",ARG_OR,              &mpxplay_programcontrol,MPXPLAY_PROGRAMC_SHUTDOWNATX,0},
{"xts",ARG1|ARG_CHAR,       &p_playtimestart,0,0},
{"xtl",ARG1|ARG_CHAR,       &p_progtimelen,0,0},
{"xte",ARG1|ARG_CHAR,       &p_progtimeexit,0,0},
{"xel",ARG_OR,              &playcontrol,PLAYC_EXITENDLIST,0},
{"xce",ARG_OR,              &mpxplay_programcontrol,MPXPLAY_PROGRAMC_CONFIRMEXIT,0},
#ifdef MPXPLAY_GUI_CONSOLE
{"vps",ARG1|ARG_CHAR,       &display_visual_plugin_selectname,0,0},
{"vpt",ARG1|ARG_NUM,        &display_visual_plugin_starttime ,0,0},
{"vos",ARG1|ARG_CHAR,       &videoout_infos.config_screenhandler_name,0,0},
{"vom",ARG1|ARG_NUM|ARG_HEX,&videoout_infos.config_mode,0,0},
{"vox",ARG1|ARG_NUM,        &videoout_infos.config_res_x,0,0},
{"voy",ARG1|ARG_NUM,        &videoout_infos.config_res_y,0,0},
{"vob",ARG1|ARG_NUM,        &videoout_infos.config_bpp,0,0},
{"volm",0,                  &do_listvideooutmodes,1,0},
#endif
{"dlls",0,                  &do_listdlls,  1,0},
{"?"  ,0,                   &do_printhelp, 1,0},
{"h"  ,0,                   &do_printhelp, 1,0},
{0    ,0,                   0,             0,0}
};

static mpxini_var_s gl[]={
{"UseVariables" ,&useglvariables  ,0},                  //0.
{"ProgramCntrl" ,&mpxplay_programcontrol, ARG_CONFIG_SAVE},
//file/buffer
{"Prebuffertype",&prebuffertype   ,0},
{"Intsoundcfg"  ,&intsoundconfig  ,ARG_CONFIG_SAVE},
{"Bufferblocks" ,&prebufferblocks ,0},
//playcontrol
{"PlaySongCount",&playcountsong   ,0},
{"PlayRandom"   ,&playrand        ,ARG_SAVE},
{"PlayReplay"   ,&playreplay      ,ARG_SAVE},
{"PlayControl"  ,&playcontrol     ,ARG_SAVE},
{"PlayHSstart"  ,&mpxplay_highlitescan_starttime[0], ARG_CHAR|ARG_CONFIG_SAVE},
{"PlayHScount"  ,&mpxplay_highlitescan_timecount[0], ARG_CHAR|ARG_CONFIG_SAVE}, // 10.
{"SeekFrames"   ,&mvps.seek_frames,0},
//soundcard
{"Outmode"      ,&outmode,   ARG_CONFIG_SAVE},
{"SoundcardName",&au_infos.card_selectname  ,ARG_CHAR|ARG_POINTER|ARG_CONFIG_SAVE},
{"SoundcardFreq",&au_infos.freq_set   ,ARG_CONFIG_SAVE},
{"SoundcardChan",&au_infos.chan_set   ,ARG_CONFIG_SAVE},
{"SoundcardBits",&au_infos.bits_set   ,ARG_CONFIG_SAVE},
{"SoundcardVol" ,&au_infos.card_master_volume,0},
//{"SoundcardVol" ,&au_infos.card_mixer_values[AU_MIXCHAN_MASTER][AU_MIXCHANFUNC_VOLUME],0},
{"SoundcardTrbl",&au_infos.card_mixer_values[AU_MIXCHAN_TREBLE],ARG_SAVE},
{"SoundcardBass",&au_infos.card_mixer_values[AU_MIXCHAN_BASS]  ,ARG_SAVE},
//mixer
{"MixerControl" ,&MIXER_controlbits,    ARG_CONFIG_SAVE},        // 20.
{"SoundVolume"  ,&MIXER_var_volume,     ARG_SAVE},
{"AutoVolume"   ,&MIXER_var_autovolume, ARG_SAVE},
{"Surround"     ,&MIXER_varcfg_surround,ARG_SAVE},
{"Speed"        ,&MIXER_varcfg_speed,   ARG_SAVE},
{"Balance"      ,&MIXER_var_balance,    ARG_SAVE},
{"Swapchan"     ,&MIXER_var_swapchan,   ARG_SAVE},
{"LoudnessVol"  ,&MIXER_loudness_val_volume,  ARG_CONFIG_SAVE},
{"LoudnessSurr" ,&MIXER_loudness_val_surround,ARG_CONFIG_SAVE},
{"LoudnessBass" ,&MIXER_loudness_val_bass,    ARG_CONFIG_SAVE},
{"LoudnessTrebl",&MIXER_loudness_val_treble,  ARG_CONFIG_SAVE}, // 30.
{"SoundLimitVol",&SOUNDLIMITvol   ,0},
{"SoundLimitBeg",&SOUNDLIMITbegin ,0},
{"SoundLimitLen",&SOUNDLIMITlen   ,0},
{"UseCrossfade" ,&cf_infos.usecrossfade     ,ARG_SAVE},
{"CFtype"       ,&cf_infos.crossfadetype    ,ARG_SAVE},
{"CFlimit"      ,&cf_infos.crossfadelimit   ,0},
{"CFpoint"      ,&cf_infos.crossfadepoint   ,0},
{"CFoutlength"  ,&cf_infos.crossfade_out_len,0},
{"CFinlength"   ,&cf_infos.crossfade_in_len ,0},
//decoder
{"ChannelMode"  ,&channelmode     ,0},                   // 40.
{"CDWcontrol"   ,&cdw_control_flags,0},
{"CDWspeed"     ,&cdw_control_speed,0},
//display
{"Displaymode"  ,&displaymode     ,ARG_SAVE},
#ifdef MPXPLAY_GUI_CONSOLE
{"Desktopmode"  ,&desktopmode     ,ARG_SAVE}, //for backward compatibility only
#endif
{"DesktopmodeP" ,&desktopmod_player,ARG_SAVE},
{"DesktopmodeC" ,&desktopmod_commander,ARG_SAVE},
{"TimeMode"     ,&timemode        ,ARG_SAVE},
#ifdef MPXPLAY_GUI_CONSOLE
{"EditSideBordr",&editorsideborder,ARG_SAVE},
{"EditorBegin"  ,&(dtp.relative_songposline),ARG_SAVE},
#endif
//playlist&id3tag
{"PlayListCntrl",&mpxplay_playlistcontrol,ARG_CONFIG_SAVE},
#ifdef MPXPLAY_GUI_CONSOLE
{"MaxFilenames" ,&playlist_max_filenum_list,0},          // 51.
#endif
{"Preloadinfo"  ,&preloadinfo     ,ARG_CONFIG_SAVE},
{"ID3ordertype" ,&(playlist_orderkeys[0]),0},
{"SortControl"  ,&mpxplay_sortcontrol,ARG_CONFIG_SAVE},
#ifndef MPXPLAY_UTF8
{"SortOrder"    ,&sortorder_string[0],ARG_CHAR},
#endif
{"LoadID3tag"   ,&loadid3tag      ,ARG_CONFIG_SAVE},
#ifndef MPXPLAY_UTF8
{"Conv852437"   ,&id3textconv     ,0},
{"WinChars"     ,&cp_winchars[0]  ,ARG_CHAR},
{"DosChars"     ,&cp_doschars[0]  ,ARG_CHAR},
#endif
{"UseLFN"       ,&uselfn          ,0},                   // 60.
{"VideoPlayType",&mpxplay_config_videoplayer_type, ARG_CONFIG_SAVE},
{"VideoPlayCtrl",&mpxplay_config_videoplayer_control, ARG_CONFIG_SAVE},
#ifdef MPXPLAY_GUI_CONSOLE
{"FileTypesExt" ,&mpxplay_extrafiletypes_config[0], ARG_CHAR },
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
{"PreferAudios" ,&mpxplay_config_video_preffered_audiotypes[0], ARG_CHAR },
#endif
#endif
//{"DrvHTTPcontrol",&mpxplay_diskdrive_drvhttp_config, ARG_CONFIG_SAVE},
{"StartupType"  ,&su_startupenabled,ARG_CONFIG_SAVE},
{NULL           ,NULL,0}
};

void mpxplay_control_initvar(int argc,char *argv[],struct mainvars *mvp)
{
 unsigned int i;
 struct crossfade_info *cfi;
 struct mpxpframe_s *frp0;
#if defined(MPXPLAY_UTF8) && defined(MPXPLAY_WIN32)
 LPWSTR *szArglist;
 int nArgs;
#endif

 cmdl_argc=argc;
 cmdl_argv=argv;

#ifdef MPXPLAY_UTF8
#if defined(MPXPLAY_WIN32)
 szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
 if(szArglist && (nArgs>0)){
  utf8_argv=(char **)pds_calloc(nArgs,sizeof(char *));
  if(utf8_argv){
   char *cup=&commandline_utf8[0];
   int left=sizeof(commandline_utf8)-1;
   for(i=0;(i<nArgs) && (left>1);i++){
    unsigned int len;
    utf8_argv[i]=cup;
    len=pds_str_UTF16LE_to_UTF8((mpxp_uint8_t *)cup,(mpxp_uint16_t *)szArglist[i],left)+1;
    cup+=len;
    left-=len;
   }
   cmdl_argc=i;
   cmdl_argv=utf8_argv;
  }
  LocalFree(szArglist);
 }
#else
 // TODO: non-win32 UTF8 command line handling
#endif // MPXPLAY_WIN32
#endif // MPXPLAY_UTF8

 freeopts[OPT_PROGNAME]=cmdl_argv[0];

 mvp->fr_base = mvp->fr_primary = &fr[0];
 mvp->fr_check = &fr[2];
 mvp->aui=&au_infos;
 mvp->aui->mvp=mvp;
 mvp->cfi=&cf_infos;
#ifdef MPXPLAY_GUI_CONSOLE
 mvp->voi=&videoout_infos;
 mvp->vds=&visual_datas;
#endif
 mvp->partselect=mvp->direction=mvp->foundfile=1;
 mvp->idone=MPXPLAY_ERROR_INFILE_EOF;

 //----------------------------------------------------------------------
 if(!playlist_editlist_tabs_init(mvp))
  mpxplay_close_program(MPXERROR_XMS_MEM);
 for(i=0;i<PLAYLIST_MAX_ORDERKEYS;i++)
  playlist_orderkeys[i]=ID3ORDER_DISABLED;
 //-----------------------------------------------------------------------
 cfi=mvp->cfi;
 cfi->crossfade_out_len=250;
 cfi->crossfade_in_len=200;
 cfi->crossfadepoint=120;
 cfi->crossfadetype=CFT_FADEOUT;
 //-----------------------------------------------------------------------
 frp0=&fr[0];
 frp0->allframes=1;
 frp0->fro=&fr[1];
 fr[1].fro=frp0;
 fr[2].fro=&fr[2];
 //----------------------------------------------------------------------

 mvp->seek_frames=50;

 playcountsong=0x7fffffff;
 SOUNDLIMITvol=5;
 SOUNDLIMITbegin=400;
 SOUNDLIMITlen=23;

 au_infos.card_select_config=-1;
 au_infos.card_selectname = "AUTO";
 AU_setmixer_init(mvp->aui);
 MIXER_init(mvp,mvp->aui,mvp->fr_primary);

 MIXER_varcfg_surround=MIXER_getvalue("MIX_SURROUND");
 MIXER_varcfg_speed=MIXER_getvalue("MIX_SPEED");

 displaymode=DISP_TIMEPOS|DISP_VERBOSE|DISP_FULLSCREEN|DISP_NOFULLEDIT|DISP_ANALISER|DISP_50LINES;
 desktopmod_player=DTM_SONGPOS|DTM_LISTPOS|DTM_EDIT_ELEVATOR
#ifdef MPXPLAY_GUI_CONSOLE
         |DTM_EDIT_VERTICAL
#endif
	     |DTM_EDIT_FULLPATH|DTM_EDIT_FULLTIME|DTM_EDIT_SONGTIME
	     |DTM_EDIT_SONGNUM;// =507
 desktopmod_commander=(desktopmod_player&(~(DTM_EDIT_SONGNUM|DTM_EDIT_SONGTIME)))|DTM_MASK_COMMANDER|DTM_EDIT_ALIGNFIELDS|DTM_EDIT_SHOWFIELDHEAD;
 editorsideborder=EDITOR_SIDE_SIZE_DEFAULT;
 loadid3tag=ID3LOADMODE_DEFAULT;
#ifndef MPXPLAY_UTF8
 id3textconv=ID3TEXTCONV_UTF_AUTO;
#endif
 preloadinfo=PLI_DISPLOAD;
 prebuffertype=PREBUFTYPE_LONGRING|PREBUFTYPE_INT;
 channelmode=CHM_STEREO;
 cdw_control_flags=1;
 outmode=OUTMODE_TYPE_AUDIO;
#ifdef MPXPLAY_LINK_INSTALLER
 su_startupenabled = MPXPLAY_STARTUP_FLAGS_DEFAULT;
#endif
#ifndef MPXPLAY_GUI_CONSOLE
 mpxplay_programcontrol = MPXPLAY_PROGRAMC_CONFIRMEXIT | MPXPLAY_PROGRAMC_SYSTEMAPPOPEN_UNKNOWN;
#endif

 id3savefields=IST_DEFAULT;
 useglvariables=allcpuusage=allcputime=1;
#ifndef MPXPLAY_WIN32
 uselfn=USELFN_ENABLED;
#endif
 playstartlist=control_startup_type_override=-1;
 mpxplay_control_startup_programid_select = -2; // INFFMPG_INVALID_STREAM_INDEX

 mpxplay_config_videoplayer_type = MPXPLAY_CONFIG_VIDEOPLAYERTYPE_DEFAULT;
 mpxplay_config_videoplayer_control = MPXPLAY_CONFIG_VIDEOPLAYERCONTROL_DEFAULT;

 cmdlg_alloc(MPXPLAY_CMDLG_INITSIZE);
}

static unsigned int cmdlg_alloc(unsigned int newsize)
{
 topt **newcmdlg=(topt **)pds_calloc(newsize,sizeof(*cmdl_groups));
 if(!newcmdlg)
  return 0;
 if(cmdl_groups){
  pds_memcpy((void *)newcmdlg,(void *)cmdl_groups,cmdlg_size*sizeof(*cmdl_groups));
  free(cmdl_groups);
  cmdl_groups=newcmdlg;
 }else{
  cmdl_groups=newcmdlg;
  cmdl_groups[0]=main_opts;
  cmdlg_entries=1;
 }
 cmdlg_size=newsize;
 return 1;
}

static void cmdlg_free(void)
{
 if(cmdl_groups){
  free(cmdl_groups);
  cmdl_groups=NULL;
 }
 cmdlg_size=cmdlg_entries=0;
#ifdef MPXPLAY_UTF8
 if(utf8_argv){
  free(utf8_argv);
  utf8_argv=NULL;
 }
#endif
}

static void cmdlg_loaddlls(void)
{
#ifdef MPXPLAY_LINK_DLLLOAD
 mpxplay_module_entry_s *dll_found=NULL;
 do{
  dll_found=newfunc_dllload_getmodule(MPXPLAY_DLLMODULETYPE_CONTROL_CMDLINE,0,NULL,dll_found); // get next
  //fprintf(stdout,"dll:%8.8X sv:%4.4X\n",dll_found,dll_found->module_structure_version);
  if(dll_found && (dll_found->module_structure_version==MPXPLAY_DLLMODULEVER_CONTROL_CMDLINE)){ // !!!
   if(cmdlg_entries>=cmdlg_size)
    if(!cmdlg_alloc(cmdlg_size*2))
     break;
   cmdl_groups[cmdlg_entries]=(topt *)dll_found->module_callpoint;
   if(newfunc_dllload_disablemodule(0,0,NULL,dll_found)) // we don't use it anymore
    cmdl_groups[cmdlg_entries]=NULL;  // dll has unloaded (rare)
   else
    cmdlg_entries++;                  // dll has keeped
  }
 }while(dll_found);
#endif
}

void mpxplay_control_getcommandlineopts(void)
{
 unsigned int i,g,freeoptcount,found,argc=cmdl_argc;
 char **argv=cmdl_argv;
 topt *pointer;

 if(argc>1){
  freeoptcount=1; // 0. is the progname
  cmdlg_loaddlls();
  for(i=1;i<argc;i++){
   if(argv[i][0]=='-' || argv[i][0]=='/'){
    found=0;
    for(g=0;g<cmdlg_entries && !found;g++){
     pointer=cmdl_groups[g];
     while(pointer->oname!=NULL){
      if(pds_stricmp(&argv[i][1],pointer->oname)==0){
       if(!(pointer->flags&ARG1) || ((pointer->flags&ARG2)==ARG2)){
        if(pointer->flags&ARG_OR)
         *((int *) pointer->var)|=pointer->value;
        else
         if(pointer->flags&ARG_AND)
          *((int *) pointer->var)&=pointer->value;
         else
          *((int *) pointer->var)=pointer->value;
       }
       if((pointer->flags & ARG1)==ARG1){
        char *source;
       	i++;
        source=argv[i];
        if(i<argc && source && source[0]!='-' && source[0]!='/'){
         void *target=((pointer->flags&ARG2)==ARG2)? pointer->var2:pointer->var;
         if(pointer->flags&ARG_CHAR)
          *((char **)target)=source;
         else
          if(pointer->flags&ARG_FLO)
           *((float *)target)=(float)atof(source);
          else
           if(pointer->flags&ARG_HEX)
            *((int *)target)=pds_atol16(source);
           else
            *((int *)target)=pds_atol(source);
        }else
         i--;
       }
       found=1;
       break;
      }
      pointer++;
     }
    }
   }else
    if(freeoptcount<MAXFREEOPTS)
     freeopts[freeoptcount++]=argv[i];
  }
 }
}

void mpxplay_control_checkvar(struct mainvars *mvp)
{
 struct playlist_side_info *psi;
 struct mpxplay_audioout_info_s *aui;
 struct crossfade_info *cfi;
 long i;

#ifdef MPXPLAY_GUI_CONSOLE
 if(do_listdlls){
  newfunc_dllload_list_dlls();
  mpxplay_close_program(MPXERROR_UNDEFINED);
 }
 if(do_listvideooutmodes){
  mpxplay_videoout_listmodes(mvp->voi);
  mpxplay_close_program(MPXERROR_UNDEFINED);
 }
 if(do_printhelp){
  mpxplay_printhelp();
  mpxplay_close_program(MPXERROR_UNDEFINED);
 }
#endif

 psi=mvp->psi0;

 if(mpxplay_playlistcontrol&MPXPLAY_PLAYLISTC_DIRBROWSER1){
  psi->editsidetype=0;
  //funcbit_disable(mpxplay_playlistcontrol,MPXPLAY_PLAYLISTC_DIRBROWSER2);
 }

 loadid3tag&=ID3LOADMODE_MASK_CONFIG;
 if(mpxplay_playlistcontrol&MPXPLAY_PLAYLISTC_MASK_ID3LIST)
  funcbit_enable(loadid3tag,ID3LOADMODE_LIST);
 if(writeid3tag)
  saveid3list=0;
 if(writeid3tag || saveid3list){
  psi->editsidetype=PLT_DIRECTORY;
  displaymode=0;
  preloadinfo=PLI_NOTLOAD;
  outmode=OUTMODE_TYPE_NONE;
  control_startup_type_override=0;
 }

 if(outmode&OUTMODE_TYPE_TEST){
  funcbit_disable(outmode,OUTMODE_TYPE_MASK&(~OUTMODE_TYPE_TEST));
  //funcbit_enable(intsoundconfig,INTSOUND_NOINT08); // ???
 }
 if(outmode&OUTMODE_TYPE_FILE){
  funcbit_disable(outmode,OUTMODE_TYPE_MASK&(~OUTMODE_TYPE_FILE));
  funcbit_enable(intsoundconfig,INTSOUND_NOINT08);
  funcbit_disable(psi->editsidetype,PLT_DIRECTORY);
  control_startup_type_override=0;
 }

 if(funcbit_test(prebuffertype,(PREBUFTYPE_BACK|PREBUFTYPE_PRELOADNEXT))){
  funcbit_disable(prebuffertype,(PREBUFTYPE_FULL|PREBUFTYPE_SHORTRING));
  funcbit_enable(prebuffertype,PREBUFTYPE_LONGRING);
 }

 if(funcbit_test(prebuffertype,PREBUFTYPE_INT))
  funcbit_enable(intsoundconfig,INTSOUND_DECODER);

 if(!prebuffertype || funcbit_test(prebuffertype,PREBUFTYPE_SHORTRING))
  funcbit_disable(intsoundconfig,INTSOUND_DECODER);

 if(funcbit_test(intsoundconfig,(INTSOUND_NOINTDEC|INTSOUND_NOINT08)))
  funcbit_disable(intsoundconfig,INTSOUND_FUNCTIONS);

 if(funcbit_test(intsoundconfig,INTSOUND_TSR)){
  funcbit_enable(intsoundconfig,INTSOUND_DECODER);
  if(!funcbit_test(prebuffertype,PREBUFTYPE_FULL)){
   funcbit_disable(prebuffertype,PREBUFTYPE_MASK);
   funcbit_enable(prebuffertype,PREBUFTYPE_LONGRING);
  }
 }
 if(funcbit_test(prebuffertype,PREBUFTYPE_BACK)){
  if(!prebuffermegabytes)
   prebuffermegabytes=PREBUFFERBLOCKS_LONGRING*PREBUFFERBLOCKSIZE_DECODE/1048576; // 1mbyte
  if(!backbuffermegabytes)
   backbuffermegabytes=prebuffermegabytes;
  prebuffermegabytes+=backbuffermegabytes;
  i=100-(long)(100.0*(float)backbuffermegabytes/(float)prebuffermegabytes);
  if(i>PREBUFTYPE_BACKBUF_PERCENT_MAX)
   i=PREBUFTYPE_BACKBUF_PERCENT_MAX;
  PREBUFTYPE_PUT_BACKBUF_PERCENT(prebuffertype,i);
 }

 if(prebuffermegabytes)
  prebufferblocks=prebuffermegabytes*1048576/PREBUFFERBLOCKSIZE_DECODE;

 switch(prebuffertype&PREBUFTYPE_MASK){
  case PREBUFTYPE_LONGRING :if(!prebufferblocks)
                             prebufferblocks=PREBUFFERBLOCKS_LONGRING;
  case PREBUFTYPE_SHORTRING:funcbit_disable(prebuffertype,PREBUFTYPE_MASK);
                            funcbit_enable(prebuffertype,PREBUFTYPE_RING);
                            break;
  case PREBUFTYPE_FULL     :if(!prebufferblocks && funcbit_test(intsoundconfig,INTSOUND_DECODER))
                             prebufferblocks=PREBUFFERBLOCKS_LONGRING; // if cannot alloc fullbuffer
 }
 if(prebufferblocks<PREBUFFERBLOCKS_SHORTRING)
  prebufferblocks=PREBUFFERBLOCKS_SHORTRING;

 if(outmode!=OUTMODE_TYPE_NONE){
  if((playstartpercent>=16) && (playstartpercent<=115))
   playstartpercent-=16;
  else
   playstartpercent=0;
  if(playendframe>playstartframe && !playcountframe && !playcounttime && !playcountpercent)
   playcountframe=playendframe-playstartframe;
  if(playcontrol&PLAYC_HIGHSCAN){
   if(!playstartsong && !playrand)
    playstartsong=PLAYC_HS_STARTSONG;
   if(!playstartframe && !playstarttime && !playstartpercent)
    playstarttime=&mpxplay_highlitescan_starttime[0];
   if(!playcountframe && !playcounttime && !playcountpercent && !playendtime)
    playcounttime=&mpxplay_highlitescan_timecount[0];
  }
 }

 if(!(displaymode&DISP_FULLSCREEN)){
  if(preloadinfo==PLI_DISPLOAD)   // -idl is not usefull in non-fullscreen mode
   preloadinfo=PLI_EHLINELOAD;
  funcbit_disable(gl[43].type,ARG_SAVE); // do not save displaymode in non-fullscreen mode
  funcbit_disable(gl[44].type,ARG_SAVE); // desktopmode detto
  funcbit_disable(gl[45].type,ARG_SAVE); // desktopmod_player
  funcbit_disable(gl[46].type,ARG_SAVE); // desktopmod_commander
 }
 if(!(outmode&OUTMODE_TYPE_AUDIO))
  funcbit_disable(useglvariables,2);     // do not save global variables into mpxplay.ini in non-audio mode

 timemode&=3;
 channelmode&=CHM_USERCNTRL_MASK;
 if((mpxplay_config_videoplayer_type > MPXPLAY_VIDEOPLAYERTYPE_MAX)
#ifndef MPXPLAY_LINK_ORIGINAL_FFMPEG
  || (mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_FFMPEG)
#endif
#ifndef MPXPLAY_LINK_QTMEDIA
  || (mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_QTMEDIA)
#endif
#ifndef MPXPLAY_LINK_QTAV
  || (mpxplay_config_videoplayer_type == MPXPLAY_VIDEOPLAYERTYPE_QTAV)
#endif
 ){
  mpxplay_config_videoplayer_type = MPXPLAY_CONFIG_VIDEOPLAYERTYPE_DEFAULT;
 }

 aui=mvp->aui;

 if(outmode==OUTMODE_TYPE_NONE){
  aui->card_selectname="NON";
 }else{
  if(aui->card_controlbits&AUINFOS_CARDCNTRLBIT_MIDASMANUALCFG)
   aui->card_selectname="MID";
  if(outmode&OUTMODE_TYPE_TEST)
   aui->card_selectname="TST";
  if(outmode&OUTMODE_TYPE_NULL)
   aui->card_selectname="NUL";
  if(outmode&OUTMODE_TYPE_FILE)
   aui->card_selectname="WAV";
  if(outmode&OUTMODE_CONTROL_SNDCARD_DDMA)
   funcbit_enable(au_infos.card_controlbits, AUINFOS_CARDCNTRLBIT_DOUBLEDMA);

  if(aui->chan_set>PCM_MAX_CHANNELS)
   aui->chan_set=PCM_MAX_CHANNELS;
  if(aui->bits_set>PCM_MAX_BITS)
   aui->bits_set=PCM_MAX_BITS;

  MIXER_config_set(&mvp->mmi);
  //if -scv then we set the master and pcm volume with it and we turn off the mute switches
  /*if(aui->card_mixer_values[AU_MIXCHAN_MASTERVOL]){
   aui->card_mixer_values[AU_MIXCHAN_PCMVOL]=aui->card_mixer_values[AU_MIXCHAN_MASTERVOL];
   aui->card_mixer_values[AU_MIXCHAN_MASTERSWITCH]=aui->card_mixer_values[AU_MIXCHAN_PCMSWITCH]=100;
  }*/

  MIXER_setfunction(mvp->fr_primary->mpi,"MIX_SURROUND",MIXER_SETMODE_ABSOLUTE,MIXER_varcfg_surround);
  MIXER_setfunction(mvp->fr_primary->mpi,"MIX_SPEED",MIXER_SETMODE_ABSOLUTE,MIXER_varcfg_speed);

  MIXER_checkallfunc_setflags(mvp->fr_primary->mpi);

  //crossfade-lens check
  cfi=mvp->cfi;
  if(cfi->crossfadepoint>cfi->crossfade_out_len)
   cfi->crossfadepoint=cfi->crossfade_out_len>>1;
  if(cfi->crossfade_in_len<(cfi->crossfade_out_len-cfi->crossfadepoint))
   cfi->crossfade_in_len=cfi->crossfade_out_len-cfi->crossfadepoint+1;

  //start-time, exit-time -> run-time calc
  if(p_progtimelen || p_progtimeexit || p_playtimestart){
   if(p_playtimestart){
    mpxplay_playtimestart=pds_strtime_to_hextime(p_playtimestart,1);
    mpxplay_playtimestart=PDS_HEXTIME_TO_SECONDS(mpxplay_playtimestart);
    funcbit_enable(playcontrol,PLAYC_PAUSENEXT);
   }
   if(p_progtimelen){
    mpxplay_progtimelen=pds_strtime_to_hextime(p_progtimelen,1);
    mpxplay_progtimelen=PDS_HEXTIME_TO_SECONDS(mpxplay_progtimelen);
   }
   if(p_progtimeexit){
    mpxplay_progtimeexit=pds_strtime_to_hextime(p_progtimeexit,1);
    mpxplay_progtimeexit=PDS_HEXTIME_TO_SECONDS(mpxplay_progtimeexit);
   }
   mpxplay_control_start_progexectime();
  }
 }

 mpxplay_control_extrafiletypes_slice();

 //playlist init
 mvp->psil=psi+1;
 if(freeopts[OPT_INPUTFILE]){
  playlist_loadsub_getinputfile(mvp->psil);
  if(!(playlistload&PLL_LOADLIST))
   if(playlist_loadlist_check_extension(freeopts[OPT_INPUTFILE]) && !pds_strchr(freeopts[OPT_INPUTFILE],'*') && !pds_strchr(freeopts[OPT_INPUTFILE],'?'))
    funcbit_enable(playlistload,PLL_LOADLIST);
 }
 if(playlistload&PLL_LOADLIST){
  funcbit_disable(playlistload,PLL_DRIVESCAN|PLL_DIRSCAN);
 }else{
  if(drivescanletters)
   funcbit_enable(playlistload,PLL_DRIVESCAN);
  else
   funcbit_disable(playlistload,PLL_DRIVESCAN);
  if(freeopts[OPT_INPUTFILE])
   funcbit_enable(playlistload,PLL_DIRSCAN);
 }

 if(desktopmode){ // backward compatibility (<=v1.57)
  if((desktopmode&DTM_MASK_COMMANDER)==DTM_MASK_COMMANDER){
   funcbit_enable(displaymode,DISP_DESKTOPMODEC);
   desktopmod_commander=desktopmode;
  }else{
   funcbit_disable(displaymode,DISP_DESKTOPMODEC);
   desktopmod_player=desktopmode;
  }
 }else{
  if(displaymode&DISP_DESKTOPMODEC)
   desktopmode=desktopmod_commander;
  else
   desktopmode=desktopmod_player;
 }
}

static void mpxplay_control_progexectime(struct mainvars *mvp)
{
 unsigned long currtime=pds_gettime();
 currtime=PDS_HEXTIME_TO_SECONDS(currtime);
 if(currtime<mpxplay_progtimebegin) // after midnight
  currtime+=24*3600;                // +1 day
 currtime-=mpxplay_progtimebegin;   // elapsed run-time (in seconds)
 if(mpxplay_playtimestart){
  if(currtime>=mpxplay_playtimestart){
   if(mvp->fr_primary->filetype & HFT_FILE_BUILTIN){
    AU_prestart(mvp->aui);
   }else{
    funcbit_enable(playcontrol,PLAYC_STARTNEXT);
    mvp->adone=ADONE_RESTART;
   }
   mpxplay_playtimestart=0;
   refdisp|=RDT_OPTIONS;
  }
 }
 if(mpxplay_progtimelen){
  if(currtime>mpxplay_progtimelen)
   mpxplay_keyboard_execute_exit(mvp);
 }
}

void mpxplay_control_start_progexectime(void)
{
 if(!mpxplay_playtimestart && !mpxplay_progtimelen && !mpxplay_progtimeexit)
  return;
 mpxplay_progtimebegin=pds_gettime();
 mpxplay_progtimebegin=PDS_HEXTIME_TO_SECONDS(mpxplay_progtimebegin);
 if(mpxplay_playtimestart){
  if(mpxplay_playtimestart < mpxplay_progtimebegin)
   mpxplay_playtimestart+=24*3600;
  mpxplay_playtimestart-=mpxplay_progtimebegin;
 }
 if(mpxplay_progtimelen){
  if(mpxplay_playtimestart)
   mpxplay_progtimelen+=mpxplay_playtimestart;
 }
 if(mpxplay_progtimeexit){
  if(mpxplay_progtimeexit < mpxplay_progtimebegin)
   mpxplay_progtimeexit+=24*3600;                    // exit next day
  mpxplay_progtimeexit-=mpxplay_progtimebegin;
  if(!mpxplay_progtimelen || (mpxplay_progtimeexit<mpxplay_progtimelen))
   mpxplay_progtimelen=mpxplay_progtimeexit;
 }

 mpxplay_timer_addfunc(&mpxplay_control_progexectime,&mvps,MPXPLAY_TIMERTYPE_REPEAT,mpxplay_timer_secs_to_counternum(1));
}

void mpxplay_control_extrafiletypes_slice(void)
{
 pds_strcpy(mpxplay_extrafiletypes_str, mpxplay_extrafiletypes_config);
 mpxplay_newfunc_string_listline_slice(&mpxplay_extrafiletype_extptrs[0], MPXINI_MAX_EXTRA_FILETYPES, ',', mpxplay_extrafiletypes_str);
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
 pds_strcpy(mpxplay_preferedaudiotypes_str, mpxplay_config_video_preffered_audiotypes);
 mpxplay_newfunc_string_listline_slice(&mpxplay_config_video_preffered_audiotype_ptrs[0], MPXINI_MAX_EXTRA_FILETYPES, ',', mpxplay_preferedaudiotypes_str);
#endif
}

static void mpxplay_control_savevar_set(void)
{
 if(funcbit_test(displaymode,DISP_DESKTOPMODEC))
  desktopmod_commander=desktopmode;
 else
  desktopmod_player=desktopmode;
 if((dispmode_mpxpini&DISP_FULLSCREEN) && !(displaymode&DISP_FULLSCREEN))
  displaymode=dispmode_mpxpini; // keep fullscreen mode (if disabled at command line)
 displaymode&=DISP_TEXTMODES;
#ifdef MPXPLAY_GUI_CONSOLE
 playcontrol=(playcontrol_mpxpini&PLAYC_LOADMASK)|(playcontrol&PLAYC_SAVEMASK);
#else
 playcontrol&=PLAYC_SAVEMASK;
 loadid3tag=loadid3tag_mpxpini;
 outmode=outmode_mpxpini;
 mpxplay_playlistcontrol=mpxplay_playlistcontrol_mpxpini&MPXPLAY_PLAYLISTC_MASK_CONFIG;
 preloadinfo=preloadinfo_mpxpini;
 mpxplay_sortcontrol&=PLAYLIST_SORTCONTROL_CONFIG;
 if(!pds_strtime_to_hexhtime(mpxplay_highlitescan_starttime))
  pds_strcpy(mpxplay_highlitescan_starttime, PLAYC_HS_STARTTIME);
 if(!pds_strtime_to_hexhtime(mpxplay_highlitescan_timecount))
  pds_strcpy(mpxplay_highlitescan_timecount, PLAYC_HS_TIMECOUNT);
#endif
 MIXER_varcfg_surround=MIXER_getvalue("MIX_SURROUND");
 MIXER_varcfg_speed=MIXER_getvalue("MIX_SPEED");
}

//-----------------------------------------------------------------------
//load/save, init/close mpxplay.ini parts/infos (keycodes,global vars,startup,other hardwares)
static void mpxplay_control_global_loadini(mpxini_line_t *,struct mpxini_part_t *);
static void mpxplay_control_global_saveini(mpxini_line_t *,struct mpxini_part_t *,FILE *);

static mpxini_part_t mpxini_parts[]={
//    partlinenum=1 is a hack to initialize the part without mpxplay.ini
{"[keyboard]"  ,0,1,0,0,&mpxplay_control_keyboard_loadini,NULL, &mpxplay_control_keyboard_init, NULL},
{"[keygroups]" ,0,0,0,0,&mpxplay_control_keygroup_loadini,NULL, NULL, NULL},
{"[fastlists]" ,0,0,0,0,&mpxplay_control_fastlist_loadini,NULL, NULL, NULL},
#ifdef MPXPLAY_GUI_QT
{"[mouse]"     ,0,1,0,0,&mpxplay_dispqt_mouse_loadini,   NULL, &mpxplay_dispqt_mouse_init, &mpxplay_dispqt_mouse_close},
#elif defined(MPXPLAY_GUI_CONSOLE)
{"[mouse]"     ,0,1,0,0,&mpxplay_control_mouse_loadini,   NULL, &mpxplay_control_mouse_init, &mpxplay_control_mouse_close},
#endif
{"[joystick]"  ,0,0,0,0,&mpxplay_control_joy_loadini,     NULL, &mpxplay_control_joy_init,   NULL},
{"[serialport]",0,0,0,0,&mpxplay_control_serial_loadini,  NULL, &mpxplay_control_serial_init,&mpxplay_control_serial_close},
#ifdef __DOS__
{"[LCDdisplay]",0,0,0,0,&mpxplay_display_lcd_loadini,     NULL, &mpxplay_display_lcd_init,   &mpxplay_display_lcd_close},
#endif
{"[global]"    ,0,0,0,0,&mpxplay_control_global_loadini,  &mpxplay_control_global_saveini, NULL, NULL},                       // recommended last-1
};

#define MPXINI_PARTNUM (sizeof(mpxini_parts)/sizeof(mpxini_part_t))

#define CONTROL_CFGFILE_FLAG_READONLY    1
#define CONTROL_CFGFILE_FLAG_PARTSINITOK 2

static FILE *configfile;
static char *configmem;
static mpxini_line_t mpxini_lines[MPXINI_MAX_LINES];
static mpxini_part_t *mpxini_lastfoundpart;
static unsigned int control_cfgfile_flags;

static void mpxinifunc_search_partbegin(char *currline,unsigned int currlinenum)
{
 unsigned int i=MPXINI_PARTNUM;
 mpxini_part_t *partp=&mpxini_parts[0];

 do{
  if(pds_strncmp(currline,partp->partname,pds_strlen(partp->partname))==0){
   if(mpxini_lastfoundpart)
    mpxini_lastfoundpart->partlinenum=currlinenum-mpxini_lastfoundpart->partbegin_linenum;
   mpxini_lastfoundpart=partp;
   partp->partbegin_linenum=currlinenum+1;
   partp->filepos=currline-configmem+currlinenum+pds_strlen(currline)+1;
   break;
  }
  partp++;
 }while(--i);
}

void mpxplay_control_get_prg_path(char *strbuf)
{
 pds_getpath_from_fullname(strbuf,freeopts[OPT_PROGNAME]);
}

int mpxplay_control_get_ini_path(char *strbuf)
{
 int retval = MPXPLAY_ERROR_OK;
 *strbuf = 0;
#ifdef MPXPLAY_LINK_INSTALLER
  strbuf[0] = 0;
  SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_DEFAULT, strbuf);
  pds_strcat(strbuf, "\\");
  pds_strcat(strbuf, mpxplay_application_publicname);
  if(!pds_dir_exists(strbuf)){
   pds_mkdir(strbuf);
   if(!pds_dir_exists(strbuf))
    retval = MPXPLAY_ERROR_FILEHAND_CANTOPEN;
  }
#else
 mpxplay_control_get_prg_path(strbuf);
#endif
 return retval;
}

//open a file in Mpxplay's dir
FILE *mpxplay_control_configfile_open(char *filename, const char *mode_pri, const char *mode_sec, unsigned int *ctrl_cfgfile_flags)
{
 FILE *filehand;
 char cfgfilename[MAX_PATHNAMELEN];

 if(!mode_pri)
  return NULL;

 if(mpxplay_control_get_ini_path(cfgfilename) != MPXPLAY_ERROR_OK)
  return NULL;
 pds_filename_assemble_fullname(cfgfilename, cfgfilename, filename);

 filehand=fopen(cfgfilename,mode_pri);
 if(!filehand && mode_sec){
  filehand=fopen(cfgfilename,mode_sec);
  if(!filehand)
   return NULL;
  if(ctrl_cfgfile_flags) {
   funcbit_enable(*ctrl_cfgfile_flags,CONTROL_CFGFILE_FLAG_READONLY);
   display_warning_message("Warning: mpxplay.ini is read-only!");
  }
 }
 return filehand;
}

//load ini file content into mpxini_line_t structure (name=value lines)
int mpxplay_control_configfile_load(FILE *filehand, char **memfield, mpxini_line_t *cfg_lines, unsigned int max_linenum, unsigned int search_parts)
{
 char *s,*s2,*s3;
 unsigned int len,mpxini_linecount;

#ifdef __GNUC__
 len=pds_filelength(filehand->_file);
#else
 len=pds_filelength(filehand->_handle);
#endif
 if(len>MPXINI_MAX_SIZE)
  return 0;
 *memfield=pds_malloc(len+1024);
 if(!*memfield)
  return 0;

 s=*memfield;
 mpxini_linecount=0;
 while(fgets(s,MPXINI_MAX_CHARSPERLINE,filehand) && (mpxini_linecount<max_linenum)){
  char *s_next=s+pds_strlen(s);

  if(search_parts) // in mpxplay.ini
   mpxinifunc_search_partbegin(s,mpxini_linecount); // search [partnames] (ie:[keyboard],[serialport])

  //cut one line to variablename,value and comment parts (if possible)
  if((s[0]!=';') && (s2=pds_strchr(s,'='))){ // if line begins with ';' then it's comment allways
   *s2++=0;
   cfg_lines[mpxini_linecount].varnamep=s;              // variable name
   cfg_lines[mpxini_linecount].varnamelen=pds_strlen(s);// save original variable len
   pds_strcutspc(s);
   s3=pds_strchr(s2,';');
   if(s3){
    *s3++=0;
    cfg_lines[mpxini_linecount].commentp=s3; // comment
   }else
    s3=s2;
   s3=pds_strchr(s3,'\n');   // cut eol
   if(s3)
    s3[0]=0;
   cfg_lines[mpxini_linecount].valuelen=pds_strlen(s2); // save original value len
   //pds_strcutspc(s2); // FIXME: we should cut this here
   cfg_lines[mpxini_linecount].valuep=s2; // value
  }else{ // it's a comment line only
   cfg_lines[mpxini_linecount].commentp=s;
   s3=pds_strchr(s,'\n');   // cut eol
   if(s3)
    s3[0]=0;
  }

  mpxini_linecount++;
  s=s_next;
 }
 return mpxini_linecount;
}

//open mpxplay.ini and load configurations/variables
void mpxplay_control_configfile_parts_loadini(void)
{
 int mpxini_linecount,i;
 struct mpxini_part_t *partp;

 configfile = mpxplay_control_configfile_open(mpxplay_main_ini_name, "r+", "r", &control_cfgfile_flags);
#ifdef MPXPLAY_LINK_INSTALLER
 if(!configfile){
  char ini_src[MAX_PATHNAMELEN], ini_dest[MAX_PATHNAMELEN];
  mpxplay_control_get_prg_path(&ini_src[0]);
  pds_filename_assemble_fullname(ini_src, ini_src, mpxplay_main_ini_name);
  mpxplay_control_get_ini_path(&ini_dest[0]);
  pds_filename_assemble_fullname(ini_dest, ini_dest, mpxplay_main_ini_name);
  mpxplay_diskdrive_copy_file(ini_src, ini_dest);
  configfile = mpxplay_control_configfile_open(mpxplay_main_ini_name, "r+", "r", &control_cfgfile_flags);
 }
#endif
 if(!configfile)
  goto err_out_loadini;
 mpxini_linecount = mpxplay_control_configfile_load(configfile, &configmem, mpxini_lines, MPXINI_MAX_LINES, 1);
 if(mpxini_linecount <= 0)
  goto err_out_loadini;

 if(mpxini_lastfoundpart) // close of the part search
  mpxini_lastfoundpart->partlinenum=mpxini_linecount-mpxini_lastfoundpart->partbegin_linenum;

  // load and work up (handle) the configuration of parts
 partp=&mpxini_parts[0];
 i=MPXINI_PARTNUM;
 do{
  if(partp->loadini && !(partp->flags&MPXINI_FLAG_LOADINI_POST) && partp->partbegin_linenum && partp->partlinenum)
   partp->loadini(mpxini_lines,partp);
  partp++;
 }while(--i);

err_out_loadini:
#ifdef MPXPLAY_LINK_WINREGCONFIG
 if(!configfile || (mpxini_linecount <= 0))
  mpxplay_control_configreg_load();
#endif
 dispmode_mpxpini = displaymode;
#ifdef MPXPLAY_GUI_CONSOLE
 playcontrol_mpxpini = playcontrol;
#else
 loadid3tag_mpxpini = loadid3tag;
 outmode_mpxpini = outmode;
 mpxplay_playlistcontrol_mpxpini = mpxplay_playlistcontrol;
 preloadinfo_mpxpini = preloadinfo;
#endif
}

// save variables (parts) into mpxplay.ini
void mpxplay_control_configfile_parts_saveini(void)
{
 mpxplay_control_savevar_set();

 if(!configmem || funcbit_test(control_cfgfile_flags,CONTROL_CFGFILE_FLAG_READONLY)){ // failed open or malloc at load
#ifdef MPXPLAY_LINK_WINREGCONFIG
  mpxplay_control_configreg_save();
#else
  return;
#endif
 }else{
  unsigned int i=MPXINI_PARTNUM;
  struct mpxini_part_t *partp=&mpxini_parts[0];
  do{
   if(partp->saveini && partp->partbegin_linenum && partp->partlinenum)
    partp->saveini(mpxini_lines,partp,configfile);
   partp++;
  }while(--i);
 }

 mpxplay_control_startup_save_mpxptabsini();
}

//initialize parts (hardwares)
void mpxplay_control_configfile_parts_init(void)
{
 unsigned int i;
 struct mpxini_part_t *partp;

 partp=&mpxini_parts[0];
 i=MPXINI_PARTNUM;
 do{
  if(partp->loadini && (partp->flags&MPXINI_FLAG_LOADINI_POST) && partp->partbegin_linenum && partp->partlinenum)
   partp->loadini(mpxini_lines,partp);
  if(partp->init && partp->partlinenum)
   partp->init();
  partp++;
 }while(--i);
 funcbit_enable(control_cfgfile_flags,CONTROL_CFGFILE_FLAG_PARTSINITOK);
}

//close parts (hardwares)
void mpxplay_control_configfile_parts_close(void)
{
 unsigned int i;
 struct mpxini_part_t *partp;

 if(!funcbit_test(control_cfgfile_flags,CONTROL_CFGFILE_FLAG_PARTSINITOK))
  return;
 funcbit_disable(control_cfgfile_flags,CONTROL_CFGFILE_FLAG_PARTSINITOK); // run close once

 partp=&mpxini_parts[0];
 i=MPXINI_PARTNUM;
 do{
  if(partp->close && partp->partlinenum)
   partp->close();
  partp++;
 }while(--i);
}

// close mpxplay.ini
void mpxplay_control_configfile_close(void)
{
 if(configmem){
  free(configmem);
  configmem=NULL;
 }
 if(configfile){
  fclose(configfile);
  configfile=NULL;
 }
 control_cfgfile_flags=0;
 cmdlg_free();
}

void mpxplay_control_general_loadini(
	  mpxini_line_t *mpxini__lines, // structure of all mpxplay.ini lines
	  mpxini_part_t *mpxini_partp,  // part info
	  mpxini_var_s *vars_begin      // variable structure
	 )
{
 unsigned int i;
 mpxini_var_s *varp;

 mpxini__lines+=mpxini_partp->partbegin_linenum;

 for(i=0;i<mpxini_partp->partlinenum;i++){
  if(mpxini__lines->varnamep){
   varp=vars_begin;
   while(varp->name!=NULL){
    if(pds_stricmp(mpxini__lines->varnamep,(char *)varp->name)==0){
     if(varp->type&ARG_CHAR){
      if(mpxini__lines->valuep[0]!=0 && mpxini__lines->valuep[0]!=' '){
       if(varp->type&ARG_POINTER){
        if(varp->type&ARG_ALLOC){
         int len = pds_strlen(mpxini__lines->valuep) + 1;
         *((char **)(varp->c)) = pds_malloc(len);
         pds_memcpy(*((char **)(varp->c)), mpxini__lines->valuep, len);
         pds_strcutspc(*((char **)(varp->c))); // FIXME: !!! for non mpxplay.ini files only! (no such argument yet in mpxplay.ini)
        }else{
         *((char **)(varp->c))=mpxini__lines->valuep;
        }
       }else{
        pds_strncpy((char *)varp->c, mpxini__lines->valuep, MPXINI_MAX_CHARDATA_LEN);
        ((char *)varp->c)[MPXINI_MAX_CHARDATA_LEN - 1] = 0;
        pds_strcutspc((char *)(varp->c));
       }
      }
     }else{
      if(varp->type&ARG_HEX)
       *((unsigned int *)varp->c)=pds_atol16(mpxini__lines->valuep);
      else
       *((int *)varp->c)=pds_atol(mpxini__lines->valuep);
     }
     mpxini__lines->storepoint=(void *)varp;
     break;
    }
    varp++;
   }
  }
  mpxini__lines++;
 }
}

void mpxplay_control_general_saveini(
    mpxini_line_t *mpxini__lines, // structure of all mpxplay.ini lines
	mpxini_part_t *mpxini_partp,  // part info
    FILE *conffile )
{
 unsigned int i;
 char sout[MPXINI_MAX_CHARSPERLINE];

 fseek(conffile,mpxini_partp->filepos,SEEK_SET);

 mpxini__lines+=mpxini_partp->partbegin_linenum;

 for(i=0;i<mpxini_partp->partlinenum;i++){
  if(mpxini__lines->varnamep && mpxini__lines->storepoint){
   mpxini_var_s *varp=(mpxini_var_s *)mpxini__lines->storepoint;

   if(varp->type&ARG_SAVE){
    if(varp->type&ARG_CHAR){
     if(varp->type&ARG_POINTER)
      snprintf(sout,sizeof(sout),"%-13s=%s ",varp->name,(*((char **)varp->c) && **((char **)varp->c))? *((char **)varp->c):"");
     else{ // static string field
      pds_str_extendc((char *)varp->c, min(mpxini__lines->valuelen, MPXINI_MAX_CHARDATA_LEN), ' '); // FIXME: correct?
      snprintf(sout,sizeof(sout),"%-13s=%s ",varp->name,(((char *)varp->c) && *((char *)varp->c))? (char *)varp->c:"");
     }
    }else{
     if(varp->type&ARG_HEX)
      sprintf(sout,"%-13s=%8.8X ",varp->name,*((unsigned int *)varp->c));
     else // decimal
      sprintf(sout,"%-13s=%-5d ",varp->name,*((int *)varp->c));
    }
   }else{
    snprintf(sout,sizeof(sout),"%-13s=%s",varp->name,mpxini__lines->valuep);
   }

   if(mpxini__lines->commentp){
    pds_strcat(sout,";");
    pds_strcat(sout,mpxini__lines->commentp);
   }
   pds_strcat(sout,"\n");

  }else if(mpxini__lines->commentp)
   snprintf(sout,sizeof(sout),"%s\n",mpxini__lines->commentp);
  else
   sout[0]=0;
  fputs(sout,conffile);
  mpxini__lines++;
 }
}

//-------------------------------------------------------------------------
// load [global] variables
static void mpxplay_control_global_loadini(mpxini_line_t *mpxini__lines,struct mpxini_part_t *mpxini_partp)
{
 unsigned int i;

 mpxini__lines+=mpxini_partp->partbegin_linenum;

 for(i=0;i<mpxini_partp->partlinenum && (useglvariables&1);i++){
  if(mpxini__lines->varnamep){
   mpxini_var_s *glpoint=&gl[0];
   while((glpoint->name!=NULL) && (useglvariables&1)){
    if(pds_stricmp(mpxini__lines->varnamep,(char *)glpoint->name)==0){
     if(glpoint->type&ARG_CHAR){
      if(mpxini__lines->valuep[0]!=0 && mpxini__lines->valuep[0]!=' '){
       if(glpoint->type&ARG_POINTER)
        *((char **)(glpoint->c))=mpxini__lines->valuep;
       else{
        pds_strncpy((char *)glpoint->c,mpxini__lines->valuep, MPXINI_MAX_CHARDATA_LEN);
        ((char *)glpoint->c)[MPXINI_MAX_CHARDATA_LEN - 1] = 0;
        pds_strcutspc((char *)(glpoint->c));
       }
      }
     }else{
      *((int *)glpoint->c)=pds_atol(mpxini__lines->valuep);
     }
     mpxini__lines->storepoint=(void *)glpoint;
     break;
    }
    glpoint++;
   }
  }
  mpxini__lines++;
 }
}

//#define MPXPLAY_CONTROL_GLOBAL_SAVEINI_DEBUG 1

// save [global] variables
static void mpxplay_control_global_saveini(mpxini_line_t *mpxini__lines,struct mpxini_part_t *mpxini_partp,FILE *conffile)
{
 unsigned int i;
 mpxini_var_s *glpoint;
 mpxini_line_t *mpxini_global_lines;
 char sval[MPXINI_MAX_CHARSPERLINE],sout[MPXINI_MAX_CHARSPERLINE];

 if(!(useglvariables&2))
  return;

 fseek(conffile,mpxini_partp->filepos,SEEK_SET);

 mpxini_global_lines = mpxini__lines + mpxini_partp->partbegin_linenum;
 mpxini__lines = mpxini_global_lines;

 // save loaded (found) mpxplay.ini global elements
 for(i=0;i<mpxini_partp->partlinenum;i++){
  char *sp=&sout[0];
  if(mpxini__lines->varnamep && mpxini__lines->storepoint){
   glpoint=(mpxini_var_s *)mpxini__lines->storepoint;
   pds_strcpy(sp,(char *)glpoint->name);
   sp+=pds_str_fixlenc(sp,mpxini__lines->varnamelen,' ');
   sp+=pds_strcpy(sp,"=");
   if(glpoint->type&ARG_SAVE){
    if(glpoint->type&ARG_CHAR){ // ??? not tested (no such case yet)
     if(glpoint->type&ARG_POINTER)
      pds_strcpy(sval,*((char **)glpoint->c)); // ???
     else
      pds_strcpy(sval,(char *)glpoint->c);
    }else{
     int num_val=*((int *)glpoint->c);
     sprintf(sval,"%-9d",num_val);
    }
   }else{
    pds_strcpy(sval,mpxini__lines->valuep);
   }
   pds_str_fixlenc(sval,mpxini__lines->valuelen,' ');
   sp+=pds_strcpy(sp,sval);
  }else{
#ifdef MPXPLAY_CONTROL_GLOBAL_SAVEINI_DEBUG
   if(mpxini__lines->varnamep || mpxini__lines->storepoint || mpxini__lines->valuep){
    snprintf(sout,sizeof(sout),"mpxini save error: %8.8X %8.8X %8.8X %s %s",mpxini__lines->varnamep,mpxini__lines->storepoint,mpxini__lines->valuep,
     ((mpxini__lines->varnamep)? mpxini__lines->varnamep:""),((mpxini__lines->valuep)? mpxini__lines->valuep:""));
    pds_textdisplay_printf(sout);
    getch();
   }
#endif
   if(mpxini__lines->varnamep && mpxini__lines->valuep){ // this can happen at an unknown variable (ie: if the exe and ini version don't match)
    pds_strcpy(sp,mpxini__lines->varnamep);
    sp+=pds_str_fixlenc(sp,mpxini__lines->varnamelen,' ');
    sp+=pds_strcpy(sp,"=");
    pds_strcpy(sval,mpxini__lines->valuep);
    pds_str_fixlenc(sval,mpxini__lines->valuelen,' ');
    sp+=pds_strcpy(sp,sval);
   }
  }
  if(mpxini__lines->commentp){
   if(mpxini__lines->commentp[0]!=';')
    sp+=pds_strcpy(sp,";");
   sp+=pds_strcpy(sp,mpxini__lines->commentp);
  }
  if(sp>(&sout[0])){
   pds_strcpy(sp,"\n");
   fputs(sout,conffile);
  }
  mpxini__lines++;
 }

 // append missing (not found) global elements to (older) mpxplay.ini
 mpxini__lines = mpxini_global_lines;
 glpoint = &gl[0];
 do{
	unsigned int found_in_mpxini;
	char *sp;
	if(!(glpoint->type & ARG_SAVE))
		continue;
	found_in_mpxini = 0;
	mpxini__lines = mpxini_global_lines;
	for(i = 0; i < mpxini_partp->partlinenum; i++, mpxini__lines++){
		if(mpxini__lines->storepoint == glpoint){
			found_in_mpxini = 1;
			break;
		}
	}
	if(found_in_mpxini)
		continue;
	sp = &sout[0];
	pds_strcpy(sp, (char *)glpoint->name);
	sp += pds_str_fixlenc(sp, MPXINI_MAX_VARNAME_LEN, ' ');
	sp += pds_strcpy(sp, "=");
	if(glpoint->type & ARG_CHAR){ // ??? not tested (no such case yet)
		if(glpoint->type & ARG_POINTER)
			pds_strcpy(sval, *((char **)glpoint->c)); // ???
		else
			pds_strcpy(sval, (char *)glpoint->c);
	}else{
		int num_val = *((int *)glpoint->c);
		sprintf(sval, "%-9d", num_val);
	}
	pds_str_fixlenc(sval, MPXINI_MAX_VALUE_LEN, ' ');
	sp += pds_strcpy(sp, sval);
	sp += pds_strcpy(sp, "; added by program (you use an older ini file)");
	pds_strcpy(sp,"\n");
	fputs(sout,conffile);
 }while((++glpoint)->name);
}

#ifdef MPXPLAY_LINK_WINREGCONFIG

#include <winreg.h>

#define MPXPLAY_REG_KEY     HKEY_CURRENT_USER
#define MPXPLAY_REG_PARENT  "Software\\GNU\\"
#define MPXPLAY_REG_CLASS   "config"

static void mpxplay_control_configreg_getregfolder(char *strbuf)
{
    int i = pds_strcpy(strbuf, MPXPLAY_REG_PARENT);
    pds_strcpy(&strbuf[i], mpxplay_application_publicname);
}

static void mpxplay_control_configreg_load(void)
{
    mpxini_var_s *inivars = &gl[0];
    const unsigned int nb_inivrs = sizeof(gl) / sizeof(mpxini_var_s);
    DWORD   i_size, i;
    HKEY    hKey;
    char temp[MAX_PATHNAMELEN];

    mpxplay_control_configreg_getregfolder(temp);
    if (RegOpenKeyEx(MPXPLAY_REG_KEY, temp, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
        return;

    i_size = sizeof(temp);
    memset(temp, 0, sizeof(temp));
    if((RegQueryValueEx(hKey, "Outmode", NULL, NULL, (LPBYTE)temp, &i_size) != ERROR_SUCCESS) || !(*((mpxp_int32_t *)&temp[0]) & OUTMODE_TYPE_MASK)) // TODO: more precise test
        return;

    for( i = 0; i < nb_inivrs; i++, inivars++) {
        if(!(inivars->type & ARG_SAVE) || !inivars->c)
            continue;
        i_size = sizeof(temp);
        memset(temp, 0, sizeof(temp));
        if(RegQueryValueEx(hKey, inivars->name, NULL, NULL, (LPBYTE)temp, &i_size) == ERROR_SUCCESS) {
            if(inivars->type & ARG_CHAR) {
                if(inivars->type & ARG_POINTER) {
                    if(pds_strcmp((char *)inivars->name, "SoundcardName") == 0){  // TODO: optimize bullshit
                        if(pds_strlicmp(temp, "WDS") == 0)
                            mvps.aui->card_selectname = "WDS";
                        else if(pds_strlicmp(temp, "WIN") == 0)
                            mvps.aui->card_selectname = "WIN";
                        else if(pds_strlicmp(temp, "NUL") == 0)
                            mvps.aui->card_selectname = "NUL";
                        else
                            mvps.aui->card_selectname = "AUTO";
                    }
                }else{
                    pds_strncpy((char *)inivars->c, &temp[0], MPXINI_MAX_CHARDATA_LEN);
                    ((char *)inivars->c)[MPXINI_MAX_CHARDATA_LEN - 1] = 0;
                }
                mpxplay_debugf(MPXPLAY_DEBUGOUT_REG, "LOAD %s %s", inivars->name, (inivars->type & ARG_POINTER)? *((char **)inivars->c) : ((char *)inivars->c));
            }else{
                //mpxplay_debugf(MPXPLAY_DEBUGOUT_REG, "LOAD %s %d", inivars->name, *((mpxp_int32_t *)&temp[0]));
                *((mpxp_int32_t *)inivars->c) = *((mpxp_int32_t *)&temp[0]);
            }
        }
    }

    RegCloseKey(hKey);
}

static void mpxplay_control_configreg_save(void)
{
    mpxini_var_s *inivars = &gl[0];
    const unsigned int nb_inivrs = sizeof(gl) / sizeof(mpxini_var_s);
    DWORD   dwDisposition, i;
    HKEY    hKey;
    char temp[MAX_PATHNAMELEN];

    mpxplay_control_configreg_getregfolder(temp);
    if (RegCreateKeyEx(MPXPLAY_REG_KEY, temp, 0, MPXPLAY_REG_CLASS, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &hKey, &dwDisposition) != ERROR_SUCCESS)
        return;

    for( i = 0; i < nb_inivrs; i++, inivars++) {
        if(!(inivars->type & ARG_SAVE))
            continue;
        if(inivars->type & ARG_CHAR){
            char *str = (inivars->type & ARG_POINTER)? *((char **)inivars->c) : ((char *)inivars->c);
            RegSetValueEx(hKey, inivars->name, 0, REG_SZ, (LPBYTE)str, pds_strlen(str) + 1); // TODO: stored in native Mpxplay char data (UTF8)
            mpxplay_debugf(MPXPLAY_DEBUGOUT_REG, "SAVE %s \"%s\"", inivars->name, str);
        }else{
            RegSetValueEx(hKey, inivars->name, 0, REG_DWORD, (LPBYTE)inivars->c, sizeof(mpxp_int32_t));
            //mpxplay_debugf(MPXPLAY_DEBUGOUT_REG, "SAVE %s %d", inivars->name, *((mpxp_int32_t *)inivars->c));
        }
    }

    RegCloseKey(hKey);
}
#endif

//-------------------------------------------------------------------------
#ifdef MPXPLAY_GUI_CONSOLE
// print help
static void mpxplay_printhelp(void)
{
#if defined(__DATE__) && defined(__TIME__)
 char sout[128];
#endif
#ifdef MPXPLAY_LINK_FULL
 #ifdef MPXPLAY_WIN32
 #ifdef MPXPLAY_LINK_INFILE_FF_MPEG
 pds_textdisplay_printf("Mpxplay v1.63  Audio player for Windows (FFmpeg/console)  by PDSoft 1998-2018");
 #else
 pds_textdisplay_printf("Mpxplay v1.63  Audio player for Windows (console/unicode)  by PDSoft 1998-2018");
 #endif
 #else
 pds_textdisplay_printf("Mpxplay v1.63          Audio player for DOS          by PDSoft 1998-2018");
 #endif
#if defined(__DATE__) && defined(__TIME__)
 snprintf(sout, sizeof(sout), "                                      Build date: %s %s", __DATE__, __TIME__);
 pds_textdisplay_printf(sout);
#else
 pds_textdisplay_printf(" ");
#endif
 pds_textdisplay_printf("Supported fileformats:");
#ifdef MPXPLAY_LINK_INFILE_FF_MPEG
 pds_textdisplay_printf(" All audio formats and containers by FFmpeg");
 pds_textdisplay_printf(" see https://ffmpeg.org/general.html#File-Formats");
#else
 pds_textdisplay_printf(" -native formats: AAC,AC3,APE,FLAC,MP2,MP3,MPC,WV,CDW");
 pds_textdisplay_printf(" -containers: AIF,ASF/WMA,AVI,MKV/WEBM,MP4/MOV,MPG/VOB,OGG,TS,WAV,W64");
#endif
 pds_textdisplay_printf(" -playlists: CUE,FPL,M3U,MXU,PLS,ASX,XSPF");
#else
 pds_textdisplay_printf("Mpxplay v1.63 Audio player for DOS  (light version) by PDSoft 1998-2014");
 pds_textdisplay_printf(" ");
 pds_textdisplay_printf("Supported fileformats: AAC,MP2/MP3,MP4,MPC,OGG,WAV,CDW");
#endif
 pds_textdisplay_printf(" ");
 pds_textdisplay_printf("usage: MPXPLAY.EXE [option(s)] [file/playlist/searchmask] [file2] [file3] [...]");
 pds_textdisplay_printf(" ");
 pds_textdisplay_printf(" -f[0/f/l/s/e] (full)screen mode(s) -ds multiple drive scan (cde -> c: d: e:)");
 pds_textdisplay_printf(" -b[p/l] pre/full input buffer      -sc[s/t/v] sound card select/test/volume");
 pds_textdisplay_printf(" -p[ss/sf/sp] start song/frame/%    -p[rn/re] randomize/repeat playlist ");
 pds_textdisplay_printf(" -c[l/m] left channel only/downmix  -cf[i/p/o/l/t] crossfade settings");
 pds_textdisplay_printf(" -t   testmode (no output)          -o  write sound output to a wav file");
 pds_textdisplay_printf(" -s[v/r/p] sound volume/surround/speed");
 pds_textdisplay_printf(" ");
 pds_textdisplay_printf("Playing controls (see readme.txt for the full list of options and keys):");
 pds_textdisplay_printf("ESC - exit                             P  - start/pause playing");
 pds_textdisplay_printf(" -  - step back in playlist            +  - step to next song in playlist");
 pds_textdisplay_printf(" -> - forward (right arrow)            <- - rewind (left arrow)");
 pds_textdisplay_printf("BACKSPC - restart song                NUM - fast step (step to n. song)");
 pds_textdisplay_printf(" A-spectrum analyser T-time mode V-auto volume ,.-volume ;'-surround []-speed");
 pds_textdisplay_printf(" D-hilitescan C-crossfade F-cf-fadeout/in N-random R-replay M-mute X-swapchan");
}
#endif
