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