//
// GFX.H: General Futureware eXtensions          (c) in 1997 by FUTUREWARE 2001
//

#ifndef __cplusplus
#error GFX.H braucht C++ !!!
#endif

#ifndef _GFX_H
#define _GFX_H

#ifdef __i386__
#define __386__
#endif

#ifndef max
  #define min(x,y) ((x)<(y)?(x):(y))
  #define max(x,y) ((x)<(y)?(y):(x))
#endif
#define minmax(x,y,z) (max(x,min(y,z)))

#include <stdio.h>
#include <stdlib.h>
#ifndef __linux
#include <dos.h>
#include <mem.h>
#include <conio.h>
#else
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <ggi/ggi.h>
#include <signal.h>
#endif
#include <time.h>

#include "types.hpp"

void memadd(U1 *Pointer,U1 Delta,U2 Size);

#ifdef __WATCOMC__
  #include <i86.h>
  #include <graph.h>
  #define gotoxy(col,row) _settextposition(row,col)
  #define GetGFXMode _getvideomode
  #define clrscr() _clearscreen(_GCLEARSCREEN)
#endif

#ifdef __386__
  #define int86 int386
  #define int86x int386x
#endif

#define Font8x8High128  0x0
#define FontActual      0x1
#define Font8x14        0x2
#define Font8x8         0x3
#define Font9x14Changes 0x5
#define Font8x16        0x6
#define Font9x16Changes 0x7
#define M40x25x16       0x01
#define M80x25x16       0x03
#define M640x480x16     0x12
#define M320x200x256    0x13

#ifndef __linux
#define out_MiscOutput(value)         outp(0x3c2,value);
#define in_MiscOutput                 inp(0x3cc);
#define out_FeatureControl(value)     outp(0x3da,value);
#define in_FeatureControl             inp(0x3ca);
#define in_InputStatus0               inp(0x3c2);
#define in_InputStatus1               inp(0x3da);
#define out_GraphicsPosition1(value)  outp(0x3cc,value);
#define out_GraphicsPosition2(value)  outp(0x3ca,value);
#define out_Sequencer(index,value)    {outp(0x3c4,index);outp(0x3c5,value);}
#define in_Sequencer(index,data)      {outp(0x3c4,index);data=inp(0x3c5);}
#define out_CRTC(index,value)         {outp(0x3d4,index);outp(0x3d5,value);}
#define in_CRTC(index,data)           {outp(0x3d4,index);data=inp(0x3d5);}
#define out_GfxController(index,value){outp(0x3ce,index);outp(0x3cf,value);}
#define in_GfxController(index,data)  {outp(0x3ce,index);data=inp(0x3cf);}
#define out_AttrController(index,value){in_InputStatus1;outp(0x3c0,index);\
                                      outp(0x3c0,value);outp(0x3c0,0x20);}
#define in_AttrController(index,data) {in_InputStatus1;outp(0x3c0,index);\
                                      data=inp(0x3c1);outp(0x3c0,data);\
                                      outp(0x3c0,0x20);}
typedef struct
{
  U1 VideoMode;
  U2 ScreenTextColumns;
  U2 PageSize;
  U2 OffsActivePage;
  U1 CursorColumn0,CursorRow0;
  U1 CursorColumn1,CursorRow1;
  U1 CursorColumn2,CursorRow2;
  U1 CursorColumn3,CursorRow3;
  U1 CursorColumn4,CursorRow4;
  U1 CursorColumn5,CursorRow5;
  U1 CursorColumn6,CursorRow6;
  U1 CursorColumn7,CursorRow7;
  U1 CursorStart,CursorEnd;
  U1 ActivePage;
  U2 CRTCPortAddress;
  U1 ModeControlRegister;
  U1 ColorSelectRegister;
  U1 NoVGA[29];
  U1 ScreenTextRows;
  U2 CharacterHeight;
  U1 StatusInfos;
  U1 ConfigurationInfos;
  U1 NoVGA2[0x1F];
  void far *SaveTablePointer;
} BD;
#endif

#ifdef __TURBOC__
BD far *BIOSData      = (BD far *) MK_FP(0,0x449);
U1 far *GfxScreen   = (U1 far *) MK_FP(0xa000,0);
#endif
#ifdef __WATCOMC__
BD *BIOSData          = (BD *)0x449;
S1 *GfxScreen       = (S1 *)0xa0000;
#endif
#ifdef __linux
S1 *GfxScreen=NULL;
ggi_visual_t vis=NULL;
const ggi_directbuffer *db=NULL;
#endif

char Filenamepuffer[256];


U1 fexist(S1 *Name)
{
  FILE *Handle=fopen(Name,"r");
  if(!Handle) return 0;
  fclose(Handle);
  return 1;
}

#ifndef AppDirs
char *AppDirs[]={""};
#endif
S1 *fsearch(S1 *Filename, S1 *Puffer)
{
  int i;
  for(i=0;AppDirs[i]!=NULL;i++)
  {
    strcpy(Puffer,AppDirs[i]);
    strcat(Puffer,Filename);
    printf("Teste: %s\n",Puffer);
    if(fexist(Puffer)) return Puffer;
  }
  return Puffer;
}

