
#include "stdafx.h"

#ifdef _MSVC_CE2002ARM // Pocket PC video 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;

struct MAPPING
{
  short srcx,  srcy,
        width, height,
        dstx,  dsty,
        copymode,
        palette;
  unsigned int checksum;
  unsigned int firstGroupOffset;  
      //The i16 index of the first group.                            
  unsigned int preMask, numWholeGroup, postMask;
      //These are the masks used when computing hashcodes (or checksums).
      //If the first pixel is on a 16-bit boundary then preMask will be 0.
      //Likewise, if the last pixel is at the end of a group, postMask will be 0.
      // AND if premask contains all the bits then postMask will be zero.
      //numWholeGroup is the number of groups of 16 pixels.
      //
      // preMask and postMask are 32 bits so that two 'planes' can be
      // processed simultaneously.
  void CreateChecksumMasks(void);
};



void MAPPING::CreateChecksumMasks()
{
  // Fill in the firstGoupOffset,
  //             preMask, post Mask,
  //             numWholeGroup
  //
  int numBits, firstGroupOnLine, firstBitInFirstGroup;
  firstGroupOffset = srcy*320/16*4; // Four groups of i16 per 16 bits.
  firstGroupOnLine = srcx/16*4;
  firstGroupOffset += firstGroupOnLine;
  firstBitInFirstGroup = srcx%16;
  preMask = 0;
  postMask = 0;
  numWholeGroup = 0;
  numBits = width;
  if (firstBitInFirstGroup != 0)
  {
    preMask = 0xffff >> firstBitInFirstGroup;
    preMask = ((preMask>>8) | (preMask<<8)) & 0xffff;
    preMask |= preMask <<16;
    numBits -= 16 - firstBitInFirstGroup;
  };
  if (numBits > 0)
  { // There are more bits than will fit in preMask.
    numWholeGroup = numBits/16;
    numBits -= 16*numWholeGroup;
  };
  if (numBits != 0)
  { // Compute postMask.
    postMask = (0xffff0000 >> ((numBits+16)&15)) & 0xffff;
    postMask = (postMask<<8) | (postMask>>8);
    postMask |= postMask <<16;
  };
  if (numBits < 0)
  {
    preMask &= postMask;
    postMask = 0;
  };
}


int screenMapStart = 0;  // index of first map
int screenMapNum = 1;    // # of maps


MAPPING screenMap[] =
{ 
  { 0,   0,240,200,  0,  0, 0, 0},   // 0 = full screen centered
  {40,   0,240,200,  0,  0, 0, 0},   // 1 = Centered horizontal, full vertical
  {80,   0,240,200,  0,  0, 0, 0},   // 2 = Shifted Left, full vertical.  Prison door.

  {  0,  0, 64, 29,  0,  0, 1, 0}, // 3 = Portrait A
  { 69,  0, 64, 29, 60,  0, 1, 0}, // 4 = Portrait B
  {138,  0, 64, 29,120,  0, 1, 0}, // 5 = Portrait C
  {207,  0, 64, 29,180,  0, 1, 0}, // 6 = Portrait D
  {  0, 33,224,136,  8, 29, 0, 1}, // 7 = Viewport
  {233, 32, 87, 43,  0,169, 0, 1}, // 8 = Spell Controls
  {233, 77, 87, 45,102,167, 0, 0}, // 9 = Attack Controls
  {233,124, 29, 22,153,212, 0, 0}, // 10 = Turn Left
  {262,124, 28, 22,182,212, 0, 0}, // 11 = Move Forward
  {290,124, 30, 22,210,212, 0, 0}, // 12 = Turn Right
  {233,146, 29, 23,153,234, 0, 0}, // 13 = Slide Left
  {262,146, 28, 23,182,234, 0, 0}, // 14 = Move Backward
  {290,146, 30, 23,210,234, 0, 0}, // 15 = Slide Right
  {0,  169,153, 30,  0,212, 0, 0}, // 16 = Scrolling Text
  //{0,  169,240, 30,  0,0, 0, 1}, // 16 = Scrolling Text
  {275,  0, 52, 33,195,169, 0, 1}  // 17 = Marching Order
};   


