//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2011 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: text-display handing

#include "mpxplay.h"
#include "display\display.h"
#include <malloc.h>
#ifdef MPXPLAY_WIN32
 #include <wincon.h>
 static HANDLE hConsoleOutput;
 static WORD *consolesave_attrib;
 static mpxp_wchar_t *consolesave_text;
 static unsigned int textscreen_size,consolesave_size;
#endif

#define TEXTSCREEN_BASE_ADDR 0xb8000

unsigned long textscreen_linear_address=TEXTSCREEN_BASE_ADDR;
unsigned int textscreen_mode,textscreen_maxx,textscreen_maxy,oldposrow;
//#ifdef MPXPLAY_UTF8
//unsigned int textscreen_console_codepage;
//#else
unsigned int textscreen_console_codepage=437;
//#endif

#define TEXTSCREEN_LIN_ADDR textscreen_linear_address

static unsigned int textscreen_oldmaxy,textscreen_oldcursor_y;
static unsigned int textscreen_memory_size;
#ifdef __DOS__
static unsigned int *vidmemsave_field,vidmemsave_size;
#endif

#ifdef MPXPLAY_WIN32

void newfunc_textdisplay_init(void)
{
 COORD size;
 hConsoleOutput=GetStdHandle(STD_OUTPUT_HANDLE);

 textscreen_mode=pds_textdisplay_getmode();
 pds_textdisplay_getresolution();

 size.X=textscreen_maxx;
 size.Y=textscreen_maxy;
 SetConsoleScreenBufferSize(hConsoleOutput,size);

 if(textscreen_console_codepage)
  SetConsoleOutputCP(textscreen_console_codepage);

 textscreen_size=textscreen_maxx*textscreen_maxy;
 textscreen_memory_size=textscreen_size*TEXTSCREEN_BYTES_PER_CHAR;
 if(consolesave_size<textscreen_size){
  consolesave_size=textscreen_size*2;
  if(consolesave_attrib)
   pds_free(consolesave_attrib);
  consolesave_attrib=(WORD *)pds_malloc(consolesave_size*sizeof(WORD));
  if(consolesave_text)
   pds_free(consolesave_text);
  consolesave_text=(mpxp_wchar_t *)pds_malloc(consolesave_size*sizeof(mpxp_wchar_t));
  if(!consolesave_attrib || !consolesave_text)
   consolesave_size=0;
 }
}

#else

void newfunc_textdisplay_init(void)
{
 textscreen_mode=pds_textdisplay_getmode();
 pds_textdisplay_getresolution();
 textscreen_memory_size=textscreen_maxx*textscreen_maxy*TEXTSCREEN_BYTES_PER_CHAR;
 if(vidmemsave_size<textscreen_memory_size){
  vidmemsave_size=textscreen_memory_size*2;
  if(vidmemsave_field)
   pds_free(vidmemsave_field);
  vidmemsave_field=pds_malloc(vidmemsave_size);
  if(!vidmemsave_field)
   vidmemsave_size=0;
 }
 //pds_cpu_mtrr_enable_wc(0xA0000,128);
}
#endif

void newfunc_textdisplay_close(void)
{
#ifdef MPXPLAY_WIN32
 if(consolesave_attrib){
  pds_free(consolesave_attrib);
  consolesave_attrib=NULL;
 }
 if(consolesave_text){
  pds_free(consolesave_text);
  consolesave_text=NULL;
 }
 consolesave_size=0;
#else
 //pds_cpu_mtrr_disable_wc(0xA0000);
 if(vidmemsave_field){
  pds_free(vidmemsave_field);
  vidmemsave_field=NULL;
 }
 vidmemsave_size=0;
#endif
}

void pds_textdisplay_charxy(unsigned int color,unsigned int outx,unsigned int outy,mpxp_wchar_t outchar)
{
#ifdef MPXPLAY_UTF8
 outchar=pds_cvchar_CP437_to_UTF16LE(outchar);
#endif
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  DWORD dlo;
  mpxp_wchar_t chdata=outchar;
  curpos.X = outx;
  curpos.Y = outy;
  FillConsoleOutputAttribute(hConsoleOutput,color,1,curpos,&dlo);
#ifdef MPXPLAY_UTF8
  WriteConsoleOutputCharacterW(hConsoleOutput,&chdata,1,curpos,&dlo);
#else
  WriteConsoleOutputCharacterA(hConsoleOutput,&chdata,1,curpos,&dlo);
#endif
 }else