#ifndef __linux
#ifdef __386__
U2 ralloc(U4 size, U2 *Deskriptor) //Liefert Segmentadresse eines Realmodepuffers zurück 
{
  union REGS regs;
  struct SREGS sregs;

  _fmemset(&sregs,0,sizeof(sregs));
  regs.w.ax=0x0100;
  regs.w.bx=(size+15)>>4;
  int86x( 0x31, &regs, &regs, &sregs);
  if (regs.x.cflag) return 0;
  if (Deskriptor) *Deskriptor=regs.w.dx;
  if (regs.w.ax) memset((void *)(regs.w.ax<<4),0,size);
  return regs.w.ax;
}

void rfree(U2 Deskriptor)
{
  union REGS regs;
  struct SREGS sregs;
  regs.w.ax=0x0101;
  regs.w.dx=Deskriptor;
  int86( 0x31, &regs, &regs);
}

U2 VesaInfoBlock=0;
U2 VesaDeskriptor=0;
#else
U1 VesaInfoBlock[256];
#endif
#endif
U4 *VesaInfo=NULL;

S1 InitGFXMode(U2 Number) // Funktioniert
{
  #ifndef __linux
  union REGS regs; struct SREGS sregs;
  if (Number<0x0100) // VGA Normal 
  {
    regs.w.ax = Number;
    int86(0x10,&regs,&regs);
    return 1;
  }
  else               // SVGA
  {
    #ifdef __386__
    if (!VesaInfoBlock)
    {
      VesaInfoBlock=ralloc(256,&VesaDeskriptor);
      VesaInfo=(U4 *) (VesaInfoBlock<<4);
    }
    #endif

    regs.w.ax=0x4F01;
    regs.w.cx=Number;
    regs.w.di=0;
    segread(&sregs);
    #ifdef __386__
    sregs.es=VesaDeskriptor;
    #endif
    int86x(0x10,&regs,&regs,&sregs);
    if (regs.w.ax != 0x004F ) return 0;
    GfxScreen=(S1 *) VesaInfo[0x28/4];
    if (regs.w.ax != 0x004F) return 0;
    regs.w.ax=0x4F02;
    regs.w.bx=Number;
    int86(0x10,&regs,&regs);
    return 1;
  }
  #else
  //GGI Code einfügen
    if(Number==0x13)
    {
      GfxScreen=new char[320*300];

      if(vis!=NULL) return 0;
      if(ggiInit()) return 0;
      printf("GGI_DISPLAY=%s\n",getenv("GGI_DISPLAY"));
      if((vis=ggiOpen(NULL))==NULL) return 0;
      printf("GGI-Visual ok.\n");
      if(ggiSetGraphMode(vis,320,200,320,200,GT_8BIT)) 
      {
        printf("Der 8-Bit Farbmodus funktioniert nicht.\n");
        printf("Das Visual wird wieder geschlossen ...\n");
        if(vis!=NULL) if(ggiClose(vis)) return 0;
        printf("Es wird nun versucht, mit dem palemu Modus, den 8-Bit Farbmodus zu emulieren...\n");
        if((vis=ggiOpen("display-palemu",NULL))==NULL) return 0;
        printf("Palemu-Visual ok.\n");
        if(ggiSetGraphMode(vis,320,200,320,200,GT_8BIT)) return 0;
        printf("Palemu funktioniert.\n");
      }
      printf("Der Grafikmodus scheint in Ordnung zu sein.\n");
      if((db=ggiDBGetBuffer(vis,0))==NULL)
      {
        printf("DirectBuffer ist nicht verfügbar, normale Pufferung wird verwendet.\n");
      }
      else
      {
        printf("DirectBuffer ist verfügbar und wird verwendet.\n");
        //if(db->type!=GGI_DB_SIMPLE_PLB|GGI_DB_NORMAL) return 0;
        GfxScreen=(S1*)db->write;
      }
    }
    else
    {
      //Der folgende Code wurde deaktiviert, weil er auf unserem Testsystem zu Abstürzen führt.
#if 0
      if(vis!=NULL)
      {
        if(ggiClose(vis)) return 0;
        if(ggiExit()) return 0;
      }
#endif
    }

  return 1;
  #endif
};

void Put()
{
  if(vis==NULL) return;
  if(db==NULL) ggiPutBox(vis,0,0,320,200,GfxScreen);
  ggiFlush(vis);
};

U1 GetGFXMode(void)
{
  #ifndef __linux
  union REGS regs;
  regs.h.ah = 0x0f;
  int86 (0x10, &regs, &regs);
  return regs.h.al;
  #else
  //GGI Code
  ggi_mode tm;
  if(vis!=NULL)
  {
    ggiGetMode(vis,&tm);
    if(tm.visible.x==320 && tm.visible.y==200 ) return 0x13;
  }
  return 3;
  #endif
}

#ifdef __WATCOMC__
void SetPoint(S2 x,S2 y,U1 c)
{
  _setcolor(c);
  _setpixel(x,y);
}
#endif

#ifndef __TURBOC__
#define SetPoint_13(x,y,c) GfxScreen[(y)*320+(x)]=(c)
#define GetPoint_13(x,y) GfxScreen[(y)*320+(x)]
#endif


#ifdef __TURBOC__
U1 GetPoint_13(U2 x,U2 y)
{
  asm {
    mov ax,0xA000
    mov es,ax
    mov ax,y
    mov cx,320
    mul cx
    add ax,x
    mov bx,ax
    mov al,es:[bx]
  }
}
void SetPoint_13(U2 x,U2 y,U1 c)
{
  asm {
    mov ax,0xA000
    mov es,ax
    mov ax,y
    mov cx,320
    mul cx
    add ax,x
    mov bx,ax
    mov dl,c
    mov es:[bx],dl
  }
}
#endif