void MapCoordinate(i32 a, i32 b, i32& x, i32& y)
{//Translate physical pixel units on actual screen.
 // to pixel units on Atari screen
  i32 i, area;
  for (i=0; i<screenMapNum; i++)
  {
    area = screenMapStart + i;
    if (a < screenMap[area].dstx) continue;
    if (a >= screenMap[area].dstx + screenMap[area].width) continue;
    if (b < screenMap[area].dsty)    continue;
    if (b >= screenMap[area].dsty + screenMap[area].height) continue;
    x = a - screenMap[area].dstx  + screenMap[area].srcx;
    y = b - screenMap[area].dsty  + screenMap[area].srcy;
    return;
  };
  x = 0;
  y = 200;
  return;
}

bool IsPortraitBar(int x, int y)
{
  int i;
  for (i=3; i<=6; i++)
  {
    if (   (x >= screenMap[i].srcx + 60 - 20)
        && (x <  screenMap[i].srcx + 60)
        && (y >= screenMap[i].srcy)
        && (y <  screenMap[i].srcy + screenMap[i].height)
       )
    {
      return true;
    };
  };
  return false;
}

i32 screenSize=1;
i32 palette16[16];
i16 counter;
i16 bitSkip;
i32 dstLineLen;
BITMAPINFO bitmapInfo;
i16 palette[2][16];
i16 oldPalette[2][16];
pnt logbase(void);
unsigned int nibbles[40*320];

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,
                      ui32 *pOldChecksum)
{
  unsigned int sum1=0, sum2=0;
  char *pSTScreen=STScreen;
  int numGroup;
  int lineCount = numLine;
  for (lineCount=numLine; lineCount>0; lineCount--)
  { //Do 1 Line
    for (numGroup=lineLen; numGroup>=4; numGroup-=4)
    { //Do 1 group of 16 bytes
      sum1 += ((unsigned int *)pSTScreen)[0];
      sum2 += ((unsigned int *)pSTScreen)[1];
      sum1 += ((unsigned int *)pSTScreen)[2];
      sum2 += ((unsigned int *)pSTScreen)[3];
      pSTScreen += 16;
    };
    for (;numGroup>0; numGroup--)
    {
      sum1 = (sum1<<1) + (sum1>>31);
      sum1 += ((unsigned int *)pSTScreen)[0];
      pSTScreen +=4;
    };
    sum1 = (sum1>>1) + (sum1<<31);
    pSTScreen += lineIncrement;
  }; //For each line.
  sum2 = (sum2>>1) + (sum2<<31);
  sum1 += sum2;
  if ((unsigned)(*pOldChecksum) == sum1) return false;
  *pOldChecksum = sum1;
  return true;
}
*/
/*
void createPalette32(i16 *palette, VIDEOCE *vce)
{ // 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;
  };
  vce->SetPalette(palette32);
}
*/

void createPalette16(i16 *palette, VIDEOCE *)
{ // 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;
}

unsigned int bit0Table[256];
unsigned int bit1Table[256];
unsigned int bit2Table[256];
unsigned int bit3Table[256];
bool bitTablesInitialized = false;

void BuildBitTables(void)
{
  int i, j;
  unsigned int n;
  for (i=0; i<256; i++)
  {
    bit0Table[i] = bit1Table[i] = bit2Table[i] = bit3Table[i] = 0;
    n = i;
    for (j=28; j>=0; j-=4)
    {
      if (n & 1)
      {
        bit0Table[i] |= 1<<j;
        bit1Table[i] |= 2<<j;
        bit2Table[i] |= 4<<j;
        bit3Table[i] |= 8<<j;
      };
      n >>= 1;
    };
  };
  for (i=0; i< sizeof (screenMap)/sizeof(screenMap[0]); i++)
  {
    screenMap[i].CreateChecksumMasks();
  };
  bitTablesInitialized = true;
}