#endif
 {
  mpxp_uint8_t *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR;
  addr=(char *)outy;
  *((mpxp_wchar_t *)addr)=outchar;
  addr[TEXTSCREEN_BYTES_ATTRIB_POS]=color;
 }
}

unsigned int pds_textdisplay_textxy(unsigned int color,unsigned int outx,unsigned int outy,char *string_s)
{
#if defined(MPXPLAY_WIN32)
 DWORD dlen=pds_strlen(string_s);
#ifdef MPXPLAY_UTF8
 mpxp_wchar_t *u16buf=alloca((dlen+2)*sizeof(mpxp_wchar_t));
 if(!u16buf)
  return 0;
 dlen=pds_str_CP437_to_UTF16LE(u16buf,string_s,(dlen+1)*sizeof(mpxp_wchar_t));
#endif
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  DWORD dlo;
  curpos.X = outx;
  curpos.Y = outy;
  FillConsoleOutputAttribute(hConsoleOutput,color,dlen,curpos,&dlo);
#ifdef MPXPLAY_UTF8
  WriteConsoleOutputCharacterW(hConsoleOutput,u16buf,dlen,curpos,&dlo);
#else
  WriteConsoleOutputCharacterA(hConsoleOutput,string_s,dlen,curpos,&dlo);
#endif
  return dlen;
 }else
#endif
 {
#ifdef MPXPLAY_UTF8
  mpxp_wchar_t *string=u16buf;
#else
  char *string=string_s;
#endif
  mpxp_uint8_t *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR;
  addr=(mpxp_uint8_t *)outy;
  while(string[0]){
   *((mpxp_wchar_t *)addr)=*string++;
   addr[TEXTSCREEN_BYTES_ATTRIB_POS]=color;
   addr+=TEXTSCREEN_BYTES_PER_CHAR;
  }
#ifdef MPXPLAY_WIN32
  return dlen;
#else
  return (string-string_s); // lenght of string
#endif
 }
}

// cut string or fill end with c by len
unsigned int pds_textdisplay_utf8_textxync(unsigned int color,unsigned int outx,unsigned int outy,char *string_s,unsigned int len,char c)
{
 mpxp_wchar_t *buf,wc=pds_cvchar_CP437_to_UTF16LE(c);
 long dlen;
#ifdef MPXPLAY_UTF8
 if(!len){
  dlen=pds_strlen(string_s);
  if(!dlen)
   return 0;
  len=dlen;
 }
 buf=alloca((len+2)*sizeof(mpxp_wchar_t));
 if(!buf)
  return 0;
 dlen=pds_str_UTF8_to_UTF16LE(buf,string_s,(len+1)*sizeof(mpxp_wchar_t));
 if(!dlen)
  return 0;
 if(wc){
  while(dlen<len)
   buf[dlen++]=wc;
  buf[dlen]=0;
 }
#else
 dlen=pds_strlen(string_s);
 if(wc && (dlen<len)){
  buf=alloca((len+2)*sizeof(mpxp_wchar_t));
  if(!buf)
   return 0;
  pds_strcpy(buf,string_s);
  while(dlen<len)
   buf[dlen++]=wc;
  buf[dlen]=0;
 }else{
  if(!len || (len>dlen))
   len=dlen;
  else
   dlen=len;
  buf=string_s;
 }
#endif
#ifdef MPXPLAY_WIN32
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  DWORD dlo;
  curpos.X = outx;
  curpos.Y = outy;
  FillConsoleOutputAttribute(hConsoleOutput,color,dlen,curpos,&dlo);
#ifdef MPXPLAY_UTF8
  WriteConsoleOutputCharacterW(hConsoleOutput,buf,dlen,curpos,&dlo);
#else
  WriteConsoleOutputCharacterA(hConsoleOutput,buf,dlen,curpos,&dlo);
#endif
 }else
#endif
 {
  mpxp_uint8_t *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR;
  addr=(mpxp_uint8_t *)outy;
  do{
   *((mpxp_wchar_t *)addr)=*buf++;
   addr[TEXTSCREEN_BYTES_ATTRIB_POS]=color;
   addr+=TEXTSCREEN_BYTES_PER_CHAR;
  }while(--len);
 }
 return dlen;
}