#define ScrWidth 320
#ifndef __linux
void SetPoint_Tweak(U2 x,U2 y,U1 color)
{
  // Map Mask Register setzen, um die Bitplane zu selektieren, in die
  // das folgende Byte geschrieben werden soll
  out_Sequencer(2,1 << (x & 3));
  // Position berechnen (Breite * Y-Wert + X-Wert) / 4 (wegen 4 Planes)
  GfxScreen[(ScrWidth * y + x) >> 2] = color; // und Pixel setzen
}
void copy_tweak_pic(U1 *picture,U2 x_off,U2 y_off,U2 x_len, U2 y_len)
{
  U2 x,y,i=0;
  for (y=y_off;y<y_off+y_len;y++)
    for (x=x_off;x<x_off+x_len;x++)
      SetPoint_Tweak(x,y,picture[i++]);
}
#endif


#if Anz_Schriften
typedef struct
{
  U1 FontBuffer[35000];
  U2 FontHeight,FontWidth,FontFirstLetter,FontNumberOfLetters;
} Schrift;

Schrift Font[Anz_Schriften];

void LoadFont(S1 *filename,U1 Schrift)
{
  FILE *Handle;
  long i;
  Handle=fopen(fsearch(filename,Filenamepuffer),"rb");
  if(Handle==NULL) return;
  fread(&Font[Schrift].FontHeight,1,2,Handle);
  fread(&Font[Schrift].FontWidth,1,2,Handle);
  fread(&Font[Schrift].FontFirstLetter,1,2,Handle);
  fread(&Font[Schrift].FontNumberOfLetters,1,2,Handle);
  for(i=0;i<Font[Schrift].FontHeight*Font[Schrift].FontWidth*Font[Schrift].FontNumberOfLetters;i++)
    Font[Schrift].FontBuffer[i]=fgetc(Handle);
  fclose(Handle);
}
void Save_Font(S1 *filename,U1 Schrift)
{
  FILE *Handle;
  long i;
  Handle=fopen(fsearch(filename,Filenamepuffer),"wb");
  if(Handle==NULL) return;
  fwrite(&Font[Schrift].FontHeight,1,2,Handle);
  fwrite(&Font[Schrift].FontWidth,1,2,Handle);
  fwrite(&Font[Schrift].FontFirstLetter,1,2,Handle);
  fwrite(&Font[Schrift].FontNumberOfLetters,1,2,Handle);
  for(i=0;i<Font[Schrift].FontHeight*Font[Schrift].FontWidth*Font[Schrift].FontNumberOfLetters;i++)
    fputc(Font[Schrift].FontBuffer[i],Handle);
  fclose(Handle);
}

void SetFontVariables(U1 FontNumber,U1 Schrift)
{
  Font[Schrift].FontWidth=8;
  switch (FontNumber)
  {
    case Font8x8High128:
      Font[Schrift].FontHeight=8;
      Font[Schrift].FontFirstLetter=128;
      Font[Schrift].FontNumberOfLetters=128;
    break;
    case Font8x14:
      Font[Schrift].FontHeight=14;
      Font[Schrift].FontFirstLetter=0;
      Font[Schrift].FontNumberOfLetters=255;
    break;
    case Font8x8:
      Font[Schrift].FontHeight=8;
      Font[Schrift].FontFirstLetter=0;
      Font[Schrift].FontNumberOfLetters=128;
    break;
    case Font8x16:
      Font[Schrift].FontHeight=16;
      Font[Schrift].FontFirstLetter=0;
      Font[Schrift].FontNumberOfLetters=255;
    break;
  }
}

#ifdef __TURBOC__
U1 far *FontAddress(U1 FontNumber)
{
  union REGPACK regs;
  memset(&regs,0,sizeof(union REGPACK));
  regs.x.ax=0x1130;
  regs.h.bh=FontNumber;
  intr(0x10,&regs);
  return ((U1 far*) MK_FP(regs.x.es,regs.x.bp)); //ES:BP zeigt auf den Zeichensatz
};
void ExpandFont(U1 FontNumber,U1 Colorstart,U1 Colorinc,U1 Schrift)
{
  U1 *FontPtr;
  U2 i,j,FontPos,FontOffs=0,Color;
  U1 FontRow;

  SetFontVariables(FontNumber,Schrift);
  FontPtr=FontAddress(FontNumber); //Adresse des Zeichensatzes holen
  Color=Colorstart;
  FontPos=1;
  for(i=0;i<256*Font[Schrift].FontHeight;i++)
  {
    FontRow=*FontPtr++;       //Pixelzeile holen
    for(j=0;j<8;j++)
    {                     //wenn das oberste Bit gesetzt ist, Farbe schreiben
      if (FontRow & 0x80) Font[Schrift].FontBuffer[FontOffs++]=Color;
        else Font[Schrift].FontBuffer[FontOffs++]=0; 
      FontRow<<=1;                    //Bits der Pixelzeile nach oben schieben
    }
    Color+=Colorinc;
    if (++FontPos>Font[Schrift].FontHeight) {Color=Colorstart;FontPos=1;};
  }
}
#endif