unsigned int DePlane(unsigned char *src, int count, int shift, int numLines)
{
  // src points to a group of 8 bytes representing 16 pixels
  //  src is the 4-bitplane Atari format.
  // The destination is the nibbles array of unsigned int.
  // 8 pixels will be placed in the form of 32-bit integers.
  //  The first pixel is in the bottom four bits of the first integer 
  //  and the eighth pixel is in the top four bits of the first integer.
  //  The ninth is in the bottom four bits of the second integer, and so on.
  // count is the number of pixels to be stored.
  // shift is the number of pixels to skip in the first 16-pixel group.
  // Repeat for numlines.  For each line src+=160, dst+=40
  // Result is the hashcode of the entire bitmap.
  int i, sDelta, sd, s1, s2, numGroup;
  unsigned char *s;
  unsigned int *d, *dst;
  unsigned int hashCode = 0, n, m, mask;
  static unsigned int masks[8] = 
  {
    0xffffffff,
    0x0000000f,
    0x000000ff,
    0x00000fff,
    0x0000ffff,
    0x000fffff,
    0x00ffffff,
    0x0fffffff,
  };
  numGroup = (count + 7) >> 3;
  dst = nibbles;
  if (shift < 8)
  {
    sDelta = 1;
  }
  else
  {
    src++;
    sDelta = 7;
  };
  mask = masks[count & 7];
  if ((shift & 7) == 0)
  {
    for (; numLines>0; numLines--,src+=160,dst+=40)
    {
      for (i=0,s=src,d=dst,sd=sDelta; i<numGroup; s+=sd,sd^=6,i++,d++)
      {
        n =  bit0Table[s[0]]
           | bit1Table[s[2]]
           | bit2Table[s[4]]
           | bit3Table[s[6]];
        *d = n;
        if (hashCode & 0x80000000)
        {
          hashCode <<= 1;
          hashCode ^= 0x8d;
        }
        else
        {
          hashCode <<= 1;
        }
        hashCode ^= n;
      };
      d--;
      hashCode ^= *d;
      *d &= mask;
      hashCode ^= *d;
    };
  }
  else
  {
    s1 = 4 * (shift & 7);
    s2 = 32 - s1;
    for (; numLines>0; numLines--,src+=160,dst+=40)
    {
      m =  bit0Table[src[0]]
         | bit1Table[src[2]]
         | bit2Table[src[4]]
         | bit3Table[src[6]];
      for (i=0,s=src,d=dst,sd=sDelta; s+=sd,sd^=6,i<numGroup; i++,d++)
      {
        n =  bit0Table[s[0]]
           | bit1Table[s[2]]
           | bit2Table[s[4]]
           | bit3Table[s[6]];
        *d = (m>>s1) | (n<<s2);
        if (hashCode & 0x80000000)
        {
          hashCode <<= 1;
          hashCode ^= 0x8d;
        }
        else
        {
          hashCode <<= 1;
        }
        hashCode ^= *d;
        m = n;
      };
      d--;
      hashCode ^= *d;
      *d &= mask;
      hashCode ^= *d;
    };
  };
  return hashCode;
}


void ModifyPortraits(VIDEOCE *pVCE, MAPPING *pMAP, int *width, int *height)
{
  int i;
  unsigned short *pW;
  pW = (unsigned short *)pVCE->GetBitmap();
  for (i=0; i<pMAP->height; i++, pW+=320)
  {
    *((ui32 *)(pW+44)) = *((ui32 *)(pW+46));
    *((ui32 *)(pW+46)) = *((ui32 *)(pW+48));
    pW[48] = pW[50];
    pW[49] = pW[53];
    *((ui32 *)(pW+50)) = *((ui32 *)(pW+54));
    *((ui32 *)(pW+52)) = *((ui32 *)(pW+56));
    *((ui32 *)(pW+54)) = *((ui32 *)(pW+60));
    *((ui32 *)(pW+56)) = *((ui32 *)(pW+62));
  };
  *width = 60;
  *height = pMAP->height;
}

