gpfce patch
[fceu.git] / drivers / win / video.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2002 Ben Parnell
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 static int RecalcCustom(void);
22
23 #define VF_DDSTRETCHED     1
24
25 #define VEF_LOSTSURFACE 1
26 #define VEF____INTERNAL 2
27
28 #define VMDF_DXBLT 1
29 #define VMDF_STRFS 2
30
31 typedef struct {
32         int x;
33         int y;
34         int bpp;
35         int flags;
36         int xscale;
37         int yscale;
38         RECT srect;
39         RECT drect;        
40 } vmdef;
41
42 // left, top, right, bottom
43 static vmdef vmodes[11]={
44                          {320,240,8,0,1,1}, //0
45                          {320,240,8,0,1,1}, //1
46                          {512,384,8,0,1,1}, //2
47                          {640,480,8,0,1,1}, //3
48                          {640,480,8,0,1,1}, //4
49                          {640,480,8,0,1,1}, //5
50                          {640,480,8,VMDF_DXBLT,2,2}, //6
51                          {1024,768,8,VMDF_DXBLT,4,3}, //7
52                          {1280,1024,8,VMDF_DXBLT,5,4}, //8
53                          {1600,1200,8,VMDF_DXBLT,6,5}, //9
54                          {800,600,8,VMDF_DXBLT|VMDF_STRFS,0,0}    //10
55                        };
56 static DDCAPS caps;
57 static int mustrestore=0;
58 static DWORD CBM[3];
59
60 static int bpp;
61 static int vflags;
62 static int veflags;
63
64 int fssync=0;
65 int winsync=0;
66
67 static uint32 *palettetranslate=0;
68
69 PALETTEENTRY color_palette[256];
70 static int PaletteChanged=0;
71
72 LPDIRECTDRAWCLIPPER lpClipper=0;
73 LPDIRECTDRAW  lpDD=0;
74 LPDIRECTDRAW4 lpDD4=0;
75 LPDIRECTDRAWPALETTE lpddpal;
76
77 DDSURFACEDESC2 ddsd;
78
79 DDSURFACEDESC2        ddsdback;
80 LPDIRECTDRAWSURFACE4  lpDDSPrimary=0;
81 LPDIRECTDRAWSURFACE4  lpDDSDBack=0;
82 LPDIRECTDRAWSURFACE4  lpDDSBack=0;
83
84 static void ShowDDErr(char *s)
85 {
86  char tempo[512];
87  sprintf(tempo,"DirectDraw: %s",s);
88  FCEUD_PrintError(tempo);
89 }
90
91 int RestoreDD(int w)
92 {
93  if(w)
94   {
95    if(!lpDDSBack) return 0;
96    if(IDirectDrawSurface4_Restore(lpDDSBack)!=DD_OK) return 0;
97   }
98  else
99   {
100    if(!lpDDSPrimary) return 0;
101    if(IDirectDrawSurface4_Restore(lpDDSPrimary)!=DD_OK) return 0;
102   }
103  veflags|=1;
104  return 1;
105 }
106
107 void FCEUD_SetPalette(unsigned char index, unsigned char r, unsigned char g, unsigned char b)
108 {
109         color_palette[index].peRed=r;
110         color_palette[index].peGreen=g;
111         color_palette[index].peBlue=b;
112         PaletteChanged=1;
113 }
114
115 void FCEUD_GetPalette(unsigned char i, unsigned char *r, unsigned char *g, unsigned char *b)
116 {
117         *r=color_palette[i].peRed;
118         *g=color_palette[i].peGreen;
119         *b=color_palette[i].peBlue;
120 }
121
122 int InitializeDDraw(void)
123 {
124         ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
125         if (ddrval != DD_OK)
126         {
127          ShowDDErr("Error creating DirectDraw object.");
128          return 0;
129         }
130
131         ddrval = IDirectDraw_QueryInterface(lpDD,&IID_IDirectDraw4,(LPVOID *)&lpDD4);
132         IDirectDraw_Release(lpDD);
133
134         if (ddrval != DD_OK)
135         {
136          ShowDDErr("Error querying interface.");
137          return 0;
138         }
139
140         caps.dwSize=sizeof(caps);
141         if(IDirectDraw4_GetCaps(lpDD4,&caps,0)!=DD_OK)
142         {
143          ShowDDErr("Error getting capabilities.");
144          return 0;
145         }
146         return 1;
147 }
148
149 static int GetBPP(void)
150 {
151         DDPIXELFORMAT ddpix;
152
153         memset(&ddpix,0,sizeof(ddpix));
154         ddpix.dwSize=sizeof(ddpix);
155
156         ddrval=IDirectDrawSurface4_GetPixelFormat(lpDDSPrimary,&ddpix);
157         if (ddrval != DD_OK)
158         {
159          ShowDDErr("Error getting primary surface pixel format.");
160          return 0;
161         }
162
163         if(ddpix.dwFlags&DDPF_RGB)
164         {
165          bpp=ddpix.DUMMYUNIONNAMEN(1).dwRGBBitCount;
166          CBM[0]=ddpix.DUMMYUNIONNAMEN(2).dwRBitMask;
167          CBM[1]=ddpix.DUMMYUNIONNAMEN(3).dwGBitMask;
168          CBM[2]=ddpix.DUMMYUNIONNAMEN(4).dwBBitMask;
169         }
170         else
171         {
172          ShowDDErr("RGB data not valid.");
173          return 0;
174         }
175         if(bpp==15) bpp=16;
176
177         return 1;
178 }
179
180 static int InitBPPStuff(void)
181 {
182    if(bpp==16)
183     palettetranslate=malloc(65536*4);
184    else if(bpp>=24)
185     palettetranslate=malloc(256*4);
186    else if(bpp==8)
187    {
188     ddrval=IDirectDraw4_CreatePalette( lpDD4, DDPCAPS_8BIT|DDPCAPS_ALLOW256|DDPCAPS_INITIALIZE,color_palette,&lpddpal,NULL);
189     if (ddrval != DD_OK)
190     {
191      ShowDDErr("Error creating palette object.");
192      return 0;
193     }
194     ddrval=IDirectDrawSurface4_SetPalette(lpDDSPrimary, lpddpal);
195     if (ddrval != DD_OK)
196     {
197      ShowDDErr("Error setting palette object.");
198      return 0;
199     }
200    }
201    return 1;
202 }
203
204 int SetVideoMode(int fs)
205 {
206         if(!lpDD4)      // DirectDraw not initialized
207          return(1);
208
209         if(fs)
210          if(!vmod)
211           if(!RecalcCustom())
212            return(0);
213
214         vflags=0;
215         veflags=1;
216         PaletteChanged=1;
217
218         ResetVideo();
219
220         if(!fs)
221         { 
222          ShowCursorAbs(1);
223          windowedfailed=1;
224          HideFWindow(0);
225
226          ddrval = IDirectDraw4_SetCooperativeLevel ( lpDD4, hAppWnd, DDSCL_NORMAL);
227          if (ddrval != DD_OK)
228          {
229           ShowDDErr("Error setting cooperative level.");
230           return 1;
231          }
232
233          /* Beginning */
234          memset(&ddsd,0,sizeof(ddsd));
235          ddsd.dwSize = sizeof(ddsd);
236          ddsd.dwFlags = DDSD_CAPS;
237          ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
238
239          ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsd, &lpDDSPrimary,(IUnknown FAR*)NULL);
240          if (ddrval != DD_OK)
241          {
242           ShowDDErr("Error creating primary surface.");
243           return 1;
244          }
245
246          memset(&ddsdback,0,sizeof(ddsdback));
247          ddsdback.dwSize=sizeof(ddsdback);
248          ddsdback.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
249          ddsdback.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN;
250
251          ddsdback.dwWidth=256;
252          ddsdback.dwHeight=240;
253
254          /* If no blit hardware is present, make sure buffer is created
255             in system memory.
256          */
257          if(!(caps.dwCaps&DDCAPS_BLT))
258           ddsdback.ddsCaps.dwCaps|=DDSCAPS_SYSTEMMEMORY;
259         
260          ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsdback, &lpDDSBack, (IUnknown FAR*)NULL);
261          if (ddrval != DD_OK)
262          {
263           ShowDDErr("Error creating secondary surface.");
264           return 0;
265          }
266
267          if(!GetBPP())
268           return 0;
269
270          if(bpp!=16 && bpp!=24 && bpp!=32)
271          {
272           ShowDDErr("Current bit depth not supported!");
273           return 0;
274          }
275
276          if(!InitBPPStuff())
277           return 0;
278
279          ddrval=IDirectDraw4_CreateClipper(lpDD4,0,&lpClipper,0);
280          if (ddrval != DD_OK)
281          {
282           ShowDDErr("Error creating clipper.");
283           return 0;
284          }
285
286          ddrval=IDirectDrawClipper_SetHWnd(lpClipper,0,hAppWnd);
287          if (ddrval != DD_OK)
288          {
289           ShowDDErr("Error setting clipper window.");
290           return 0;
291          }
292          ddrval=IDirectDrawSurface4_SetClipper(lpDDSPrimary,lpClipper);
293          if (ddrval != DD_OK)
294          {
295           ShowDDErr("Error attaching clipper to primary surface.");
296           return 0;
297          }
298
299          windowedfailed=0;
300          SetMainWindowStuff();
301         }
302         else
303         {
304          HideFWindow(1);
305
306          ddrval = IDirectDraw4_SetCooperativeLevel ( lpDD4, hAppWnd,DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
307          if (ddrval != DD_OK)
308          {
309           ShowDDErr("Error setting cooperative level.");
310           return 0;
311          }
312
313          ddrval = IDirectDraw4_SetDisplayMode(lpDD4, vmodes[vmod].x, vmodes[vmod].y,vmodes[vmod].bpp,0,0);
314          if (ddrval != DD_OK)
315          {
316           ShowDDErr("Error setting display mode.");
317           return 0;
318          }
319          if(vmodes[vmod].flags&VMDF_DXBLT)
320          {
321           memset(&ddsdback,0,sizeof(ddsdback));
322           ddsdback.dwSize=sizeof(ddsdback);
323           ddsdback.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
324           ddsdback.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN;
325
326           ddsdback.dwWidth=256; //vmodes[vmod].srect.right;
327           ddsdback.dwHeight=240; //vmodes[vmod].srect.bottom;
328
329           if(!(caps.dwCaps&DDCAPS_BLT))
330            ddsdback.ddsCaps.dwCaps|=DDSCAPS_SYSTEMMEMORY;
331
332           ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsdback, &lpDDSBack, (IUnknown FAR*)NULL);
333           if(ddrval!=DD_OK)
334           {
335            ShowDDErr("Error creating secondary surface.");
336            return 0;
337           }
338          }
339
340          // create foreground surface
341         
342          memset(&ddsd,0,sizeof(ddsd));
343          ddsd.dwSize = sizeof(ddsd);
344  
345          ddsd.dwFlags = DDSD_CAPS;
346          ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
347
348          if(fssync==2) // Double buffering.
349          {
350           ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
351           ddsd.dwBackBufferCount = 1;
352           ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
353          }
354
355          ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsd, &lpDDSPrimary,(IUnknown FAR*)NULL);
356          if (ddrval != DD_OK)
357          {
358           ShowDDErr("Error creating primary surface.");
359           return 0;
360          } 
361
362          if(fssync==2)
363          {
364           DDSCAPS2 tmp;
365
366           memset(&tmp,0,sizeof(tmp));
367           tmp.dwCaps=DDSCAPS_BACKBUFFER;
368
369           if(IDirectDrawSurface4_GetAttachedSurface(lpDDSPrimary,&tmp,&lpDDSDBack)!=DD_OK)
370           {
371            ShowDDErr("Error getting attached surface.");
372            return 0;
373           }
374          }
375
376          if(!GetBPP())
377           return 0;
378          if(!InitBPPStuff())
379           return 0;
380
381          mustrestore=1;
382          ShowCursorAbs(0);
383         }
384
385         InputScreenChanged(fs);
386         fullscreen=fs;
387         return 1;
388 }
389
390 static void BlitScreenWindow(uint8 *XBuf);
391 static void BlitScreenFull(uint8 *XBuf);
392
393 void FCEUD_BlitScreen(uint8 *XBuf)
394 {
395  doagain:
396
397  UpdateFCEUWindow();
398
399  if(!(eoptions&EO_BGRUN))
400   while(nofocus)
401   {
402    Sleep(50);
403    BlockingCheck();
404   }
405
406
407  /* This complex statement deserves some explanation.
408     Make sure this special speed throttling hasn't been disabled by the user
409     first. Second, we don't want to throttle the speed if the fast-forward
410     button is pressed down(or during certain network play conditions).
411        
412     Now, if we're at this point, we'll throttle speed if sound is disabled.
413     Otherwise, it gets a bit more complicated.  We'll throttle speed if focus
414     to FCE Ultra has been lost and we're writing to the primary sound buffer
415     because our sound code won't block.  Blocking does seem to work when
416     writing to a secondary buffer, so we won't throttle when a secondary
417     buffer is used.
418  */
419
420  if(!(eoptions&EO_NOTHROTTLE))
421   if(!NoWaiting)
422    if(!soundo || (soundo && nofocus && !(soundoptions&SO_SECONDARY)) )
423        SpeedThrottle();
424
425  if(fullscreen)
426  {
427   if(fssync==1 && !NoWaiting)
428    IDirectDraw4_WaitForVerticalBlank(lpDD4,DDWAITVB_BLOCKBEGIN,0);
429
430   BlitScreenFull(XBuf);
431  }
432  else
433  {
434   if(winsync && !NoWaiting)
435    IDirectDraw4_WaitForVerticalBlank(lpDD4,DDWAITVB_BLOCKBEGIN,0);
436
437   if(!windowedfailed)
438    BlitScreenWindow(XBuf);
439  }
440  if(userpause)
441  {
442   StopSound();
443   Sleep(50);
444   BlockingCheck();
445   goto doagain;
446  }
447 }
448
449 static INLINE void BlitVidHi(uint8 *src, uint8 *dest, /*int xr,*/ int yr, int pitch)
450 {
451  int x,y;
452  int pinc;
453
454  if(!(eoptions&EO_CLIPSIDES))
455   switch(bpp)
456   {
457    case 32:
458
459    pinc=pitch-(256<<2);
460    for(y=yr;y;y--)
461    {
462     for(x=256;x;x--)
463     {
464      *(uint32 *)dest=palettetranslate[(uint32)*src];     
465      dest+=4;
466      src++;
467     }
468     dest+=pinc;
469     src+=16;
470    }
471    break;
472
473   case 24:
474    pinc=pitch-(256*3);
475    for(y=yr;y;y--)
476    {
477     for(x=256;x;x--)
478     {
479      uint32 tmp;
480      tmp=palettetranslate[(uint32)*src];
481      *(uint16*)dest=(uint16)tmp;
482      *&dest[2]=(uint8)(tmp>>16);
483      dest+=3;
484      src++;
485     }
486     dest+=pinc;
487     src+=16;
488    }
489    break;
490
491   case 16:
492    pinc=pitch-(256<<1);
493    for(y=yr;y;y--)
494    {
495     for(x=256>>1;x;x--)
496     {
497      *(unsigned long *)dest=palettetranslate[*(unsigned short *)src];
498      dest+=4;
499      src+=2;
500     }
501     dest+=pinc;
502     src+=16;
503    }
504    break;
505  }
506  else
507   switch(bpp)
508   {
509    case 32:
510
511    pinc=pitch-(240<<2);
512    for(y=yr;y;y--)
513    {
514     for(x=240;x;x--)
515     {
516      *(uint32 *)dest=palettetranslate[(uint32)*src];
517      dest+=4;
518      src++;
519     }
520     dest+=pinc;
521     src+=32;
522    }
523    break;
524
525   case 24:
526    pinc=pitch-(240*3);
527    for(y=yr;y;y--)
528    {
529     for(x=240;x;x--)
530     {
531      uint32 tmp;
532      tmp=palettetranslate[(uint32)*src];
533      *(uint16*)dest=(uint16)tmp;
534      *&dest[2]=(uint8)(tmp>>16);
535      dest+=3;
536      src++;
537     }
538     dest+=pinc;
539     src+=32;
540    }
541    break;
542   case 16:
543    pinc=pitch-(240<<1);
544    for(y=yr;y;y--)
545    {
546     for(x=240>>1;x;x--)
547     {
548      *(unsigned long *)dest=palettetranslate[*(unsigned short *)src];
549      dest+=4;
550      src+=2;
551     }
552     dest+=pinc;
553     src+=32;
554    }
555    break;
556  }
557 }
558
559 static INLINE void FixPaletteHi(void)
560 {
561    int x;
562
563    switch(bpp)
564    {
565     case 16:{
566              int cshiftr[3];
567              int cshiftl[3];
568              int a,x,z,y;
569         
570              cshiftl[0]=cshiftl[1]=cshiftl[2]=-1;
571              for(a=0;a<3;a++)
572              {
573               for(x=0,y=-1,z=0;x<16;x++)
574               {
575                if(CBM[a]&(1<<x))
576                {
577                 if(cshiftl[a]==-1) cshiftl[a]=x;
578                 z++;
579                }
580               }
581               cshiftr[a]=(8-z);
582              }
583
584              for(x=0;x<65536;x++)
585              { 
586               uint16 lower,upper;   
587               lower=(color_palette[x&255].peRed>>cshiftr[0])<<cshiftl[0];
588               lower|=(color_palette[x&255].peGreen>>cshiftr[1])<<cshiftl[1];
589               lower|=(color_palette[x&255].peBlue>>cshiftr[2])<<cshiftl[2];
590               upper=(color_palette[x>>8].peRed>>cshiftr[0])<<cshiftl[0];
591               upper|=(color_palette[x>>8].peGreen>>cshiftr[1])<<cshiftl[1];
592               upper|=(color_palette[x>>8].peBlue>>cshiftr[2])<<cshiftl[2];      
593               palettetranslate[x]=lower|(upper<<16);
594              }
595             }
596             break;
597     case 24:
598     case 32:
599             for(x=0;x<256;x++)
600             {
601              uint32 t;
602              t=0;
603              t=color_palette[x].peBlue;
604              t|=color_palette[x].peGreen<<8;
605              t|=color_palette[x].peRed<<16;
606              palettetranslate[x]=t;
607             }
608             break;
609    }
610 }
611
612 static void BlitScreenWindow(unsigned char *XBuf)
613 {
614  int pitch;
615  unsigned char *ScreenLoc;
616  static RECT srect;
617  RECT drect;
618
619  srect.top=srect.left=0;
620  srect.right=VNSWID;
621  srect.bottom=totallines;
622
623  if(PaletteChanged==1)
624  {
625   FixPaletteHi();
626   PaletteChanged=0;
627  }
628  if(!GetClientAbsRect(&drect)) return;
629
630  ddrval=IDirectDrawSurface4_Lock(lpDDSBack,NULL,&ddsdback, 0, NULL);
631  if(ddrval!=DD_OK)
632  {
633   if(ddrval==DDERR_SURFACELOST) RestoreDD(1);
634   return;
635  }
636  pitch=ddsdback.DUMMYUNIONNAMEN(1).lPitch;
637  ScreenLoc=ddsdback.lpSurface;
638
639  if(veflags&1)
640  {
641   memset(ScreenLoc,0,pitch*240);
642   veflags&=~1;
643  }
644
645  BlitVidHi(XBuf+srendline*272+VNSCLIP, ScreenLoc, /*VNSWID,*/ totallines, pitch);
646
647  IDirectDrawSurface4_Unlock(lpDDSBack, NULL);
648
649  if(IDirectDrawSurface4_Blt(lpDDSPrimary, &drect,lpDDSBack,&srect,DDBLT_ASYNC,0)!=DD_OK)
650  {
651   ddrval=IDirectDrawSurface4_Blt(lpDDSPrimary, &drect,lpDDSBack,&srect,DDBLT_WAIT,0);
652   if(ddrval!=DD_OK)
653   {
654    if(ddrval==DDERR_SURFACELOST) {RestoreDD(1);RestoreDD(0);}
655    return;
656   }
657  }
658 }
659
660 static void BlitScreenFull(uint8 *XBuf)
661 {
662   static int pitch;
663   char *ScreenLoc;
664   unsigned long x;
665   uint8 y;
666   RECT srect,drect;
667   LPDIRECTDRAWSURFACE4 lpDDSVPrimary;
668
669
670   if(fssync==2)
671    lpDDSVPrimary=lpDDSDBack;
672   else
673    lpDDSVPrimary=lpDDSPrimary;
674
675   if(PaletteChanged==1)
676   {
677    if(bpp>=16)
678     FixPaletteHi();
679    else
680     for(x=0;x<=0x80;x+=0x80)
681     {
682      ddrval=IDirectDrawPalette_SetEntries(lpddpal,0,0x80^x,128,&color_palette[x]);
683      if(ddrval!=DD_OK)
684      {
685       if(ddrval==DDERR_SURFACELOST) RestoreDD(0);
686       return;
687      }
688     }
689    PaletteChanged=0;
690   }
691
692  if(vmodes[vmod].flags&VMDF_DXBLT)
693  {
694   ddrval=IDirectDrawSurface4_Lock(lpDDSBack,NULL,&ddsdback, 0, NULL);
695   if(ddrval!=DD_OK)
696   {
697    if(ddrval==DDERR_SURFACELOST) RestoreDD(1);
698    return;
699   }
700   ScreenLoc=ddsdback.lpSurface;
701   pitch=ddsdback.DUMMYUNIONNAMEN(1).lPitch;
702
703   srect.top=0;
704   srect.left=0;
705   srect.right=VNSWID;
706   srect.bottom=totallines;
707   if(vmodes[vmod].flags&VMDF_STRFS)
708   {
709    drect.top=0;
710    drect.left=0;
711    drect.right=vmodes[vmod].x;
712    drect.bottom=vmodes[vmod].y;
713   }
714   else
715   {
716    drect.top=(vmodes[vmod].y-(totallines*vmodes[vmod].yscale))>>1;
717    drect.bottom=drect.top+(totallines*vmodes[vmod].yscale);
718    drect.left=(vmodes[vmod].x-VNSWID*vmodes[vmod].xscale)>>1;
719    drect.right=drect.left+VNSWID*vmodes[vmod].xscale;
720   }
721  }
722  else
723  {
724   ddrval=IDirectDrawSurface4_Lock(lpDDSVPrimary,NULL,&ddsd, 0, NULL);
725   if(ddrval!=DD_OK)
726   {
727    if(ddrval==DDERR_SURFACELOST) RestoreDD(0);
728    return;
729   }
730
731   ScreenLoc=ddsd.lpSurface;
732   pitch=ddsd.DUMMYUNIONNAMEN(1).lPitch;
733  }
734
735  if(veflags&1)
736  {
737   if(vmodes[vmod].flags&VMDF_DXBLT)
738   {
739    veflags|=2;
740    memset((char *)ScreenLoc,0,pitch*srect.bottom);
741   }
742   else
743   {
744    memset((char *)ScreenLoc,0,pitch*vmodes[vmod].y);
745   }
746   PaletteChanged=1;
747   veflags&=~1;
748  }
749
750  if(vmod==5)
751  {
752   if(eoptions&EO_CLIPSIDES)
753   {
754    asm volatile(
755       "xorl %%edx, %%edx\n\t"
756       "akoop1:\n\t"
757       "movb $120,%%al     \n\t"
758       "akoop2:\n\t"
759       "movb 1(%%esi),%%dl\n\t"
760       "shl  $16,%%edx\n\t"
761       "movb (%%esi),%%dl\n\t"
762       "xorl $0x00800080,%%edx\n\t"
763       "movl %%edx,(%%edi)\n\t"
764       "addl $2,%%esi\n\t"
765       "addl $4,%%edi\n\t"
766       "decb %%al\n\t"
767       "jne akoop2\n\t"
768       "addl $32,%%esi\n\t"
769       "addl %%ecx,%%edi\n\t"
770       "decb %%bl\n\t"
771       "jne akoop1\n\t"
772       :
773       : "S" (XBuf+srendline*272+VNSCLIP), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-(VNSWID<<1))/2),"b" (totallines), "c" ((pitch-VNSWID)<<1)
774       : "%al", "%edx", "%cc" );
775   }
776   else
777   {
778    asm volatile(
779      "xorl %%edx, %%edx\n\t"
780      "koop1:\n\t"
781      "movb $128,%%al     \n\t"
782      "koop2:\n\t"
783      "movb 1(%%esi),%%dl\n\t"
784      "shl  $16,%%edx\n\t"
785      "movb (%%esi),%%dl\n\t"
786      "xorl $0x00800080,%%edx\n\t"
787      "movl %%edx,(%%edi)\n\t"
788      "addl $2,%%esi\n\t"
789      "addl $4,%%edi\n\t"
790      "decb %%al\n\t"
791      "jne koop2\n\t"
792      "addl $16,%%esi\n\t"
793      "addl %%ecx,%%edi\n\t"
794      "decb %%bl\n\t"
795      "jne koop1\n\t"
796       :
797       : "S" (XBuf+srendline*272), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-512)/2),"b" (totallines), "c" (pitch-512+pitch)
798       : "%al", "%edx", "%cc" );
799   }
800  }
801  else if(vmod==4)
802  {
803   if(eoptions&EO_CLIPSIDES)
804   {
805    asm volatile(
806       "ayoop1:\n\t"
807       "movb $120,%%al     \n\t"
808       "ayoop2:\n\t"
809       "movb 1(%%esi),%%dh\n\t"
810       "movb %%dh,%%dl\n\t"
811       "shl  $16,%%edx\n\t"
812       "movb (%%esi),%%dl\n\t"
813       "movb %%dl,%%dh\n\t"               // Ugh
814       "xorl $0x80808080,%%edx\n\t"
815       "movl %%edx,(%%edi)\n\t"
816       "addl $2,%%esi\n\t"
817       "addl $4,%%edi\n\t"
818       "decb %%al\n\t"
819       "jne ayoop2\n\t"
820       "addl $32,%%esi\n\t"
821       "addl %%ecx,%%edi\n\t"
822       "decb %%bl\n\t"
823       "jne ayoop1\n\t"
824       :
825       : "S" (XBuf+srendline*272+VNSCLIP), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-(VNSWID<<1))/2),"b" (totallines), "c" ((pitch-VNSWID)<<1)
826       : "%al", "%edx", "%cc" );
827   }
828   else
829   {
830    asm volatile(
831       "yoop1:\n\t"
832       "movb $128,%%al     \n\t"
833       "yoop2:\n\t"
834       "movb 1(%%esi),%%dh\n\t"
835       "movb %%dh,%%dl\n\t"
836       "shl  $16,%%edx\n\t"
837       "movb (%%esi),%%dl\n\t"
838       "movb %%dl,%%dh\n\t"               // Ugh
839       "xorl $0x80808080,%%edx\n\t"
840       "movl %%edx,(%%edi)\n\t"
841       "addl $2,%%esi\n\t"
842       "addl $4,%%edi\n\t"
843       "decb %%al\n\t"
844       "jne yoop2\n\t"
845       "addl $16,%%esi\n\t"
846       "addl %%ecx,%%edi\n\t"
847       "decb %%bl\n\t"
848       "jne yoop1\n\t"
849       :
850       : "S" (XBuf+srendline*272), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-512)/2),"b" (totallines), "c" (pitch-512+pitch)
851       : "%al", "%edx", "%cc" );
852   }
853  }
854  else
855  {
856   if(!(vmodes[vmod].flags&VMDF_DXBLT))
857   {  
858    ScreenLoc+=((vmodes[vmod].x-VNSWID)>>1)*(bpp>>3)+(((vmodes[vmod].y-totallines)>>1))*pitch;
859   }
860   if(bpp>=16)
861   {
862    BlitVidHi(XBuf+srendline*272+VNSCLIP, ScreenLoc, /*VNSWID,*/ totallines, pitch);
863   }
864   else
865   {
866    XBuf+=srendline*272+VNSCLIP;
867    if(eoptions&EO_CLIPSIDES)
868    {
869     for(y=totallines;y;y--)
870     {
871      for(x=60;x;x--)
872      {
873       *(long *)ScreenLoc=(*(long *)XBuf)^0x80808080;
874       ScreenLoc+=4;
875       XBuf+=4;
876      }
877      ScreenLoc+=pitch-240;
878      XBuf+=32;
879     }
880    }
881    else
882    {
883     for(y=totallines;y;y--)
884     {
885      for(x=64;x;x--)
886      {
887       *(long *)ScreenLoc=(*(long *)XBuf)^0x80808080;
888       ScreenLoc+=4;
889       XBuf+=4;
890      }
891      ScreenLoc+=pitch-256;
892      XBuf+=16;
893     }
894    }
895   }
896  }
897
898  if(vmodes[vmod].flags&VMDF_DXBLT)
899  { 
900   IDirectDrawSurface4_Unlock(lpDDSBack, NULL);
901
902   if(veflags&2)
903   {
904    if(IDirectDrawSurface4_Lock(lpDDSVPrimary,NULL,&ddsd, 0, NULL)==DD_OK)
905    {
906     memset(ddsd.lpSurface,0,ddsd.DUMMYUNIONNAMEN(1).lPitch*vmodes[vmod].y);
907     IDirectDrawSurface4_Unlock(lpDDSVPrimary, NULL);
908     veflags&=~2;
909    }
910   }
911
912  
913   if(IDirectDrawSurface4_Blt(lpDDSVPrimary, &drect,lpDDSBack,&srect,DDBLT_ASYNC,0)!=DD_OK)
914   {
915    ddrval=IDirectDrawSurface4_Blt(lpDDSVPrimary, &drect,lpDDSBack,&srect,DDBLT_WAIT,0);
916    if(ddrval!=DD_OK)
917    {
918     if(ddrval==DDERR_SURFACELOST)
919     {
920      RestoreDD(0);
921      RestoreDD(1);
922     }
923     return;
924    }
925
926   }
927  }
928  else
929   IDirectDrawSurface4_Unlock(lpDDSVPrimary, NULL);
930  if(fssync==2)
931  {
932   IDirectDrawSurface4_Flip(lpDDSPrimary,0,0);
933
934  }
935 }
936
937 void ResetVideo(void)
938 {
939  ShowCursorAbs(1);
940  if(palettetranslate) {free(palettetranslate);palettetranslate=0;}
941  if(lpDD4)
942   if(mustrestore)
943    {IDirectDraw4_RestoreDisplayMode(lpDD4);mustrestore=0;}
944  if(lpDDSBack) {IDirectDrawSurface4_Release(lpDDSBack);lpDDSBack=0;}
945  if(lpDDSPrimary) {IDirectDrawSurface4_Release(lpDDSPrimary);lpDDSPrimary=0;} 
946  if(lpClipper) {IDirectDrawClipper_Release(lpClipper);lpClipper=0;}
947 }
948
949 static int RecalcCustom(void)
950 {
951  vmodes[0].flags&=~VMDF_DXBLT;
952
953  if(vmodes[0].flags&VMDF_STRFS)
954  {
955   vmodes[0].flags|=VMDF_DXBLT;
956
957   vmodes[0].srect.top=srendline;
958   vmodes[0].srect.left=VNSCLIP;
959   vmodes[0].srect.right=256-VNSCLIP;
960   vmodes[0].srect.bottom=erendline+1;
961
962   vmodes[0].drect.top=vmodes[0].drect.left=0;
963   vmodes[0].drect.right=vmodes[0].x;
964   vmodes[0].drect.bottom=vmodes[0].y;
965  }
966  else if(vmodes[0].xscale!=1 || vmodes[0].yscale!=1)
967  {
968   vmodes[0].flags|=VMDF_DXBLT;
969   if(VNSWID*vmodes[0].xscale>vmodes[0].x)
970   {
971    FCEUD_PrintError("Scaled width is out of range.  Reverting to no horizontal scaling.");
972    vmodes[0].xscale=1;
973   }
974   if(totallines*vmodes[0].yscale>vmodes[0].y)
975   {
976    FCEUD_PrintError("Scaled height is out of range.  Reverting to no vertical scaling.");
977    vmodes[0].yscale=1;
978   }
979
980   vmodes[0].srect.left=VNSCLIP;
981   vmodes[0].srect.top=srendline;
982   vmodes[0].srect.right=256-VNSCLIP;
983   vmodes[0].srect.bottom=erendline+1;
984
985   vmodes[0].drect.top=(vmodes[0].y-(totallines*vmodes[0].yscale))>>1;
986   vmodes[0].drect.bottom=vmodes[0].drect.top+totallines*vmodes[0].yscale;
987   
988   vmodes[0].drect.left=(vmodes[0].x-(VNSWID*vmodes[0].xscale))>>1;
989   vmodes[0].drect.right=vmodes[0].drect.left+VNSWID*vmodes[0].xscale;
990  }
991
992  if(vmodes[0].x<VNSWID)
993  {
994   FCEUD_PrintError("Horizontal resolution is too low.");
995   return(0);
996  }
997  if(vmodes[0].y<totallines && !(vmodes[0].flags&VMDF_STRFS))
998  {
999   FCEUD_PrintError("Vertical resolution must not be less than the total number of drawn scanlines.");
1000   return(0);
1001  }
1002
1003  return(1);
1004 }
1005
1006 BOOL CALLBACK VideoConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1007 {
1008         static char *vmstr[11]={
1009                                 "Custom",
1010                                 "320x240 Full Screen",
1011                                 "512x384 Centered",
1012                                 "640x480 Centered",
1013                                 "640x480 Scanlines",
1014                                 "640x480 \"4 per 1\"",
1015                                 "640x480 2x,2y",
1016                                 "1024x768 4x,3y",
1017                                 "1280x1024 5x,4y",
1018                                 "1600x1200 6x,5y",
1019                                 "800x600 Stretched"
1020                                };
1021         int x;
1022
1023         switch(uMsg)
1024         {
1025          case WM_INITDIALOG:                
1026                 for(x=0;x<11;x++)
1027                  SendDlgItemMessage(hwndDlg,100,CB_ADDSTRING,0,(LPARAM)(LPSTR)vmstr[x]);
1028                 SendDlgItemMessage(hwndDlg,100,CB_SETCURSEL,vmod,(LPARAM)(LPSTR)0);
1029
1030                 SendDlgItemMessage(hwndDlg,202,CB_ADDSTRING,0,(LPARAM)(LPSTR)"8");
1031                 SendDlgItemMessage(hwndDlg,202,CB_ADDSTRING,0,(LPARAM)(LPSTR)"16");
1032                 SendDlgItemMessage(hwndDlg,202,CB_ADDSTRING,0,(LPARAM)(LPSTR)"24");
1033                 SendDlgItemMessage(hwndDlg,202,CB_ADDSTRING,0,(LPARAM)(LPSTR)"32");
1034                 SendDlgItemMessage(hwndDlg,202,CB_SETCURSEL,(vmodes[0].bpp>>3)-1,(LPARAM)(LPSTR)0);
1035
1036                 SetDlgItemInt(hwndDlg,200,vmodes[0].x,0);
1037                 SetDlgItemInt(hwndDlg,201,vmodes[0].y,0);
1038
1039                 SetDlgItemInt(hwndDlg,302,vmodes[0].xscale,0);
1040                 SetDlgItemInt(hwndDlg,303,vmodes[0].yscale,0);
1041                 CheckRadioButton(hwndDlg,300,301,(vmodes[0].flags&VMDF_STRFS)?301:300);
1042                 if(eoptions&EO_FSAFTERLOAD)
1043                  CheckDlgButton(hwndDlg,102,BST_CHECKED);
1044
1045                 if(eoptions&EO_CLIPSIDES)
1046                  CheckDlgButton(hwndDlg,106,BST_CHECKED);
1047
1048                 SetDlgItemInt(hwndDlg,500,srendlinen,0);
1049                 SetDlgItemInt(hwndDlg,501,erendlinen,0);
1050
1051                 SetDlgItemInt(hwndDlg,502,srendlinep,0);
1052                 SetDlgItemInt(hwndDlg,503,erendlinep,0);
1053
1054
1055                 SetDlgItemInt(hwndDlg,103,winsizemul,0);
1056
1057                 SendDlgItemMessage(hwndDlg,104,CB_ADDSTRING,0,(LPARAM)(LPSTR)"<none>");
1058                 SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"<none>");
1059
1060                 SendDlgItemMessage(hwndDlg,104,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Wait for VBlank");
1061                 SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Wait for VBlank");
1062
1063                 SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Double Buffering");
1064
1065                 SendDlgItemMessage(hwndDlg,104,CB_SETCURSEL,winsync,(LPARAM)(LPSTR)0);
1066                 SendDlgItemMessage(hwndDlg,105,CB_SETCURSEL,fssync,(LPARAM)(LPSTR)0);
1067                 break;
1068          case WM_CLOSE:
1069          case WM_QUIT: goto gornk;
1070          case WM_COMMAND:
1071                         if(!(wParam>>16))
1072                         switch(wParam&0xFFFF)
1073                         {
1074                          case 1:
1075                          gornk:
1076          
1077                          if(IsDlgButtonChecked(hwndDlg,106)==BST_CHECKED)
1078                           eoptions|=EO_CLIPSIDES;
1079                          else
1080                           eoptions&=~EO_CLIPSIDES;
1081
1082                          srendlinen=GetDlgItemInt(hwndDlg,500,0,0);
1083                          erendlinen=GetDlgItemInt(hwndDlg,501,0,0);
1084                          srendlinep=GetDlgItemInt(hwndDlg,502,0,0);
1085                          erendlinep=GetDlgItemInt(hwndDlg,503,0,0);
1086
1087
1088                          if(erendlinen>239) erendlinen=239;
1089                          if(srendlinen>erendlinen) srendlinen=erendlinen;
1090
1091                          if(erendlinep>239) erendlinep=239;
1092                          if(srendlinep>erendlinen) srendlinep=erendlinep;
1093
1094                          UpdateRendBounds();
1095
1096                          if(IsDlgButtonChecked(hwndDlg,301)==BST_CHECKED)
1097                           vmodes[0].flags|=VMDF_STRFS;
1098                          else
1099                           vmodes[0].flags&=~VMDF_STRFS;
1100
1101                          vmod=SendDlgItemMessage(hwndDlg,100,CB_GETCURSEL,0,(LPARAM)(LPSTR)0);
1102                          vmodes[0].x=GetDlgItemInt(hwndDlg,200,0,0);
1103                          vmodes[0].y=GetDlgItemInt(hwndDlg,201,0,0);
1104                          vmodes[0].bpp=(SendDlgItemMessage(hwndDlg,202,CB_GETCURSEL,0,(LPARAM)(LPSTR)0)+1)<<3;
1105
1106                          vmodes[0].xscale=GetDlgItemInt(hwndDlg,302,0,0);
1107                          vmodes[0].yscale=GetDlgItemInt(hwndDlg,303,0,0);
1108         
1109                          if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED)
1110                           fullscreen=1;
1111                          else
1112                           fullscreen=0;
1113                          if(IsDlgButtonChecked(hwndDlg,102)==BST_CHECKED)
1114                           eoptions|=EO_FSAFTERLOAD;
1115                          else
1116                           eoptions&=~EO_FSAFTERLOAD;
1117
1118                          {
1119                           int t=GetDlgItemInt(hwndDlg,103,0,0);
1120                           if(t>0 && t<60)
1121                            winsizemul=t;
1122                          }
1123                          winsync=SendDlgItemMessage(hwndDlg,104,CB_GETCURSEL,0,(LPARAM)(LPSTR)0);
1124                          fssync=SendDlgItemMessage(hwndDlg,105,CB_GETCURSEL,0,(LPARAM)(LPSTR)0);
1125                          EndDialog(hwndDlg,0);
1126                          break;
1127                }
1128               }
1129   return 0;
1130 }
1131
1132 static void SetFSVideoMode(void)
1133 {
1134  changerecursive=1;
1135  if(!SetVideoMode(1))
1136   SetVideoMode(0);
1137  changerecursive=0;
1138 }
1139
1140
1141
1142 void ConfigVideo(void)
1143 {
1144         DialogBox(fceu_hInstance,"VIDEOCONFIG",hAppWnd,VideoConCallB); 
1145         UpdateRendBounds();
1146         if(fullscreen)
1147          SetFSVideoMode();
1148         else
1149          SetMainWindowStuff();
1150 }
1151
1152 void DoVideoConfigFix(void)
1153 {
1154         UpdateRendBounds();
1155 }
1156
1157
1158 #ifdef moo
1159                          if(!vmod)
1160                          {
1161                           if(vmodes[0].x<VNSWID)
1162                           {
1163                            FCEUD_PrintError("Horizontal resolution is too low.");
1164                            return 0;
1165                           }
1166                           if(vmodes[0].y<totallines && !(vmodes[0].flags&VMDF_STRFS))
1167                           {
1168                            FCEUD_PrintError("Vertical resolution must not be less than the total number of drawn scanlines.");
1169                            return 0;
1170                           }
1171                          }
1172
1173
1174 #endif