/* FCE Ultra - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 2002 Ben Parnell
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>

#include <crt0.h>
#include <pc.h>
#include <dpmi.h>
#include <go32.h>
#include <sys/farptr.h>

#include "../../driver.h"

#include "main.h"
#include "video.h"
#include "input.h"
#include "joystick.h"
#include "sb.h"

#include "../common/config.h"
#include "../common/args.h"

/* _CRT0_FLAG_LOCK_MEMORY might not always result in all memory being locked.
   Bummer.  I'll add code to explicitly lock the data touched by the sound
   interrupt handler(and the handler itself), if necessary(though that might
   be tricky...).  I'll also to cover the data the keyboard
   interrupt handler touches.
*/

int _crt0_startup_flags = _CRT0_FLAG_FILL_SBRK_MEMORY | _CRT0_FLAG_LOCK_MEMORY | _CRT0_FLAG_USE_DOS_SLASHES;
static int ntsccol=0,ntschue,ntsctint;

static int f8bit=0;
static int soundvol=100;
static char BaseDirectory[2048];
int eoptions=0;

static int keyinit=0;
static char TempArray[2048];

static void DriverKill(void);
static int DriverInitialize(void);

int soundo=44100;

void FCEUD_PrintError(char *s)
{
 puts(s);
}

static char *cpalette=0;
static void LoadCPalette(void)
{
 char tmpp[192];
 FILE *fp;

 if(!(fp=fopen(cpalette,"rb")))
 {
  printf(" Error loading custom palette from file: %s\n",cpalette);
  return;
 }
 fread(tmpp,1,192,fp);
 FCEUI_SetPaletteArray(tmpp);
 fclose(fp);
}

#define CONFIGITEMS 16
static CFGSTRUCT fceuconfig[CONFIGITEMS]={
        AC(soundvol),
        ACS(cpalette),
        NAC("sound",soundo),
	NAC("vmode",FCEUDvmode),
	NACA("joybmap",joyBMap),
	AC(joy),
        AC(ntsctint),
        AC(ntschue),
        AC(ntsccol),
        AC(f8bit),
        AC(UsrInputTypeFC),
        ACA(UsrInputType),
        AC(powerpadside),
        AC(eoptions),
        ACA(srendlinev),
        ACA(erendlinev),
};

static void SaveConfig(void)
{	
        sprintf(TempArray,"%s\\fceu.cfg",BaseDirectory);
        DriverInterface(DES_GETNTSCTINT,&ntsctint);
        DriverInterface(DES_GETNTSCHUE,&ntschue);
        SaveFCEUConfig(TempArray,fceuconfig,CONFIGITEMS);
}

static void LoadConfig(void)
{
        DriverInterface(DES_GETNTSCTINT,&ntsctint);
        DriverInterface(DES_GETNTSCHUE,&ntschue);
        sprintf(TempArray,"%s\\fceu.cfg",BaseDirectory);
        LoadFCEUConfig(TempArray,fceuconfig,CONFIGITEMS);
        DriverInterface(DES_SETNTSCTINT,&ntsctint);
        DriverInterface(DES_SETNTSCHUE,&ntschue);
}

static void CreateDirs(void)
{
 char *subs[5]={"fcs","snaps","gameinfo","sav","cheats"};
 int x;

 mkdir(BaseDirectory,S_IRWXU);
 for(x=0;x<5;x++)
 {
  sprintf(TempArray,"%s/%s",BaseDirectory,subs[x]);
  mkdir(TempArray,S_IRWXU);
 }
}

static void GetBaseDirectory(char *s)
{
 int x;

 for(x=strlen(s);x>=0;x--)
 {
  if(s[x]=='/' || s[x]=='\\')
  {
   strncpy(BaseDirectory,s,x);
   break;
  }
 }

 BaseDirectory[x]=0;

 FCEUI_SetBaseDirectory(BaseDirectory);
}

static void SetSignals(void (*t)(int))
{
  int sigs[11]={SIGINT,SIGTERM,SIGHUP,SIGPIPE,SIGSEGV,SIGFPE,SIGKILL,SIGALRM,SIGABRT,SIGUSR1,SIGUSR2};
  int x;
  for(x=0;x<11;x++)
   signal(sigs[x],t);
}

static void CloseStuff(int signum)
{
	DriverKill();
        printf("\nSignal %d has been caught and dealt with...\n",signum);
        switch(signum)
        {
         case SIGINT:printf("How DARE you interrupt me!\n");break;
         case SIGTERM:printf("MUST TERMINATE ALL HUMANS\n");break;
         case SIGHUP:printf("Reach out and hang-up on someone.\n");break;
         case SIGPIPE:printf("The pipe has broken!  Better watch out for floods...\n");break;
         case SIGSEGV:printf("Iyeeeeeeeee!!!  A segmentation fault has occurred.  Have a fluffy day.\n");break;
         case SIGFPE:printf("Those darn floating points.  Ne'er know when they'll bite!\n");break;
         case SIGALRM:printf("Don't throw your clock at the meowing cats!\n");break;
         case SIGABRT:printf("Abort, Retry, Ignore, Fail?\n");break;
         case SIGUSR1:
         case SIGUSR2:printf("Killing your processes is not nice.\n");break;
        }
        exit(1);
}