void WriteCharacter(S4 x,S4 y,U1 c,U1 Farbe,S4 Schrift)
{
  U2 i,j,FontOffs,ScrOffs;

  if (Schrift==0 && c==225) c=128;

  if (c<Font[Schrift].FontFirstLetter||c>Font[Schrift].FontFirstLetter+Font[Schrift].FontNumberOfLetters) return;
  FontOffs=Font[Schrift].FontHeight*Font[Schrift].FontWidth*(c-Font[Schrift].FontFirstLetter); //Offset des Zeichens
  ScrOffs=y*320+x; //Offset auf dem Bildschirm
  for(i=0;i<Font[Schrift].FontHeight;i++) //für jede Pixelzeiel
  {
    for (j=0;j<Font[Schrift].FontWidth;j++)
      if (*(Font[Schrift].FontBuffer+FontOffs+j))
        *(GfxScreen+ScrOffs+j)=*(Font[Schrift].FontBuffer+FontOffs+j)+Farbe-1;
    ScrOffs+=320; //eine Zeile weiter
    FontOffs+=Font[Schrift].FontWidth; //eine Pixelreihe weiter
  };
}

S1 BreakLine(S1 *Text,U2 Laenge)
{
  if (Text==NULL) return 0;
  if (memchr(Text,'\0',Laenge)!=NULL) return 0;
  if (memchr(Text,' ',Laenge)!=NULL) return 0;
  if (memchr(Text,'\n',Laenge)!=NULL) return 0;
  return 1;
}

S4 WriteString(S4 xl,S4 xr,S4 y,S1 *String,U1 Farbe,U1 Schrift)
{
  S4 x=xl; 
  #define NEXTLINE {y+=Font[Schrift].FontHeight;x=xl;}
  while (*String!='\0') //solange nicht das Ende des Strings erreicht ist
  {
    switch (*String) //Zeichen untersuchen
    {
      case '':
      case '\n': //ist es ein Enter
        NEXTLINE;
      break; 
      case ' ': //vielleicht Zeilenwechsel?
        if (Font[Schrift].FontWidth) if (BreakLine(String+1,(xr-x)/Font[Schrift].FontWidth)) NEXTLINE
        else x+=Font[Schrift].FontWidth;
      break;
      default:
        if((xr-x)<Font[Schrift].FontWidth) NEXTLINE;
        WriteCharacter(x,y,*String,Farbe,Schrift); //Zeichen ausgeben
        x+=Font[Schrift].FontWidth; //eine Spalte weiter
      break;
    }
    String++; //n„chstes Zeichen
  }
  #undef NEXTLINE
  //Put();
  return y;
}
#endif

/******************************************************************************
Funktion schaltet in den TWEAK-MODE
******************************************************************************/
#ifndef __linux
void InitTweak(void)
{
  U1 RegisterValue;
  // Modus 13h initialisieren
  InitGFXMode(0x13);
  // Chain-4 Bit l”schen
  in_Sequencer(4,RegisterValue);
  out_Sequencer(4,RegisterValue & 0xf7);
  // DOPPELWORT-Modus ausschalten
  in_CRTC(0x14,RegisterValue);
  out_CRTC(0x14,RegisterValue & 0xbf);
  // WORT-Modus ausschalten -> byte-Modus
  in_CRTC(0x17,RegisterValue);
  out_CRTC(0x17,RegisterValue | 0x40);
  // Bildspeicher l”schen
  _fmemset(GfxScreen,0,0xffff);
 // BIOS-Variablen updaten
  BIOSData->ScreenTextColumns=80;
}

void Set_Line_Compare(U2 LineNo)
{
  U1 value;
  // LINE COMPARE Register Bits 0-7 setzen
  out_CRTC(0x18,LineNo & 0xff);
  // OVERFLOW Register auslesen
  in_CRTC(0x7,value);
  // wenn Bit acht des LINE COMPARE Registers gesetzt ist
  // -> Eintrag im OVERLOW Register
  if (LineNo & 0x100) out_CRTC(0x7,value | 0x10)
  // ansonsten das Bit l”schen
  else out_CRTC(0x7,value & 0xef);
  // Wert des MAXIMUN SCANLINE Register auslesen
  in_CRTC(0x9,value);
  // wenn Bit neun des 0LINE COMPARE Registers gesetzt ist
  // -> Eintrag im MAXIMUN SCANLINE Register
  if (LineNo & 0x200) out_CRTC(0x9,value | 0x40)
  // ansonsten das Bit l”schen
  else out_CRTC(0x9,value & 0xbf);
}

// Routine wartet auf den vertikalen Strahlenrcklauf
void wait_vretrace(void)
{
  while ((inp(0x03da) & 8) != 0); // auf Displayperiode warten
  while ((inp(0x03da) & 8) == 0); // auf vertikalen Retrace warten
}

// Routine wartet auf den horizontalen Strahlenrcklauf
void wait_hretrace(void)
{
  while ((inp(0x03da) & 1) != 0); // auf Displayperiode warten
  while ((inp(0x03da) & 9) != 1); // auf horiz. Retrace warten
}

// Routine wartet auf den Beginn der Displayperiode
void wait_display(void)
{
  while ((inp(0x03da) & 8) == 0); // auf vert. Retrace warten
  while ((inp(0x03da) & 8) != 0); // auf Displayperiode warten
}

// Funktion wartet auf einen Retrace (egal ob vertikal oder horizontal)
void wait_retrace(void)
{
  while ((inp(0x03da) & 1) != 0); // auf Displayperiode warten
  while ((inp(0x03da) & 1) == 0); // auf Retrace warten
}