void pds_textdisplay_wchar_textxyan(unsigned int x,unsigned int y,mpxp_wchar_t *text,unsigned short *attribs,unsigned int len)
{
 if(!len)
  return;
#ifdef MPXPLAY_WIN32
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  DWORD dlo;
  curpos.X = x;
  curpos.Y = y;
  WriteConsoleOutputAttribute(hConsoleOutput,attribs,(DWORD)len,curpos,&dlo);
#ifdef MPXPLAY_UTF8
  WriteConsoleOutputCharacterW(hConsoleOutput,text,(DWORD)len,curpos,&dlo);
#else
  WriteConsoleOutputCharacterA(hConsoleOutput,text,(DWORD)len,curpos,&dlo);
#endif
 }else
#endif
 {
  char *addr;
  y*=textscreen_maxx;
  y+=x;
  y*=TEXTSCREEN_BYTES_PER_CHAR;
  y+=TEXTSCREEN_LIN_ADDR;
  addr=(char *)y;
  do{
   *((mpxp_wchar_t *)addr)=*text++;
   addr[TEXTSCREEN_BYTES_ATTRIB_POS]=*attribs++;
   addr+=TEXTSCREEN_BYTES_PER_CHAR;
  }while(--len);
 }
}

void pds_textdisplay_clrscr(void)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos={0,0};
  DWORD dlo;
  FillConsoleOutputAttribute(hConsoleOutput,(CLB_BASE<<4)|(CL_BASE<<0),textscreen_size,curpos,&dlo);
  FillConsoleOutputCharacterA(hConsoleOutput,(char)' ',textscreen_size,curpos,&dlo);
 }else
#endif
 {
#ifdef MPXPLAY_UTF8
  const unsigned int cval=(CL_BASE<<16)|(CLB_BASE<<20);
#else
  const unsigned int cval=(CL_BASE<<24)|(CLB_BASE<<28)|(CL_BASE<<8)|(CLB_BASE<<12);//0x07000700;
#endif
  register unsigned int *addr=(unsigned int *)TEXTSCREEN_LIN_ADDR;
  register unsigned int len=textscreen_memory_size/sizeof(unsigned int);
  do{
   *addr++=cval;
  }while(--len);
 }
}

void pds_textdisplay_scrollup(unsigned int num)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  SMALL_RECT lpScrollRectangle;
  COORD dwDestinationOrigin;
  CHAR_INFO lpFill={' ',(CLB_BASE<<4)|(CL_BASE<<0)};
  dwDestinationOrigin.X = 0;
  dwDestinationOrigin.Y = 0;
  lpScrollRectangle.Left=0;
  lpScrollRectangle.Top=num;
  lpScrollRectangle.Right=textscreen_maxx;
  lpScrollRectangle.Bottom=textscreen_maxy;
  ScrollConsoleScreenBufferA(hConsoleOutput,&lpScrollRectangle,NULL,dwDestinationOrigin,&lpFill);
 }else
#endif
 {
#ifdef MPXPLAY_UTF8
  const unsigned int cval=(CL_BASE<<16)|(CLB_BASE<<20);
#else
  const unsigned int cval=(CL_BASE<<24)|(CLB_BASE<<28)|(CL_BASE<<8)|(CLB_BASE<<12);//0x07000700;
#endif
  register unsigned int *addr=(unsigned int *)TEXTSCREEN_LIN_ADDR,*saveaddr=addr;
  unsigned int i,linesize=textscreen_maxx*TEXTSCREEN_BYTES_PER_CHAR/sizeof(unsigned int);

  for(;num;num--){
   addr=saveaddr;
   i=linesize*(textscreen_maxy-1);
   for(;i;i--){
    addr[0]=addr[linesize];
    addr++;
   }
   i=linesize;
   for(;i;i--)
    *addr++=cval;
  }
 }
}

void pds_textdisplay_printf(char *outtext)
{
 unsigned int len=pds_utf8_strlen(outtext);
 do{
  pds_textdisplay_utf8_textxync(7,0,oldposrow,outtext,textscreen_maxx,0x20);
  if(oldposrow>=(textscreen_maxy-1))
   pds_textdisplay_scrollup(1);
  else{
   oldposrow++;
   pds_textdisplay_setcursor_position(0,oldposrow);
  }
  if(len<=textscreen_maxx)
   break;
  len-=textscreen_maxx;
  outtext+=pds_utf8_strpos(outtext,textscreen_maxx);
 }while(1);
}

