
#include "stdafx.h"

#ifdef _MSVC_INTEL // Windows screen handling


#include "UI.h"

#include <stdio.h>

#include "Objects.h"
#include "Dispatch.h"
#include "CSB.h"
#include "data.h"

extern i32 WindowX;
extern i32 WindowY;
extern i32 VBLMultiplier;
extern i16 globalPalette[16];
extern bool fullscreenRequested;
extern bool fullscreenActive;
//extern CDC *OnDrawDC;
ui32 STBLTCount=0;

i32 screenSize=2;
i16 palette16[16];
i16 bitmap[320*4*200*4];
i16 counter;
i16 bitSkip;
i32 dstLineLen;
BITMAPINFO bitmapInfo;
i16 palette1[16];
i16 palette2[16];
i16 oldPalette1[16];
i16 oldPalette2[16];
pnt logbase(void);

bool HasPaletteChanged(i16 *palette, i16 *oldpalette)
{
  bool change=false;
  for (i32 i=0; i<16; i++)
  {
    if (oldpalette[i] != palette[i])
    {
      oldpalette[i]=palette[i];
      change=true;
    };
  };
  return change;
}


bool HasScreenChanged(i8 *STScreen,    // First byte to test
                      i32 lineLen,     // quadwords
                      i32 numLine,
                      i32 lineIncrement,
                      i32 *pOldChecksum)
{
  bool result;
  __asm
  {
    sub edx,edx   // The summation
    sub ebx,ebx
    mov esi, STScreen
    mov edi,numLine //
do1line:
    mov ecx,lineLen
    cmp ecx,4
do16Bytes:
    jc do4Bytes
    lodsd
    add edx,eax
    lodsd
    add ebx,eax
    lodsd
    add edx,eax
    lodsd
    add ebx,eax
    sub ecx,4
    cmp ecx,4
    jnc do16Bytes
    or  cx,cx
    jz  lineDone
do4Bytes:
    rol edx,1
    lodsd
    //add ebx,eax
    //ror ebx,1
    add edx,eax
    loop do4Bytes
lineDone:
    or  edx,edx //clear carry
    rcl edx,1
    jnc edxzero
    xor edx,0x8d
edxzero:
    or  ebx,ebx //clear carry
    rcl ebx,1
    jnc ebxzero
    xor ebx,0x8d
ebxzero:
    add esi,lineIncrement
    dec edi
    jnz do1line
    rol ebx,1
    add edx,ebx
    mov esi,pOldChecksum
    mov result,0
    cmp edx,[esi]
    jz  ret0
    mov result,1
ret0:
    mov [esi],edx
  };
  return result;
}


/*
void createPalette32(i16 *palette)
{ // Create 32-bit color palette from the ST 9-bit palette
  for (i32 i=0; i<16; i++)
  {
    ASSERT(palette[i]<=0x777);
    palette32[i]  = (((palette[i]>>8) & 7)*0xff/0x7)<<16;
    palette32[i] |= (((palette[i]>>4) & 7)*0xff/0x7)<< 8;
    palette32[i] |= (((palette[i]>>0) & 7)*0xff/0x7)<< 0;
  };
}
*/

void createPalette16(i16 *palette)
{ // Create 16-bit color palette from the ST 9-bit palette
  for (i32 i=0; i<16; i++)
  {
    ASSERT(palette[i]<=0x777);
    palette16[i]=0;
    palette16[i] |= (((palette[i]>>8) & 7)*0x1f/0x7)<<10;//red
    palette16[i] |= (((palette[i]>>4) & 7)*0x1f/0x7)<< 5;//green
    palette16[i] |= (((palette[i]>>0) & 7)*0x1f/0x7)<< 0;//blue
  };
}


bool ForcedScreenDraw = false;

void ForceScreenDraw(void)
{
  ForcedScreenDraw = true;
}

void Unpack(char *src, unsigned char *dst, i32 shift, i32 count)
{
  _asm
  {
    mov esi,src;
    mov ax,[esi+6]
    mov bx,[esi+4]
    mov cx,[esi+2]
    mov dx,[esi+0]
    rol ax,8
    rol bx,8
    rol cx,8
    rol dx,8
    rol ecx,16
    mov esi,ecx
    mov ecx,shift
    rol ax,cl
    rol bx,cl
    rol esi,cl
    rol dx,cl
    mov edi,dst
next:
    sub ecx,ecx
    rcl ax,1
    rcl ecx,1
    rcl bx,1
    rcl ecx,1
    rcl esi,1
    rcl ecx,1
    rcl dx,1
    rcl ecx,1
    mov [edi],cl
    inc edi
    dec count
    jnz next
  };
}

