Fix signed/unsigned comparison warnings.
[wine] / programs / winemine / main.c
1 /*
2  * WineMine (main.c)
3  *
4  * Copyright 2000 Joshua Thielen
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <windows.h>
25 #include "main.h"
26 #include "dialog.h"
27 #include "resource.h"
28
29 /* Work around a Wine bug which defines handles as UINT rather than LPVOID */
30 #ifdef WINE_STRICT
31 #define NULL_HANDLE NULL
32 #else
33 #define NULL_HANDLE 0
34 #endif
35
36 #ifdef DUMB_DEBUG
37 #include <stdio.h>
38 #define DEBUG(x) fprintf(stderr,x)
39 #else
40 #define DEBUG(x)
41 #endif
42
43 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
44 {
45     MSG msg;
46     WNDCLASS wc;
47     HWND hWnd;
48     HACCEL haccel;
49     char appname[9];
50
51     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
52
53     wc.style = 0;
54     wc.lpfnWndProc = MainProc;
55     wc.cbClsExtra = 0;
56     wc.cbWndExtra = 0;
57     wc.hInstance = hInst;
58     wc.hIcon = LoadIcon( hInst, appname );
59     wc.hCursor = LoadCursor( NULL_HANDLE, IDI_APPLICATION );
60     wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
61     wc.lpszMenuName = "MENU_WINEMINE";
62     wc.lpszClassName = appname;
63
64     if (!RegisterClass(&wc)) exit(1);
65     hWnd = CreateWindow( appname, appname,
66         WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
67         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
68         NULL_HANDLE, NULL_HANDLE, hInst, NULL );
69
70     if (!hWnd) exit(1);
71
72     ShowWindow( hWnd, cmdshow );
73     UpdateWindow( hWnd );
74
75     haccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDA_WINEMINE) );
76     SetTimer( hWnd, ID_TIMER, 1000, NULL );
77
78     while( GetMessage(&msg, NULL_HANDLE, 0, 0) ) {
79         if (!TranslateAccelerator( hWnd, haccel, &msg ))
80             TranslateMessage( &msg );
81
82         DispatchMessage( &msg );
83     }
84     return msg.wParam;
85 }
86
87 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
88 {
89     HDC hdc;
90     PAINTSTRUCT ps;
91     HMENU hMenu;
92     static BOARD board;
93
94     switch( msg ) {
95     case WM_CREATE:
96         board.hInst = ((LPCREATESTRUCT) lParam)->hInstance;
97         board.hWnd = hWnd;
98         InitBoard( &board );
99         CreateBoard( &board );
100         return 0;
101
102     case WM_PAINT:
103       {
104         HDC hMemDC;
105
106         DEBUG("WM_PAINT\n");
107         hdc = BeginPaint( hWnd, &ps );
108         hMemDC = CreateCompatibleDC( hdc );
109
110         DrawBoard( hdc, hMemDC, &ps, &board );
111
112         DeleteDC( hMemDC );
113         EndPaint( hWnd, &ps );
114
115         return 0;
116       }
117
118     case WM_MOVE:
119         DEBUG("WM_MOVE\n");
120         board.pos.x = (unsigned) LOWORD(lParam);
121         board.pos.y = (unsigned) HIWORD(lParam);
122         return 0;
123
124     case WM_DESTROY:
125         SaveBoard( &board );
126         DestroyBoard( &board );
127         PostQuitMessage( 0 );
128         return 0;
129
130     case WM_TIMER:
131         if( board.status == PLAYING ) {
132             board.time++;
133                   RedrawWindow( hWnd, &board.timer_rect, NULL_HANDLE,
134                     RDW_INVALIDATE | RDW_UPDATENOW );
135         }
136         return 0;
137
138     case WM_LBUTTONDOWN:
139         DEBUG("WM_LBUTTONDOWN\n");
140         if( wParam & MK_RBUTTON )
141             msg = WM_MBUTTONDOWN;
142         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
143         SetCapture( hWnd );
144         return 0;
145
146     case WM_LBUTTONUP:
147         DEBUG("WM_LBUTTONUP\n");
148         if( wParam & MK_RBUTTON )
149             msg = WM_MBUTTONUP;
150         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
151         ReleaseCapture();
152         return 0;
153
154     case WM_RBUTTONDOWN:
155         DEBUG("WM_RBUTTONDOWN\n");
156         if( wParam & MK_LBUTTON ) {
157             board.press.x = 0;
158             board.press.y = 0;
159             msg = WM_MBUTTONDOWN;
160         }
161         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
162         return 0;
163
164     case WM_RBUTTONUP:
165         DEBUG("WM_RBUTTONUP\n");
166         if( wParam & MK_LBUTTON )
167             msg = WM_MBUTTONUP;
168         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
169         return 0;
170
171     case WM_MBUTTONDOWN:
172         DEBUG("WM_MBUTTONDOWN\n");
173         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
174         return 0;
175
176     case WM_MBUTTONUP:
177         DEBUG("WM_MBUTTONUP\n");
178         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam), msg );
179         return 0;
180
181     case WM_MOUSEMOVE:
182     {
183         if( (wParam & MK_LBUTTON) && (wParam & MK_RBUTTON) ) {
184             msg = WM_MBUTTONDOWN;
185         }
186         else if( wParam & MK_LBUTTON ) {
187             msg = WM_LBUTTONDOWN;
188         }
189         else {
190             return 0;
191         }
192
193         TestBoard( hWnd, &board, LOWORD(lParam), HIWORD(lParam),  msg );
194
195         return 0;
196     }
197
198     case WM_COMMAND:
199         switch(LOWORD(wParam)) {
200         case IDM_NEW:
201             CreateBoard( &board );
202             return 0;
203
204         case IDM_MARKQ:
205             hMenu = GetMenu( hWnd );
206             board.IsMarkQ = !board.IsMarkQ;
207             if( board.IsMarkQ )
208                 CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
209             else
210                 CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
211             return 0;
212
213         case IDM_BEGINNER:
214             SetDifficulty( &board, BEGINNER );
215             CreateBoard( &board );
216             return 0;
217
218         case IDM_ADVANCED:
219             SetDifficulty( &board, ADVANCED );
220             CreateBoard( &board );
221             return 0;
222
223         case IDM_EXPERT:
224             SetDifficulty( &board, EXPERT );
225             CreateBoard( &board );
226             return 0;
227
228         case IDM_CUSTOM:
229             SetDifficulty( &board, CUSTOM );
230             CreateBoard( &board );
231             return 0;
232
233         case IDM_EXIT:
234             SendMessage( hWnd, WM_CLOSE, 0, 0);
235             return 0;
236
237         case IDM_TIMES:
238             DialogBoxParam( board.hInst, "DLG_TIMES", hWnd,
239                     TimesDlgProc, (LPARAM) &board);
240             return 0;
241
242         case IDM_ABOUT:
243             DialogBox( board.hInst, "DLG_ABOUT", hWnd, AboutDlgProc );
244             return 0;
245         default:
246             DEBUG("Unknown WM_COMMAND command message received\n");
247             break;
248         }
249     }
250     return( DefWindowProc( hWnd, msg, wParam, lParam ));
251 }
252
253 void InitBoard( BOARD *p_board )
254 {
255     HMENU hMenu;
256
257     p_board->hMinesBMP = LoadBitmap( p_board->hInst, "mines");
258     p_board->hFacesBMP = LoadBitmap( p_board->hInst, "faces");
259     p_board->hLedsBMP = LoadBitmap( p_board->hInst, "leds");
260
261     LoadBoard( p_board );
262
263     if( p_board->pos.x < GetSystemMetrics( SM_CXFIXEDFRAME ))
264         p_board->pos.x = GetSystemMetrics( SM_CXFIXEDFRAME );
265
266     if( p_board->pos.x > (GetSystemMetrics( SM_CXSCREEN )
267     - GetSystemMetrics( SM_CXFIXEDFRAME ))) {
268         p_board->pos.x = GetSystemMetrics( SM_CXSCREEN )
269         - GetSystemMetrics( SM_CXFIXEDFRAME );
270     }
271
272     if( p_board->pos.y < (GetSystemMetrics( SM_CYMENU )
273     + GetSystemMetrics( SM_CYCAPTION )
274     + GetSystemMetrics( SM_CYFIXEDFRAME ))) {
275         p_board->pos.y = GetSystemMetrics( SM_CYMENU ) +
276         GetSystemMetrics( SM_CYCAPTION ) +
277         GetSystemMetrics( SM_CYFIXEDFRAME );
278     }
279
280     if( p_board->pos.y > (GetSystemMetrics( SM_CYSCREEN )
281     - GetSystemMetrics( SM_CYFIXEDFRAME ))) {
282         p_board->pos.y = GetSystemMetrics( SM_CYSCREEN )
283         - GetSystemMetrics( SM_CYFIXEDFRAME );
284     }
285
286     hMenu = GetMenu( p_board->hWnd );
287     CheckMenuItem( hMenu, IDM_BEGINNER + (unsigned) p_board->difficulty,
288             MF_CHECKED );
289     if( p_board->IsMarkQ )
290         CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
291     else
292         CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
293     CheckLevel( p_board );
294 }
295
296 void LoadBoard( BOARD *p_board )
297 {
298     DWORD size;
299     DWORD type;
300     HKEY hkey;
301     char data[16];
302     char key_name[8];
303     unsigned i;
304
305
306     RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Wine\\WineMine",
307             0, KEY_QUERY_VALUE, &hkey );
308
309     size = sizeof( data );
310     if( RegQueryValueEx( hkey, "Xpos", NULL, (LPDWORD) &type,
311             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS ) {
312         p_board->pos.x = atoi( data );
313     }
314     else
315         p_board->pos.x = GetSystemMetrics( SM_CXFIXEDFRAME );
316
317     size = sizeof( data );
318     if( RegQueryValueEx( hkey, "Ypos", NULL, (LPDWORD) &type,
319             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
320         p_board->pos.y = atoi( data );
321     else
322         p_board->pos.y = GetSystemMetrics( SM_CYMENU )
323         + GetSystemMetrics( SM_CYCAPTION )
324         + GetSystemMetrics( SM_CYFIXEDFRAME );
325
326     size = sizeof( data );
327     if( RegQueryValueEx( hkey, "Rows", NULL, (LPDWORD) &type,
328             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
329         p_board->rows = atoi( data );
330     else
331         p_board->rows = BEGINNER_ROWS;
332
333     size = sizeof( data );
334     if( RegQueryValueEx( hkey, "Cols", NULL, (LPDWORD) &type,
335             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
336         p_board->cols = atoi( data );
337     else
338         p_board->cols = BEGINNER_COLS;
339
340     size = sizeof( data );
341     if( RegQueryValueEx( hkey, "Mines", NULL, (LPDWORD) &type,
342             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
343         p_board->mines = atoi( data );
344     else
345         p_board->rows = BEGINNER_ROWS;
346
347     size = sizeof( data );
348     if( RegQueryValueEx( hkey, "Difficulty", NULL, (LPDWORD) &type,
349             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
350         p_board->difficulty = (DIFFICULTY) atoi( data );
351     else
352         p_board->difficulty = BEGINNER;
353
354     size = sizeof( data );
355     if( RegQueryValueEx( hkey, "MarkQ", NULL, (LPDWORD) &type,
356             (LPBYTE) data, (LPDWORD) &size ) == ERROR_SUCCESS )
357         p_board->IsMarkQ = atoi( data );
358     else
359         p_board->IsMarkQ = TRUE;
360
361     for( i = 0; i < 3; i++ ) {
362         wsprintf( key_name, "Name%d", i );
363         size = sizeof( data );
364         if( RegQueryValueEx( hkey, key_name, NULL, (LPDWORD) &type,
365                 (LPBYTE) data,
366                 (LPDWORD) &size ) == ERROR_SUCCESS )
367                     strncpy( p_board->best_name[i], data, sizeof( data ) );
368         else
369             LoadString( p_board->hInst, IDS_NOBODY, p_board->best_name[i], 16 );
370     }
371
372     for( i = 0; i < 3; i++ ) {
373         wsprintf( key_name, "Time%d", i );
374         size = sizeof( data );
375         if( RegQueryValueEx( hkey, key_name, NULL, (LPDWORD) &type,
376                 (LPBYTE) data,
377                 (LPDWORD) &size ) == ERROR_SUCCESS )
378             p_board->best_time[i] = atoi( data );
379         else
380             p_board->best_time[i] = 999;
381     }
382     RegCloseKey( hkey );
383 }
384
385 void SaveBoard( BOARD *p_board )
386 {
387     HKEY hkey;
388     unsigned i;
389     char data[16];
390     char key_name[8];
391
392     if( RegCreateKeyEx( HKEY_LOCAL_MACHINE,
393                 "Software\\Wine\\WineMine", 0, NULL,
394                 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
395                 &hkey, NULL ) != ERROR_SUCCESS)
396         return;
397
398     wsprintf( data, "%d", p_board->pos.x );
399     RegSetValueEx( hkey, "Xpos", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
400
401     wsprintf( data, "%d", p_board->pos.x );
402     RegSetValueEx( hkey, "Ypos", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
403
404     wsprintf( data, "%d", (int) p_board->difficulty );
405     RegSetValueEx( hkey, "Difficulty", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
406
407     wsprintf( data, "%d", p_board->rows );
408     RegSetValueEx( hkey, "Rows", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
409
410     wsprintf( data, "%d", p_board->cols );
411     RegSetValueEx( hkey, "Cols", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
412
413     wsprintf( data, "%d", p_board->mines );
414     RegSetValueEx( hkey, "Mines", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
415
416     wsprintf( data, "%d", (int) p_board->IsMarkQ );
417     RegSetValueEx( hkey, "MarkQ", 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
418
419     for( i = 0; i < 3; i++ ) {
420         wsprintf( key_name, "Name%u", i );
421         strncpy( data, p_board->best_name[i], sizeof( data ) );
422         RegSetValueEx( hkey, key_name, 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
423     }
424
425     for( i = 0; i < 3; i++ ) {
426         wsprintf( key_name, "Time%u", i );
427         wsprintf( data, "%d", p_board->best_time[i] );
428         RegSetValueEx( hkey, key_name, 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
429     }
430     RegCloseKey( hkey );
431 }
432
433 void DestroyBoard( BOARD *p_board )
434 {
435     DeleteObject( p_board->hFacesBMP );
436     DeleteObject( p_board->hLedsBMP );
437     DeleteObject( p_board->hMinesBMP );
438 }
439
440 void SetDifficulty( BOARD *p_board, DIFFICULTY difficulty )
441 {
442     HMENU hMenu;
443
444     if ( difficulty == CUSTOM )
445         if (DialogBoxParam( p_board->hInst, "DLG_CUSTOM", p_board->hWnd,
446                     CustomDlgProc, (LPARAM) p_board) != 0)
447            return;
448
449     hMenu = GetMenu( p_board->hWnd );
450     CheckMenuItem( hMenu, IDM_BEGINNER + p_board->difficulty, MF_UNCHECKED );
451     p_board->difficulty = difficulty;
452     CheckMenuItem( hMenu, IDM_BEGINNER + difficulty, MF_CHECKED );
453
454     switch( difficulty ) {
455     case BEGINNER:
456         p_board->cols = BEGINNER_COLS;
457         p_board->rows = BEGINNER_ROWS;
458         p_board->mines = BEGINNER_MINES;
459         break;
460
461     case ADVANCED:
462         p_board->cols = ADVANCED_COLS;
463         p_board->rows = ADVANCED_ROWS;
464         p_board->mines = ADVANCED_MINES;
465         break;
466
467     case EXPERT:
468         p_board->cols = EXPERT_COLS;
469         p_board->rows = EXPERT_ROWS;
470         p_board->mines = EXPERT_MINES;
471         break;
472
473     case CUSTOM:
474         break;
475     }
476 }
477
478 void CreateBoard( BOARD *p_board )
479 {
480     int left, top, bottom, right, wnd_x, wnd_y, wnd_width, wnd_height;
481
482     p_board->mb = MB_NONE;
483     p_board->boxes_left = p_board->cols * p_board->rows - p_board->mines;
484     p_board->num_flags = 0;
485
486     CreateBoxes( p_board );
487
488     p_board->width = p_board->cols * MINE_WIDTH + BOARD_WMARGIN * 2;
489
490     p_board->height = p_board->rows * MINE_HEIGHT + LED_HEIGHT
491         + BOARD_HMARGIN * 3;
492
493     wnd_x = p_board->pos.x - GetSystemMetrics( SM_CXFIXEDFRAME );
494     wnd_y = p_board->pos.y - GetSystemMetrics( SM_CYMENU )
495         - GetSystemMetrics( SM_CYCAPTION )
496         - GetSystemMetrics( SM_CYFIXEDFRAME );
497     wnd_width = p_board->width
498         + GetSystemMetrics( SM_CXFIXEDFRAME ) * 2;
499     wnd_height = p_board->height
500         + GetSystemMetrics( SM_CYMENU )
501         + GetSystemMetrics( SM_CYCAPTION )
502         + GetSystemMetrics( SM_CYFIXEDFRAME ) * 2;
503
504     /* setting the mines rectangle boundary */
505     left = BOARD_WMARGIN;
506     top = BOARD_HMARGIN * 2 + LED_HEIGHT;
507     right = left + p_board->cols * MINE_WIDTH;
508     bottom = top + p_board->rows * MINE_HEIGHT;
509     SetRect( &p_board->mines_rect, left, top, right, bottom );
510
511     /* setting the face rectangle boundary */
512     left = p_board->width / 2 - FACE_WIDTH / 2;
513     top = BOARD_HMARGIN;
514     right = left + FACE_WIDTH;
515     bottom = top + FACE_HEIGHT;
516     SetRect( &p_board->face_rect, left, top, right, bottom );
517
518     /* setting the timer rectangle boundary */
519     left = BOARD_WMARGIN;
520     top = BOARD_HMARGIN;
521     right = left + LED_WIDTH * 3;
522     bottom = top + LED_HEIGHT;
523     SetRect( &p_board->timer_rect, left, top, right, bottom );
524
525     /* setting the counter rectangle boundary */
526     left =  p_board->width - BOARD_WMARGIN - LED_WIDTH * 3;
527     top = BOARD_HMARGIN;
528     right = p_board->width - BOARD_WMARGIN;
529     bottom = top + LED_HEIGHT;
530     SetRect( &p_board->counter_rect, left, top, right, bottom );
531
532     p_board->status = WAITING;
533     p_board->face_bmp = SMILE_BMP;
534     p_board->time = 0;
535
536     MoveWindow( p_board->hWnd, wnd_x, wnd_y, wnd_width, wnd_height, TRUE );
537     RedrawWindow( p_board->hWnd, NULL, NULL_HANDLE,
538             RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE );
539 }
540
541
542 void CheckLevel( BOARD *p_board )
543 {
544     if( p_board->rows < BEGINNER_ROWS )
545         p_board->rows = BEGINNER_ROWS;
546
547     if( p_board->rows > MAX_ROWS )
548         p_board->rows = MAX_ROWS;
549
550     if( p_board->cols < BEGINNER_COLS )
551         p_board->cols = BEGINNER_COLS;
552
553     if( p_board->cols > MAX_COLS )
554         p_board->cols = MAX_COLS;
555
556     if( p_board->mines < BEGINNER_MINES )
557         p_board->mines = BEGINNER_MINES;
558
559     if( p_board->mines > p_board->cols * p_board->rows - 1 )
560         p_board->mines = p_board->cols * p_board->rows - 1;
561 }
562
563
564 void CreateBoxes( BOARD *p_board )
565 {
566     int i, j;
567     unsigned col, row;
568
569     srand( (unsigned) time( NULL ) );
570
571     /* Create the boxes...
572      * We actually create them with an empty border,
573      * so special care doesn't have to be taken on the edges
574      */
575
576     for( col = 0; col <= p_board->cols + 1; col++ )
577       for( row = 0; row <= p_board->rows + 1; row++ ) {
578         p_board->box[col][row].IsPressed = FALSE;
579         p_board->box[col][row].IsMine = FALSE;
580         p_board->box[col][row].FlagType = NORMAL;
581         p_board->box[col][row].NumMines = 0;
582       }
583
584     /* create mines */
585     i = 0;
586      while( (unsigned) i < p_board->mines ) {
587           col = (int) (p_board->cols * (float) rand() / RAND_MAX + 1);
588           row = (int) (p_board->rows * (float) rand() / RAND_MAX + 1);
589
590         if( !p_board->box[col][row].IsMine ) {
591             i++;
592             p_board->box[col][row].IsMine = TRUE;
593         }
594     }
595
596     /*
597      * Now we label the remaining boxes with the
598      * number of mines surrounding them.
599      */
600
601     for( col = 1; col < p_board->cols + 1; col++ )
602     for( row = 1; row < p_board->rows + 1; row++ ) {
603         for( i = -1; i <= 1; i++ )
604         for( j = -1; j <= 1; j++ ) {
605             if( p_board->box[col + i][row + j].IsMine ) {
606                 p_board->box[col][row].NumMines++ ;
607             }
608         }
609     }
610 }
611
612 void DrawMines ( HDC hdc, HDC hMemDC, BOARD *p_board )
613 {
614     HGDIOBJ hOldObj;
615     unsigned col, row;
616     hOldObj = SelectObject (hMemDC, p_board->hMinesBMP);
617
618     for( row = 1; row <= p_board->rows; row++ ) {
619       for( col = 1; col <= p_board->cols; col++ ) {
620         DrawMine( hdc, hMemDC, p_board, col, row, FALSE );
621       }
622     }
623     SelectObject( hMemDC, hOldObj );
624 }
625
626 void DrawMine( HDC hdc, HDC hMemDC, BOARD *p_board, unsigned col, unsigned row, BOOL IsPressed )
627 {
628     MINEBMP_OFFSET offset = BOX_BMP;
629
630     if( col == 0 || col > p_board->cols || row == 0 || row > p_board->rows )
631            return;
632
633     if( p_board->status == GAMEOVER ) {
634         if( p_board->box[col][row].IsMine ) {
635             switch( p_board->box[col][row].FlagType ) {
636             case FLAG:
637                 offset = FLAG_BMP;
638                 break;
639             case COMPLETE:
640                 offset = EXPLODE_BMP;
641                 break;
642             case QUESTION:
643                 /* fall through */
644             case NORMAL:
645                 offset = MINE_BMP;
646             }
647         } else {
648             switch( p_board->box[col][row].FlagType ) {
649             case QUESTION:
650                 offset = QUESTION_BMP;
651                 break;
652             case FLAG:
653                 offset = WRONG_BMP;
654                 break;
655             case NORMAL:
656                 offset = BOX_BMP;
657                 break;
658             case COMPLETE:
659                 /* Do nothing */
660                 break;
661             default:
662                 DEBUG("Unknown FlagType during game over in DrawMine\n");
663                 break;
664             }
665         }
666     } else {    /* WAITING or PLAYING */
667         switch( p_board->box[col][row].FlagType ) {
668         case QUESTION:
669             if( !IsPressed )
670                 offset = QUESTION_BMP;
671             else
672                 offset = QPRESS_BMP;
673             break;
674         case FLAG:
675             offset = FLAG_BMP;
676             break;
677         case NORMAL:
678             if( !IsPressed )
679                 offset = BOX_BMP;
680             else
681                 offset = MPRESS_BMP;
682             break;
683         case COMPLETE:
684             /* Do nothing */
685             break;
686         default:
687             DEBUG("Unknown FlagType while playing in DrawMine\n");
688             break;
689         }
690     }
691
692     if( p_board->box[col][row].FlagType == COMPLETE
693         && !p_board->box[col][row].IsMine )
694           offset = (MINEBMP_OFFSET) p_board->box[col][row].NumMines;
695
696     BitBlt( hdc,
697             (col - 1) * MINE_WIDTH + p_board->mines_rect.left,
698             (row - 1) * MINE_HEIGHT + p_board->mines_rect.top,
699             MINE_WIDTH, MINE_HEIGHT,
700             hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY );
701 }
702
703 void DrawLeds( HDC hdc, HDC hMemDC, BOARD *p_board, int number, int x, int y )
704 {
705     HGDIOBJ hOldObj;
706     unsigned led[3], i;
707     int count;
708
709     count = number;
710     if( count < 1000 ) {
711         if( count >= 0 ) {
712             led[0] = count / 100 ;
713             count -= led[0] * 100;
714         }
715         else {
716             led[0] = 10; /* negative sign */
717             count = -count;
718         }
719         led[1] = count / 10;
720         count -= led[1] * 10;
721         led[2] = count;
722     }
723     else {
724         for( i = 0; i < 3; i++ )
725             led[i] = 10;
726     }
727
728     /* use unlit led if not playing */
729     if( p_board->status == WAITING )
730         for( i = 0; i < 3; i++ )
731             led[i] = 11;
732
733     hOldObj = SelectObject (hMemDC, p_board->hLedsBMP);
734
735     for( i = 0; i < 3; i++ ) {
736         BitBlt( hdc,
737             i * LED_WIDTH + x,
738             y,
739             LED_WIDTH,
740             LED_HEIGHT,
741             hMemDC,
742             0,
743             led[i] * LED_HEIGHT,
744             SRCCOPY);
745     }
746
747     SelectObject( hMemDC, hOldObj );
748 }
749
750
751 void DrawFace( HDC hdc, HDC hMemDC, BOARD *p_board )
752 {
753     HGDIOBJ hOldObj;
754
755     hOldObj = SelectObject (hMemDC, p_board->hFacesBMP);
756
757     BitBlt( hdc,
758         p_board->face_rect.left,
759         p_board->face_rect.top,
760         FACE_WIDTH,
761         FACE_HEIGHT,
762         hMemDC, 0, p_board->face_bmp * FACE_HEIGHT, SRCCOPY);
763
764     SelectObject( hMemDC, hOldObj );
765 }
766
767
768 void DrawBoard( HDC hdc, HDC hMemDC, PAINTSTRUCT *ps, BOARD *p_board )
769 {
770     RECT tmp_rect;
771
772     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->counter_rect ) )
773         DrawLeds( hdc, hMemDC, p_board, p_board->mines - p_board->num_flags,
774                   p_board->counter_rect.left,
775                   p_board->counter_rect.top );
776
777     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->timer_rect ) )
778         DrawLeds( hdc, hMemDC, p_board, p_board->time,
779                   p_board->timer_rect.left,
780                   p_board->timer_rect.top );
781
782     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->face_rect ) )
783         DrawFace( hdc, hMemDC, p_board );
784
785     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->mines_rect ) )
786         DrawMines( hdc, hMemDC, p_board );
787 }
788
789
790 void TestBoard( HWND hWnd, BOARD *p_board, unsigned x, unsigned y, int msg )
791 {
792     POINT pt;
793     unsigned col,row;
794
795     pt.x = x;
796     pt.y = y;
797
798     if( PtInRect( &p_board->mines_rect, pt ) && p_board->status != GAMEOVER
799     && p_board->status != WON )
800         TestMines( p_board, pt, msg );
801     else {
802         UnpressBoxes( p_board,
803             p_board->press.x,
804             p_board->press.y );
805         p_board->press.x = 0;
806         p_board->press.y = 0;
807     }
808
809     if( p_board->boxes_left == 0 ) {
810         p_board->status = WON;
811
812         if (p_board->num_flags < p_board->mines) {
813             for( row = 1; row <= p_board->rows; row++ ) {
814                 for( col = 1; col <= p_board->cols; col++ ) {
815                     if (p_board->box[col][row].IsMine && p_board->box[col][row].FlagType != FLAG)
816                         p_board->box[col][row].FlagType = FLAG;
817                 }
818             }
819
820             p_board->num_flags = p_board->mines;
821
822             RedrawWindow( p_board->hWnd, NULL, NULL_HANDLE,
823                 RDW_INVALIDATE | RDW_UPDATENOW );
824         }
825
826         if( p_board->difficulty != CUSTOM &&
827                     p_board->time < p_board->best_time[p_board->difficulty] ) {
828             p_board->best_time[p_board->difficulty] = p_board->time;
829
830             DialogBoxParam( p_board->hInst, "DLG_CONGRATS", hWnd,
831                     CongratsDlgProc, (LPARAM) p_board);
832
833             DialogBoxParam( p_board->hInst, "DLG_TIMES", hWnd,
834                     TimesDlgProc, (LPARAM) p_board);
835         }
836     }
837     TestFace( p_board, pt, msg );
838 }
839
840 void TestMines( BOARD *p_board, POINT pt, int msg )
841 {
842     BOOL draw = TRUE;
843     int col, row;
844
845     col = (pt.x - p_board->mines_rect.left) / MINE_WIDTH + 1;
846     row = (pt.y - p_board->mines_rect.top ) / MINE_HEIGHT + 1;
847
848     switch ( msg ) {
849     case WM_LBUTTONDOWN:
850         if( p_board->press.x != col || p_board->press.y != row ) {
851             UnpressBox( p_board,
852                     p_board->press.x, p_board->press.y );
853             p_board->press.x = col;
854             p_board->press.y = row;
855             PressBox( p_board, col, row );
856         }
857         draw = FALSE;
858         break;
859
860     case WM_LBUTTONUP:
861         if( p_board->press.x != col || p_board->press.y != row )
862             UnpressBox( p_board,
863                     p_board->press.x, p_board->press.y );
864         p_board->press.x = 0;
865         p_board->press.y = 0;
866         if( p_board->box[col][row].FlagType != FLAG )
867             p_board->status = PLAYING;
868         CompleteBox( p_board, col, row );
869         break;
870
871     case WM_MBUTTONDOWN:
872         PressBoxes( p_board, col, row );
873         draw = FALSE;
874         break;
875
876     case WM_MBUTTONUP:
877         if( p_board->press.x != col || p_board->press.y != row )
878             UnpressBoxes( p_board,
879                     p_board->press.x, p_board->press.y );
880         p_board->press.x = 0;
881         p_board->press.y = 0;
882         CompleteBoxes( p_board, col, row );
883         break;
884
885     case WM_RBUTTONDOWN:
886         AddFlag( p_board, col, row );
887         p_board->status = PLAYING;
888         break;
889     default:
890         DEBUG("Unknown message type received in TestMines\n");
891         break;
892     }
893
894     if( draw )
895     {
896         RedrawWindow( p_board->hWnd, NULL, NULL_HANDLE,
897             RDW_INVALIDATE | RDW_UPDATENOW );
898     }
899 }
900
901
902 void TestFace( BOARD *p_board, POINT pt, int msg )
903 {
904     if( p_board->status == PLAYING || p_board->status == WAITING ) {
905         if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN )
906             p_board->face_bmp = OOH_BMP;
907         else p_board->face_bmp = SMILE_BMP;
908     }
909     else if( p_board->status == GAMEOVER )
910         p_board->face_bmp = DEAD_BMP;
911     else if( p_board->status == WON )
912             p_board->face_bmp = COOL_BMP;
913
914     if( PtInRect( &p_board->face_rect, pt ) ) {
915         if( msg == WM_LBUTTONDOWN )
916             p_board->face_bmp = SPRESS_BMP;
917
918         if( msg == WM_LBUTTONUP )
919             CreateBoard( p_board );
920     }
921
922     RedrawWindow( p_board->hWnd, &p_board->face_rect, NULL_HANDLE,
923         RDW_INVALIDATE | RDW_UPDATENOW );
924 }
925
926
927 void CompleteBox( BOARD *p_board, unsigned col, unsigned row )
928 {
929     int i, j;
930
931     if( p_board->box[col][row].FlagType != COMPLETE &&
932             p_board->box[col][row].FlagType != FLAG &&
933             col > 0 && col < p_board->cols + 1 &&
934             row > 0 && row < p_board->rows + 1 ) {
935         p_board->box[col][row].FlagType = COMPLETE;
936
937         if( p_board->box[col][row].IsMine ) {
938             p_board->face_bmp = DEAD_BMP;
939             p_board->status = GAMEOVER;
940         }
941         else if( p_board->status != GAMEOVER )
942             p_board->boxes_left--;
943
944         if( p_board->box[col][row].NumMines == 0 )
945         {
946             for( i = -1; i <= 1; i++ )
947             for( j = -1; j <= 1; j++ )
948                 CompleteBox( p_board, col + i, row + j  );
949         }
950     }
951 }
952
953
954 void CompleteBoxes( BOARD *p_board, unsigned col, unsigned row )
955 {
956     unsigned numFlags = 0;
957     int i, j;
958
959     if( p_board->box[col][row].FlagType == COMPLETE ) {
960         for( i = -1; i <= 1; i++ )
961           for( j = -1; j <= 1; j++ ) {
962             if( p_board->box[col+i][row+j].FlagType == FLAG )
963                 numFlags++;
964           }
965
966         if( numFlags == p_board->box[col][row].NumMines ) {
967             for( i = -1; i <= 1; i++ )
968               for( j = -1; j <= 1; j++ ) {
969                 if( p_board->box[col+i][row+j].FlagType != FLAG )
970                     CompleteBox( p_board, col+i, row+j );
971               }
972         }
973     }
974 }
975
976
977 void AddFlag( BOARD *p_board, unsigned col, unsigned row )
978 {
979     if( p_board->box[col][row].FlagType != COMPLETE ) {
980         switch( p_board->box[col][row].FlagType ) {
981         case FLAG:
982             if( p_board->IsMarkQ )
983                 p_board->box[col][row].FlagType = QUESTION;
984             else
985                 p_board->box[col][row].FlagType = NORMAL;
986             p_board->num_flags--;
987             break;
988
989         case QUESTION:
990             p_board->box[col][row].FlagType = NORMAL;
991             break;
992
993         default:
994             p_board->box[col][row].FlagType = FLAG;
995             p_board->num_flags++;
996         }
997     }
998 }
999
1000
1001 void PressBox( BOARD *p_board, unsigned col, unsigned row )
1002 {
1003     HDC hdc;
1004     HGDIOBJ hOldObj;
1005     HDC hMemDC;
1006
1007     hdc = GetDC( p_board->hWnd );
1008     hMemDC = CreateCompatibleDC( hdc );
1009     hOldObj = SelectObject (hMemDC, p_board->hMinesBMP);
1010
1011     DrawMine( hdc, hMemDC, p_board, col, row, TRUE );
1012
1013     SelectObject( hMemDC, hOldObj );
1014     DeleteDC( hMemDC );
1015     ReleaseDC( p_board->hWnd, hdc );
1016 }
1017
1018
1019 void PressBoxes( BOARD *p_board, unsigned col, unsigned row )
1020 {
1021     int i, j;
1022
1023     for( i = -1; i <= 1; i++ )
1024       for( j = -1; j <= 1; j++ ) {
1025         p_board->box[col + i][row + j].IsPressed = TRUE;
1026         PressBox( p_board, col + i, row + j );
1027     }
1028
1029     for( i = -1; i <= 1; i++ )
1030       for( j = -1; j <= 1; j++ ) {
1031         if( !p_board->box[p_board->press.x + i][p_board->press.y + j].IsPressed )
1032             UnpressBox( p_board, p_board->press.x + i, p_board->press.y + j );
1033     }
1034
1035     for( i = -1; i <= 1; i++ )
1036       for( j = -1; j <= 1; j++ ) {
1037         p_board->box[col + i][row + j].IsPressed = FALSE;
1038         PressBox( p_board, col + i, row + j );
1039     }
1040
1041     p_board->press.x = col;
1042     p_board->press.y = row;
1043 }
1044
1045
1046 void UnpressBox( BOARD *p_board, unsigned col, unsigned row )
1047 {
1048     HDC hdc;
1049     HGDIOBJ hOldObj;
1050     HDC hMemDC;
1051
1052     hdc = GetDC( p_board->hWnd );
1053     hMemDC = CreateCompatibleDC( hdc );
1054     hOldObj = SelectObject( hMemDC, p_board->hMinesBMP );
1055
1056     DrawMine( hdc, hMemDC, p_board, col, row, FALSE );
1057
1058     SelectObject( hMemDC, hOldObj );
1059     DeleteDC( hMemDC );
1060     ReleaseDC( p_board->hWnd, hdc );
1061 }
1062
1063
1064 void UnpressBoxes( BOARD *p_board, unsigned col, unsigned row )
1065 {
1066     int i, j;
1067
1068     for( i = -1; i <= 1; i++ )
1069       for( j = -1; j <= 1; j++ ) {
1070         UnpressBox( p_board, col + i, row + j );
1071       }
1072 }