// Funktion setzt das Offset-Register
void Set_Offset_Register(U1 value)
{
  outp(0x03d4,0x13);         // CRTC Index 13h
  outp(0x03d5,value);
  BIOSData->ScreenTextColumns=value*2; // BIOS-Variablen updaten
}

// Funktion wartet eine gegebene Anzahl von Rasterzeilen
void wait_rasters(U2 number)
{
  U2 i;
  for (i=0;i<number;i++) wait_retrace(); // solange auf einen Retrace warten
}                                        // bis Z„hler abgelaufen

// Funktion setzt einen Teil einer gegebenen Palette ber die Ports
void Set_Palette(U2 start,U2 end,U1 *palette,char Shift)
{
  U2 i;
  outp(0x03c8,start);
  start*=3;
  end++; end*=3;
  for (i=start;i<end;i++) outp(0x03c9,(((palette[i-start]<<1)>>Shift)+1)>>1);
}

// Funktion liest einen Teil der aktuellen Palette in den Puffer 'palette'
// das Auslesen erfolgt ber die Ports
void Get_Palette(U2 start,U2 end,U1 *palette)
{
  U2 i;
  outp(0x03c7,start);
  start*=3;
  end++; end*=3;
  for (i=start;i<end;i++) palette[i] = inp(0x03c9);
}

//Funktion setzt eine gegebene Palette mit einer angegebenen Helligkeit(0-128)
void Set_Palette2(U2 start,U2 end,U1 *palette,U1 blend)
{
  U2 i;
  outp(0x03c8,start);
  start*=3;
  end++;
  end*=3;
  for (i=start;i<=end;i++) outp(0x03c9, (palette[i]*blend >> 7));
}

/****************************************************************************
 Diese Funktion setzt die Palette ohne Flimmern - jeden horizontalen Retrace
 wird eine Farbe gesetzt. Der Trick bei der Sache ist, daá der Blau-Wert
 einer Farbe w„hrend des horizontalen Strahlenrcklaufs gesetzt wird
 (zu dieser Zeit ist der Rasterstrahl dunkelgeschaltet !)
 und so auch bei „lteren Grafikkarten kein st”render Grafikschnee auftritt.
 Der Parameter blend bestimmt die Helligkeit des angezeigten Bildes (0-128)
****************************************************************************/
void Set_Palette3(U2 start,U2 end,U1 *palette,U1 blend)
{
  U2 i;
  U1 blue_value;
#ifdef __TURBOC__
  asm cli;                        // bitte nicht st”ren !
#endif
  wait_display();                    // auf Beginn der DIsplayperiode warten
  outp(0x03c8,start);            // Startfarbe setzen
  start*=3;
  end++;
  end*=3;
  for (i=start;i<end;)               // fr alle Farben zu „ndernden Farben
  {
    // Rot-Wert schreiben
    outp(0x03c9,palette[i++]*blend >> 7);
    // Grn-Wert schreiben
    outp(0x03c9,palette[i++]*blend >> 7);
    // Blauwert vorberechnen
    blue_value = palette[i++]*blend >> 7;
    // auf Beginn des horiz. Retrace warten
    wait_retrace();
    // Blau-Wert schreiben
    outp(0x03c9,blue_value);
  }
#ifdef __TURBOC__
  asm sti;                        // Interrupts wieder an !
#endif
}

/****************************************************************************
 Funktion dient zum Erzeugen eines FLASH-Effekts
 wenn der Parameter flash von 128 auf 0 l„uft, wird die Palette von
 WEISS auf ihre ursprnglichen Farben gezogen
 wenn der Parameter flash von 0 bis 128 l„uft, wird die Palette von
 ihrer ursprnglichen Farbe auf WEISS gezogen
****************************************************************************/
void Flash(U1 *palette,U1 flash,U2 start_color,U2 end_color)
{
  U2 i;
  // Startfarbe setzen
  outp(0x03c8,start_color);
  // wegen RGB-Werten startfarbe mit 3 multiplizieren
  start_color*=3;
  // Endfarbe erh”hen und auch mit 3 multiplizieren
  end_color++;
  end_color*=3;
  // Palette im Bereich von start_color bis end_color setzen
  for (i=start_color;i<end_color;i++)
    outp(0x03c9, ((63-palette[i])*flash >> 7)+palette[i]);
}

/****************************************************************************
 Funktion zieht die Palette von WEISS auf SCHWARZ
****************************************************************************/
void black2white(U2 start_color,U2 end_color,U2 speed)
{
  S1 i,j;
  // Endfarbe erh”hen (wegen der Konstruktion der folgenden
  // FOR-Schleife)
  end_color++;
  for (j=63;j>=0;j-=speed)
  {
    // wegen der zeitlichen Synchronisation auf den
    // vertikalen Retrace warten
    wait_vretrace();
    // Startfarbe setzen
    outp(0x03c8,start_color);
    // alle Farben im Bereich von start_color bis end_color
    // von WEISS (63,63,63) auf SCHWARZ (0,0,0) ziehen
    for (i=start_color*3;i<end_color*3;i++) outp(0x03c9,j);
  }
}

