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