]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/win32/win_syscon.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / sys / win32 / win_syscon.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include <errno.h>
33 #include <float.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <direct.h>
37 #include <io.h>
38 #include <conio.h>
39
40 #include "win_local.h"
41 #include "rc/AFEditor_resource.h"
42 #include "rc/doom_resource.h"
43
44 #define COPY_ID                 1
45 #define QUIT_ID                 2
46 #define CLEAR_ID                3
47
48 #define ERRORBOX_ID             10
49 #define ERRORTEXT_ID    11
50
51 #define EDIT_ID                 100
52 #define INPUT_ID                101
53
54 #define COMMAND_HISTORY 64
55
56 typedef struct {
57         HWND            hWnd;
58         HWND            hwndBuffer;
59
60         HWND            hwndButtonClear;
61         HWND            hwndButtonCopy;
62         HWND            hwndButtonQuit;
63
64         HWND            hwndErrorBox;
65         HWND            hwndErrorText;
66
67         HBITMAP         hbmLogo;
68         HBITMAP         hbmClearBitmap;
69
70         HBRUSH          hbrEditBackground;
71         HBRUSH          hbrErrorBackground;
72
73         HFONT           hfBufferFont;
74         HFONT           hfButtonFont;
75
76         HWND            hwndInputLine;
77
78         char            errorString[80];
79
80         char            consoleText[512], returnedText[512];
81         bool            quitOnClose;
82         int                     windowWidth, windowHeight;
83          
84         WNDPROC         SysInputLineWndProc;
85
86         idEditField     historyEditLines[COMMAND_HISTORY];
87
88         int                     nextHistoryLine;// the last line in the history buffer, not masked
89         int                     historyLine;    // the line being displayed from history buffer
90                                                                 // will be <= nextHistoryLine
91
92         idEditField     consoleField;
93
94 } WinConData;
95
96 static WinConData s_wcd;
97
98 static LONG WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
99         char *cmdString;
100         static bool s_timePolarity;
101
102         switch (uMsg) {
103                 case WM_ACTIVATE:
104                         if ( LOWORD( wParam ) != WA_INACTIVE ) {
105                                 SetFocus( s_wcd.hwndInputLine );
106                         }
107                 break;
108                 case WM_CLOSE:
109                         if ( cvarSystem->IsInitialized() && com_skipRenderer.GetBool() ) {
110                                 cmdString = Mem_CopyString( "quit" );
111                                 Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString );
112                         } else if ( s_wcd.quitOnClose ) {
113                                 PostQuitMessage( 0 );
114                         } else {
115                                 Sys_ShowConsole( 0, false );
116                                 win32.win_viewlog.SetBool( false );
117                         }
118                         return 0;
119                 case WM_CTLCOLORSTATIC:
120                         if ( ( HWND ) lParam == s_wcd.hwndBuffer ) {
121                                 SetBkColor( ( HDC ) wParam, RGB( 0x00, 0x00, 0x80 ) );
122                                 SetTextColor( ( HDC ) wParam, RGB( 0xff, 0xff, 0x00 ) );
123                                 return ( long ) s_wcd.hbrEditBackground;
124                         } else if ( ( HWND ) lParam == s_wcd.hwndErrorBox ) {
125                                 if ( s_timePolarity & 1 ) {
126                                         SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) );
127                                         SetTextColor( ( HDC ) wParam, RGB( 0xff, 0x0, 0x00 ) );
128                                 } else {
129                                         SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) );
130                                         SetTextColor( ( HDC ) wParam, RGB( 0x00, 0x0, 0x00 ) );
131                                 }
132                                 return ( long ) s_wcd.hbrErrorBackground;
133                         }
134                         break;
135                 case WM_SYSCOMMAND:
136                         if ( wParam == SC_CLOSE ) {
137                                 PostQuitMessage( 0 );
138                         }
139                         break;
140                 case WM_COMMAND:
141                         if ( wParam == COPY_ID ) {
142                                 SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
143                                 SendMessage( s_wcd.hwndBuffer, WM_COPY, 0, 0 );
144                         } else if ( wParam == QUIT_ID ) {
145                                 if ( s_wcd.quitOnClose ) {
146                                         PostQuitMessage( 0 );
147                                 } else {
148                                         cmdString = Mem_CopyString( "quit" );
149                                         Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString );
150                                 }
151                         } else if ( wParam == CLEAR_ID ) {
152                                 SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
153                                 SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" );
154                                 UpdateWindow( s_wcd.hwndBuffer );
155                         }
156                         break;
157                 case WM_CREATE:
158                         s_wcd.hbrEditBackground = CreateSolidBrush( RGB( 0x00, 0x00, 0x80 ) );
159                         s_wcd.hbrErrorBackground = CreateSolidBrush( RGB( 0x80, 0x80, 0x80 ) );
160                         SetTimer( hWnd, 1, 1000, NULL );
161                         break;
162 /*
163                 case WM_ERASEBKGND:
164                         HGDIOBJ oldObject;
165                         HDC hdcScaled;
166                         hdcScaled = CreateCompatibleDC( ( HDC ) wParam );
167                         assert( hdcScaled != 0 );
168                         if ( hdcScaled ) {
169                                 oldObject = SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo );
170                                 assert( oldObject != 0 );
171                                 if ( oldObject )
172                                 {
173                                         StretchBlt( ( HDC ) wParam, 0, 0, s_wcd.windowWidth, s_wcd.windowHeight, 
174                                                 hdcScaled, 0, 0, 512, 384,
175                                                 SRCCOPY );
176                                 }
177                                 DeleteDC( hdcScaled );
178                                 hdcScaled = 0;
179                         }
180                         return 1;
181 */
182                 case WM_TIMER:
183                         if ( wParam == 1 ) {
184                                 s_timePolarity = (bool)!s_timePolarity;
185                                 if ( s_wcd.hwndErrorBox ) {
186                                         InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE );
187                                 }
188                         }
189                         break;
190     }
191
192     return DefWindowProc( hWnd, uMsg, wParam, lParam );
193 }
194
195 LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
196         int key, cursor;
197         switch ( uMsg ) {
198         case WM_KILLFOCUS:
199                 if ( ( HWND ) wParam == s_wcd.hWnd || ( HWND ) wParam == s_wcd.hwndErrorBox ) {
200                         SetFocus( hWnd );
201                         return 0;
202                 }
203                 break;
204
205         case WM_KEYDOWN:
206                 key = MapKey( lParam );
207
208                 // command history
209                 if ( ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ) {
210                         if ( s_wcd.nextHistoryLine - s_wcd.historyLine < COMMAND_HISTORY && s_wcd.historyLine > 0 ) {
211                                 s_wcd.historyLine--;
212                         }
213                         s_wcd.consoleField = s_wcd.historyEditLines[ s_wcd.historyLine % COMMAND_HISTORY ];
214
215                         SetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer() );
216                         SendMessage( s_wcd.hwndInputLine, EM_SETSEL, s_wcd.consoleField.GetCursor(), s_wcd.consoleField.GetCursor() );
217                         return 0;
218                 }
219
220                 if ( ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ) {
221                         if ( s_wcd.historyLine == s_wcd.nextHistoryLine ) {
222                                 return 0;
223                         }
224                         s_wcd.historyLine++;
225                         s_wcd.consoleField = s_wcd.historyEditLines[ s_wcd.historyLine % COMMAND_HISTORY ];
226
227                         SetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer() );
228                         SendMessage( s_wcd.hwndInputLine, EM_SETSEL, s_wcd.consoleField.GetCursor(), s_wcd.consoleField.GetCursor() );
229                         return 0;
230                 }
231                 break;
232
233         case WM_CHAR:
234                 key = MapKey( lParam );
235
236                 GetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer(), MAX_EDIT_LINE );
237                 SendMessage( s_wcd.hwndInputLine, EM_GETSEL, (WPARAM) NULL, (LPARAM) &cursor );
238                 s_wcd.consoleField.SetCursor( cursor );
239
240                 // enter the line
241                 if ( key == K_ENTER || key == K_KP_ENTER ) {
242                         strncat( s_wcd.consoleText, s_wcd.consoleField.GetBuffer(), sizeof( s_wcd.consoleText ) - strlen( s_wcd.consoleText ) - 5 );
243                         strcat( s_wcd.consoleText, "\n" );
244                         SetWindowText( s_wcd.hwndInputLine, "" );
245
246                         Sys_Printf( "]%s\n", s_wcd.consoleField.GetBuffer() );
247
248                         // copy line to history buffer
249                         s_wcd.historyEditLines[s_wcd.nextHistoryLine % COMMAND_HISTORY] = s_wcd.consoleField;
250                         s_wcd.nextHistoryLine++;
251                         s_wcd.historyLine = s_wcd.nextHistoryLine;
252
253                         s_wcd.consoleField.Clear();
254
255                         return 0;
256                 }
257
258                 // command completion
259                 if ( key == K_TAB ) {
260                         s_wcd.consoleField.AutoComplete();
261
262                         SetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer() );
263                         //s_wcd.consoleField.SetWidthInChars( strlen( s_wcd.consoleField.GetBuffer() ) );
264                         SendMessage( s_wcd.hwndInputLine, EM_SETSEL, s_wcd.consoleField.GetCursor(), s_wcd.consoleField.GetCursor() );
265
266                         return 0;
267                 }
268
269                 // clear autocompletion buffer on normal key input
270                 if ( ( key >= K_SPACE && key <= K_BACKSPACE ) || 
271                         ( key >= K_KP_SLASH && key <= K_KP_PLUS ) || ( key >= K_KP_STAR && key <= K_KP_EQUALS ) ) {
272                         s_wcd.consoleField.ClearAutoComplete();
273                 }
274                 break;
275         }
276
277         return CallWindowProc( s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam );
278 }
279
280 /*
281 ** Sys_CreateConsole
282 */
283 void Sys_CreateConsole( void ) {
284         HDC hDC;
285         WNDCLASS wc;
286         RECT rect;
287         const char *DEDCLASS = WIN32_CONSOLE_CLASS;
288         int nHeight;
289         int swidth, sheight;
290         int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;
291         int i;
292
293         memset( &wc, 0, sizeof( wc ) );
294
295         wc.style         = 0;
296         wc.lpfnWndProc   = (WNDPROC) ConWndProc;
297         wc.cbClsExtra    = 0;
298         wc.cbWndExtra    = 0;
299         wc.hInstance     = win32.hInstance;
300         wc.hIcon         = LoadIcon( win32.hInstance, MAKEINTRESOURCE(IDI_ICON1));
301         wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
302         wc.hbrBackground = (struct HBRUSH__ *)COLOR_WINDOW;
303         wc.lpszMenuName  = 0;
304         wc.lpszClassName = DEDCLASS;
305
306         if ( !RegisterClass (&wc) ) {
307                 return;
308         }
309
310         rect.left = 0;
311         rect.right = 540;
312         rect.top = 0;
313         rect.bottom = 450;
314         AdjustWindowRect( &rect, DEDSTYLE, FALSE );
315
316         hDC = GetDC( GetDesktopWindow() );
317         swidth = GetDeviceCaps( hDC, HORZRES );
318         sheight = GetDeviceCaps( hDC, VERTRES );
319         ReleaseDC( GetDesktopWindow(), hDC );
320
321         s_wcd.windowWidth = rect.right - rect.left + 1;
322         s_wcd.windowHeight = rect.bottom - rect.top + 1;
323
324         //s_wcd.hbmLogo = LoadBitmap( win32.hInstance, MAKEINTRESOURCE( IDB_BITMAP_LOGO) );
325
326         s_wcd.hWnd = CreateWindowEx( 0,
327                                                            DEDCLASS,
328                                                            GAME_NAME,
329                                                            DEDSTYLE,
330                                                            ( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1,
331                                                            NULL,
332                                                            NULL,
333                                                            win32.hInstance,
334                                                            NULL );
335
336         if ( s_wcd.hWnd == NULL ) {
337                 return;
338         }
339
340         //
341         // create fonts
342         //
343         hDC = GetDC( s_wcd.hWnd );
344         nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 );
345
346         s_wcd.hfBufferFont = CreateFont( nHeight, 0, 0, 0, FW_LIGHT, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN | FIXED_PITCH, "Courier New" );
347
348         ReleaseDC( s_wcd.hWnd, hDC );
349
350         //
351         // create the input line
352         //
353         s_wcd.hwndInputLine = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | 
354                                                                                                 ES_LEFT | ES_AUTOHSCROLL,
355                                                                                                 6, 400, 528, 20,
356                                                                                                 s_wcd.hWnd, 
357                                                                                                 ( HMENU ) INPUT_ID,     // child window ID
358                                                                                                 win32.hInstance, NULL );
359
360         //
361         // create the buttons
362         //
363         s_wcd.hwndButtonCopy = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
364                                                                                                 5, 425, 72, 24,
365                                                                                                 s_wcd.hWnd, 
366                                                                                                 ( HMENU ) COPY_ID,      // child window ID
367                                                                                                 win32.hInstance, NULL );
368         SendMessage( s_wcd.hwndButtonCopy, WM_SETTEXT, 0, ( LPARAM ) "copy" );
369
370         s_wcd.hwndButtonClear = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
371                                                                                                 82, 425, 72, 24,
372                                                                                                 s_wcd.hWnd, 
373                                                                                                 ( HMENU ) CLEAR_ID,     // child window ID
374                                                                                                 win32.hInstance, NULL );
375         SendMessage( s_wcd.hwndButtonClear, WM_SETTEXT, 0, ( LPARAM ) "clear" );
376
377         s_wcd.hwndButtonQuit = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
378                                                                                                 462, 425, 72, 24,
379                                                                                                 s_wcd.hWnd, 
380                                                                                                 ( HMENU ) QUIT_ID,      // child window ID
381                                                                                                 win32.hInstance, NULL );
382         SendMessage( s_wcd.hwndButtonQuit, WM_SETTEXT, 0, ( LPARAM ) "quit" );
383
384
385         //
386         // create the scrollbuffer
387         //
388         s_wcd.hwndBuffer = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | 
389                                                                                                 ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
390                                                                                                 6, 40, 526, 354,
391                                                                                                 s_wcd.hWnd, 
392                                                                                                 ( HMENU ) EDIT_ID,      // child window ID
393                                                                                                 win32.hInstance, NULL );
394         SendMessage( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
395
396         s_wcd.SysInputLineWndProc = ( WNDPROC ) SetWindowLong( s_wcd.hwndInputLine, GWL_WNDPROC, ( long ) InputLineWndProc );
397         SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
398
399 // don't show it now that we have a splash screen up
400         if ( win32.win_viewlog.GetBool() ) {
401                 ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT);
402                 UpdateWindow( s_wcd.hWnd );
403                 SetForegroundWindow( s_wcd.hWnd );
404                 SetFocus( s_wcd.hwndInputLine );
405         }
406
407
408
409         s_wcd.consoleField.Clear();
410
411         for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
412                 s_wcd.historyEditLines[i].Clear();
413         }
414 }
415
416 /*
417 ** Sys_DestroyConsole
418 */
419 void Sys_DestroyConsole( void ) {
420         if ( s_wcd.hWnd ) {
421                 ShowWindow( s_wcd.hWnd, SW_HIDE );
422                 CloseWindow( s_wcd.hWnd );
423                 DestroyWindow( s_wcd.hWnd );
424                 s_wcd.hWnd = 0;
425         }
426 }
427
428 /*
429 ** Sys_ShowConsole
430 */
431 void Sys_ShowConsole( int visLevel, bool quitOnClose ) {
432
433         s_wcd.quitOnClose = quitOnClose;
434
435         if ( !s_wcd.hWnd ) {
436                 return;
437         }
438
439         switch ( visLevel ) {
440                 case 0:
441                         ShowWindow( s_wcd.hWnd, SW_HIDE );
442                 break;
443                 case 1:
444                         ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL );
445                         SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
446                 break;
447                 case 2:
448                         ShowWindow( s_wcd.hWnd, SW_MINIMIZE );
449                 break;
450                 default:
451                         Sys_Error( "Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel );
452                 break;
453         }
454 }
455
456 /*
457 ** Sys_ConsoleInput
458 */
459 char *Sys_ConsoleInput( void ) {
460         
461         if ( s_wcd.consoleText[0] == 0 ) {
462                 return NULL;
463         }
464                 
465         strcpy( s_wcd.returnedText, s_wcd.consoleText );
466         s_wcd.consoleText[0] = 0;
467         
468         return s_wcd.returnedText;
469 }
470
471 /*
472 ** Conbuf_AppendText
473 */
474 void Conbuf_AppendText( const char *pMsg )
475 {
476 #define CONSOLE_BUFFER_SIZE             16384
477
478         char buffer[CONSOLE_BUFFER_SIZE*2];
479         char *b = buffer;
480         const char *msg;
481         int bufLen;
482         int i = 0;
483         static unsigned long s_totalChars;
484
485         //
486         // if the message is REALLY long, use just the last portion of it
487         //
488         if ( strlen( pMsg ) > CONSOLE_BUFFER_SIZE - 1 ) {
489                 msg = pMsg + strlen( pMsg ) - CONSOLE_BUFFER_SIZE + 1;
490         } else {
491                 msg = pMsg;
492         }
493
494         //
495         // copy into an intermediate buffer
496         //
497         while ( msg[i] && ( ( b - buffer ) < sizeof( buffer ) - 1 ) ) {
498                 if ( msg[i] == '\n' && msg[i+1] == '\r' ) {
499                         b[0] = '\r';
500                         b[1] = '\n';
501                         b += 2;
502                         i++;
503                 } else if ( msg[i] == '\r' ) {
504                         b[0] = '\r';
505                         b[1] = '\n';
506                         b += 2;
507                 } else if ( msg[i] == '\n' ) {
508                         b[0] = '\r';
509                         b[1] = '\n';
510                         b += 2;
511                 } else if ( idStr::IsColor( &msg[i] ) ) {
512                         i++;
513                 } else {
514                         *b= msg[i];
515                         b++;
516                 }
517                 i++;
518         }
519         *b = 0;
520         bufLen = b - buffer;
521
522         s_totalChars += bufLen;
523
524         //
525         // replace selection instead of appending if we're overflowing
526         //
527         if ( s_totalChars > 0x7000 ) {
528                 SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
529                 s_totalChars = bufLen;
530         }
531
532         //
533         // put this text into the windows console
534         //
535         SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
536         SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 );
537         SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) buffer );
538 }
539
540 /*
541 ** Win_SetErrorText
542 */
543 void Win_SetErrorText( const char *buf ) {
544         idStr::Copynz( s_wcd.errorString, buf, sizeof( s_wcd.errorString ) );
545         if ( !s_wcd.hwndErrorBox ) {
546                 s_wcd.hwndErrorBox = CreateWindow( "static", NULL, WS_CHILD | WS_VISIBLE | SS_SUNKEN,
547                                                                                                         6, 5, 526, 30,
548                                                                                                         s_wcd.hWnd, 
549                                                                                                         ( HMENU ) ERRORBOX_ID,  // child window ID
550                                                                                                         win32.hInstance, NULL );
551                 SendMessage( s_wcd.hwndErrorBox, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
552                 SetWindowText( s_wcd.hwndErrorBox, s_wcd.errorString );
553
554                 DestroyWindow( s_wcd.hwndInputLine );
555                 s_wcd.hwndInputLine = NULL;
556         }
557 }