unsigned int pds_textdisplay_getbkcolorxy(unsigned int outx,unsigned int outy)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  WORD color;
  DWORD dlo;
  curpos.X = outx;
  curpos.Y = outy;
  ReadConsoleOutputAttribute(hConsoleOutput,&color,1,curpos,&dlo);
  return (unsigned int)(color>>4);
 }else
#endif
 {
  char *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR+TEXTSCREEN_BYTES_ATTRIB_POS;
  addr=(char *)outy;
  return(addr[0]>>4);
 }
}

unsigned int pds_textdisplay_lowlevel_getbkcolorxy(unsigned int outx,unsigned int outy)
{
#ifdef __DOS__
 char *addr;
 outy*=textscreen_maxx;
 outy+=outx;
 outy*=TEXTSCREEN_BYTES_PER_CHAR;
 outy+=TEXTSCREEN_BASE_ADDR+TEXTSCREEN_BYTES_ATTRIB_POS;
 addr=(char *)outy;
 return(addr[0]>>4);
#else
 return 0;
#endif
}

void pds_textdisplay_setcolorxy(unsigned int color,unsigned int outx,unsigned int outy)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  DWORD dlo;
  curpos.X = outx;
  curpos.Y = outy;
  FillConsoleOutputAttribute(hConsoleOutput,color,1,curpos,&dlo);
 }else
#endif
 {
  unsigned char *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR+TEXTSCREEN_BYTES_ATTRIB_POS;
  addr=(char *)outy;
  addr[0]&=0xf0;
  addr[0]|=color;
 }
}

void pds_textdisplay_setbkcolorxy(unsigned int bkcolor,unsigned int outx,unsigned int outy)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  WORD color;
  DWORD dlo;
  curpos.X = outx;
  curpos.Y = outy;
  ReadConsoleOutputAttribute(hConsoleOutput,&color,1,curpos,&dlo);
  color&=0x0f;
  color|=(bkcolor<<4);
  FillConsoleOutputAttribute(hConsoleOutput,color,1,curpos,&dlo);
 }else
#endif
 {
  unsigned char *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR+TEXTSCREEN_BYTES_ATTRIB_POS;
  addr=(char *)outy;
  addr[0]&=0x0f;
  addr[0]|=(bkcolor<<4);
 }
}

void pds_textdisplay_lowlevel_setbkcolorxy(unsigned int bkcolor,unsigned int outx,unsigned int outy)
{
#ifdef __DOS__
 unsigned char *addr;
 outy*=textscreen_maxx;
 outy+=outx;
 outy*=TEXTSCREEN_BYTES_PER_CHAR;
 outy+=TEXTSCREEN_BASE_ADDR+TEXTSCREEN_BYTES_ATTRIB_POS;
 addr=(char *)outy;
 addr[0]&=0x0f;
 addr[0]|=(bkcolor<<4);
#endif
}

void pds_textdisplay_spacecxyn(unsigned int bkcolor,unsigned int x,unsigned int y,unsigned int len)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  COORD curpos;
  DWORD dlo;
  curpos.X=x;
  curpos.Y=y;
  FillConsoleOutputAttribute(hConsoleOutput,((bkcolor<<4)|CL_BASE),len,curpos,&dlo);
  FillConsoleOutputCharacterA(hConsoleOutput,(char)' ',len,curpos,&dlo);
 }else
#endif
 {
  bkcolor<<=4;
  bkcolor|=CL_BASE;
  for(;len;len--)
   pds_textdisplay_charxy(bkcolor,x++,y,32);
 }
}

#ifdef MPXPLAY_UTF8
void pds_textdisplay_char2buf(unsigned int color,mpxp_textbuf_t *bufptr,char c)
{
 mpxp_wchar_t wc=pds_cvchar_CP437_to_UTF16LE(c);
 *bufptr=((mpxp_textbuf_t)wc)|(color<<16);
}
#endif