void UpdateScreenArea(
                      VIDEOCE *vce,
                      i8   *STScreen,
                      MAPPING *pMapping,
                      i16  *palette,
                      bool  paletteChanged,
                      ui32 *pOldHash)
{
//  i32 LineEnd   = (((pMapping->srcx&0xff0)+pMapping->width+15)/16) * 8;
  i32 LineStart = (pMapping->srcx/16) * 8;
//  unsigned char nibbles[320];
//  unsigned char *pNibbles = nibbles;
  i32 i, line, rem, numGroup, g;
  i32 width, height;
  i32 numPixel;
  unsigned short *pBitmap, *d;
  unsigned int newHash;
  unsigned int *s, *src;
  unsigned int n;
  newHash = DePlane((unsigned char *)(STScreen+160*pMapping->srcy+LineStart), //The first 16-pixel group
                    pMapping->width,
                    pMapping->srcx & 15,
                    pMapping->height);
  if (!paletteChanged)
  {
//    if (!HasScreenChanged(STScreen+160*pMapping->srcy+LineStart,
//                          (LineEnd-LineStart) / 4,
//                          pMapping->height,
//                          160-(LineEnd-LineStart),
//                          pOldChecksum)) return;
    if (newHash == *pOldHash) return;
  }
  *pOldHash = newHash;
  createPalette16(palette,vce);
  STBLTCount++;

  numPixel = pMapping->width;
  rem = numPixel & 7;
  numGroup = numPixel >> 3;
  pBitmap = vce->GetBitmap();
  src = nibbles;
  for (line=0, s=src; line<pMapping->height; line++, src+=40, pBitmap+=320)
  {
    for (g=0, d=pBitmap,s=src; g<numGroup; g++)
    {
      n = *(s++);
      for (i=0; i<8; i++)
      {
        *(d++) = palette16[n&15];
        n >>= 4;
      };
    };
    n = *(s++);
    for (i=0; i<rem; i++)
    {
      *(d++) = palette16[n&15];
      n >>= 4;
    };
  };
  switch (pMapping->copymode)
  {
  case 0:
    height = pMapping->height;
    width = pMapping->width;
    break;
  case 1:
    ModifyPortraits(vce, pMapping, &width, &height);
    break;
  default:
    die(0xf3f5);
  };
  vce->DrawBitmap(0,0,pMapping->dstx,pMapping->dsty,width,height);
};


void DeterminePictureMapping(char *screen)
{
  switch (videoMode)
  {
  case VM_CREDITS:
  case VM_THEEND:
  case VM_CENTERMENU:
  case VM_PRESENTS:
    screenMapNum = 1;
    screenMapStart = 1;
    break;
  case VM_PRISONDOOR:
    screenMapNum = 1;
    screenMapStart = 2;
    break;
  case VM_LEFTMENU:
  case VM_ADVENTURE:
    screenMapNum = 15;
    screenMapStart = 3;
    break;
  case VM_CAST:
  default:
    screenMapNum = 1;
    screenMapStart = 0;
    break;
  };
}

bool pc[2];  //Two palette-changed flags
void display (void){
  VIDEOCE vce;
  int i, area;
  static i32 numDisplay = 0;
  if (!bitTablesInitialized) BuildBitTables();
  DeterminePictureMapping(physbase());
  vce.GetBitmap(320,320);
  numDisplay++;
  if ((VBLMultiplier!=1) && ((d.Time&0xf)!=0) && (VBLMultiplier!=99)) return;
  if (d.DynamicPaletteSwitching)
  {
    memcpy(palette[0],&d.Palette11978, 32);
    memcpy(palette[1],&d.Palette11946, 32);
    memcpy(globalPalette,&d.Palette11978,32);
  }
  else
  {
    memcpy(palette[0],globalPalette, 32);
    memcpy(palette[1],globalPalette, 32);
  };
  pc[0] = HasPaletteChanged(palette[0], oldPalette[0]);
  pc[1] = HasPaletteChanged(palette[1], oldPalette[1]);
  pc[0] = pc[0] || ForcedScreenDraw;
  pc[1] = pc[1] || ForcedScreenDraw;
  ForcedScreenDraw = false;
  for (i=0; i<screenMapNum; i++)
  {
    area = screenMapStart+i;
    UpdateScreenArea(             //portrait area
                     &vce,
                     physbase(),//STScreen,
                     &screenMap[area],
                     palette[screenMap[area].palette],
                     pc[screenMap[area].palette],
                     &screenMap[area].checksum);
  };
}

#endif