Kart-Public/src/djgppdos/i_system.c
2014-03-15 13:11:35 -04:00

1753 lines
38 KiB
C

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright (C) 1993-1996 by id Software, Inc.
// Portions Copyright (C) 1998-2000 by DooM Legacy Team.
//
// 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.
//-----------------------------------------------------------------------------
/// \file
/// \brief Misc. stuff
///
/// Startup & Shutdown routines for music,sound,timer,keyboard,
/// Signal handler to trap errors and exit cleanly.
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <io.h>
#include <stdarg.h>
#include <sys/time.h>
#include <fcntl.h>
#ifdef DJGPP
#include <dpmi.h>
#include <go32.h>
#include <pc.h>
#include <dos.h>
#include <crt0.h>
#include <sys/segments.h>
#include <sys/nearptr.h>
#include <keys.h>
#endif
#include "../doomdef.h"
#include "../m_misc.h"
#include "../i_video.h"
#include "../i_sound.h"
#include "../i_system.h"
#include "../d_net.h"
#include "../g_game.h"
#include "../d_main.h"
#include "../m_argv.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../g_input.h"
#include "../console.h"
#ifdef __GNUG__
#pragma implementation "../i_system.h"
#endif
#include "../i_joy.h"
//### let's try with Allegro ###
#define alleg_mouse_unused
//#define alleg_timer_unused
#define alleg_keyboard_unused
#define ALLEGRO_NO_KEY_DEFINES
//#define alleg_joystick_unused
#define alleg_gfx_driver_unused
#define alleg_palette_unused
#define alleg_graphics_unused
#define alleg_vidmem_unused
#define alleg_flic_unused
#define alleg_sound_unused
#define alleg_file_unused
#define alleg_datafile_unused
#define alleg_math_unused
#define alleg_gui_unused
#include <allegro.h>
//### end of Allegro include ###
#ifndef DOXYGEN
#ifndef MAX_JOYSTICKS
#define MAX_JOYSTICKS 4
#endif
#ifndef MAX_JOYSTICK_AXIS
#define MAX_JOYSTICK_AXIS 3
#endif
#ifndef MAX_JOYSTICK_STICKS
#define MAX_JOYSTICK_STICKS 4
#endif
#ifndef MAX_JOYSTICK_BUTTONS
#define MAX_JOYSTICK_BUTTONS 12
#endif
#endif
#if ALLEGRO_VERSION == 4
static char JOYFILE[] = "allegro4.cfg";
#elif ALLEGRO_VERSION == 3
static char JOYFILE[] = "allegro.cfg";
#endif
/// \brief max number of joystick buttons
#define JOYBUTTONS_MAX MAX_JOYSTICK_BUTTONS // <allegro/joystick.h>
/// \brief max number of joystick button events
#define JOYBUTTONS_MIN min((JOYBUTTONS),(JOYBUTTONS_MAX))
/// \brief max number of joysick axies
#define JOYAXISSET_MAX MAX_JOYSTICK_STICKS
// \brief max number ofjoystick axis events
#define JOYAXISSET_MIN min((JOYAXISSET),(JOYAXISSET_MAX))
/// \brief max number of joystick hats
#define JOYHATS_MAX MAX_JOYSTICK_STICKS
/// \brief max number of joystick hat events
#define JOYHATS_MIN min((JOYHATS),(JOYHATS_MAX))
/// \brief max number of mouse buttons
#define MOUSEBUTTONS_MAX 16 // 16 bit of BL
/// \brief max number of muse button events
#define MOUSEBUTTONS_MIN min((MOUSEBUTTONS),(MOUSEBUTTONS_MAX))
// Do not execute cleanup code more than once. See Shutdown_xxx() routines.
UINT8 graphics_started = false;
UINT8 keyboard_started = false;
UINT8 sound_started = false;
static UINT8 timer_started = false;
/* Mouse stuff */
static UINT8 mouse_detected = false;
static UINT8 wheel_detected = false;
static volatile tic_t ticcount; //returned by I_GetTime(), updated by timer interrupt
void I_Tactile(FFType Type, const JoyFF_t *Effect)
{
// UNUSED.
Type = EvilForce;
Effect = NULL;
}
void I_Tactile2(FFType Type, const JoyFF_t *Effect)
{
// UNUSED.
Type = EvilForce;
Effect = NULL;
}
static ticcmd_t emptycmd;
ticcmd_t * I_BaseTiccmd(void)
{
return &emptycmd;
}
static ticcmd_t emptycmd2;
ticcmd_t * I_BaseTiccmd2(void)
{
return &emptycmd2;
}
void I_SetupMumble(void)
{
}
void I_UpdateMumble(const MumblePos_t *MPos)
{
(void)MPos;
}
//
// Allocates the base zone memory,
// this function returns a valid pointer and size,
// else it should interrupt the program immediately.
//
//added:11-02-98: now checks if mem could be allocated, this is still
// prehistoric... there's a lot to do here: memory locking, detection
// of win95 etc...
//
#if ALLEGRO_VERSION == 3
extern int os_type;
extern int windows_version;
extern int windows_sub_version;
extern int i_love_bill;
#elif ALLEGRO_VERSION == 4
#endif
static void I_DetectOS (void)
{
#if ALLEGRO_VERSION == 3
char buf[16];
__dpmi_regs r;
union REGS regs;
/* check which OS we are running under */
r.x.ax = 0x1600;
__dpmi_int(0x2F, &r);
if ((r.h.al != 0) && (r.h.al != 1) && (r.h.al != 0x80) && (r.h.al != 0xFF))
{
/* win 3.1 or 95 */
if (r.h.al == 4)
{
if (r.h.ah < 10)
{
os_type = OSTYPE_WIN95;
}
else
{
os_type = OSTYPE_WIN98;
}
}
else
{
os_type = OSTYPE_WIN3;
}
windows_version = r.h.al;
windows_sub_version = r.h.ah;
i_love_bill = TRUE;
}
else
{
if (_get_dos_version(1) == 0x0532)
{
/* win NT */
os_type = OSTYPE_WINNT;
windows_version = 0x100;
windows_sub_version = 0;
i_love_bill = TRUE;
}
else
{
/* see if OS/2 is present */
r.x.ax = 0x4010;
__dpmi_int(0x2F, &r);
if (r.x.ax != 0x4010)
{
if (r.x.ax == 0x0000)
{
/* OS/2 Warp 3 */
os_type = OSTYPE_WARP;
i_love_bill = TRUE;
}
else
{
/* no Warp, but previous OS/2 is available */
os_type = OSTYPE_OS2;
i_love_bill = TRUE;
}
}
else
{
/* check if running under Linux DOSEMU */
dosmemget(0xFFFF5, 10, buf);
buf[8] = 0;
if (!strcmp(buf, "02/25/93"))
{
regs.x.ax = 0;
int86(0xE6, &regs, &regs);
if (regs.x.ax == 0xAA55)
{
os_type = OSTYPE_DOSEMU;
windows_version = -1;
windows_sub_version = -1;
i_love_bill = TRUE; /* (evil chortle) */
}
}
else
{
/* check if running under OpenDOS */
r.x.ax = 0x4452;
__dpmi_int(0x21, &r);
if ((r.x.ax >= 0x1072) && !(r.x.flags & 1))
{
os_type = OSTYPE_OPENDOS;
/* now check for OpenDOS EMM386.EXE */
r.x.ax = 0x12FF;
r.x.bx = 0x0106;
__dpmi_int(0x2F, &r);
if ((r.x.ax == 0x0) && (r.x.bx == 0xEDC0))
{
i_love_bill = TRUE;
}
}
}
}
}
}
#elif ALLEGRO_VERSION == 4
/// \todo: add Allegro 4 version
#endif
}
UINT32 I_GetFreeMem(UINT32 *total)
{
__dpmi_free_mem_info info;
__dpmi_get_free_memory_information(&info);
if ( total )
*total = info.total_number_of_physical_pages<<12; // <<12 for convert page to byte
return info.total_number_of_free_pages<<12;
}
/*==========================================================================*/
// I_GetTime ()
/*==========================================================================*/
tic_t I_GetTime (void)
{
return ticcount;
}
void I_Sleep(void)
{
if (cv_sleep.value > 0)
rest(cv_sleep.value);
}
static UINT8 joystick_detected = false;
static UINT8 joystick2_detected = false;
//
// I_Init
//
FUNCINLINE static ATTRINLINE int I_WaitJoyButton (int js)
{
CON_Drawer ();
I_FinishUpdate (); // page flip or blit buffer
do
{
if (I_GetKey())
return false;
poll_joystick();
} while (!(joy[js].button[0].b || joy[js].button[1].b));
return true;
}
/** \brief Joystick 1 buttons states
*/
static INT64 lastjoybuttons = 0;
/** \brief Joystick 1 hats state
*/
static INT64 lastjoyhats = 0;
void I_InitJoystick (void)
{
//init the joystick
if (joystick_detected && !joystick2_detected)
remove_joystick();
joystick_detected=0;
if (M_CheckParm("-nojoy"))
return;
load_joystick_data(JOYFILE);
if (cv_usejoystick.value)
{
if (cv_usejoystick.value > MAX_JOYSTICKS)
cv_usejoystick.value = MAX_JOYSTICKS;
if (install_joystick(JOY_TYPE_AUTODETECT) == 0)
{
int js = cv_usejoystick.value -1;
// only gamepadstyle joysticks
if (joy[js].stick[0].flags & JOYFLAG_DIGITAL) Joystick.bGamepadStyle=true;
while (joy[js].flags & JOYFLAG_CALIBRATE)
{
const char *msg = calibrate_joystick_name(js);
CONS_Printf("%s, and press a button\n", msg);
if (I_WaitJoyButton(js))
calibrate_joystick(js);
else
{
if (joystick_detected && !joystick2_detected)
remove_joystick();
joystick_detected=0;
CV_SetValue(&cv_usejoystick, 0);
return;
}
}
joystick_detected=1;
save_joystick_data(JOYFILE);
}
else
{
CONS_Printf("\2No Joystick detected.\n");
}
}
else
{
int i;
event_t event;
event.type=ev_keyup;
event.data2 = 0;
event.data3 = 0;
lastjoybuttons = lastjoyhats = 0;
// emulate the up of all joystick buttons
for (i=0;i<JOYBUTTONS;i++)
{
event.data1=KEY_JOY1+i;
D_PostEvent(&event);
}
// emulate the up of all joystick hats
for (i=0;i<JOYHATS*4;i++)
{
event.data1=KEY_HAT1+i;
D_PostEvent(&event);
}
// reset joystick position
event.type = ev_joystick;
for (i=0;i<JOYAXISSET; i++)
{
event.data1 = i;
D_PostEvent(&event);
}
}
}
/** \brief Joystick 2 buttons states
*/
static INT64 lastjoy2buttons = 0;
/** \brief Joystick 2 hats state
*/
static INT64 lastjoy2hats = 0;
void I_InitJoystick2 (void)
{
//init the joystick
if (joystick2_detected && !joystick_detected)
remove_joystick();
joystick2_detected=0;
if (M_CheckParm("-nojoy"))
return;
if (cv_usejoystick2.value)
{
if (cv_usejoystick2.value > MAX_JOYSTICKS)
cv_usejoystick2.value = MAX_JOYSTICKS;
if (install_joystick(JOY_TYPE_AUTODETECT) == 0)
{
int js = cv_usejoystick2.value -1;
// only gamepadstyle joysticks
load_joystick_data(JOYFILE);
if (joy[js].stick[0].flags & JOYFLAG_DIGITAL) Joystick2.bGamepadStyle=true;
while (joy[js].flags & JOYFLAG_CALIBRATE)
{
const char *msg = calibrate_joystick_name(js);
CONS_Printf("%s, and press a button\n", msg);
if (I_WaitJoyButton(js))
calibrate_joystick(js);
else
{
if (joystick2_detected && !joystick_detected)
remove_joystick();
joystick2_detected=0;
CV_SetValue(&cv_usejoystick2, 0);
return;
}
}
joystick2_detected=1;
save_joystick_data(JOYFILE);
}
else
{
CONS_Printf("\2No Joystick detected.\n");
}
}
else
{
int i;
event_t event;
event.type=ev_keyup;
event.data2 = 0;
event.data3 = 0;
lastjoy2buttons = lastjoy2hats = 0;
// emulate the up of all joystick buttons
for (i=0;i<JOYBUTTONS;i++)
{
event.data1=KEY_2JOY1+i;
D_PostEvent(&event);
}
// emulate the up of all joystick hats
for (i=0;i<JOYHATS*4;i++)
{
event.data1=KEY_2HAT1+i;
D_PostEvent(&event);
}
// reset joystick position
event.type = ev_joystick2;
for (i=0;i<JOYAXISSET; i++)
{
event.data1 = i;
D_PostEvent(&event);
}
}
}
//added:18-02-98: put an error message (with format) on stderr
void I_OutputMsg (const char *error, ...)
{
va_list argptr;
va_start (argptr,error);
vfprintf (stderr,error,argptr);
va_end (argptr);
// dont flush the message!
}
static int errorcount=0; // fuck recursive errors
static int shutdowning=false;
//
// I_Error
//
//added 31-12-97 : display error messy after shutdowngfx
void I_Error (const char *error, ...)
{
va_list argptr;
// added 11-2-98 recursive error detecting
M_SaveConfig (NULL); //save game config, cvars..
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
if (demorecording)
G_CheckDemoStatus();
D_QuitNetGame ();
if (shutdowning)
{
errorcount++;
if (errorcount==5)
I_ShutdownGraphics();
if (errorcount==6)
I_ShutdownSystem();
if (errorcount>7)
exit(-1); // recursive errors detected
}
shutdowning=true;
//added:18-02-98: save one time is enough!
if (!errorcount)
{
M_SaveConfig (NULL); //save game config, cvars..
G_SaveGameData(); // Tails 12-08-2002
}
//added:16-02-98: save demo, could be useful for debug
// NOTE: demos are normally not saved here.
I_ShutdownMusic();
I_ShutdownSound();
I_ShutdownCD();
I_ShutdownGraphics();
I_ShutdownSystem();
// put message to stderr
va_start (argptr,error);
fprintf (stderr, "Error: ");
vfprintf (stderr,error,argptr);
#ifdef DEBUGFILE
if (debugfile)
{
fprintf (debugfile,"I_Error :");
vfprintf (debugfile,error,argptr);
}
#endif
va_end (argptr);
fprintf (stderr, "\nPress ENTER");
fflush( stderr );
getchar();
W_Shutdown();
exit(-1);
}
//
// I_Quit : shutdown everything cleanly, in reverse order of Startup.
//
void I_Quit (void)
{
UINT8 *endoom = NULL;
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.
M_SaveConfig (NULL); //save game config, cvars..
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
if (demorecording)
G_CheckDemoStatus();
D_QuitNetGame ();
I_ShutdownMusic();
I_ShutdownSound();
I_ShutdownCD();
I_ShutdownGraphics();
I_ShutdownSystem();
if (W_CheckNumForName("ENDOOM")!=LUMPERROR) endoom = W_CacheLumpName("ENDOOM",PU_CACHE);
//added:03-01-98: maybe it needs that the ticcount continues,
// or something else that will be finished by ShutdownSystem()
// so I do it before.
if (endoom)
{
puttext(1,1,80,25,endoom);
gotoxy(1,24);
Z_Free(endoom);
}
if (shutdowning || errorcount)
I_Error("Error detected (%d)",errorcount);
fflush(stderr);
W_Shutdown();
exit(0);
}
//added:12-02-98: does want to work!!!! rhaaahahha
void I_WaitVBL(INT32 count)
{
while (count-->0);
{
do { } while (inportb(0x3DA) & 8);
do { } while (!(inportb(0x3DA) & 8));
}
}
// Fab: this is probably to activate the 'loading' disc icon
// it should set a flag, that I_FinishUpdate uses to know
// whether it draws a small 'loading' disc icon on the screen or not
//
// also it should explicitly draw the disc because the screen is
// possibly not refreshed while loading
//
void I_BeginRead (void)
{
}
// Fab: see above, end the 'loading' disc icon, set the flag false
//
void I_EndRead (void)
{
}
#define MOUSE2
/* Secondary Mouse*/
#ifdef MOUSE2
static _go32_dpmi_seginfo oldmouseinfo,newmouseinfo;
static boolean mouse2_started=0;
static UINT16 mouse2port;
static UINT8 mouse2irq;
static volatile int handlermouse2buttons;
static volatile int handlermouse2x,handlermouse2y;
// internal use
static volatile int bytenum;
static volatile UINT8 combytes[8];
//
// support a secondary mouse without mouse driver !
//
// take from the PC-GPE by Mark Feldman
static void I_MicrosoftMouseIntHandler(void)
{
char dx,dy;
UINT8 inbyte;
// Get the port byte
inbyte = inportb(mouse2port);
// Make sure we are properly "synched"
if ((inbyte & 64)== 64 || bytenum>7)
bytenum = 0;
// Store the byte and adjust bytenum
combytes[bytenum] = inbyte;
bytenum++;
// Have we received all 3 bytes?
if (bytenum==3)
{
// Yes, so process them
dx = ((combytes[0] & 3) << 6) + combytes[1];
dy = ((combytes[0] & 12) << 4) + combytes[2];
handlermouse2x+= dx;
handlermouse2y+= dy;
handlermouse2buttons = (combytes[0] & (32+16)) >>4;
}
else if (bytenum==4) // for logitech 3 buttons
{
if (combytes[3] & 32)
handlermouse2buttons |= 4;
else
handlermouse2buttons &= ~4;
}
// Acknowledge the interrupt
outportb(0x20,0x20);
}
#ifndef DOXYGEN
static END_OF_FUNCTION(I_MicrosoftMouseIntHandler);
#endif
// wait ms milliseconde
FUNCINLINE static ATTRINLINE void I_Delay(int ms)
{
tic_t starttime;
if (timer_started)
{
starttime=I_GetTime()+(NEWTICRATE*ms)/1000;
while (starttime>=I_GetTime())
I_Sleep();
}
else
delay(ms);
}
//
// Removes the mouse2 handler.
//
static void I_ShutdownMouse2(void)
{
event_t event;
int i;
if ( !mouse2_started )
return;
outportb(mouse2port+4,0x00); // shutdown mouse (DTR & RTS = 0)
I_Delay(1);
outportb(mouse2port+1,0x00); // disable COM interuption
asm("cli");
_go32_dpmi_set_protected_mode_interrupt_vector(mouse2irq, &oldmouseinfo);
_go32_dpmi_free_iret_wrapper(&newmouseinfo);
asm("sti");
handlermouse2x=handlermouse2y=handlermouse2buttons=0;
// emulate the up of all mouse buttons
for (i=0;i<MOUSEBUTTONS;i++)
{
event.type=ev_keyup;
event.data1=KEY_2MOUSE1+i;
D_PostEvent(&event);
}
mouse2_started=false;
}
static UINT8 ComIrq[4]={0x0c,0x0b,0x0c,0x0b};
static UINT16 ComPort[4]={0x3F8,0x2F8,0x3E8,0x2E8};
//
// Installs the mouse2 handler.
//
void I_StartupMouse2(void)
{
tic_t i;
boolean found;
__dpmi_regs r;
if ( mouse2_started )
I_ShutdownMouse2();
if (!cv_usemouse2.value)
return;
handlermouse2x=handlermouse2y=handlermouse2buttons=0;
mouse2irq =ComIrq[cv_mouse2port.value-1];
mouse2port=ComPort[cv_mouse2port.value-1];
CONS_Printf("Using %s (irq %d, port 0x%x)\n",cv_mouse2port.string,mouse2irq-8,mouse2port);
r.x.ax=0x24;
__dpmi_int(0x33,&r);
if (r.h.cl+8==mouse2irq)
{
CONS_Printf("Irq conflict with mouse 1\n"
"Use mouse2port to change the port\n");
return;
}
// install irq wrapper
asm("cli");
_go32_dpmi_get_protected_mode_interrupt_vector(mouse2irq, &oldmouseinfo);
newmouseinfo.pm_selector=_go32_my_cs();
newmouseinfo.pm_offset=(int)I_MicrosoftMouseIntHandler;
_go32_dpmi_allocate_iret_wrapper(&newmouseinfo);
_go32_dpmi_set_protected_mode_interrupt_vector(mouse2irq, &newmouseinfo);
LOCK_VARIABLE(bytenum);
LOCK_VARIABLE(handlermouse2x);
LOCK_VARIABLE(handlermouse2y);
LOCK_VARIABLE(handlermouse2buttons);
LOCK_VARIABLE(mouse2port);
_go32_dpmi_lock_data(&combytes,sizeof (combytes));
LOCK_FUNCTION(I_MicrosoftMouseIntHandler);
asm("sti");
outportb(mouse2port+4,0 ); // shutdown mouse (DTR & RTS = 0)
I_Delay(1);
outportb(mouse2port+1,0 ); // disable COM interuption
I_Delay(1);
outportb(mouse2port+3,0x80); // change status of port +0 et +1
I_Delay(1); // for baudrate programmation
outportb(mouse2port ,0x60); // 1200 LSB
I_Delay(1);
outportb(mouse2port+1,0 ); // 1200 MSB
I_Delay(1);
outportb(mouse2port+3,0x02); // set port protocol 7N1
I_Delay(1);
outportb(mouse2port+1,0x01); // enable COM interuption
I_Delay(1);
outportb(0x21,0x0);
// wait to be sure the mouse have shutdown
I_Delay(100);
outportb(mouse2port+4,0x0b); // restart mouse
i=I_GetTime()+NEWTICRATE;
found=cv_usemouse2.value==2;
while (I_GetTime()<i || !found)
if (combytes[0]!='M')
found=true;
if (found || cv_usemouse2.value==2)
{
CONS_Printf("Microsoft compatible Secondary Mouse detected\n");
//register shutdown mouse2 code.
I_AddExitFunc(I_ShutdownMouse2);
mouse2_started = true;
}
else
{
CONS_Printf("Secondary Mouse not found\n");
// remove irq wraper
I_ShutdownMouse2();
}
}
#endif
// Initialise the mouse. Doesnt need to be shutdown.
//
void I_StartupMouse (void)
{
__dpmi_regs r;
// mouse detection may be skipped by setting usemouse false
if (cv_usemouse.value == 0)
{
mouse_detected=false;
I_ShutdownMouse2();
return;
}
//detect mouse presence
r.x.ax=0;
__dpmi_int(0x33,&r);
//added:03-01-98:
if ( r.x.ax == 0 && cv_usemouse.value != 2)
{
mouse_detected=false;
CONS_Printf("\2I_StartupMouse: mouse not present.\n");
}
else
{
mouse_detected=true;
// Check for CTMOUSE wheel
r.x.ax = 0x11;
__dpmi_int(0x33,&r);
if ( r.x.ax == 0x574D && r.x.cx & 0x1) // check for "MW" in AX and wheel mouse in CX
{
wheel_detected=true;
}
//hide cursor
r.x.ax=0x02;
__dpmi_int(0x33,&r);
//reset mickey count
r.x.ax=0x0b;
__dpmi_int(0x33,&r);
}
}
void I_GetJoystickEvents(void)
{
event_t event;
INT64 joybuttons = 0;
INT64 joyhats = 0;
int s = 0, i;
int js = cv_usejoystick.value - 1;
if (!joystick_detected)
return;
// I assume that true is 1
for (i = JOYBUTTONS_MIN - 1; i >= 0; i--)
{
joybuttons <<= 1;
if (joy[js].button[i].b)
joybuttons |= 1;
}
for (i = JOYHATS_MIN -1; i >=0;)
{
if (joy[js].stick[s].flags & JOYFLAG_DIGITAL)
{
if (joy[js].stick[s].axis[1].d1) joyhats |= 1<<(0 + 4*i);
if (joy[js].stick[s].axis[1].d2) joyhats |= 1<<(1 + 4*i);
if (joy[js].stick[s].axis[0].d1) joyhats |= 1<<(2 + 4*i);
if (joy[js].stick[s].axis[0].d2) joyhats |= 1<<(3 + 4*i);
i--;
}
if (s == JOYHATS_MAX) i = -1;
s++;
}
// post key event for buttons
if (joybuttons!=lastjoybuttons)
{
INT64 j = 1; // only changed bit to 1
INT64 k = (joybuttons ^ lastjoybuttons);
lastjoybuttons=joybuttons;
for (i=0;i<JOYBUTTONS && i<JOYBUTTONS_MAX;i++,j<<=1)
if (k & j) // test the eatch bit and post the corresponding event
{
if (joybuttons & j)
event.type=ev_keydown;
else
event.type=ev_keyup;
event.data1=KEY_JOY1+i;
D_PostEvent(&event);
}
}
// post key event for hats
if (joyhats!=lastjoyhats)
{
INT64 j = 1; // only changed bit to 1
INT64 k = (joyhats ^ lastjoyhats);
lastjoyhats=joyhats;
for (i=0;i<JOYHATS && i<JOYHATS_MAX;i++,j<<=1)
if (k & j) // test the eatch bit and post the corresponding event
{
if (joyhats & j)
event.type=ev_keydown;
else
event.type=ev_keyup;
event.data1=KEY_HAT1+i;
D_PostEvent(&event);
}
}
event.type=ev_joystick;
s = 0;
for (i = JOYAXISSET_MIN -1; i >=0;)
{
event.data1 = i;
event.data2 = event.data3 = 0;
if (joy[js].stick[s].flags & JOYFLAG_DIGITAL)
{
if (joy[js].stick[s].axis[0].d1)
event.data2=-1;
if (joy[js].stick[s].axis[0].d2)
event.data2=1;
if (joy[js].stick[s].axis[1].d1)
event.data3=-1;
if (joy[js].stick[s].axis[1].d2)
event.data3=1;
D_PostEvent(&event);
i++;
}
else if (joy[js].stick[s].flags & JOYFLAG_ANALOGUE)
{
event.data2 = joy[js].stick[s].axis[0].pos*32;
event.data3 = joy[js].stick[s].axis[1].pos*32;
D_PostEvent(&event);
i++;
}
if (s == JOYAXISSET_MAX*2) i = -1;
s++;
}
}
void I_GetJoystick2Events(void)
{
event_t event;
INT64 joybuttons= 0;
INT64 joyhats = 0;
int s = 0, i;
int js = cv_usejoystick2.value - 1;
if (!joystick2_detected)
return;
// I assume that true is 1
for (i = JOYBUTTONS_MIN - 1; i >= 0; i--)
{
joybuttons <<= 1;
if (joy[js].button[i].b)
joybuttons |= 1;
}
for (i = JOYHATS_MIN -1; i >=0;)
{
if (joy[js].stick[s].flags & JOYFLAG_DIGITAL)
{
if (joy[js].stick[s].axis[1].d1) joyhats |= 1<<(0 + 4*i);
if (joy[js].stick[s].axis[1].d2) joyhats |= 1<<(1 + 4*i);
if (joy[js].stick[s].axis[0].d1) joyhats |= 1<<(2 + 4*i);
if (joy[js].stick[s].axis[0].d2) joyhats |= 1<<(3 + 4*i);
i--;
}
if (s == JOYHATS_MAX) i = -1;
s++;
}
// post key event for buttons
if (joybuttons!=lastjoy2buttons)
{
INT64 j = 1; // only changed bit to 1
INT64 k = (joybuttons ^ lastjoy2buttons);
lastjoy2buttons=joybuttons;
for (i=0;i<JOYBUTTONS && i<JOYBUTTONS_MAX;i++,j<<=1)
if (k & j) // test the eatch bit and post the corresponding event
{
if (joybuttons & j)
event.type=ev_keydown;
else
event.type=ev_keyup;
event.data1=KEY_2JOY1+i;
D_PostEvent(&event);
}
}
// post key event for hats
if (joyhats!=lastjoy2hats)
{
INT64 j=1; // only changed bit to 1
INT64 k = (joyhats ^ lastjoy2hats);
lastjoy2hats=joyhats;
for (i=0;i<JOYHATS && i<JOYHATS_MAX;i++,j<<=1)
if (k & j) // test the eatch bit and post the corresponding event
{
if (joyhats & j)
event.type=ev_keydown;
else
event.type=ev_keyup;
event.data1=KEY_2HAT1+i;
D_PostEvent(&event);
}
}
event.type=ev_joystick2;
s = 0;
for (i = JOYAXISSET_MIN - 1; i >=0;)
{
event.data1 = i;
event.data2 = event.data3 = 0;
if (joy[js].stick[s].flags & JOYFLAG_DIGITAL)
{
if (joy[js].stick[s].axis[0].d1)
event.data2=-1;
if (joy[js].stick[s].axis[0].d2)
event.data2=1;
if (joy[js].stick[s].axis[1].d1)
event.data3=-1;
if (joy[js].stick[s].axis[1].d2)
event.data3=1;
D_PostEvent(&event);
i++;
}
else if (joy[js].stick[s].flags & JOYFLAG_ANALOGUE)
{
event.data2 = joy[js].stick[s].axis[0].pos*32;
event.data3 = joy[js].stick[s].axis[1].pos*32;
D_PostEvent(&event);
i++;
}
if (s == JOYAXISSET_MAX*2) i = -1;
s++;
}
}
void I_GetMouseEvents(void)
{
//mouse movement
event_t event;
int xmickeys,ymickeys,buttons,wheels = 0;
static int lastbuttons=0;
__dpmi_regs r;
if (!mouse_detected)
return;
r.x.ax=0x0b; // ask the mouvement not the position
__dpmi_int(0x33,&r);
xmickeys=(INT16)r.x.cx;
ymickeys=(INT16)r.x.dx;
r.x.ax=0x03;
__dpmi_int(0x33,&r);
buttons=r.h.bl;
if (wheel_detected)
wheels=(signed char)r.h.bh;
// post key event for buttons
if (buttons!=lastbuttons)
{
int j=1,k,i;
k=(buttons ^ lastbuttons); // only changed bit to 1
lastbuttons=buttons;
for (i=0;i<MOUSEBUTTONS;i++,j<<=1)
if (k & j)
{
if (buttons & j)
event.type=ev_keydown;
else
event.type=ev_keyup;
event.data1=KEY_MOUSE1+i;
D_PostEvent(&event);
}
}
event.type=ev_keyup;
event.data1 = 0;
if (wheels > 0)
event.data1 = KEY_MOUSEWHEELUP;
else if (wheels < 0)
event.data1 = KEY_MOUSEWHEELDOWN;
if (event.data1)
D_PostEvent(&event);
if ((xmickeys!=0)||(ymickeys!=0))
{
event.type=ev_mouse;
event.data1=0;
//event.data1=buttons; // not needed
event.data2=xmickeys;
event.data3=-ymickeys;
D_PostEvent(&event);
}
//reset wheel like in win32, I don't understand it but works
gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0;
}
void I_GetEvent (void)
{
#ifdef MOUSE2
// mouse may be disabled during the game by setting usemouse false
if (mouse2_started)
{
event_t event;
//mouse movement
static UINT8 lastbuttons2=0;
// post key event for buttons
if (handlermouse2buttons!=lastbuttons2)
{
int j=1,k,i;
k=(handlermouse2buttons ^ lastbuttons2); // only changed bit to 1
lastbuttons2=handlermouse2buttons;
for (i=0;i<MOUSEBUTTONS;i++,j<<=1)
if (k & j)
{
if (handlermouse2buttons & j)
event.type=ev_keydown;
else
event.type=ev_keyup;
event.data1=KEY_2MOUSE1+i;
D_PostEvent(&event);
}
}
if ((handlermouse2x!=0)||(handlermouse2y!=0))
{
event.type=ev_mouse2;
event.data1=0;
//event.data1=buttons; // not needed
event.data2=handlermouse2x<<1;
event.data3=-handlermouse2y<<1;
D_PostEvent(&event);
handlermouse2x=0;
handlermouse2y=0;
}
}
#endif
//mouse
I_GetMouseEvents();
//joystick
if (joystick_detected || joystick2_detected)
poll_joystick();
//joystick1
I_GetJoystickEvents();
//joystick2
I_GetJoystick2Events();
}
INT32 I_NumJoys(void)
{
return MAX_JOYSTICKS;
}
const char *I_GetJoyName(INT32 joyindex)
{
#if MAX_JOYSTICKS > 8
"More Joystick Names?"
#endif
if (joyindex == 1) return "Joystick A";
else if (joyindex == 2) return "Joystick B";
else if (joyindex == 3) return "Joystick C";
else if (joyindex == 4) return "Joystick D";
else if (joyindex == 5) return "Joystick E";
else if (joyindex == 6) return "Joystick F";
else if (joyindex == 7) return "Joystick G";
else if (joyindex == 8) return "Joystick H";
else return NULL;
}
//
// Timer user routine called at ticrate.
//
static void I_TimerISR (void)
{
// IO_PlayerInput(); // old doom did that
ticcount++;
}
#ifndef DOXYGEN
static END_OF_FUNCTION(I_TimerISR);
#endif
//added:08-01-98: we don't use allegro_exit() so we have to do it ourselves.
static inline void I_ShutdownTimer (void)
{
if ( !timer_started )
return;
remove_timer();
}
//
// Installs the timer interrupt handler with timer speed as NEWTICRATE.
//
void I_StartupTimer(void)
{
ticcount = 0;
//lock this from being swapped to disk! BEFORE INSTALLING
LOCK_VARIABLE(ticcount);
LOCK_FUNCTION(I_TimerISR);
if ( install_timer() != 0 )
I_Error("I_StartupTimer: could not install timer.");
if ( install_int_ex( I_TimerISR, BPS_TO_TIMER(NEWTICRATE) ) != 0 )
//should never happen since we use only one.
I_Error("I_StartupTimer: no room for callback routine.");
//added:08-01-98: remove the timer explicitly because we don't use
// Allegro 's allegro_exit() shutdown code.
I_AddExitFunc(I_ShutdownTimer);
timer_started = true;
}
//added:07-02-98:
//
//
static UINT8 ASCIINames[128] =
{
// 0 1 2 3
// 4 5 6 7
// 8 9 A B
// C D E F
0, 27, '1', '2',
'3', '4', '5', '6',
'7', '8', '9', '0',
KEY_MINUS, KEY_EQUALS, KEY_BACKSPACE, KEY_TAB,
'q', 'w', 'e', 'r',
't', 'y', 'u', 'i',
'o', 'p', '[', ']',
KEY_ENTER, KEY_LCTRL, 'a', 's',
'd', 'f', 'g', 'h',
'j', 'k', 'l', ';',
'\'', '`', KEY_LSHIFT, '\\',
'z', 'x', 'c', 'v',
'b', 'n', 'm', ',',
'.', '/', KEY_RSHIFT, '*',
KEY_LALT, KEY_SPACE, KEY_CAPSLOCK, KEY_F1,
KEY_F2, KEY_F3, KEY_F4, KEY_F5,
KEY_F6, KEY_F7, KEY_F8, KEY_F9,
KEY_F10,KEY_NUMLOCK,KEY_SCROLLLOCK,KEY_KEYPAD7,
KEY_KEYPAD8,KEY_KEYPAD9, KEY_MINUSPAD,KEY_KEYPAD4,
KEY_KEYPAD5,KEY_KEYPAD6, KEY_PLUSPAD,KEY_KEYPAD1,
KEY_KEYPAD2,KEY_KEYPAD3, KEY_KEYPAD0,KEY_KPADDEL,
0, 0, 0, KEY_F11,
KEY_F12, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
static volatile int pausepressed=0;
static volatile char nextkeyextended;
static void I_KeyboardHandler(void)
{
UINT8 ch;
event_t event;
ch=inportb(0x60);
if (pausepressed>0)
pausepressed--;
else if (ch==0xE1) // pause key
{
event.type=ev_keydown;
event.data1=KEY_PAUSE;
D_PostEvent(&event);
pausepressed=5;
}
else if (ch==0xE0) // extended key handled at next call
{
nextkeyextended=1;
}
else
{
if ((ch&0x80)==0)
event.type=ev_keydown;
else
event.type=ev_keyup;
ch&=0x7f;
if (nextkeyextended)
{
nextkeyextended=0;
if (ch==70) // crtl-break
{
asm ("movb $0x79, %%al\ncall ___djgpp_hw_exception"
: : :"%eax","%ebx","%ecx","%edx","%esi","%edi","memory");
}
// remap lonely keypad slash
if (ch==53)
event.data1 = KEY_KPADSLASH;
else if (ch>=91 && ch<=93) // remap the bill gates keys...
event.data1 = ch + 0x80; // leftwin, rightwin, menu
else if (ch>=71 && ch<=83) // remap non-keypad extended keys to a value<128, but
event.data1 = 0x80 + ch + 30; // make them different than the KEYPAD keys.
else if (ch==28)
event.data1 = KEY_ENTER; // keypad enter -> return key
else if (ch==29)
event.data1 = KEY_RCTRL; // rctrl -> lctrl
else if (ch==56)
event.data1 = KEY_RALT; // ralt -> lalt
else
ch = 0;
if (ch)
D_PostEvent(&event);
}
else
{
if (ASCIINames[ch]!=0)
event.data1=ASCIINames[ch];
else
event.data1=ch+0x80;
D_PostEvent(&event);
}
}
outportb(0x20,0x20);
}
#ifndef DOXYGEN
static END_OF_FUNCTION(I_KeyboardHandler);
#endif
// Return a key that has been pushed, or 0
// (replace getchar() at game startup)
//
INT32 I_GetKey (void)
{
int rc=0;
if ( keyboard_started )
{
event_t *ev;
// return the first keypress from the event queue
for ( ; eventtail != eventhead ; eventtail = (eventtail+1)&(MAXEVENTS-1) )
{
ev = &events[eventtail];
if (ev->type == ev_keydown || ev->type == ev_console)
{
rc = ev->data1;
continue;
}
}
return rc;
}
// keyboard not started use the bios call trouth djgpp
if (_conio_kbhit())
{
rc=getch();
if (rc==0) rc=getch()+256;
}
else
rc = 0;
return rc;
}
/* Keyboard handler stuff */
static _go32_dpmi_seginfo oldkeyinfo,newkeyinfo;
//
// Removes the keyboard handler.
//
static inline void I_ShutdownKeyboard(void)
{
if ( !keyboard_started )
return;
asm("cli");
_go32_dpmi_set_protected_mode_interrupt_vector(9, &oldkeyinfo);
_go32_dpmi_free_iret_wrapper(&newkeyinfo);
asm("sti");
keyboard_started=false;
}
//
// Installs the keyboard handler.
//
void I_StartupKeyboard(void)
{
if (keyboard_started)
return;
nextkeyextended=0;
asm("cli");
_go32_dpmi_get_protected_mode_interrupt_vector(9, &oldkeyinfo);
newkeyinfo.pm_offset=(int)I_KeyboardHandler;
newkeyinfo.pm_selector=_go32_my_cs();
_go32_dpmi_allocate_iret_wrapper(&newkeyinfo);
_go32_dpmi_set_protected_mode_interrupt_vector(9, &newkeyinfo);
LOCK_VARIABLE(nextkeyextended);
LOCK_VARIABLE(pausepressed);
_go32_dpmi_lock_data(ASCIINames,sizeof (ASCIINames));
LOCK_FUNCTION(I_KeyboardHandler);
_go32_dpmi_lock_data(events,sizeof (events));
LOCK_VARIABLE(eventhead);
LOCK_FUNCTION(D_PostEvent);
asm("sti");
//added:08-01-98:register shutdown keyboard code.
I_AddExitFunc(I_ShutdownKeyboard);
keyboard_started = true;
}
//added:08-01-98:
//
// Clean Startup & Shutdown handling, as does Allegro.
// We need this services for ourselves too, and we don't want to mix
// with Allegro, because someone might not use Allegro.
// (all 'exit' was renamed to 'quit')
//
#define MAX_QUIT_FUNCS 16
static quitfuncptr quit_funcs[MAX_QUIT_FUNCS] =
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
//added:08-01-98:
//
// Adds a function to the list that need to be called by I_SystemShutdown().
//
void I_AddExitFunc(void (*func)())
{
int c;
for (c=0; c<MAX_QUIT_FUNCS; c++)
{
if (!quit_funcs[c])
{
quit_funcs[c] = func;
break;
}
}
}
//added:08-01-98:
//
// Removes a function from the list that need to be called by
// I_SystemShutdown().
//
void I_RemoveExitFunc(void (*func)())
{
int c;
for (c=0; c<MAX_QUIT_FUNCS; c++)
{
if (quit_funcs[c] == func)
{
while (c<MAX_QUIT_FUNCS-1)
{
quit_funcs[c] = quit_funcs[c+1];
c++;
}
quit_funcs[MAX_QUIT_FUNCS-1] = NULL;
break;
}
}
}
//added:03-01-98:
//
// signal_handler:
// Used to trap various signals, to make sure things get shut down cleanly.
//
static inline void exception_handler(int num)
{
static char msg[255];
sprintf(msg,"Sonic Robo Blast 2 "VERSIONSTRING"\r\n"
"This is a error of SRB2, try to send the following info to programmers\r\n");
//D_QuitNetGame (); //say 'byebye' to other players when your machine
// crashes?... hmm... do they have to die with you???
I_ShutdownSystem();
_write(STDERR_FILENO, msg, strlen(msg));
signal(num, SIG_DFL);
raise(num);
/// \todo write it in a log !!
}
static inline void break_handler(int num)
{
static char msg[] = "Oh no! Back to reality!\r\n";
//D_QuitNetGame (); //say 'byebye' to other players when your machine
// crashes?... hmm... do they have to die with you???
I_ShutdownSystem();
_write(STDERR_FILENO, msg, sizeof (msg)-1);
signal(num, SIG_DFL);
raise(num);
}
//added:08-01-98: now this replaces allegro_init()
//
// REMEMBER: THIS ROUTINE MUST BE STARTED IN i_main.c BEFORE D_SRB2Main()
//
// This stuff should get rid of the exception and page faults when
// SRB2 bugs out with an error. Now it should exit cleanly.
//
INT32 I_StartupSystem(void)
{
I_DetectOS();
check_cpu();
// some 'more globals than globals' things to initialize here ?
graphics_started = false;
keyboard_started = false;
sound_started = false;
timer_started = false;
cdaudio_started = false;
// check for OS type and version here ?
signal(SIGABRT, exception_handler);
signal(SIGFPE , exception_handler);
signal(SIGILL , exception_handler);
signal(SIGSEGV, exception_handler);
signal(SIGINT , break_handler);
signal(SIGKILL, break_handler);
signal(SIGQUIT, break_handler);
return 0;
}
//added:08-01-98:
//
// Closes down everything. This includes restoring the initial
// pallete and video mode, and removing whatever mouse, keyboard, and
// timer routines have been installed.
//
// NOTE : Shutdown user funcs. are effectively called in reverse order.
//
void I_ShutdownSystem(void)
{
int c;
for (c=MAX_QUIT_FUNCS-1; c>=0; c--)
if (quit_funcs[c])
(*quit_funcs[c])();
}
void I_GetDiskFreeSpace(INT64 *freespace)
{
struct diskfree_t df;
if (_dos_getdiskfree(0,&df))
*freespace = (unsigned long)df.avail_clusters *
(unsigned long)df.bytes_per_sector *
(unsigned long)df.sectors_per_cluster;
else
*freespace = INT32_MAX;
}
char *I_GetUserName(void)
{
static char username[MAXPLAYERNAME];
char *p;
if ((p=getenv("USER"))==NULL)
if ((p=getenv("user"))==NULL)
if ((p=getenv("USERNAME"))==NULL)
if ((p=getenv("username"))==NULL)
return NULL;
strncpy(username,p,MAXPLAYERNAME);
if (strcmp(username,"")==0 )
return NULL;
return username;
}
INT32 I_mkdir(const char *pdirname, INT32 unixright)
{
return mkdir(pdirname, unixright);
}
char * I_GetEnv(const char *name)
{
return getenv(name);
}
INT32 I_PutEnv(char *variable)
{
return putenv(variable);
}
const CPUInfoFlags *I_CPUInfo(void)
{
static CPUInfoFlags DOS_CPUInfo;
memset(&DOS_CPUInfo,0,sizeof (DOS_CPUInfo));
#if ALLEGRO_VERSION == 3
if (!cpu_cpuid) return NULL;
DOS_CPUInfo.CPUID = true;
DOS_CPUInfo.MMX = cpu_mmx;
DOS_CPUInfo.AMD3DNow = cpu_3dnow;
#else
DOS_CPUInfo.CPUID = ((cpu_capabilities&CPU_ID) == CPU_ID);
DOS_CPUInfo.FPU = ((cpu_capabilities&CPU_FPU) == CPU_FPU);
#ifdef CPU_IA64
DOS_CPUInfo.IA64 = ((cpu_capabilities&CPU_IA64) == CPU_IA64);
#endif
#ifdef CPU_AMD64
DOS_CPUInfo.AMD64 = ((cpu_capabilities&CPU_AMD64) == CPU_AMD64);
#endif
DOS_CPUInfo.MMX = ((cpu_capabilities&CPU_MMX) == CPU_MMX);
DOS_CPUInfo.MMXExt = ((cpu_capabilities&CPU_MMXPLUS) == CPU_MMXPLUS);
DOS_CPUInfo.SSE = ((cpu_capabilities&CPU_SSE) == CPU_SSE);
DOS_CPUInfo.SSE2 = ((cpu_capabilities&CPU_SSE2) == CPU_SSE2);
#ifdef CPU_SEE3
DOS_CPUInfo.SSE3 = ((cpu_capabilities&CPU_SSE3) == CPU_SSE3);
#endif
DOS_CPUInfo.AMD3DNow = ((cpu_capabilities&CPU_3DNOW) == CPU_3DNOW);
DOS_CPUInfo.AMD3DNowExt = ((cpu_capabilities&CPU_ENH3DNOW) == CPU_ENH3DNOW);
DOS_CPUInfo.CMOV = ((cpu_capabilities&CPU_CMOV) == CPU_CMOV);
#endif
return &DOS_CPUInfo;
}
void I_RegisterSysCommands(void) {}