unsigned int pds_textdisplay_utf8_text2bufn(unsigned int color,mpxp_textbuf_t *destbuf,char *string_s,unsigned int maxlen)
{
 unsigned int dlen=pds_strlen(string_s);
 mpxp_wchar_t *srcbuf;
 if(!dlen)
  return 0;
 if(!maxlen)
  maxlen=dlen;
#ifdef MPXPLAY_UTF8
 srcbuf=alloca((maxlen+2)*sizeof(mpxp_wchar_t));
 if(!srcbuf)
  return 0;
 dlen=pds_str_UTF8_to_UTF16LE(srcbuf,string_s,(maxlen+1)*sizeof(mpxp_wchar_t));
#else
 srcbuf=string_s;
#endif
 do{
#ifdef MPXPLAY_UTF8
  *destbuf=(srcbuf[0])|(color<<16);
#else
  *destbuf=(srcbuf[0])|(color<<8);
#endif
  destbuf++;srcbuf++;maxlen--;
 }while(srcbuf[0] && maxlen);
 destbuf[0]=0;
 return dlen;
}

unsigned int pds_textdisplay_text2buf(unsigned int color,mpxp_textbuf_t *buf,char *string_s,unsigned int len)
{
 char *string=string_s;
 if(!buf || !len || !string || !string[0])
  return 0;
 do{
#ifdef MPXPLAY_UTF8
  mpxp_wchar_t wc=pds_cvchar_CP437_to_UTF16LE(string[0]);
  *buf=((mpxp_textbuf_t)wc)|(color<<16);
#else
  *buf=((unsigned short)string[0])|((unsigned short)color<<8);
#endif
  buf++;string++;len--;
 }while(string[0] && len);
 buf[0]=0x0000;
 return (string-string_s);
}

unsigned int pds_textdisplay_text2field(unsigned int color,mpxp_textbuf_t *buf,char *string_s)
{
 char *string=string_s;
 if(!buf || !string || !string[0])
  return 0;
 do{
#ifdef MPXPLAY_UTF8
  mpxp_wchar_t wc=pds_cvchar_CP437_to_UTF16LE(string[0]);
  *buf=((mpxp_textbuf_t)wc)|(color<<16);
#else
  buf[0]=((unsigned short)string[0])|((unsigned short)color<<8);
#endif
  buf++;string++;
 }while(string[0]);
 return (string-string_s);
}

// cut string or fill end with c by len, negative maxlen -> use end of string, !destbuf -> length only
unsigned int pds_textdisplay_utf8_text2fieldnn(unsigned int color,mpxp_textbuf_t *destbuf,char *string_s,unsigned int minlen,int maxlen)
{
 unsigned long dlen=pds_strlen(string_s),endcut,alllen;
 mpxp_wchar_t *srcbuf;
 if(!dlen)
  return 0;
 if(maxlen<0){
  maxlen=-maxlen;
  endcut=1;
 }else{
  if(maxlen==0)
   maxlen=dlen;
  endcut=0;
 }
 if(minlen>maxlen)
  minlen=maxlen;
 alllen=max(dlen,maxlen);
 srcbuf=alloca((alllen+2)*sizeof(mpxp_wchar_t));
 if(!srcbuf)
  return 0;
#ifdef MPXPLAY_UTF8
 dlen=pds_str_UTF8_to_UTF16LE(srcbuf,string_s,(alllen+1)*sizeof(mpxp_wchar_t));
#else
 dlen=pds_strncpy(srcbuf,string_s,alllen);
#endif
 while(dlen<minlen)
  srcbuf[dlen++]=0x20; // space
 if(dlen>maxlen){
  if(endcut)
   srcbuf+=(dlen-maxlen);
  dlen=maxlen;
 }else
  maxlen=dlen;
 if(destbuf){
  do{
#ifdef MPXPLAY_UTF8
   *destbuf=(srcbuf[0])|(color<<16);
#else
   *destbuf=(srcbuf[0])|(color<<8);
#endif
   destbuf++;srcbuf++;
  }while(--maxlen);
 }
 return dlen;
}

void pds_textdisplay_textbufxyn(unsigned int outx,unsigned int outy,mpxp_textbuf_t *buf,unsigned int maxlen)
{
#if defined(MPXPLAY_WIN32)
 if(textscreen_linear_address==TEXTSCREEN_BASE_ADDR){
  unsigned int i;
  unsigned short *abuf;
  mpxp_wchar_t *cbuf;
  abuf=(unsigned short *)alloca(maxlen*sizeof(*abuf));
  if(!abuf)
   return;
  cbuf=(mpxp_wchar_t *)alloca(maxlen*sizeof(*cbuf));
  if(!cbuf)
   return;
  for(i=0;i<maxlen;i++){
#ifdef MPXPLAY_UTF8
   abuf[i]=buf[0]>>16;
   cbuf[i]=buf[0]&0xffff;
#else
   abuf[i]=buf[0]>>8;
   cbuf[i]=buf[0]&0xff;
#endif
   buf++;
  }
  pds_textdisplay_wchar_textxyan(outx,outy,cbuf,abuf,maxlen);
 }else
#endif
 {
  mpxp_textbuf_t *addr;
  outy*=textscreen_maxx;
  outy+=outx;
  outy*=TEXTSCREEN_BYTES_PER_CHAR;
  outy+=TEXTSCREEN_LIN_ADDR;
  addr=(mpxp_textbuf_t *)outy;
  while(maxlen){
   addr[0]=buf[0];
   addr++;buf++;maxlen--;
  }
 }
}

