/******************************************************************* RCS
 * $Id: c.i_init 1.7 99/10/23 18:32:27 gerph Exp $
 *
 * $Author: gerph $
 * $Date: 99/10/23 18:32:27 $
 *
 * $Log:	c.i_init $
 * Revision 1.7  99/10/23  18:32:27  gerph
 * Added -version support.
 * Added runtime SMUL replacement.
 *
 * Revision 1.6  99/05/01  22:29:04  gerph
 * Tidied up a bit.
 *
 * Revision 1.5  99/04/10  11:46:58  gerph
 * Added slightly more helpful signal handler.
 *
 * Revision 1.4  98/11/15  00:53:18  gerph
 * Added I_Broken support.
 *
 * Revision 1.3  98/10/26  14:13:30  gerph
 * Added include of m_misc.h.
 *
 * Revision 1.2  98/10/03  02:26:41  gerph
 * Added support for backtrace on error.
 *
 * Revision 1.1  98/08/13  14:34:34  eddie
 * RISC OS port
 *
 ******************************************************************/

/* i_init.c - initilization and termination code */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include "doomdef.h"
#include "m_misc.h"
#include "kernel.h"
#include "swis.h"
#include "i_header.h"
#include "i_machine.h"
#include "wimp.h"
#include "w_declare.h"
#include "w_text.h"
#include "version.h"

/* Define this for debugging */
#define DEBUG

/* Define this if you want more debugging when an error occurs */
/* #define DEBUG_BACKTRACE */

_MDA *Support;

extern void flushkeys(void);

void I_Init (void)
{
  Support->D_ClearEvents(Support);
  I_InitSound();
}

/*
================
=
= I_Quit
=
================
*/

void I_Quit(void)
{
  W_QuitMsg(0);
}

#define _kernel_procname(pc) c_kernel_procname(pc)

char *c_kernel_procname(int pc)
{
#if 1
  {
    int cl_base, cl_limit;
    _kernel_swi_regs r;
    /* JRF: This can't happen as part of the RTSK system */
    r.r[0] = 18;
    r.r[1] = (int)"SharedCLibrary";
    cl_base = 0;
    cl_limit = 0;
    if (!_kernel_swi(OS_Module, &r, &r)) {
        cl_base = r.r[3];
        cl_limit = cl_base + *((int *)(cl_base - 4));
    }
    if (pc < cl_limit && pc > cl_base)
      return "SharedCLibrary function";
  }
#endif
  {
    char *name=NULL;
    int namelen;
    int *z=(int*)pc;
    while ( (z>(int*)0x8000) && (pc-(int)z < 64*1024) )
    {
      int w = *--z;
      if (((w & 0xffff0000) == 0xff000000) && ((w & 3) == 0))
      {
        namelen=w & 0xffff;
        name = (char *)z - namelen;
        break;
      }
    }
    if (name!=NULL)
    {
      int len = strlen(name)+1;
      if (namelen == ((len+3) & ~3))
        return name;
    }
  }
  return NULL;
}

void I_Error(char *error, ...)
{
  va_list  argptr;
  char     msg[512];

  va_start(argptr, error);
  vsprintf(msg, error, argptr);
  va_end(argptr);

#ifdef DEBUG
  printf("**** ERROR: %s\n",msg);
#ifdef DEBUG_BACKTRACE
  {
    int count=0;
    int state;
    void _setup_kernel_unwind_block(_kernel_unwindblock *inout);
    _kernel_unwindblock inout;
    char *language;

    _setup_kernel_unwind_block(&inout);
    while ( ((state=_kernel_unwind(&inout,&language))>0) && (count<256))
    {
      char *rout;
      int pc=inout.pc & 0x03fffffc;
      rout=_kernel_procname(pc);
      if (rout==NULL)
        rout="unknown routine";
      if (language[0]=='\0')
        language="?";
      printf("%8x: %s: %s\n",pc,language,rout);
      count++;
    }
    if (state<0)
      printf("Stack Corrupt!!!\n");
    if (count>=256)
      printf("Probable stack corruption - > 256 calls made\n");
  }
#endif
#endif

#ifdef BETA
  Z_DumpHeap(0,MAXINT);
#endif

  I_ShutdownGraphics();

  W_FatalError(msg);
}

void I_Signal(int signum)
{
  static int reentered=0;
  char *sigs[11] = {
    "No signal",
    "Abort",
    "Floating point exception",
    "Illegal instruction (memory corrupt - Bad PWAD ?)",
    "User interrupt (someone has been messing!)",
    "Bad memory access (Bad PWAD ?)",
    "Termination request",
    "Stack overflow (Out of memory, or memory corrupt)",
    "User signal 1",
    "User signal 2",
    "OS Error (should never happen!)"
  };

  if (reentered)
  {
    W_FatalError("Signal handler re-entered, aborting!");
    exit(1);
  }
  reentered=signum;

  I_Error("DOOM+ has suffered a fatal internal error (%s) and must exit immediately. Sorry.",sigs[signum]);
  exit(1); /* Just to be sure! */
}

void I_Broken(char *error, ...)
{
  int answer;
  va_list  argptr;
  char     msg2[512];
  char     msg[512];

  if (M_CheckParm("-noinitwarnings"))
    return;

  va_start(argptr, error);
  vsprintf(msg, error, argptr);
  va_end(argptr);

#ifdef A5000
  strcat(msg," Cancel to Quit, OK to try to play");
#endif

  sprintf(msg2,"Broken PWAD: %s",msg);

  I_ShutdownGraphics();

  W_PrintF(WTT_WARNING,msg2);

  answer=W_Report(4,msg2,"sdoom","Play,Abort");
  if (answer==1 /* OK */ || answer==3 /* PLAY */)
    return;

  /* otherwise we terminate with predjudice */

  /* I_ShutdownGraphics(); */

  W_QuitMsg(1);
}

void I_Exit(void)
{
  static int done=0;

  if (!done)
  {
    I_ShutdownGraphics();
#ifndef BETA
    {
      _kernel_swi_regs ARM;
      ARM.r[0] = 4;
      ARM.r[1] = (int)"DOOMSupport";
      _kernel_swi(OS_Module, &ARM,&ARM);
    }

    I_FreeZone();
#endif
    done=1;
  }
}

int main(int argc, char **argv)
{
  _kernel_swi_regs  ARM;

#if (ASMMUL == 2)
  {
    extern void ReplaceFixedMulBySMUL(void);
    ReplaceFixedMulBySMUL();
  }
#endif

  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);

  myargc = argc;
  myargv = argv;

  if (M_CheckParm("-version"))
  {
    printf("Source: %i\nRISC OS Version: %s\n",VERSION,W_Version());
#ifdef USE_NEW_BUTTON_SPECIALS
    printf("New button specials enabled\n");
#endif
    exit(1);
  }

  ARM.r[0] = 18;
  ARM.r[1] = (int)"DOOMSupport";

  if (_kernel_swi(OS_Module, &ARM, &ARM))
  {
    I_Error("DOOMSupport module is not loaded");
  }

  Support = (_MDA *)ARM.r[4];

  ARM.r[0] = 4;
  ARM.r[1] = 1;
  _kernel_swi(OS_Byte, &ARM, &ARM);

  signal(SIGINT, SIG_IGN);
  signal(SIGTERM, SIG_IGN);
  signal(SIGABRT, I_Signal);
#ifndef BETA
  signal(SIGSEGV, I_Signal);
#endif
  atexit(I_Exit);

  W_Init();

  I_ExamineMachine();

  I_PreinitGraphics();

  W_Poll();

  W_CreateProgressWin();

  W_Poll();

  D_DoomMain();
}