/****************************************************************************
 Funktion setzt einen Teil einer gegebenen Palette ber die Ports
 nimmt die Farbwerte vom Beginn des Feldes (nicht mitten daraus)
****************************************************************************/
void Set_PalRange(U2 start,U2 end,U1 *palette)
{
  U2 i;
  outp(0x03c8,start);
  start*=3;
  end++; end*=3;
  for (i=0;i<end-start;i++) outp(0x03c9,palette[i]);
}

// Funktion setzt die Startadresse des Bildschirms
void Set_Start_Address(U2 Address)
{
  out_CRTC(0x0c,Address >> 8);
  out_CRTC(0x0d,Address & 0xff);
  BIOSData->OffsActivePage = Address;
}

// Funktion schaltet den Bildschirmaufbau aus
void Screen_Off(void)
{
  U1 old_clock;
  // CLOCKING MODE Register des Sequencers auslesen
  in_Sequencer(1,old_clock);
  // Bit 5 setzen und damit den Bildrefresh abschalten
  out_Sequencer(1,old_clock | 0x20);
}

// Funktion schaltet den Bildschirmaufbau wieder an
void Screen_On(void)
{
  U1 old_clock;
  // CLOCKING MODE Register des Sequencers auslesen
  in_Sequencer(1,old_clock);
  // Bit 5 l”schen und damit den Bildrefresh wieder anschalten
  out_Sequencer(1,old_clock & 0xDF);
}

// Funktion setzt das Overscan-Register
void Set_Overscan_Color(U1 color)
{
  // auf einen Retrace warten (horizontal oder vertikal)
  // dadurch Lesen des Input Status #1 Registers zum
  // Zurcksetzen des Index/Daten-Schalters
  wait_retrace();
  outp(0x3c0,0x11);  // Index fr Overscan-Register schreiben
  outp(0x3c0,color); // Farbe setzen
  outp(0x3c0,0x20);  // Bit 5 im Indexregister l”schen
}
#endif

#ifdef __linux
int kbhit()
{
  struct timeval tval;
  tval.tv_sec=0;
  tval.tv_usec=0;
  int ret=0;

  if(vis!=NULL)  
  {
    ret=ggiEventPoll(vis,(ggi_event_mask) ((int)emKeyPress|(int)emKeyRepeat),&tval);
  }
  return ret;
}
int getch()
{
  if(vis!=NULL)
  {
    gii_event_mask mask;
    gii_event event;
    struct timeval tval;
    tval.tv_sec=60;
    tval.tv_usec=0;
    while((mask=ggiEventPoll(vis,(ggi_event_mask)((int)emKeyPress|(int)emKeyRepeat),&tval))!=0)
    {
      ggiEventRead(vis,&event,(ggi_event_mask)((int)emKeyPress|(int)emKeyRepeat));
      switch(event.any.type)
      {
        case evKeyPress: case evKeyRepeat:
         #ifdef DEBUG
         printf("sym=%d label=%d button=%d\n",event.key.sym,event.key.label,event.key.button);
         #endif
         return(event.key.sym);
        break;
        case evKeyRelease:
        break;
        default:
         printf("Da ist irgendwas mit der Tastatur. Keine Ahnung, was.\n");
      }
    };
  }
  return getchar();
}
void wait_vretrace(){};
void Set_Palette(U2 start,U2 end,U1 *palette,char Shift)
{
  if(vis==NULL) return;
  ggi_color myGGIpal[256];
U2 i;
  for (i=0;i<=(end-start);i++) 
  {
    myGGIpal[i].r=palette[i*3+0]<<(10-Shift);
    myGGIpal[i].g=palette[i*3+1]<<(10-Shift);
    myGGIpal[i].b=palette[i*3+2]<<(10-Shift);
  }
  ggiSetPalette(vis,start,end-start+1,myGGIpal);
}
void Get_Palette(U2 start,U2 end,U1 *palette){};
void Set_Palette2(U2 start,U2 end,U1 *palette,U1 blend)
{
  if(vis==NULL) return;
  ggi_color myGGIpal[256];
U2 i;
  for (i=0;i<=(end-start);i++) 
  {
    myGGIpal[i].r=palette[i*3+0]*blend<<3;
    myGGIpal[i].g=palette[i*3+1]*blend<<3;
    myGGIpal[i].b=palette[i*3+2]*blend<<3;
  }
  ggiSetPalette(vis,start,end-start+1,myGGIpal);

};
void Screen_Off(void){};
void Screen_On(void){};
void Set_Overscan_Color(U1 color){};

#endif

// Funktion blendet eine Grafik ein (erh”hen der Helligkeit)
U1 PALETTE[768]; // zugeh”rige Palette
void blend_in(void)
{
  S2 blend_count;
  // Helligkeit von 0 bis 128 erh”hen -> Einblenden
  for (blend_count=0;blend_count<128 && !kbhit();blend_count++)
  {
    // wegen der zeitlichen Synchronisation
    // auf den vertikalen Retrace warten
    wait_vretrace();
    // Palette mit Helligkeit "blend_count" setzen
    Set_Palette2(0,255,PALETTE,blend_count);
  }
}

// Funktion blendet eine Grafik aus (verringern der Helligkeit)
void blend_out(void)
{
  S2 blend_count;
  // Helligkeit von 128 auf 0 erniedrigen -> Abblenden
  Get_Palette(0,255,PALETTE);
  for (blend_count=128;blend_count>=0;blend_count-=2)
  {
    // wegen der zeitlichen Synchronisation
    // auf den vertikalen Retrace warten
    wait_vretrace();
    // Palette mit Helligkeit "blend_count" setzen
    Set_Palette2(0,255,PALETTE,blend_count);
  }
}