static void DoArgs(int argc, char *argv[])
{
        static char *cortab[5]={"none","gamepad","zapper","powerpad","arkanoid"};
        static int cortabi[5]={SI_NONE,SI_GAMEPAD,
                               SI_ZAPPER,SI_POWERPAD,SI_ARKANOID};
        static char *fccortab[5]={"none","arkanoid","shadow","4player","fkb"};
        static int fccortabi[5]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW,
                                 SIFC_4PLAYER,SIFC_FKB};
	int x;
	static char *inputa[2]={0,0};
	static char *fcexp=0;
        static int docheckie[4];

        static ARGPSTRUCT FCEUArgs[22]={
         {"-soundvol",0,&soundvol,0},
         {"-cpalette",0,&cpalette,0x4001},
         {"-vmode",0,&FCEUDvmode,0},
	 {"-ntsccol",0,&ntsccol,0},
         {"-pal",&docheckie[0],0,0},
	 {"-sound",0,&soundo,0},
         {"-f8bit",0,&f8bit,0},
	 {"-nofs",0,&eoptions,0x8000|EO_NOFOURSCORE},
         {"-input1",0,&inputa[0],0x4001},
	 {"-input2",0,&inputa[1],0x4001},
         {"-joy",0,&joy,0},
         {"-fcexp",0,&fcexp,0x4001},
	 {"-gg",&docheckie[1],0,0},
	 {"-no8lim",0,&eoptions,0x8000|EO_NO8LIM},
	 {"-subase",&docheckie[3],&eoptions,0x8000|EO_SUBASE},
	 {"-vsync",0,&eoptions,0x8004},
         {"-slstart",0,&srendlinev[0],0},
	 {"-slend",0,&erendlinev[0],0},
         {"-slstartp",0,&srendlinev[1],0},
	 {"-slendp",0,&erendlinev[1],0},
         {"-snapname",0,&eoptions,0x8000|EO_SNAPNAME},
         {"-clipsides",0,&eoptions,0x8000|EO_CLIPSIDES},
	};

        memset(docheckie,0,sizeof(docheckie));

        ParseArguments(argc, argv, FCEUArgs, 22);
	if(cpalette)
	{
  	 if(cpalette[0]=='0')
	  if(cpalette[1]==0)
	  {
	   free(cpalette);
	   cpalette=0;
	  }
	}

	if(docheckie[0])
	 FCEUI_SetVidSystem(1);
	if(docheckie[1])
	 FCEUI_SetGameGenie(1);

        FCEUI_DisableSpriteLimitation(eoptions&EO_NO8LIM);
        FCEUI_SaveExtraDataUnderBase(eoptions&EO_SUBASE);
        FCEUI_SetSnapName(eoptions&EO_SNAPNAME);

	for(x=0;x<2;x++)
	{
         if(srendlinev[x]<0 || srendlinev[x]>239) srendlinev[x]=0;
         if(erendlinev[x]<srendlinev[x] || erendlinev[x]>239) erendlinev[x]=239;
	}

        FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);

        FCEUI_SetSoundVolume(soundvol);
	DriverInterface(DES_NTSCCOL,&ntsccol);

        if(!joy) memset(joyBMap,0,4);

        if(fcexp)
        {
         int y;
         for(y=0;y<5;y++)
         {
          if(!strncmp(fccortab[y],fcexp,8))
          {
           UsrInputTypeFC=fccortabi[y];
           break;
          }
         }
         free(fcexp);
        }

	for(x=0;x<2;x++)
	{
	 int y;

         if(!inputa[x])
	  continue;

	 for(y=0;y<5;y++)	 
	 {
	  if(!strncmp(cortab[y],inputa[x],8))
	  {
	   UsrInputType[x]=cortabi[y];
	   if(y==3)
	   {
	    powerpadside&=~(1<<x);
	    powerpadside|=((((inputa[x][8])-'a')&1)^1)<<x;
	   }
	   free(inputa[x]);
	  }
	 }
	}	
}

#include "usage.h"

int main(int argc, char *argv[])
{
	FCEUGI *tmp;
	int ret;

        puts("\nStarting FCE Ultra "VERSION_STRING"...\n");

	if(!(ret=FCEUI_Initialize()))
         return(1);

        GetBaseDirectory(argv[0]);
	CreateDirs();
        if(argc<=1) 
        {
         ShowUsage(argv[0]);
         return 1;
        }
        LoadConfig();
        DoArgs(argc-2,&argv[1]);

	if(cpalette)
	 LoadCPalette();
        if(soundo)
        {
	 if(soundo==1)
          soundo=44100;

         soundo=InitSound(soundo,f8bit?0:1);
         FCEUI_Sound(soundo);
        }

        if(!(tmp=FCEUI_LoadGame(argv[argc-1])))
        {
         ret=0;
         goto dk;
        }
        //ParseGI(tmp);
 //FCEUI_GetCurrentVidSystem(&srendline,&erendline);

        if(!DriverInitialize())
        {
         ret=0;
         goto dk;
        }
	FCEUI_Emulate();

	dk:
	DriverKill();
        return(ret?0:1);
}

static int DriverInitialize(void)
{
   SetSignals((void *)CloseStuff);

   if(joy)
    InitJoystick();

   if(!InitVideo()) return 0;
   if(!InitKeyboard()) return 0;
   keyinit=1;
   InitOtherInput();
   return 1;
}

static void DriverKill(void)
{
 SaveConfig();
 SetSignals(SIG_IGN);

 if(keyinit)
  keyboard_close();
 KillVideo();
 if(soundo) 
  KillSound(); 
}

void FCEUD_WriteSoundData(int32 *Buffer, int Count)
{
 WriteSBSound(Buffer,Count,NoWaiting);
}

void DOSMemSet(uint32 A, uint8 V, uint32 count)
{
 uint32 x;

 _farsetsel(_dos_ds);
 for(x=0;x<count;x++)
  _farnspokeb(A+x,V); 
  
}

