text: /*
   * Copyright (c) 2003 NONAKA Kimihiro
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include <compiler.h>
  
  #include <np2.h>
  #include <pccore.h>
  #include <statsave.h>
  #include <dosio.h>
  #include <vram/scrndraw.h>
  #include <timing.h>
  #include <toolkit.h>
  
  #include "kdispwin.h"
  #include "toolwin.h"
  #include <debug/viewer.h>
  #include <debug/debugwin.h>
  #include "skbdwin.h"
  
  #include <commng.h>
  #include <joymng.h>
  #include "kbdmng.h"
  #include <mousemng.h>
  #include <scrnmng.h>
  #include <soundmng.h>
  #include <sysmng.h>
  #include <taskmng.h>
  
  #if defined(SUPPORT_IA32_HAXM)
  #include	<i386hax/haxfunc.h>
  #include	<i386hax/haxcore.h>
  #endif
  
  #include <stdint.h>
  #include <sys/socket.h>
  #include "c9.h"
  #include "c9hl.h"
  extern struct c9hl_state c9s;
  extern int c9hl_listenfd;
  int c9hl_active = 0;
  extern int c9hl_clientfd;
  extern int c9hl_server_init(void);
  extern void c9hl_server_accept(void);
  
  NP2OSCFG np2oscfg = {
  #if !defined(CPUCORE_IA32)		/* titles */
  	"Neko Project II kai",
  #else
  	"Neko Project II kai + IA32",
  #endif
  
  	0, 			/* paddingx */
  	0,			/* paddingy */
  
  	0,			/* NOWAIT */
  	0,			/* DRAW_SKIP */
  
  	0,			/* DISPCLK */
  
  	KEY_KEY106,		/* KEYBOARD */
  	0,			/* F12KEY */
  
  	0,			/* MOUSE_SW */
  
  	0,			/* JOYPAD1 */
  	0,			/* JOYPAD2 */
  	{ 1, 2, 5, 6 },		/* JOY1BTN */
  	{
  		{ 0, 1 },		/* JOYAXISMAP[0] */
  		{ 0, 1 },		/* JOYAXISMAP[1] */
  	},
  	{
  		{ 0, 1, 0xff, 0xff },	/* JOYBTNMAP[0] */
  		{ 0, 1, 0xff, 0xff },	/* JOYBTNMAP[1] */
  	},
  	{ "", "" },		/* JOYDEV */
  
  	{ FALSE, COMPORT_MIDI, 0, 0x3e, 19200, "", "", "", "" },	/* mpu */
  #if defined(SUPPORT_SMPU98)
  	{ FALSE, COMPORT_MIDI, 0, 0x3e, 19200, "", "", "", "" },	/* s-mpu */
  	{ FALSE, COMPORT_MIDI, 0, 0x3e, 19200, "", "", "", "" },	/* s-mpu */
  #endif
  	{
  		{ TRUE, COMPORT_NONE, 0, 0x3e, 19200, "", "", "", "" },/* com1 */
  		{ TRUE, COMPORT_NONE, 0, 0x3e, 19200, "", "", "", "" },/* com2 */
  		{ TRUE, COMPORT_NONE, 0, 0x3e, 19200, "", "", "", "" },/* com3 */
  	},
  
  	0,			/* confirm */
  
  	0,			/* resume */
  
  	0,			/* statsave */
  	0,			/* toolwin */
  	0,			/* keydisp */
  	0,			/* softkbd */
  	0,			/* hostdrv_write */
  	0,			/* jastsnd */
  	0,			/* I286SAVE */
  	1,			/* xrollkey */
  
  	SNDDRV_SDL,		/* snddrv */
  	{ "", "" }, 		/* MIDIDEV */
  #if defined(SUPPORT_SMPU98)
  	{ "", "" }, 		/* MIDIDEVA */
  	{ "", "" }, 		/* MIDIDEVB */
  #endif
  	0,			/* MIDIWAIT */
  
  	MOUSE_RATIO_100,	/* mouse_move_ratio */
  
  	0,			/* disablemmx */
  	INTERP_NEAREST,		/* drawinterp */
  	0,			/* F11KEY */
  
  	0,			/* readonly */
  };
  
  volatile sig_atomic_t np2running = 0;
  UINT8 scrnmode = 0;
  int ignore_fullscreen_mode = 0;
  
  UINT framecnt = 0;
  UINT waitcnt = 0;
  UINT framemax = 1;
  
  BOOL s98logging = FALSE;
  int s98log_count = 0;
  
  char hddfolder[MAX_PATH];
  char fddfolder[MAX_PATH];
  char bmpfilefolder[MAX_PATH];
  char modulefile[MAX_PATH];
  char statpath[MAX_PATH];
  
  const char np2flagext[] = "s%02d";
  const char np2resumeext[] = "sav";
  
  #ifndef FONTFACE
  #define FONTFACE "-misc-fixed-%s-r-normal--%d-*-*-*-*-*-*-*"
  #endif
  char fontname[1024] = FONTFACE;
  
  char timidity_cfgfile_path[MAX_PATH];
  
  int verbose = 0;
  
  static  UINT		lateframecount; // フレーム遅れ数
  
  UINT32
  gettick(void)
  {
  	struct timeval tv;
  
  	gettimeofday(&tv, 0);
  	return tv.tv_usec / 1000 + tv.tv_sec * 1000;
  }
  
  static void
  getstatfilename(char* path, const char* ext, int size)
  {
  
  	/*
  	 * default:
  	 * e.g. resume:   "/home/user_name/.config/xnp2kai/sav/np2.sav"
  	 *      statpath: "/home/user_name/.config/xnp2kai/sav/np2.s00"
  	 *      config:   "/home/user_name/.config/xnp2kai/np2rc"
  	 *
  	 * --config option:
  	 * e.g. resume:   "/config_file_path/sav/np2.sav"
  	 *      statpath: "/config_file_path/sav/np2.s00"
  	 *      config:   "/config_file_path/config_file_name"
  	 */
  	file_cpyname(path, statpath, size);
  	file_catname(path, ".", size);
  	file_catname(path, ext, size);
  }
  
  int
  flagsave(const char* ext)
  {
  	char path[MAX_PATH];
  	int ret;
  
  	getstatfilename(path, ext, sizeof(path));
  	soundmng_stop();
  	ret = statsave_save(path);
  	if (ret) {
  		file_delete(path);
  	}
  	else {
  		ret = statsave_save_hdd(ext);
  	}
  	soundmng_play();
  
  	return ret;
  }
  
  void
  flagdelete(const char* ext)
  {
  	char path[MAX_PATH];
  
  	getstatfilename(path, ext, sizeof(path));
  	file_delete(path);
  }
  
  int
  flagload(const char* ext, const char* title, BOOL force)
  {
  	char path[MAX_PATH];
  	char buf[1024];
  	int ret;
  	int rv = 0;
  
  	getstatfilename(path, ext, sizeof(path));
  	ret = statsave_check(path, buf, sizeof(buf));
  	if (ret & (~STATFLAG_DISKCHG)) {
  		toolkit_msgbox(title, "Couldn't restart",
  		    TK_MB_OK|TK_MB_ICON_ERROR);
  		rv = 1;
  	} else if ((!force) && (ret & STATFLAG_DISKCHG)) {
  		ret = toolkit_msgbox(title, "Conflict!\nContinue?",
  		    TK_MB_YESNO|TK_MB_ICON_QUESTION);
  		if (ret != TK_MB_YES) {
  			rv = 1;
  		}
  	}
  	if (rv == 0) {
  		statsave_load_hdd(ext);
  		statsave_load(path);
  		toolwin_setfdd(0, fdd_diskname(0));
  		toolwin_setfdd(1, fdd_diskname(1));
  	}
  	sysmng_workclockreset();
  	sysmng_updatecaption(1);
  
  	return rv;
  }
  
  void
  changescreen(UINT8 newmode)
  {
  	UINT8 change;
  	UINT8 renewal;
  
  	change = scrnmode ^ newmode;
  	renewal = (change & SCRNMODE_FULLSCREEN);
  	if (newmode & SCRNMODE_FULLSCREEN) {
  		renewal |= (change & SCRNMODE_HIGHCOLOR);
  	} else {
  		renewal |= (change & SCRNMODE_ROTATEMASK);
  	}
  	if (renewal) {
  		if (renewal & SCRNMODE_FULLSCREEN) {
  			toolwin_destroy();
  			kdispwin_destroy();
  		}
  		soundmng_stop();
  		mouse_running(MOUSE_STOP);
  		scrnmng_destroy();
  		if (scrnmng_create(newmode) == SUCCESS) {
  			scrnmode = newmode;
  		} else {
  			if (scrnmng_create(scrnmode) != SUCCESS) {
  				toolkit_widget_quit();
  				return;
  			}
  		}
  		scrndraw_redraw();
  		if (renewal & SCRNMODE_FULLSCREEN) {
  			if (!scrnmng_isfullscreen()) {
  				if (np2oscfg.toolwin) {
  					toolwin_create();
  				}
  				if (np2oscfg.keydisp) {
  					kdispwin_create();
  				}
  			}
  		}
  		mouse_running(MOUSE_CONT);
  		soundmng_play();
  	} else {
  		scrnmode = newmode;
  	}
  }
  
  void
  framereset(UINT cnt)
  {
  
  	framecnt = 0;
  	scrnmng_dispclock();
  	kdispwin_draw((UINT8)cnt);
  	skbdwin_process();
  	debugwin_process();
  	toolwin_draw((UINT8)cnt);
  	viewer_allreload(FALSE);
  	if (np2oscfg.DISPCLK & 3) {
  		if (sysmng_workclockrenewal()) {
  			sysmng_updatecaption(3);
  		}
  	}
  }
  
  void
  processwait(UINT cnt)
  {
  
  	if (timing_getcount() >= cnt) {
  #if defined(SUPPORT_IA32_HAXM)
  		if (np2hax.enable) {
  			np2haxcore.hltflag = 0;
  			if(lateframecount > 0 && np2haxcore.I_ratio < 254){
  				np2haxcore.I_ratio++;
  			}else if(np2haxcore.I_ratio > 1){
  				//np2haxcore.I_ratio--;
  			}
  			lateframecount = 0;
  		}
  #endif
  		timing_setcount(0);
  		framereset(cnt);
  	} else {
  #if defined(SUPPORT_IA32_HAXM)
  		if (np2hax.enable) {
  			if(np2haxcore.I_ratio > 1){
  				np2haxcore.I_ratio--;
  			}
  		}
  #endif
  		taskmng_sleep(1);
  	}
  }
  
  void
  c9step(void)
  {
  	if(!c9hl_active) {
  		if(c9hl_listenfd < 0)
  			return;
  		fd_set r, e;
  		FD_ZERO(&r);
  		FD_SET(c9hl_listenfd, &r);
  		FD_ZERO(&e);
  		FD_SET(c9hl_listenfd, &e);
  		struct timeval t;
  		memset(&t, 0, sizeof(t));
  		t.tv_usec = 1000;
  		errno = 0;
  		if(select(c9hl_listenfd+1, &r, NULL, &e, &t) < 0 || FD_ISSET(c9hl_listenfd, &e)) {
  			if(errno == EINTR)
  				return;
  		}
  		if(FD_ISSET(c9hl_listenfd, &r)) {
  			c9hl_server_accept();
  			c9hl_active = 1;
  		}
  		return;
  	}
  
  	if(c9hl_step(&c9s) != 0) {
  		if(c9s.err != NULL)
  			fprintf(stderr, "c9hl error: %s\n", c9s.err);
  		c9hl_active = 0;
  		c9hl_deinit();
  		shutdown(c9hl_clientfd, SHUT_RDWR);
  		close(c9hl_clientfd);
  		c9hl_clientfd = -1;
  		c9hl_server_init();
  	}
  }
  
  int
  mainloop(void *p)
  {
  	if(g_u8ControlState == 1) {
  		statsave_save_d();
  	} else if(g_u8ControlState == 2) {
  		statsave_load_d();
  	}
  	g_u8ControlState = 0;
  
  	c9step();
  	if (np2oscfg.NOWAIT) {
  		joymng_sync();
  		mousemng_callback();
  		pccore_exec(framecnt == 0);
  		if (np2oscfg.DRAW_SKIP) {
  			/* nowait frame skip */
  			framecnt++;
  			if (framecnt >= np2oscfg.DRAW_SKIP) {
  				processwait(0);
  			}
  		} else {
  			/* nowait auto skip */
  			framecnt = 1;
  			if (timing_getcount()) {
  				processwait(0);
  			}
  		}
  	} else if (np2oscfg.DRAW_SKIP) {
  		/* frame skip */
  		if (framecnt < np2oscfg.DRAW_SKIP) {
  			joymng_sync();
  			mousemng_callback();
  			pccore_exec(framecnt == 0);
  			framecnt++;
  		} else {
  			processwait(np2oscfg.DRAW_SKIP);
  		}
  	} else {
  		/* auto skip */
  		if (waitcnt == 0) {
  			UINT cnt;
  			joymng_sync();
  			mousemng_callback();
  			pccore_exec(framecnt == 0);
  			framecnt++;
  			cnt = timing_getcount();
  			if (framecnt > cnt) {
  				waitcnt = framecnt;
  				if (framemax > 1) {
  					framemax--;
  				}
  			} else if (framecnt >= framemax) {
  				if (framemax < 12) {
  					framemax++;
  				}
  				if (cnt >= 12) {
  					timing_reset();
  				} else {
  					timing_setcount(cnt - framecnt);
  				}
  				framereset(0);
  			}
  		} else {
  			processwait(waitcnt);
  			waitcnt = framecnt;
  		}
  	}
  
  	return TRUE;
  }
  
  int mmxflag;
  
  int
  havemmx(void)
  {
  #if !defined(GCC_CPU_ARCH_IA32)
  	return 0;
  #else	/* GCC_CPU_ARCH_IA32 */
  	int rv;
  
  #if defined(GCC_CPU_ARCH_AMD64)
  	rv = 1;
  #else	/* !GCC_CPU_ARCH_AMD64 */
  	asm volatile (
  		"pushf;"
  		"popl	%%eax;"
  		"movl	%%eax, %%edx;"
  		"xorl	$0x00200000, %%eax;"
  		"pushl	%%eax;"
  		"popf;"
  		"pushf;"
  		"popl	%%eax;"
  		"subl	%%edx, %%eax;"
  		"je	.nocpuid;"
  		"xorl	%%eax, %%eax;"
  		"incl	%%eax;"
  		"pushl	%%ebx;"
  		"cpuid;"
  		"popl	%%ebx;"
  		"movl	%%edx, %0;"
  		"andl	$0x00800000, %0;"
  	".nocpuid:"
  		: "=a" (rv));
  #endif /* GCC_CPU_ARCH_AMD64 */
  	return rv;
  #endif /* GCC_CPU_ARCH_IA32 */
  }
  