void Cursor_Off()
{
#ifndef __linux
  union REGS inregs, outregs;
  inregs.w.ax = 0x0100;
  inregs.w.cx = 0x2020;
  int86(0x10, &inregs, &outregs);
#endif
}

void Cursor_On()
{
#ifndef __linux
  union REGS inregs;
  inregs.w.ax = 0x0100;
  inregs.w.cx = 0x0607;
  int86 (0x10, &inregs, &inregs);
#endif
}

S1 Text_Low[256];

S1 *strlow(S1 *text)
{
  U2 i;
  Text_Low[0]=0;
  if(text==NULL) return (Text_Low);
  for(i=0;i<=strlen(text)&&i<255;i++)
  {
    Text_Low[i]=tolower(text[i]);
  }
  return(Text_Low);
}
S1 *floadfile(S1 *Name,S1 *Typ,long *Anzahl)
{
  FILE *Handle;
  char *Puffer;

  if (Name==NULL || Typ==NULL) return(NULL);
  Handle=fopen(Name,Typ);
  if (!Handle) return(NULL);
  fseek (Handle,0,SEEK_END);
  *Anzahl=ftell(Handle);
  Puffer=new S1[(*Anzahl)+1];
  if(Puffer==NULL) return (NULL);
  memset(Puffer,0,*Anzahl);
  fseek(Handle,0,SEEK_SET);
  fread(Puffer,1,*Anzahl,Handle);
  fclose(Handle);
  return(Puffer);
}

U1 *fload(S1 *Name,S1 *Typ,U4 &Anzahl,U1 *Puffer,U4 Head,U2 *Deskriptor=NULL)
{
  FILE *Handle;
  if (!Name) return(0);
  Handle=fopen(Name,Typ);
  if (!Handle) return(0);
  if (Anzahl==0)
  {
    fseek (Handle,0,SEEK_END);
    Anzahl=ftell(Handle)-Head;
  }
  if (Puffer==NULL)
  {
    Puffer=(U1*)malloc(Anzahl);
    if(Puffer==NULL) return 0;
  }
  fseek(Handle,Head,SEEK_SET);
  Anzahl=fread(Puffer,1,Anzahl,Handle);
  fclose(Handle);
  return(Puffer);
}
#ifndef __linux
#ifdef __386__
U1 *rfload(S1 *Name,S1 *Typ,U4 &Anzahl,U1 *Puffer,U4 Head,U2 *Deskriptor)
{
  FILE *Handle;
  if (!Name) return(0);
  Handle=fopen(Name,Typ);
  if (!Handle) return(0);
  fseek (Handle,0,SEEK_END);
  if (Anzahl==0)
  {
    fseek (Handle,0,SEEK_END);
    Anzahl=ftell(Handle)-Head;
    Puffer=(U1*)(ralloc(Anzahl,Deskriptor)<<4);
    if(Puffer==NULL) return 0;
  }
  fseek(Handle,Head,SEEK_SET);
  fread(Puffer,1,Anzahl,Handle);
  fclose(Handle);
  return(Puffer);
}
#endif
#endif

void freadt(U1 *Puffer,U4 Anzahl,FILE *Handle)
{
  U2 Character;
  while(Anzahl--)
  {
    Character=fgetc(Handle);
    if (Character==0xffff) return;
    if (Character) *Puffer=(U1)Character;
    Puffer++;
  }
}

U4 floadt(S1 *Name,S1 *Typ, U4 Anzahl,U1 *Puffer,U4 Head)
{
  FILE *Handle;
  if (!Name) return(0);
  Handle=fopen(Name,Typ);
  if (!Handle) return(0);
  if (Anzahl==0)
  {
    fseek (Handle,0,SEEK_END);
    Anzahl=ftell(Handle);
    fclose(Handle);
    return(Anzahl);
  }
  fseek(Handle,Head,SEEK_SET);
  freadt(Puffer,Anzahl,Handle);
  fclose(Handle);
  return(Anzahl);
}

S1 Load_Sprite(S1 *Name,U2 Anzahl,U4 Size,U4 Abstand,U1 *Puffer,S1 Add)
{
  U2 i;
  FILE *Handle=fopen(Name,"rb");
  if (!Handle) return 0;
  for (i=0;i<Anzahl;i++)
  {
    fread(Puffer,1,Size,Handle);
    memadd(Puffer,Add,Size);
    Puffer+=Abstand;
  }
  fclose(Handle);
  return 1;
}

void Show_Sprite(U1 *SPuffer,U1 *Name,U2 Anzahl,U2 LSize,U2 Size,U2 RSize, S2 Abstand,S1 Alpha=0,S1 Flip=0)
{
  U2 i,k;
  if(Alpha)
  {
    if(Flip)
    {
      SPuffer+=LSize;Name+=LSize;
      for (i=0;i<Anzahl;i++)
      {
        for(k=0;k<Size;k++,SPuffer++) if(*(Name+Size+RSize-LSize-k)) *(SPuffer)=*(Name+Size+RSize-LSize-k);
        SPuffer+=Abstand-Size;
        Name+=LSize+Size+RSize;
      }
    }
    else
    {
      SPuffer+=LSize;Name+=LSize;
      for (i=0;i<Anzahl;i++)
      {
        for(k=0;k<Size;k++,SPuffer++) if(*(Name+k)) *SPuffer=*(Name+k);
        SPuffer+=Abstand-Size;
        Name+=LSize+Size+RSize;
      }
    }
  }
  else
  {
    SPuffer+=LSize;Name+=LSize;
    for (i=0;i<Anzahl;i++)
    {
      memcpy(SPuffer,Name,Size);
      SPuffer+=Abstand;
      Name+=LSize+Size+RSize;
    }
  }
  Put();
}