//-------------------------------------------------------------------------
#ifdef MPXPLAY_WIN32
void pds_textdisplay_consolevidmem_merge(unsigned short *attribs,mpxp_wchar_t *text,char *destbuf,unsigned int bufsize)
{
 if(!attribs || !text || !destbuf || !bufsize)
  return;
 bufsize-=bufsize%TEXTSCREEN_BYTES_PER_CHAR;
 do{
  *((mpxp_wchar_t *)destbuf)=*text++;
  destbuf[TEXTSCREEN_BYTES_ATTRIB_POS]=*attribs++;
  destbuf+=TEXTSCREEN_BYTES_PER_CHAR;
  bufsize-=TEXTSCREEN_BYTES_PER_CHAR;
 }while(bufsize);
}

void pds_textdisplay_consolevidmem_read(unsigned short *attribs,mpxp_wchar_t *text,unsigned int maxsize)
{
 COORD curpos;
 DWORD dlen,dlo;
 curpos.X = 0;
 curpos.Y = 0;
 dlen=min(maxsize,textscreen_size);
 ReadConsoleOutputAttribute(hConsoleOutput,attribs,dlen,curpos,&dlo);
#ifdef MPXPLAY_UTF8
 ReadConsoleOutputCharacterW(hConsoleOutput,text,dlen,curpos,&dlo);
#else
 ReadConsoleOutputCharacterA(hConsoleOutput,text,dlen,curpos,&dlo);
#endif
}

void pds_textdisplay_vidmem_save(void)
{
 unsigned int len;

 newfunc_textdisplay_init();

 len=min(textscreen_size,consolesave_size);
 if(!len)
  return;

 pds_textdisplay_consolevidmem_read(consolesave_attrib,consolesave_text,consolesave_size);

 textscreen_oldmaxy=textscreen_maxy;
 textscreen_oldcursor_y=pds_textdisplay_getcursor_y();
}

#else

void pds_textdisplay_vidmem_save(void)
{
 unsigned int len;
 unsigned int *addr_scr=(unsigned int *)TEXTSCREEN_LIN_ADDR;
 unsigned int *addr_sav=vidmemsave_field;

 newfunc_textdisplay_init();

 len=min(textscreen_memory_size,vidmemsave_size);
 if(!len)
  return;

 pds_qmemcpy(addr_sav,addr_scr,len/4);

 textscreen_oldmaxy=textscreen_maxy;
 textscreen_oldcursor_y=pds_textdisplay_getcursor_y();
}

#endif

#ifdef MPXPLAY_WIN32
void pds_textdisplay_consolevidmem_separate(unsigned short *attribs,mpxp_wchar_t *text,char *srcbuf,unsigned int bufsize)
{
 if(!attribs || !text || !srcbuf || !bufsize)
  return;
 bufsize-=bufsize%TEXTSCREEN_BYTES_PER_CHAR;
 do{
  *text++=*((mpxp_wchar_t *)srcbuf);
  *attribs++=*((mpxp_uint8_t *)&srcbuf[TEXTSCREEN_BYTES_ATTRIB_POS]);
  srcbuf+=TEXTSCREEN_BYTES_PER_CHAR;
  bufsize-=TEXTSCREEN_BYTES_PER_CHAR;
 }while(bufsize);
}

void pds_textdisplay_consolevidmem_write(unsigned short *attribs,mpxp_wchar_t *text,unsigned int maxsize)
{
 COORD curpos;
 DWORD dlen,dlo;
 curpos.X = 0;
 curpos.Y = 0;
 dlen=min(maxsize,textscreen_size);
 WriteConsoleOutputAttribute(hConsoleOutput,attribs,textscreen_size,curpos,&dlo);
#ifdef MPXPLAY_UTF8
 WriteConsoleOutputCharacterW(hConsoleOutput,text,textscreen_size,curpos,&dlo);
#else
 WriteConsoleOutputCharacterA(hConsoleOutput,text,textscreen_size,curpos,&dlo);
#endif
}