void BLT1(unsigned char *src, i16 *dst, i32 num)
{
  _asm
  {
    mov esi,src
    mov edi,dst
    mov ecx,num
    sub ebx,ebx
next:
    mov bl,[esi]
    inc esi
    mov ax,[palette16+ebx*2]
    mov [edi],ax
    add edi,2
    loop next
  };
}

void BLT2(unsigned char *src, i16 *dst, i32 num)
{
  _asm
  {
    mov esi,src
    mov edi,dst
    mov ecx,num
    sub ebx,ebx
next:
    mov bl,[esi]
    inc esi
    mov ax,[palette16+ebx*2]
    mov [edi],ax
    mov [edi+320*2*4],ax
    add edi,2
    mov [edi],ax
    mov [edi+320*2*4],ax
    add edi,2
    loop next
  };
}

void BLT3(unsigned char *src, i16 *dst, i32 num)
{
  _asm
  {
    mov esi,src
    mov edi,dst
    mov ecx,num
    sub ebx,ebx
next:
    mov bl,[esi]
    inc esi
    mov ax,[palette16+ebx*2]
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    add edi,2
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    add edi,2
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    add edi,2
    loop next
  };
}

void BLT4(unsigned char *src, i16 *dst, i32 num)
{
  _asm
  {
    mov esi,src
    mov edi,dst
    mov ecx,num
    sub ebx,ebx
next:
    mov bl,[esi]
    inc esi
    mov ax,[palette16+ebx*2]
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    mov [edi+3*320*2*4],ax
    add edi,2
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    mov [edi+3*320*2*4],ax
    add edi,2
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    mov [edi+3*320*2*4],ax
    add edi,2
    mov [edi],ax
    mov [edi+1*320*2*4],ax
    mov [edi+2*320*2*4],ax
    mov [edi+3*320*2*4],ax
    add edi,2
    loop next
  };
}

void UpdateScreenArea(
                      i8  *STScreen,
                      i32  x0,
                      i32  y0,
                      i32  width,
                      i32  height,
                      i32  dstX,
                      i32  dstY,
                      i16 *palette,
                      bool paletteChanged,
                      i32 *pOldChecksum,
                      i32  size)
{
  i32 LineEnd   = (((x0&0xff0)+width+15)/16) * 8;
  i32 LineStart = (x0/16) * 8;
  unsigned char nibbles[320];
  unsigned char *pNibbles = nibbles;
  i32 line;
  i32 numPixel, skipPixel, n;
  char *pFirstGroup;
  if (!paletteChanged)
  {
    if (!HasScreenChanged(STScreen+160*y0+LineStart,
                          (LineEnd-LineStart) / 4,
                          height,
                          160-(LineEnd-LineStart),
                          pOldChecksum)) return;
  };
  createPalette16(palette);
  STBLTCount++;

  
  for (line=0; line<height; line++)
  {
    pFirstGroup = STScreen + 160*(y0+line) + LineStart;
    numPixel = width;
    skipPixel = x0 & 15;
    pNibbles = nibbles;
    if (skipPixel != 0)
    {
      n = 16 - skipPixel;
      if (n > numPixel) n = numPixel;
      Unpack(pFirstGroup, pNibbles, skipPixel, n);
      pNibbles += n;
      numPixel -= n;
      pFirstGroup += 8;
    };
    while (numPixel > 0)
    {
      n = 16;
      if (n > numPixel) n = numPixel;
      Unpack(pFirstGroup, pNibbles, 0, n);
      pNibbles += n;
      numPixel -= n;
      pFirstGroup += 8;
    };
    switch (size)
    {
    case 1:
      BLT1(nibbles, 
           bitmap + 1*320*4*line, 
           width);
      break;
    case 2:
      BLT2(nibbles,
           bitmap + 2*320*4*line,
           width);
      break;
    case 3:
      BLT3(nibbles,
           bitmap + 3*320*4*line,
           width);
      break;
    case 4:
      BLT4(nibbles,
           bitmap + 4*320*4*line,
           width);
      break;
    default: break;
    };
  };

  bitmapInfo.bmiHeader.biSize=0x28;
  bitmapInfo.bmiHeader.biWidth=320*4;
  bitmapInfo.bmiHeader.biHeight=-height * size;
  bitmapInfo.bmiHeader.biPlanes=1;
  bitmapInfo.bmiHeader.biBitCount=16;
  bitmapInfo.bmiHeader.biCompression=BI_RGB;
  bitmapInfo.bmiHeader.biSizeImage=0;
  bitmapInfo.bmiHeader.biXPelsPerMeter=0;
  bitmapInfo.bmiHeader.biYPelsPerMeter=0;
  bitmapInfo.bmiHeader.biClrUsed=0;
  bitmapInfo.bmiHeader.biClrImportant=0;
  UI_SetDIBitsToDevice(
                      // handle to device context
    dstX,             // x-coordinate of upper-left corner of
                      // dest. rect.
    dstY,             // y-coordinate of upper-left corner of
                      // dest. rect.
    width*size,       // source rectangle width
    height*size,      // source rectangle height
    0,                // x-coordinate of lower-left corner of
                      // source rect.
    0,                // y-coordinate of lower-left corner of
                      // source rect.
    0,                // first scan line in array
    height*size,      // number of scan lines
    (char *)bitmap,   // address of array with DIB bits
    &bitmapInfo,      // address of structure with bitmap info.
    DIB_RGB_COLORS    // RGB or palette indexes
    );
};




