1 /**
2  *   XFrisk - The classic board game for X
3  *   Copyright (C) 1993-1999 Elan Feingold (elan@aetherworks.com)
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *   $Id: cards.c,v 1.20 2000/01/12 19:40:41 morphy Exp $
20  *
21  *   $Log: cards.c,v $
22  *   Revision 1.20  2000/01/12 19:40:41  morphy
23  *   Comment changes, removed note about Argentina card color (not current anymore)
24  *
25  *   Revision 1.19  2000/01/10 22:47:40  tony
26  *   made colorstuff more private to colormap.c, only scrollbars get set wrong, rest seems to work ok now
27  *
28  *   Revision 1.18  2000/01/09 16:07:46  tony
29  *   wrong doxygen tags
30  *
31  *   Revision 1.17  2000/01/08 18:38:03  tony
32  *   oops, even dumped core
33  *
34  *   Revision 1.16  2000/01/08 17:28:03  tony
35  *   comment added
36  *
37  *   Revision 1.15  2000/01/08 01:45:34  tony
38  *   color greenland card fixed! ?
39  *
40  *   Revision 1.13  2000/01/04 21:41:53  tony
41  *   removed redundant stuff for jokers
42  *
43  *   Revision 1.12  2000/01/04 21:11:06  tony
44  *   a bit more structure by using Cards[]
45  *
46  *   Revision 1.11  1999/12/25 21:58:02  morphy
47  *   Fixed typo in doxygen file comment
48  *
49  *   Revision 1.10  1999/12/19 22:51:09  tony
50  *   cl0d fixed greenland card
51  *
52  */
53 
54 /** \file
55  * Graphical part of cards handling for client
56  */
57 
58 #include <X11/Xlib.h>
59 #include <X11/X.h>
60 #include <X11/Intrinsic.h>
61 #include <X11/StringDefs.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <unistd.h>
65 
66 #include "network.h"
67 #include "gui-vars.h"
68 #include "riskgame.h"
69 #include "client.h"
70 #include "types.h"
71 #include "utils.h"
72 #include "cards.h"
73 #include "colormap.h"
74 #include "callbacks.h"
75 #include "debug.h"
76 
77 #define PICTURE_FRACTION 0.6
78 
79 /* Private function */
80 static void _CARDS_ComputeScaleVector(Int32 *piVector, Int32 x0, Int32 y0,
81 				     Int32 x1, Int32 y1);
82 
83 /* Private data */
84 
85 /** A structure to hold the directory information */
86 static struct Directory
87 {
88   Int32   iWidth, iHeight, iLength;
89   Int32   lOffset;
90 } pDirectory[NUM_COUNTRIES];
91 
92 /** Cards */
93 static struct {
94     Pixmap pixmap;
95     Int32 color;
96 } Cards[NUM_CARDS];
97 
98 /** Font structure for card texts */
99 static XFontStruct   *pCardFont;
100 
101 
102 /**
103  * Initialize the set of cards, called once on start
104  *
105  * \b  History:
106  * \arg 27.10.99 TdH Moved out of CARDS_RenderCard
107  */
CARDS_Init()108 void CARDS_Init() {
109     int i;
110     /* Load the font */
111     if ((pCardFont=XLoadQueryFont(hDisplay, "*helvetica-b*-r-*12*")) == NULL)
112     {
113         (void)UTIL_PopupDialog("Warning",
114                                "Could not open card font (using fixed)\n", 1,
115                                "Ok", NULL, NULL);
116         /* Assume 'fixed' is always there -- not good. */
117         pCardFont = XLoadQueryFont(hDisplay, "fixed");
118     }
119     /* Init the pixmap cache */
120     for (i=0; i!=NUM_CARDS; i++)  {
121         Cards[i].pixmap = 0L;
122         Cards[i].color = -1;
123     }
124 }
125 
126 
127 /**
128  * Set color of card, create bitmap if not yet there
129  * \b History:
130  * \arg 05.01.00 TdH Created
131  *
132  * \b  Notes:
133  * Took out of CARDS_RenderCard
134  */
135 
CARDS_SetColor(Int32 iCard,Int32 iColor)136 void CARDS_SetColor(Int32 iCard, Int32 iColor) {
137     XImage  *pimageCountry;
138     Int32    iPictureWidth, iPictureHeight, iPictureOffset;
139     Int32    iFontHeight, iFontWidth;
140     Int32    blackpixel,whitepixel;
141     Int32    x, y, c;
142     char buf[256];
143 
144     Cards[iCard].color = iColor;/* new color */
145     whitepixel = WhitePixel(hDisplay, 0);
146     blackpixel = BlackPixel(hDisplay, 0);
147 
148 
149     /* Makes life easier */
150     iPictureWidth  = CARD_WIDTH;
151     iPictureHeight = (double)CARD_HEIGHT * PICTURE_FRACTION;
152     iPictureOffset = CARD_HEIGHT - iPictureHeight;
153 
154     /* Check to see if pixmap is in cache.  If not, build it */
155     if (Cards[iCard].pixmap == 0L)   {
156         Cards[iCard].pixmap = XCreatePixmap(hDisplay, pixMapImage,
157                                             CARD_WIDTH, CARD_HEIGHT,
158                                             COLOR_GetDepth());
159 
160         /* Fill the card up with white and black */
161         XSetForeground(hDisplay, hGC, blackpixel);
162         XFillRectangle(hDisplay, Cards[iCard].pixmap, hGC,
163                        0, 0,
164                        CARD_WIDTH, CARD_HEIGHT);
165         XSetForeground(hDisplay, hGC, WhitePixel(hDisplay, 0));
166         XFillRectangle(hDisplay, Cards[iCard].pixmap, hGC,
167                        0, 0,
168                        CARD_WIDTH, iPictureOffset);
169     }
170 
171     pimageCountry = CARDS_GetCountryImage(iCard, -1, -1);
172 
173     /* Compress the card image, being careful about the pointer */
174     pimageCountry = CARDS_ScaleImage(pimageCountry,
175                                     (Int32)(4.0/5.0 * (float)iPictureWidth),
176                                     (Int32)(4.0/5.0 * (float)iPictureHeight));
177 
178     /* Dump image, centered, on lower 2/3 of card */
179     if (COLOR_IsTrueColor()) {
180         for (y = 0; y < pimageCountry->height; y++) {
181             for (x = 0; x < pimageCountry->width; x++)  {
182                 c = XGetPixel(pimageCountry, x, y);
183                 if (c != blackpixel) {
184                     c = iColor;
185                 }
186                 XSetForeground(hDisplay, hGC, c);
187                 XDrawPoint(hDisplay, Cards[iCard].pixmap, hGC,
188                            (iPictureWidth - pimageCountry->width)/2 + x,
189                            iPictureOffset + (iPictureHeight - pimageCountry->height)/2 + y);
190             }
191         }
192     }  else
193         XPutImage(hDisplay, Cards[iCard].pixmap, hGC, pimageCountry, 0, 0,
194                   (iPictureWidth - pimageCountry->width)/2,
195                   iPictureOffset +
196                   (iPictureHeight - pimageCountry->height)/2,
197                   pimageCountry->width,
198                   pimageCountry->height);
199 
200 
201     /* Write the name of the country on the card */
202     iFontHeight = (pCardFont->max_bounds.ascent
203                    + pFont->max_bounds.descent);
204     iFontWidth  = XTextWidth(pCardFont,
205                              RISK_GetNameOfCountry(iCard),
206                              strlen(RISK_GetNameOfCountry(iCard)));
207 
208     /* Set the font */
209     XSetFont(hDisplay, hGC, pCardFont->fid);
210 
211     XSetForeground(hDisplay, hGC, whitepixel);
212     XDrawString(hDisplay, Cards[iCard].pixmap, hGC,
213                 (iPictureWidth - iFontWidth)/2,
214                 iPictureOffset+iFontHeight,
215                 RISK_GetNameOfCountry(iCard),
216                 strlen(RISK_GetNameOfCountry(iCard)));
217 
218     /* Lose all of the used memory */
219     XDestroyImage(pimageCountry);
220 
221     /* Now put the appropriate bitmap there */
222 #ifdef ENGLISH
223     snprintf(buf, sizeof(buf), "%s", (iCard%3==0) ? "Cavalry" :
224              (iCard%3==1) ? "Infantry" : "Artillery");
225 #endif
226 #ifdef FRENCH
227     snprintf(buf, sizeof(buf), "%s", (iCard%3==0) ? "Cavalerie" :
228              (iCard%3==1) ? "Infanterie" : "Artillerie");
229 #endif
230     iFontWidth = XTextWidth(pCardFont, buf, strlen(buf));
231     XSetForeground(hDisplay, hGC, blackpixel);
232     XDrawString(hDisplay, Cards[iCard].pixmap, hGC,
233                 (CARD_WIDTH - iFontWidth)/2,
234                 iFontHeight,
235                 buf, strlen(buf));
236 }
237 
238 
239 /**
240  * Create pixmap for joker
241  *
242  * \b History:
243  * \arg 05.01.00 TdH Created
244  *
245  * \b Notes:
246  * Took out of CARDS_RenderCard
247  */
248 
CARDS_GetJoker(Int32 iCard)249 void CARDS_GetJoker(Int32 iCard) {
250     char buf[256];
251 
252     if (Cards[iCard].pixmap == 0L){
253         Int32    iFontHeight, iFontWidth;
254 
255         /* Makes life easier */
256 
257         Cards[iCard].pixmap = XCreatePixmap(hDisplay, pixMapImage,
258                                             CARD_WIDTH, CARD_HEIGHT, COLOR_GetDepth());
259 
260         /* Jokers are all white */
261         XSetForeground(hDisplay, hGC, WhitePixel(hDisplay, 0));
262         XFillRectangle(hDisplay, Cards[iCard].pixmap, hGC,
263                        0, 0,
264                        CARD_WIDTH, CARD_HEIGHT);
265         /* Set the font */
266         XSetFont(hDisplay, hGC, pCardFont->fid);
267         /* It's a joker, put all of the bitmaps there ??*/
268         snprintf(buf, sizeof(buf), "%s", "Joker");
269         iFontWidth = XTextWidth(pCardFont, buf, strlen(buf));
270         iFontHeight = (pCardFont->max_bounds.ascent +
271                        pCardFont->max_bounds.descent);
272         XSetForeground(hDisplay, hGC, BlackPixel(hDisplay, 0));
273         XDrawString(hDisplay, Cards[iCard].pixmap, hGC,
274                     (CARD_WIDTH - iFontWidth)/2,
275                     iFontHeight,
276                     buf, strlen(buf));
277     }
278 }
279 
280 /**
281  * Draw cards
282  *
283  * \b History:
284  * \arg 02.21.94  ESF  Created.
285  * \arg 02.22.94  ESF  Fixed positioning bug, made prettier.
286  * \arg 03.02.94  ESF  Added the printing of the country name.
287  * \arg 03.29.94  ESF  Fixed the setup for printing the jokers.
288  * \arg 04.02.94  ESF  Fixed name centering, wasn't XSetFont'ing.
289  * \arg 05.06.94  ESF  Made it more beautiful, took out hacks.
290  * \arg 06.25.94  ESF  Optimized card decompression.
291  * \arg 09.02.94  ESF  Fixed off-by-two bug.
292  * \arg 23.08.95  JC   Use COLOR_Depth.
293  * \arg 27.10.99  TdH  Took out CARDS_Init to get rid of static and check
294  */
295 
CARDS_RenderCard(Int32 iCard,Int32 iPosition)296 void CARDS_RenderCard(Int32 iCard, Int32 iPosition) {
297     Int32 c;
298     /* Range check */
299     if (iCard<0 || iCard>=NUM_CARDS || iPosition>=MAX_CARDS || iPosition<0)  {
300         (void)UTIL_PopupDialog("Warning!",
301                                "Bogus request to CARDS_RenderCard()!\n", 1,
302                                "Ok", NULL, NULL);
303         return;
304     }
305 
306     /* Find out which country/image goes with the card */
307     if (iCard < NUM_COUNTRIES)  {
308         /* Check to see if the color of the card/country has changed
309          * if card has no pixmap, color will be -1, which is no existing color
310          */
311         c = COLOR_QueryColor(COLOR_CountryToColor(iCard));
312       if(Cards[iCard].color != c)
313           CARDS_SetColor(iCard,c);
314     }
315     else {/* it's a joker */
316         if (Cards[iCard].pixmap == 0L)
317             CARDS_GetJoker(iCard);
318     }
319 
320     /* Now it's built, put it in the card's bitmap resource, manage the card */
321     XtVaSetValues(wCardToggle[iPosition],XtNbitmap, Cards[iCard].pixmap, NULL);
322     XtManageChild(wCardToggle[iPosition]);
323 }
324 
325 
326 /**
327  * Using Bresenham's algorithm, squish the image passed in to
328  * a box [x, y].  Return the compressed image.  Preserve aspect
329  * ratio of the image, so find out the MAX of the compression in
330  * the x and y directions.
331  * \b History:
332  * \arg ??.??.93  ESF  Created for the XDissolver.
333  * \arg 02.22.94  ESF  Made pretty, changed to work with 8 bit images.
334  * \arg 11.29.94  ESF  Completely rewrote to use true Bresenham algorithm.
335  * \arg 01.15.95  ESF  Fixed off by one error causing memory access errors.
336  */
337 
CARDS_ScaleImage(XImage * pimageInput,Int32 iMaxWidth,Int32 iMaxHeight)338 XImage *CARDS_ScaleImage(XImage *pimageInput, Int32 iMaxWidth, Int32 iMaxHeight)
339 {
340   XImage   *pimageCompressed;
341   Byte     *pbData;
342   Int32     iOutputWidth, iOutputHeight;
343 
344   /* Believe it or not, scaling a 2D image is like drawing two lines,
345    * using Bresenham's algorithm.  This algorithm is probably analogous
346    * to the methods used in games such as Wolfenstein 3D, since one can
347    * use a similar algorithm for rotating bitmaps in 3D.
348    *
349    * The key here is that the first "line" that we use to scale is the
350    * hypotenuse of a triangle.  The base of the triangle is the width of
351    * the input image, and the other side (vertical) is the width of the
352    * output image.
353    *
354    * The second "line" is similar, except that the dimensions of the sides
355    * are the heights of the input image and output image.
356    *
357    * We proceed to "draw" (i.e. perform the Bresenham algorithm for) the
358    * two lines, in the structure of a nested loop -- for each pixel of
359    * the outer line, draw the entire inner line).
360    *
361    * The line is going to be in the first quadrant, since the dimensions
362    * of the images are positive.  Thus, the pixels of the line will either
363    * be drawn "to the right of", "above", or "above and to the right of"
364    * the anterior pixel (in other words, as we draw the line, the next
365    * pixel will either go to the right, up, or diagonally up and to the
366    * right.
367    *
368    * Remember, the base of the triangle represents the dimension of the
369    * _input_ image.  As we draw the line, if the pixel that we draw is
370    * _heigher_ than the last one, we copy a pixel from the source image
371    * to the destination image.  The pixel that we copy is the one that
372    * falls directly _below_ the pixel we are drawing, and we copy it to
373    * the location directly to the _right_ of the current pixel.
374    *
375    * You should be able to imagine in this fashion scaling a single
376    * scan-line of an image.  The source line was the base of the
377    * triangle, and the verticle side of the triangle was the
378    * destination scan-line.  Thus, in order to scale a 2D image, we
379    * simply perform two line drawings, one inside the other, as
380    * mentioned above.
381    *
382    * As for an informal proof of this method, (not like I was ever
383    * any good at _formal_ proofs), I show that the method works for
384    * a scan-line at the border conditions, and then, using smoke, magic,
385    * and poor man's induction, I handwave my way into 2 dimensions.
386    *
387    * The easiest scaling to prove is the 1:1 ratio.  This will result in
388    * a completely diagonal line.  Since we copy a pixel across whenever
389    * we go up at all, we will copy across a pixel every time we move,
390    * since we are moving diagonally all the way.  Thus the scan-lines
391    * will be identical.
392    *
393    */
394 
395   Int32    *pHorizontalLine, *pVerticalLine;
396   Int32     iCompression;
397   Int32     iWidthCompression, iHeightCompression;
398   Int32     x, y;
399 
400   /* Calculate the dimensions of the output image, preserving aspect ratio.
401    * I want to avoid using any floating point, so use a primitive fixed
402    * point representation here, simply multiply the numbers by 1024 and then
403    * divide the final result by the same factor.  This has two consequences:
404    *
405    *  a) The accuracy is limited to approximately 3 digits.
406    *  b) The range on the input parameters of the calculation is
407    *     limited to 2^32 >> 10 = 2^22.  I don't think there are
408    *     countries larger than this in width or height :)
409    */
410 
411   /* Watch out for negative numbers! */
412   D_Assert(pimageInput->width >= 0 && pimageInput->height >=0,
413 	   "Negative image?");
414 
415   /*** Scale (encoding) ***/
416   pimageInput->width  <<= 10;
417   pimageInput->height <<= 10;
418 
419   iWidthCompression = MAX(pimageInput->width / iMaxWidth, 1<<10);
420   iHeightCompression = MAX(pimageInput->height / iMaxHeight, 1<<10);
421 
422   /* To preserve the aspect ratio, choose the larger of
423    * the compression ratios, and use it in both directions.
424    */
425 
426   iCompression = MAX(iWidthCompression, iHeightCompression);
427 
428   /* Find out the size of the output image */
429   iOutputWidth = (pimageInput->width)/iCompression;
430   iOutputHeight = (pimageInput->height)/iCompression;
431 
432   /*** Scale (decoding) ***/
433   pimageInput->width  >>= 10;
434   pimageInput->height >>= 10;
435 
436   /* After we have done this, do a sanity check on the result */
437   D_Assert(iOutputWidth <= iMaxWidth && iOutputHeight <= iMaxHeight,
438 	   "Scaling algorithm meltdown!");
439 
440   /* Allocate memory for the lines */
441   pHorizontalLine = (Int32 *)MEM_Alloc(sizeof(Int32)*iOutputWidth);
442   pVerticalLine   = (Int32 *)MEM_Alloc(sizeof(Int32)*iOutputHeight);
443 
444   /* We want to draw lines that represent the hypotenuse of triangles
445    * that have heights of pimageInput->[height | width] pixels and
446    * widths of iOutput[Width | Height] pixels.  Thus, since out lines
447    * start at (0, 0), we _must_ subtract 1 from the widths and heights
448    * in order to draw a line or the correct dimensions.
449    */
450 
451   /* First "draw" the line representing the "width" compression */
452   _CARDS_ComputeScaleVector(pHorizontalLine, 0, 0,
453 			   pimageInput->width-1, iOutputWidth-1);
454 
455   /* Now "draw" the line representing the "height" compression */
456   _CARDS_ComputeScaleVector(pVerticalLine, 0, 0,
457 			   pimageInput->height-1, iOutputHeight-1);
458 
459   /* This will hold the compressed input data as we're compressing it */
460   pbData = (Byte *)XtMalloc(sizeof(Byte)*iOutputWidth*iOutputHeight);
461   pimageCompressed = XCreateImage(hDisplay, pVisual,
462 				  8, ZPixmap, 0, (char *)pbData,
463 				  iOutputWidth, iOutputHeight, 8,
464 				  iOutputWidth);
465 
466   /* Loop through the scale vectors and compress the image (UNROLL?) */
467   for (y=0; y!=iOutputHeight; y++)
468     for (x=0; x!=iOutputWidth; x++)
469       {
470 	/* CSE hand optimization */
471 	const Int32 xx = pHorizontalLine[x], yy = pVerticalLine[y];
472 
473 	/* Sanity check */
474 	D_Assert(xx>=0 && xx<pimageInput->width, "Bogus horiz. r-pixel.");
475 	D_Assert(yy>=0 && yy<pimageInput->height, "Bogus vert. r-pixel.");
476 	D_Assert(x>=0  &&  x<pimageCompressed->width, "Bogus horiz. w-pixel.");
477 	D_Assert(y>=0  &&  y<pimageCompressed->height, "Bogus vert. w-pixel.");
478 
479 	/* Copy a pixel */
480 	XPutPixel(pimageCompressed, x, y, XGetPixel(pimageInput, xx, yy));
481       }
482 
483   /* Destroy the old image */
484   XDestroyImage(pimageInput);
485 
486   /* Free memory */
487   MEM_Free(pHorizontalLine);
488   MEM_Free(pVerticalLine);
489 
490   /* Return the compressed image */
491   return(pimageCompressed);
492 }
493 
494 
495 /**
496  * "Draws" a line from (x0, y0) to (x1, y1), including both endpoints,
497  * which is stored in a vector of function values in which (V[y], y)
498  * form the point pairs.  Caller is reponsible for allocation and
499  * deallocation of vector.
500  *
501  * \b History:
502  * \arg 12.30.94  ESF  Created.
503  * \arg 01.15.95  ESF  Added check that line is in first quadrant.
504  *
505  * \par Notes:
506  * Taken from _Computer Graphics_, by Folen and van Dam et. al.,
507  * slightly hand optimized, at the cost of clarity -- sorry.
508  * Remember that the vector must have (y1-y0)+1 elements allocated.
509  * Also, this algorithm only draws lines in the first quadrant.
510  */
_CARDS_ComputeScaleVector(Int32 * piVector,Int32 x0,Int32 y0,Int32 x1,Int32 y1)511 static void _CARDS_ComputeScaleVector(Int32 *piVector, Int32 x0, Int32 y0,
512 				     Int32 x1, Int32 y1)
513 {
514   Int32 dx, dy, incrE, incrNE, d, x, y;
515 
516   /* Ensure that the line is in the first quadrant */
517   if (x0>x1 || y0>y1)
518     {
519 #ifdef ENGLISH
520       printf("Error: (_CARDS_ScaleVector) Line not in first quadrant.");
521 #endif
522 #ifdef ENGLISH
523       printf("Erreur: (_CARDS_ScaleVector) La ligne n'est pas dans le premier quadrant.");
524 #endif
525       UTIL_ExitProgram(-1);
526     }
527 
528   /* Initialize variables, prime the algorithm */
529   dx      = x1 - x0;
530   dy      = y1 - y0;
531   d       = 2*dy - dx;
532   incrE   = 2*dy;
533   incrNE  = 2*(dy-dx);
534   x       = x0;
535   y       = y0;
536 
537   /* Map one value (like drawing a pixel) */
538   piVector[y] = x;
539 
540   while (x < x1)
541     {
542       if (d <= 0)
543 	x++, d+=incrE;
544       else
545 	x++, y++, d+=incrNE;
546 
547       /* Map one value (like drawing a pixel) */
548       piVector[y] = x;
549     }
550 }
551 
552 
553 /**
554  * \b History:
555  * \arg 01.22.95  ESF  Created.
556  * \arg 06.05.97  DAH  Make hFile a local; remember to close it.
557  * \arg 06.06.97  DAH  Actually, it needs to be static.
558  * \arg 19.12.99  TdH  Cl0d suggested this fix, greenland card now shows
559  */
CARDS_GetCountryImage(Int32 iCountry,Int32 iFgColor,Int32 iBgColor)560 XImage *CARDS_GetCountryImage(Int32 iCountry, Int32 iFgColor, Int32 iBgColor)
561 {
562   static Flag  fDirectoryRead = FALSE;
563   Int32        iCardColor, iCardBackground, i;
564   Int32        iNumCountries;
565   Byte         bLength, bColor;
566   Byte        *pbImage, *pTemp, *pTemp2;
567   Byte        *pbBogus, *pbCompressed;
568   XImage      *pimageCountry;
569   static FILE *hFile;
570   char buf[256];
571 
572   const Int32  iCard = iCountry;
573 
574   if (!fDirectoryRead)
575     {
576       fDirectoryRead = TRUE;
577 
578       /* Open the file */
579       if ((hFile=UTIL_OpenFile(COUNTRYFILE, "r"))==NULL)
580 	{
581 #ifdef ENGLISH
582 	  snprintf(buf, sizeof(buf), "CARDS: Cannot open %s!\n", COUNTRYFILE);
583 	  (void)UTIL_PopupDialog("Fatal Error", buf,
584 #endif
585 #ifdef FRENCH
586 	  snprintf(buf, sizeof(buf), "CARDS: Ne peut ouvrir %s!\n", COUNTRYFILE);
587 	  (void)UTIL_PopupDialog("Erreur fatale", buf,
588 #endif
589 				 1, "Ok", NULL, NULL);
590 	  UTIL_ExitProgram(-1);
591 	}
592 
593       /* Read the directory */
594       fscanf(hFile, "%d%c", &iNumCountries, (char *)&pbBogus);
595       if (iNumCountries != NUM_COUNTRIES) /* One is for ocean */
596 	{
597 #ifdef ENGLISH
598 	  (void)UTIL_PopupDialog("Fatal Error",
599 				 "CARDS: Wrong number of countries!",
600 #endif
601 #ifdef FRENCH
602 	  (void)UTIL_PopupDialog("Erreur fatale",
603 				 "CARDS: Nombre de pays invalide!",
604 #endif
605 				 1, "Ok", NULL, NULL);
606 	  UTIL_ExitProgram(-1);
607 	}
608       fread(pDirectory, iNumCountries, sizeof(pDirectory[0]), hFile);
609 
610       /* Hack for now */
611       iNumCountries = NUM_COUNTRIES;
612     }
613 
614   /* Allocate the memory for the card */
615   pbImage = (Byte *)XtMalloc(pDirectory[iCard].iWidth *
616 			     pDirectory[iCard].iHeight);
617   pbCompressed = (Byte *)MEM_Alloc(pDirectory[iCard].iLength);
618 
619   /* Go seek the card */
620   fseek(hFile, pDirectory[iCard].lOffset, SEEK_SET);
621 
622   /* Read it in from the data file */
623   fread(pbCompressed, pDirectory[iCard].iLength, 1, hFile);
624 
625   /* Get the colors of the card.  This is an optimization,
626    * since we should get the color from the compressed card
627    * bitmap.  However, we know what color the card should
628    * be because of our nice organization of the card ->
629    * country mapping.  If the caller has passed in specific values
630    * for these, use them instead of the defaults.
631    */
632 
633   /* do NOT ask me why +1 must be used, but it seems to work */
634   iCardColor = (iFgColor == -1) ? COLOR_CountryToColor(iCard + 1) : iFgColor;
635   iCardBackground = (iFgColor == -1) ? BlackPixel(hDisplay, 0) : iBgColor;
636 
637   /* Uncompress the data */
638   for (i=0,pTemp2=pbCompressed,pTemp=pbImage;
639        i<pDirectory[iCard].iLength; i+=2)
640     {
641       /* Get a color segment */
642       bLength = *pTemp2++;
643       bColor  = *pTemp2++;
644 
645       /* Put it in the country image */
646       if (bColor==BLACK)
647 	memset(pTemp, iCardBackground, bLength);
648       else
649 	memset(pTemp, iCardColor, bLength);
650       pTemp += bLength;
651     }
652 
653   MEM_Free(pbCompressed);
654 
655   /* Create the image */
656   pimageCountry = XCreateImage(hDisplay, pVisual, 8, ZPixmap, 0,
657 			       (char *)pbImage,
658 			       pDirectory[iCard].iWidth,
659 			       pDirectory[iCard].iHeight,
660 			       8, pDirectory[iCard].iWidth);
661   return pimageCountry;
662 }
663