void pds_textdisplay_consolevidmem_xywrite(unsigned int x,unsigned int y,unsigned short *attribs,mpxp_wchar_t *text,unsigned int len)
{
 COORD curpos;
 DWORD dlo;
 curpos.X = x;
 curpos.Y = y;
 WriteConsoleOutputAttribute(hConsoleOutput,attribs,(DWORD)len,curpos,&dlo);
#ifdef MPXPLAY_UTF8
 WriteConsoleOutputCharacterW(hConsoleOutput,text,(DWORD)len,curpos,&dlo);
#else
 WriteConsoleOutputCharacterA(hConsoleOutput,text,(DWORD)len,curpos,&dlo);
#endif
}

void pds_textdisplay_vidmem_restore(void)
{
 unsigned int len;

 pds_textdisplay_setresolution(textscreen_oldmaxy+1);

 len=min(textscreen_size,consolesave_size);
 if(!len)
  return;

 pds_textdisplay_consolevidmem_write(consolesave_attrib,consolesave_text,consolesave_size);

 pds_textdisplay_setcursor_position(0,textscreen_oldcursor_y);
}

#else

void pds_textdisplay_vidmem_restore(void)
{
 unsigned int len;
 unsigned int *addr_scr=(unsigned int *)TEXTSCREEN_LIN_ADDR;
 unsigned int *addr_sav=vidmemsave_field;

 pds_textdisplay_setresolution(textscreen_oldmaxy+1);

 len=min(textscreen_memory_size,vidmemsave_size);
 if(!len)
  return;

 pds_qmemcpy(addr_scr,addr_sav,len/4);

 pds_textdisplay_setcursor_position(0,textscreen_oldcursor_y);
}
#endif

//---------------------------------------------------------------------
//bios callings
unsigned int pds_textdisplay_setmode(unsigned int mode)
{
#if defined(MPXPLAY_WIN32)
 //SetConsoleMode(hConsoleOutput,mode);
#elif defined(__DOS__)
 union REGS regs;
 pds_newfunc_regs_clear(&regs);
 regs.w.ax=mode;
 int386(0x10, &regs, &regs);
#endif
 return 1;
}

unsigned int pds_textdisplay_getmode(void)
{
#if defined(MPXPLAY_WIN32)
 //DWORD mode;
 //GetConsoleMode(hConsoleOutput,&mode);
 //return mode;
 return 0;
#elif defined(__DOS__)
 union REGS regs;
 pds_newfunc_regs_clear(&regs);
 regs.w.ax=0x0f00;
 int386(0x10, &regs, &regs);
 return (regs.w.ax&0xff);
#else
 return 0;
#endif
}

unsigned int pds_textdisplay_setlastmode(void)
{
 return pds_textdisplay_setmode(textscreen_mode);
}

void asmset50lines(void);

static void pds_textdisplay_set50lines(void)
{
#ifdef MPXPLAY_WIN32
 COORD size;
 size.X=80;
 size.Y=50;
 //#if (_WIN32_WINNT >= 0x0501)
 //SetConsoleDisplayMode(hConsoleOutput,0x03,&size);
 //#else
 SetConsoleScreenBufferSize(hConsoleOutput,size);
 //#endif
#elif defined(__DOS__)
 #if defined(NEWFUNC_ASM) && defined(__WATCOMC__)
  #pragma aux asmset50lines=\
  "mov ax,1112h"\
  "xor ebx,ebx"\
  "int 10h"\
  modify[eax ebx ecx edx edi esi];
  asmset50lines();
 #else
  union REGS regs;
  pds_newfunc_regs_clear(&regs);
  regs.w.ax=0x1112;
  regs.w.bx=0;
  int386(0x10, &regs, &regs);
 #endif
 //redefine_fonts_8x8();
#endif
}

void asmset25lines(void);