bool pc1, pc2;
void display (void){
  static i32 oldChecksumA;
  static i32 oldChecksumB;
  static i32 oldChecksumC;
  static i32 oldChecksumD;
  static i32 numDisplay = 0;
  numDisplay++;
  if ((VBLMultiplier!=1) && ((d.Time&0xf)!=0) && (VBLMultiplier!=99)) return;
  if (d.DynamicPaletteSwitching)
  {
    memcpy(palette1,&d.Palette11978, 32);
    memcpy(palette2,&d.Palette11946, 32);
    memcpy(globalPalette,&d.Palette11978,32);
  }
  else
  {
    memcpy(palette1,globalPalette, 32);
    memcpy(palette2,globalPalette, 32);
  };
  pc1 = HasPaletteChanged(palette1, oldPalette1);
  pc2 = HasPaletteChanged(palette2, oldPalette2);
  pc1 = pc1 || ForcedScreenDraw;
  pc2 = pc2 || ForcedScreenDraw;
  ForcedScreenDraw = false;
  {
    if (!fullscreenActive)
    {
      i32 size = screenSize;
      UpdateScreenArea(         //Viewport
                       physbase(),//STScreen,
                       0,
                       0x21,
                       0xe0,
                       0xa9-0x21,
                       0*size,
                       0x21*size,
                       palette2,
                       pc2,
                       &oldChecksumA,
                       size);
      UpdateScreenArea(   //Text scrolling area
                       physbase(),//STScreen,
                       0,
                       0xa9,
                       320,
                       0xc8-0xa9,
                       0*size,
                       0xa9*size,
                       palette1,
                       pc1,
                       &oldChecksumB,
                       size);
      UpdateScreenArea(             //portrait area
                       physbase(),//STScreen,
                       0,
                       0,
                       320,
                       0x21,
                       0*size,
                       0*size,
                       palette1,
                       pc1,
                       &oldChecksumC,
                       size);
      UpdateScreenArea(              //spells,weapons,moves
                       physbase(),//STScreen,
                       0xe0,
                       0x21,
                       0x140-0xe0,
                       0xa9-0x21,
                       0xe0*size,
                       0x21*size,
                       palette2,
                       pc2,
                       &oldChecksumD,
                       size);
    }
    else
    {
      if (videoSegSize[0] != 0)
      {
        UpdateScreenArea(         //Viewport
                         physbase(),//STScreen,
                         videoSegSrcX[0],
                         videoSegSrcY[0],
                         videoSegWidth[0],
                         videoSegHeight[0],
                         videoSegX[0],
                         videoSegY[0],
                         palette2,
                         pc2,
                         &oldChecksumA,
                         videoSegSize[0]);
      };
      if (videoSegSize[1] != 0)
      {
        UpdateScreenArea(             //portrait area
                         physbase(),//STScreen,
                         videoSegSrcX[1],
                         videoSegSrcY[1],
                         videoSegWidth[1],
                         videoSegHeight[1],
                         videoSegX[1],
                         videoSegY[1],
                         palette1,
                         pc1,
                         &oldChecksumC,
                         videoSegSize[1]);
      };
      if (videoSegSize[2] != 0)
      {
        UpdateScreenArea(              //spells,weapons,moves
                         physbase(),//STScreen,
                         videoSegSrcX[2],
                         videoSegSrcY[2],
                         videoSegWidth[2],
                         videoSegHeight[2],
                         videoSegX[2],
                         videoSegY[2],
                         palette2,
                         pc2,
                         &oldChecksumD,
                         videoSegSize[2]);
      };
      if (videoSegSize[3] != 0)
      {
        UpdateScreenArea(   //Text scrolling area
                         physbase(),//STScreen,
                         videoSegSrcX[3],
                         videoSegSrcY[3],
                         videoSegWidth[3],
                         videoSegHeight[3],
                         videoSegX[3],
                         videoSegY[3],
                         palette1,
                         pc1,
                         &oldChecksumB,
                         videoSegSize[3]);
      };
    }
  };
}

#endif