void Save_File(S1 *Name,U4 Anzahl,U1 *Puffer)
{
  FILE *Handle=fopen(Name,"wb");
  if (!Handle) return;
  fwrite(Puffer,1,Anzahl,Handle);
  fclose(Handle);
}

#ifndef __linux
U1 SetCPUMemControl(U2 WinAddress)
{
  union REGS inregs, outregs;
  inregs.w.ax=0x4f05;
  inregs.w.bx=0;
  inregs.w.dx=WinAddress;
  int86(0x10,&inregs,&outregs);
  return(outregs.w.ax>>8);
}
#endif

S4 GetFileList(S1 *Directory,S1 *Format,S1 *Puffer,U2 Abstand, U2 Size)
{
  S4 Anzahl=0; int i;
  S1 DirPuffer[200];
#ifdef __linux

  DIR *dirp;
  struct dirent *d;

  for(i=0;AppDirs[i]!=NULL && !Anzahl;i++)
  {
    strcpy(DirPuffer,AppDirs[i]);
    strcat(DirPuffer,Directory);
    printf("Trying: %s\n",DirPuffer);

    dirp = opendir(DirPuffer);
    if(dirp!=NULL)
    {
      while ((d=readdir(dirp))!=NULL)
        if((strcmp(Format,".")&&strstr(strlow(d->d_name),Format)!=NULL)||(!strcmp(Format,".")&&strstr(strlow(d->d_name),Format)==NULL))
      {
        strcpy(Puffer,DirPuffer);
        strcat(Puffer,d->d_name);
        Puffer+=Abstand;
        Anzahl++;
        Size--;
      }
      closedir(dirp);
    }

  }

/*
  struct dirent **namelist;

  Anzahl = scandir(".", &namelist, 0, alphasort);

  if (Anzahl < 0)
    perror("scandir");
  else
    while(Anzahl--) printf("%s\n", namelist[Anzahl]->d_name);
*/

#else

  S2 rc;
  struct find_t fileinfo;
  
  rc=_dos_findfirst(Format,_A_NORMAL,&fileinfo);
  while (!rc && Size)
  {
    Puffer[0]=0; 
    //strcpy(Puffer,Directory);
    strcat(Puffer,fileinfo.name);
    Puffer+=Abstand;
    Anzahl++;
    Size--;
    rc=_dos_findnext(&fileinfo);
  }
  #ifdef _dos_findclose
    _dos_findclose(&fileinfo);
  #endif
#endif
  return(max(0,Anzahl));
}

FILE *Report=NULL;

void Init_Report()
{
  time_t time_of_day;
  time_of_day=time( NULL );
  Report=fopen("/dev/tty14","at");
  if(Report!=NULL) fprintf(Report,"Init_Report :%s\n",ctime( &time_of_day ));
}

void Stop_Report()
{
  if (Report!=NULL) fclose(Report);
}

#ifdef SOUND
void Sound_Cleanup();
#endif

void Fehler(S1 *Text)
{
  InitGFXMode(0x03);
  printf("Fehler: %s\n",Text);
  Stop_Report();
#ifdef SOUND
  Sound_Cleanup();
#endif
  exit(0);
}

void Fehler(S1 *Text,S1 *Text2)
{
  InitGFXMode(0x03);
  printf("Fehler: ");
  printf(Text,Text2);
  printf("\n");
  Stop_Report();
  exit(0);
}

#ifndef __linux
void FehlerNr(S4 Nr)
{
  S1 Buffer[10];
  Fehler(itoa(Nr,Buffer,10));
}
#endif

void Dump(U1 *Text,U4 Length)
{
  U4 i;
  if (Report==NULL) return;
  fprintf(Report,"\n----Anfang----\n");
  for(i=0;i<Length;i++)
  {
    fprintf(Report,"%c",*(Text+i));
    if(i%80==0) fprintf(Report,"\n");
  }
  fprintf(Report,"\n---- Ende ----\n");
}

int rnd(int Lower,int Upper)
{
  if (Lower< Upper) return ((rand()%(Upper-Lower+1))+Lower);
  if (Lower==Upper) return (Lower);
  if (Lower> Upper) return ((rand()%(Lower-Upper+1))+Upper);
  return 0;
}

void memadd(U1 *Pointer,U1 Delta,U2 Size)
{
  U2 i;
  for(i=0;i<Size;i++)
  {
    *(Pointer+i) += Delta;
  }
};

#ifdef __386__
U8 Datum()
{
  time_t Time;
  tm *tm;
  Time=time(NULL);
  tm=localtime(&Time);
  return (((U8)(tm->tm_mon*32+tm->tm_mday)*24+tm->tm_hour)*60+tm->tm_min)*60+tm->tm_sec;
};
#endif

#ifdef __linux
#define stricmp strcasecmp
void delay(long n){usleep(n*1000);};
#define memicmp strncasecmp

#endif

#endif