static void pds_textdisplay_set25lines(void)
{
#ifdef MPXPLAY_WIN32
 COORD size;
 size.X=80;
 size.Y=25;
 //#if (_WIN32_WINNT >= 0x0501)
 //SetConsoleDisplayMode(hConsoleOutput,0x03,&size);
 //#else
 SetConsoleScreenBufferSize(hConsoleOutput,size);
 //#endif
#elif defined(__DOS__)
 #if defined(NEWFUNC_ASM) && defined(__WATCOMC__)
  #pragma aux asmset25lines=\
  "mov ax,1114h"\
  "xor ebx,ebx"\
  "int 10h"\
  modify[eax ebx ecx edx edi esi];
  asmset25lines();
 #else
  union REGS regs;
  pds_newfunc_regs_clear(&regs);
  regs.w.ax=0x1114;
  regs.w.bx=0;
  int386(0x10, &regs, &regs);
 #endif
 //redefine_fonts_8x16();
#endif
}

void pds_textdisplay_setresolution(unsigned int lines)
{
 if(lines>=40)
  pds_textdisplay_set50lines();
 else
  pds_textdisplay_set25lines();
 newfunc_textdisplay_init();
}

void pds_textdisplay_getresolution(void)
{
#if defined(MPXPLAY_WIN32)
 CONSOLE_SCREEN_BUFFER_INFO bufinf;
 GetConsoleScreenBufferInfo(hConsoleOutput,&bufinf);
 textscreen_maxx=bufinf.dwSize.X;
 textscreen_maxy=bufinf.dwSize.Y;
#elif defined(__DOS__)
 unsigned char *biosmem=(char *)0;
 textscreen_maxx=biosmem[0x44a];
 textscreen_maxy=biosmem[0x484]+1;
#else
 textscreen_maxx=80;
 textscreen_maxy=50;
#endif
}

unsigned int pds_textdisplay_getcursor_y(void)
{
#if defined(MPXPLAY_WIN32)
 CONSOLE_SCREEN_BUFFER_INFO bufinf;
 GetConsoleScreenBufferInfo(hConsoleOutput,&bufinf);
 return bufinf.dwCursorPosition.Y;
#elif defined(__DOS__)
 unsigned char *biosmem=(char *)0;
 return biosmem[0x451];
#else
 return 0;
#endif
}

void asm_textdisplaysetcursorposition(unsigned int);

void pds_textdisplay_setcursor_position(unsigned int x,unsigned int y)
{
#if defined(MPXPLAY_WIN32)
 COORD curpos;
 curpos.X=x;
 curpos.Y=y;
 SetConsoleCursorPosition(hConsoleOutput,curpos);
#elif defined(__DOS__)
 #if defined(NEWFUNC_ASM) && defined (__WATCOMC__)
  #pragma aux asm_textdisplaysetcursorposition=\
  "mov ax,0200h"\
  "xor ebx,ebx"\
  "int 10h"\
  parm[edx] modify[eax ebx ecx edx edi esi];
  asm_textdisplaysetcursorposition((y<<8)|x);
 #else
  union REGS regs;
  pds_newfunc_regs_clear(&regs);
  regs.w.ax=0x0200;
  regs.w.dx=((y<<8)|x);
  int386(0x10,&regs,&regs);
 #endif
#endif
}

void asm_textdisplaysetcursorshape(long);

void pds_textdisplay_setcursorshape(long value)
{
#if defined(MPXPLAY_WIN32)
 CONSOLE_CURSOR_INFO curinf;
 if(value&TEXTCURSORSHAPE_HIDDEN){
  curinf.dwSize=8;
  curinf.bVisible=0;
 }else{
  curinf.dwSize=(value==TEXTCURSORSHAPE_FULLBOX)? 96:8;
  curinf.bVisible=1;
 }
 SetConsoleCursorInfo(hConsoleOutput,&curinf);
#elif defined(__DOS__)
 #if defined(NEWFUNC_ASM) && defined (__WATCOMC__)
  #pragma aux asm_textdisplaysetcursorshape=\
  "mov ecx,eax"\
  "mov ax,0100h"\
  "int 10h"\
  parm[eax] modify[eax ebx ecx edx edi esi];
  asm_textdisplaysetcursorshape(value);
 #else
  union REGS regs;
  pds_newfunc_regs_clear(&regs);
  regs.w.ax=0x0100;
  regs.w.cx=value;
  int386(0x10,&regs,&regs);
 #endif
#endif
}

void pds_textdisplay_resetcolorpalette(void)
{
#ifdef __DOS__
 outp(0x03c8,5);  // magenta
 outp(0x03c9,0x2a);
 outp(0x03c9,0);
 outp(0x03c9,0x2a);
#endif
}
