]> icculus.org git repositories - taylor/freespace2.git/blob - src/osapi/outwnd.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / osapi / outwnd.cpp
1 /*
2  * $Logfile: /Freespace2/code/OsApi/OutWnd.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Routines for debugging output
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:48  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:10  root
14  * Initial import.
15  *
16  * 
17  * 6     6/03/99 6:37p Dave
18  * More TNT fun. Made perspective bitmaps more flexible.
19  * 
20  * 5     6/02/99 6:18p Dave
21  * Fixed TNT lockup problems! Wheeeee!
22  * 
23  * 4     12/14/98 12:13p Dave
24  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
25  * Need to test now.
26  * 
27  * 3     10/08/98 2:38p Dave
28  * Cleanup up OsAPI code significantly. Removed old functions, centralized
29  * registry functions.
30  * 
31  * 2     10/07/98 10:53a Dave
32  * Initial checkin.
33  * 
34  * 1     10/07/98 10:50a Dave
35  * 
36  * 49    5/15/98 3:36p John
37  * Fixed bug with new graphics window code and standalone server.  Made
38  * hwndApp not be a global anymore.
39  * 
40  * 48    5/14/98 5:42p John
41  * Revamped the whole window position/mouse code for the graphics windows.
42  * 
43  * 47    5/14/98 11:24a Allender
44  * throw mprints to outtext regardless of gr mode
45  * 
46  * 46    4/30/98 4:53p John
47  * Restructured and cleaned up cfile code.  Added capability to read off
48  * of CD-ROM drive and out of multiple pack files.
49  * 
50  * 45    4/16/98 6:31p Dave
51  * Doubled MAX_FILTERS to 48
52  * 
53  * 44    3/31/98 5:18p John
54  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
55  * bunch of debug stuff out of player file.  Made model code be able to
56  * unload models and malloc out only however many models are needed.
57  *  
58  * 
59  * 43    3/11/98 12:06p John
60  * Made it so output window never gets focus upon startup
61  * 
62  * 42    2/16/98 9:47a John
63  * Made so only error, general, and warning are shown if no
64  * debug_filter.cfg and  .cfg file isn't saved if so.
65  * 
66  * 41    2/05/98 9:21p John
67  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
68  * game.
69  * 
70  * 40    1/15/98 9:14p John
71  * Made it so general, error and warning can't be turned off.
72  * 
73  * 39    9/13/97 10:44a Lawrance
74  * allow logging of nprintf output to file
75  * 
76  * 38    8/23/97 11:32a Dave
77  * Made debug window size correctly in standalone mode.
78  * 
79  * 37    8/22/97 3:42p Hoffoss
80  * Lowered the filter limit to 24, and added support for filter recycling,
81  * instead of the rather nasty Assert if we should happen to exceed this
82  * limit.
83  * 
84  * 36    8/05/97 4:29p Dave
85  * Futzed with window sizes/placements for standalone mode.
86  * 
87  * 35    8/04/97 3:15p Dave
88  * Added an include for systemvars.h
89  * 
90  * 34    5/20/97 1:13p Allender
91  * fixes to make outwnd work better under Win95
92  * 
93  * 33    5/14/97 11:08a John
94  * fixed bug under 95 that occasionally hung program.
95  * 
96  * 32    5/07/97 3:01p Lawrance
97  * check for NT in mono_init
98  * 
99  * 31    5/07/97 3:06p John
100  * disabled new mono direct stuff under NT.
101  * 
102  * 30    5/07/97 2:59p John
103  * Made so mono driver works under '95.
104  * 
105  * 29    4/22/97 10:56a John
106  * fixed some resource leaks.
107  *
108  * $NoKeywords: $
109  */
110
111 #ifndef NDEBUG
112
113 #include <windows.h>
114 #include <stdio.h>
115 #include <stdarg.h>
116 #include <string.h>
117
118 // to disable otherwise well-lodged compiler warning
119 #pragma warning(disable: 4201)
120
121 #include <winioctl.h>
122 #include <conio.h>
123
124 #include "osapi.h"
125 #include "outwnd.h"
126 #include "cfile.h"
127 #include "monopub.h"
128 #include "2d.h"
129 #include "freespaceresource.h"
130 #include "systemvars.h"
131
132 #define MAX_FILTERS 48
133 #define SCROLL_BUFFER_SIZE      512
134 #define MAX_LINE_WIDTH  128
135 #define TASKBAR_HEIGHT 30
136
137 #define ID_COPY 32000
138 #define ID_FIND 32010
139 #define ID_FILTER 32100
140 #define TIMER1 1
141 #define UP_FAST 65537
142 #define DOWN_FAST 65538
143
144 HANDLE hOutputThread=NULL;
145 DWORD OutputThreadID;
146
147 HWND hOutputWnd;
148 char szOutputClass[] = "OutputWindow";
149 char spaces[MAX_LINE_WIDTH + 1];
150 int outwnd_filter_count = 0;
151 int outwnd_filter_loaded = 0;
152 char find_text[85];
153
154 struct outwnd_filter_struct {
155         char name[FILTER_NAME_LENGTH];
156         int state;
157 } *outwnd_filter[MAX_FILTERS], real_outwnd_filter[MAX_FILTERS];
158
159 int mprintf_last_line = -1;
160 char outtext[SCROLL_BUFFER_SIZE][MAX_LINE_WIDTH];
161 int old_scroll_pos = -32768;
162 int nTextHeight=0, nTextWidth=0, nCharRows=0;
163 int outwnd_inited = FALSE;
164 int outwnd_disabled = TRUE;
165 int max_scroll_pos = SCROLL_BUFFER_SIZE - 1;
166 static int marked = 0;
167 int marked_top, marked_bottom, marked_left, marked_right;
168 int old_marked = 0, old_marked_top, old_marked_bottom, old_marked_left, old_marked_right;
169 int marking_started_x, marking_started_y, client_top_yoffset, cur_line_index, marking_active = 0;
170 int scroll_wait = 1;
171 BOOL OutputActive = FALSE;
172 LPARAM last_mousemove;
173 int Outwnd_changed = 0;
174 int find_line = -1, find_pos;
175
176 // monochrome screen info
177 HANDLE  mono_driver=NULL;                               // handle to the monochrome driver
178
179 void outwnd_print(char *id, char *tmp);
180 BOOL CALLBACK find_dlg_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam);
181 void find_text_in_outwindow(int n, int p);
182 void outwnd_copy_marked_selection(HWND hwnd);
183 void outwnd_update_marking(LPARAM l_parm, HWND hwnd);
184 void outwnd_paint(HWND hwnd);
185
186 int Outwnd_no_filter_file = 0;          // 0 = .cfg file found, 1 = not found and warning not printed yet, 2 = not found and warning printed
187
188 // used for file logging
189 #ifndef NDEBUG
190         int Log_debug_output_to_file = 0;
191         FILE *Log_fp;
192         char *Freespace_logfilename = "fs.log";
193 #endif
194
195 inline void text_hilight(HDC &hdc)
196 {
197         SetBkColor(hdc, RGB(0, 0, 0));
198         SetTextColor(hdc, RGB(255, 255, 255));
199 }
200
201 inline void text_normal(HDC &hdc)
202 {
203         SetBkColor(hdc, RGB(255, 255, 255));
204         SetTextColor(hdc, RGB(0, 0, 0));
205 }
206
207 inline void fix_marking_coords(int &x, int &y, LPARAM l_parm)
208 {
209         x = (signed short) LOWORD(l_parm) / nTextWidth;
210         y = ((signed short) HIWORD(l_parm) - client_top_yoffset) / nTextHeight + cur_line_index;
211         
212         if (y < 0)
213                 y = x = 0;
214
215         if (y > SCROLL_BUFFER_SIZE)
216         {
217                 y = SCROLL_BUFFER_SIZE;
218                 x = 0;
219         }
220
221         if (x < 0)
222                 x = -1;
223
224         if (x > MAX_LINE_WIDTH)
225         {
226                 y++;
227                 x = 0;  // marks to end of line
228         }
229 }
230
231
232 //                      InvalidateRect(hOutputWnd,NULL,0);
233
234 void load_filter_info(void)
235 {
236         FILE *fp;
237         char pathname[256], inbuf[FILTER_NAME_LENGTH+4];
238         int z;
239
240         outwnd_filter_loaded = 1;
241         outwnd_filter_count = 0;
242         strcpy(pathname, "Debug_filter.cfg" );
243         fp = fopen(pathname, "rt");
244         if (!fp)        {
245                 Outwnd_no_filter_file = 1;
246
247                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
248                 strcpy( outwnd_filter[outwnd_filter_count]->name, "error" );
249                 outwnd_filter[outwnd_filter_count]->state = 1;
250                 outwnd_filter_count++;
251
252                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
253                 strcpy( outwnd_filter[outwnd_filter_count]->name, "general" );
254                 outwnd_filter[outwnd_filter_count]->state = 1;
255                 outwnd_filter_count++;
256
257                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
258                 strcpy( outwnd_filter[outwnd_filter_count]->name, "warning" );
259                 outwnd_filter[outwnd_filter_count]->state = 1;
260                 outwnd_filter_count++;
261
262                 return;
263         }
264
265         Outwnd_no_filter_file = 0;
266
267         while (fgets(inbuf, FILTER_NAME_LENGTH+3, fp))
268         {
269                 if (outwnd_filter_count == MAX_FILTERS)
270                         break;
271
272                 outwnd_filter[outwnd_filter_count] = &real_outwnd_filter[outwnd_filter_count];
273                 if (*inbuf == '+')
274                         outwnd_filter[outwnd_filter_count]->state = 1;
275                 else if (*inbuf == '-')
276                         outwnd_filter[outwnd_filter_count]->state = 0;
277                 else continue;  // skip everything else
278
279                 z = strlen(inbuf) - 1;
280                 if (inbuf[z] == '\n')
281                         inbuf[z] = 0;
282
283                 Assert(strlen(inbuf+1) < FILTER_NAME_LENGTH);
284                 strcpy(outwnd_filter[outwnd_filter_count]->name, inbuf + 1);
285
286                 if ( !stricmp( outwnd_filter[outwnd_filter_count]->name, "error" ) )    {
287                         outwnd_filter[outwnd_filter_count]->state = 1;
288                 } else if ( !stricmp( outwnd_filter[outwnd_filter_count]->name, "general" ) )   {
289                         outwnd_filter[outwnd_filter_count]->state = 1;
290                 } else if ( !stricmp( outwnd_filter[outwnd_filter_count]->name, "warning" ) )   {
291                         outwnd_filter[outwnd_filter_count]->state = 1;
292                 }
293
294                 outwnd_filter_count++;
295         }
296
297         if (ferror(fp) && !feof(fp))
298                 nprintf(("Error", "Error reading \"%s\"\n", pathname));
299
300         fclose(fp);
301 }
302
303 void save_filter_info(void)
304 {
305         FILE *fp;
306         int i;
307         char pathname[256];
308
309         if (!outwnd_filter_loaded)
310                 return;
311
312         if ( Outwnd_no_filter_file )    {
313                 return; // No file, don't save
314         }
315
316         strcpy(pathname, "Debug_filter.cfg" );
317         fp = fopen(pathname, "wt");
318         if (fp)
319         {
320                 for (i=0; i<outwnd_filter_count; i++)
321                         fprintf(fp, "%c%s\n", outwnd_filter[i]->state ? '+' : '-', outwnd_filter[i]->name);
322
323                 fclose(fp);
324         }
325 }
326
327 void outwnd_printf2(char *format, ...)
328 {
329         char tmp[MAX_LINE_WIDTH*4];
330         va_list args;
331         
332         va_start(args, format);
333         vsprintf(tmp, format, args);
334         va_end(args);
335         outwnd_print("General", tmp);
336 }
337
338
339 char mono_ram[80*25*2];
340 int mono_x, mono_y;
341 int mono_found = 0;
342
343 void mono_flush()
344 {
345         if ( !mono_found ) return;
346         memcpy( (void *)0xb0000, mono_ram, 80*25*2 );
347 }
348
349 void mono_init()
350 {
351         int i;
352
353         OSVERSIONINFO ver;
354         
355         ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
356         GetVersionEx(&ver);
357         if ( ver.dwPlatformId == VER_PLATFORM_WIN32_NT )        {
358                 mono_found = 0;
359                 return;
360         }
361
362         _outp( 0x3b4, 0x0f );
363         _outp( 0x3b4+1, 0x55 );
364
365         if ( _inp( 0x3b4+1 ) == 0x55 )  {
366                 mono_found = 1;
367                 _outp( 0x3b4+1, 0 );
368         } else {
369                 mono_found = 0;
370                 return;
371         }
372
373
374         for (i=0; i<80*25; i++ )        {
375                 mono_ram[i*2+0] = ' ';
376                 mono_ram[i*2+1] = 0x07;
377         }
378         mono_flush();
379         mono_x = mono_y = 0;
380 }
381
382
383 void mono_print( char * text, int len )
384 {
385         int i, j;
386
387         if ( !mono_found ) return;
388
389         for (i=0; i<len; i++ )  {
390                 int scroll_it = 0;
391
392                 switch( text[i] )       {
393                 case '\n':
394                         scroll_it = 1;
395                         break;
396                 default:
397                         mono_ram[mono_y*160+mono_x*2] = text[i];
398                         if ( mono_x < 79 )      {
399                                 mono_x++;
400                         } else {
401                                 scroll_it = 1;
402                         }
403                         break;
404                 }
405
406                 if ( scroll_it )        {
407                         if ( mono_y < 24 )      {
408                                 mono_y++;
409                                 mono_x = 0;
410                         } else {
411                                 memmove( mono_ram, mono_ram+160, 80*24*2 );
412                                 for (j=0; j<80; j++ )   {
413                                         mono_ram[24*160+j*2] = ' ';
414                                 }
415                                 mono_y = 24;
416                                 mono_x = 0;
417                         }
418                 }
419         }
420         mono_flush();
421 }
422
423 void outwnd_printf(char *id, char *format, ...)
424 {
425         char tmp[MAX_LINE_WIDTH*4];
426         va_list args;
427         
428         va_start(args, format);
429         vsprintf(tmp, format, args);
430         va_end(args);
431         outwnd_print(id, tmp);
432 }
433
434 void outwnd_print(char *id, char *tmp)
435 {
436         char *sptr;
437         char *dptr;
438         int i, nrows, ccol;
439         outwnd_filter_struct *temp;
440
441         if(gr_screen.mode == GR_DIRECT3D){
442                 return;
443         }
444
445         if (!outwnd_inited)
446                 return;
447
448         if ( Outwnd_no_filter_file == 1 )       {
449                 Outwnd_no_filter_file = 2;
450
451                 outwnd_print( "general", "==========================================================================\n" );
452                 outwnd_print( "general", "DEBUG SPEW: No debug_filter.cfg found, so only general, error, and warning\n" );
453                 outwnd_print( "general", "categories can be shown and no debug_filter.cfg info will be saved.\n" );
454                 outwnd_print( "general", "==========================================================================\n" );
455         }
456
457         if (!id)
458                 id = "General";
459
460         for (i=0; i<outwnd_filter_count; i++)
461                 if (!stricmp(id, outwnd_filter[i]->name))
462                         break;
463
464
465         if (i == outwnd_filter_count)  // new id found that's not yet in filter list
466         {
467                 // Only create new filters if there was a filter file
468                 if ( Outwnd_no_filter_file )    {
469                         return;
470                 }
471
472                 if (outwnd_filter_count >= MAX_FILTERS) {
473                         Assert(outwnd_filter_count == MAX_FILTERS);  // how did it get over the max?  Very bad..
474                         outwnd_printf("General", "Outwnd filter limit reached.  Recycling \"%s\" to add \"%s\"",
475                                 outwnd_filter[MAX_FILTERS - 1]->name, id);
476
477                         i--;  // overwrite the last element (oldest used filter in the list)
478                 }
479
480                 Assert(strlen(id) < FILTER_NAME_LENGTH);
481                 outwnd_filter[i] = &real_outwnd_filter[i];  // note: this assumes the list doesn't have gaps (from deleting an element for example)
482                 strcpy(outwnd_filter[i]->name, id);
483                 outwnd_filter[i]->state = 1;
484                 outwnd_filter_count = i + 1;
485                 save_filter_info();
486         }
487
488         // sort the filters from most recently used to oldest, so oldest ones will get recycled first
489         temp = outwnd_filter[i];
490         while (i--)
491                 outwnd_filter[i + 1] = outwnd_filter[i];
492
493         i++;
494         outwnd_filter[i] = temp;
495
496         if (!outwnd_filter[i]->state)
497                 return;
498
499         if (mprintf_last_line == -1 )   {
500                 for (i=0; i<SCROLL_BUFFER_SIZE;i++)     {
501                         outtext[i][0] = 0;
502                 }
503
504                 mprintf_last_line = 0;
505         }
506
507         // printf out to the monochrome screen first
508         if ( mono_driver != ((HANDLE)-1) ) {
509                 DWORD   cbReturned;
510
511                 DeviceIoControl (mono_driver, (DWORD)IOCTL_MONO_PRINT, tmp, strlen(tmp), NULL, 0, &cbReturned, 0 );
512         } else {
513                 mono_print(tmp, strlen(tmp) );
514         }
515
516         sptr = tmp;
517         ccol = strlen(outtext[mprintf_last_line] );
518         dptr = &outtext[mprintf_last_line][ccol];
519         nrows = 0;
520
521 #ifndef NDEBUG
522
523         if ( Log_debug_output_to_file ) {
524                 if ( Log_fp != NULL ) {
525                         fputs(tmp, Log_fp);     
526                         fflush(Log_fp);
527                 }
528         }
529
530 #endif
531
532         while(*sptr) {
533                 if ( (*sptr == '\n') || (ccol >= MAX_LINE_WIDTH-1 ) )   {
534                         nrows++;
535                         mprintf_last_line++;
536                         if (mprintf_last_line >= SCROLL_BUFFER_SIZE )
537                                 mprintf_last_line = 0;
538                         ccol = 0;
539                         if ( *sptr != '\n' )    {
540                                 outtext[mprintf_last_line][ccol]        = *sptr;
541                                 ccol++;
542                         }
543                         outtext[mprintf_last_line][ccol] = '\0';
544                         dptr = &outtext[mprintf_last_line][ccol];
545                 } else {
546                         *dptr++ = *sptr;
547                         *dptr = '\0';
548                         ccol++;
549                 }
550                 sptr++;
551         } 
552
553         if(gr_screen.mode == GR_DIRECT3D){
554                 return;
555         }
556 //      if ( D3D_enabled )      {
557 //              return;         // Direct3D seems to hang sometimes printing to window
558 //      }
559
560         if ( outwnd_disabled ){
561                 return;
562         }
563
564         if ( !OutputActive )    {
565                 int oldpos = GetScrollPos( hOutputWnd, SB_VERT );
566                 if ( oldpos != max_scroll_pos ) {
567                         SCROLLINFO si;
568                         si.cbSize = sizeof(SCROLLINFO);
569                         si.fMask = SIF_POS;
570                         si.nPos = max_scroll_pos;
571                         SetScrollInfo(hOutputWnd, SB_VERT, &si, 1 );
572                         InvalidateRect(hOutputWnd,NULL,0);
573                         UpdateWindow(hOutputWnd);
574                 } else {
575                         if ( nrows )    {
576                                 RECT client;
577                                 ScrollWindow(hOutputWnd,0,-nTextHeight*nrows,NULL,NULL);
578                                 GetClientRect(hOutputWnd, &client);
579                                 client.top = client.bottom - nTextHeight*(nrows+1);
580                                 InvalidateRect(hOutputWnd,&client,0);
581
582                                 UpdateWindow(hOutputWnd);
583                         } else {
584                                 Outwnd_changed++;
585                         }
586                 }
587         }
588 }
589
590 LRESULT CALLBACK outwnd_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam)
591 {
592         
593         switch(msg)     {
594         case WM_ACTIVATEAPP:
595                 // The application z-ordering has change
596                 // foreground application wParm will be
597                 OutputActive = (BOOL)wParam;
598                 break;
599
600         case WM_CREATE: {
601                         SCROLLINFO si;
602                         si.cbSize = sizeof(SCROLLINFO);
603                         si.fMask = SIF_RANGE | SIF_POS;
604                         si.nMin = 0;
605                         si.nMax = max_scroll_pos;
606                         si.nPos = max_scroll_pos;
607                         SetScrollInfo(hwnd, SB_VERT, &si, 1 );
608                 }
609                 break;
610
611         case WM_TIMER:
612                 if (scroll_wait)
613                         PostMessage(hwnd, WM_MOUSEMOVE, 0, last_mousemove);
614
615                 if ( Outwnd_changed )   {
616                         RECT client;
617                         GetClientRect(hOutputWnd, &client);
618                         client.top = client.bottom - nTextHeight;
619                         InvalidateRect(hOutputWnd,&client,0);
620                 }
621
622                 scroll_wait = 0;
623                 break;
624
625         case WM_COMMAND:        {
626                 int z;
627
628                 z = LOWORD(wParam);
629                 if (z >= ID_FILTER && z < ID_FILTER + outwnd_filter_count)
630                 {
631                         z -= ID_FILTER;
632                         outwnd_filter[z]->state = !outwnd_filter[z]->state;
633
634                         if ( !stricmp( outwnd_filter[z]->name, "error" ) )      {
635                                 outwnd_filter[z]->state = 1;
636                         } else if ( !stricmp( outwnd_filter[z]->name, "general" ) )     {
637                                 outwnd_filter[z]->state = 1;
638                         } else if ( !stricmp( outwnd_filter[z]->name, "warning" ) )     {
639                                 outwnd_filter[z]->state = 1;
640                         }
641                         save_filter_info();
642                         break;
643                 }
644
645                 switch (z)
646                 {
647                         case ID_COPY:
648                                 outwnd_copy_marked_selection(hwnd);
649                                 break;
650
651                         /*
652                         case ID_FIND:
653                                 if (DialogBox(GetModuleHandle(NULL), "FIND_DIALOG", hOutputWnd,
654                                         (int (__stdcall *)(void)) find_dlg_handler) == IDOK)
655                                 {
656                                         find_text_in_outwindow(mprintf_last_line, 0);
657                                 }
658
659                                 break;
660                                 */
661                 }
662                 break;
663         }
664
665         case WM_RBUTTONDOWN:    {
666                         HMENU h_menu = CreatePopupMenu();
667                         HMENU h_sub_menu = CreatePopupMenu();
668                         POINT pt;
669                         int i;
670
671                         for (i=0; i<outwnd_filter_count; i++)
672                         {
673                                 UINT flags = MFT_STRING;        //MF_GRAYED;
674
675                                 if ( !stricmp( outwnd_filter[i]->name, "error" ) )      {
676                                         flags |= MF_GRAYED;
677                                 } else if ( !stricmp( outwnd_filter[i]->name, "general" ) )     {
678                                         flags |= MF_GRAYED;
679                                 } else if ( !stricmp( outwnd_filter[i]->name, "warning" ) )     {
680                                         flags |= MF_GRAYED;
681                                 }
682
683                                 if (outwnd_filter[i]->state)
684                                         AppendMenu(h_sub_menu, flags | MF_CHECKED, ID_FILTER + i, outwnd_filter[i]->name);
685                                 else
686                                         AppendMenu(h_sub_menu, flags, ID_FILTER + i, outwnd_filter[i]->name);
687                         }
688
689                         AppendMenu(h_menu, MFT_STRING, ID_COPY, "&Copy\tEnter");
690                         AppendMenu(h_menu, MFT_STRING, ID_FIND, "&Find Text");
691                         AppendMenu(h_menu, MF_POPUP, (unsigned int) h_sub_menu, "Filter &Messages");
692                         pt.x = LOWORD(lParam);
693                         pt.y = HIWORD(lParam);
694                         ClientToScreen(hwnd, &pt);
695
696                         TrackPopupMenu(h_menu, 0, pt.x, pt.y, 0, hwnd, NULL);
697                         DestroyMenu(h_menu);
698                         break;
699                 }
700                 
701         case WM_LBUTTONDOWN:
702                 fix_marking_coords(marking_started_x, marking_started_y, lParam);
703                 SetCapture(hwnd);  // monopolize mouse
704                 marking_active = 1;
705                 outwnd_update_marking(lParam, hwnd);
706                 break;
707
708         case WM_MOUSEMOVE:
709                 last_mousemove = lParam;
710                 if (marking_active){
711                         outwnd_update_marking(lParam, hwnd);
712                 }
713                 break;
714
715         case WM_LBUTTONUP:
716                 if (marking_active)
717                 {
718                         ReleaseCapture();
719                         marking_active = 0;
720                         outwnd_update_marking(lParam, hwnd);
721                 }
722                 break;
723         
724         case WM_VSCROLL:        {
725                         SCROLLINFO si;
726                         int vpos = GetScrollPos( hwnd, SB_VERT );
727                         int old_vpos=vpos;
728                         switch (LOWORD(wParam)) {
729                         case SB_LINEDOWN:
730                                 vpos++;
731                                 break;
732                         case SB_LINEUP:
733                                 vpos--;
734                                 break;
735                         case SB_THUMBPOSITION:
736                                 vpos = HIWORD(wParam);
737                                 break;
738                         case SB_THUMBTRACK:
739                                 vpos = HIWORD(wParam);
740                                 break;
741                         case SB_PAGEDOWN:
742                                 vpos += nCharRows;
743                                 break;
744                         case SB_PAGEUP:
745                                 vpos -= nCharRows;
746                                 break;
747                         }
748                         if ( vpos < 0 ) vpos = 0;
749                         else if ( vpos > max_scroll_pos ) vpos = max_scroll_pos;
750                         si.cbSize = sizeof(SCROLLINFO);
751                         si.fMask = SIF_POS;
752                         si.nPos = vpos;
753                         SetScrollInfo(hwnd, SB_VERT, &si, 1 );
754                         ScrollWindow(hwnd,0,(old_vpos-vpos)*nTextHeight,NULL,NULL);
755                         UpdateWindow(hOutputWnd);
756                         //InvalidateRect(hOutputWnd,NULL,0);
757                 }
758                 break;
759
760         case WM_KEYDOWN:        {
761                         SCROLLINFO si;
762                         int vpos = GetScrollPos( hwnd, SB_VERT );
763                         int old_vpos=vpos;
764                         int nVirtKey = (int) wParam;  // virtual-key code
765                         switch(nVirtKey)        {
766                         case VK_DOWN:
767                         case VK_RIGHT:
768                                 vpos++;
769                                 break;
770                         case VK_UP:
771                         case VK_LEFT:
772                                 vpos--;
773                                 break;
774                         case VK_NEXT:
775                                 vpos += nCharRows;
776                                 break;
777                         case VK_PRIOR:
778                                 vpos -= nCharRows;
779                                 break;
780                         case VK_END:
781                                 vpos = 0;
782                                 break;
783                         case VK_HOME:
784                                 vpos = max_scroll_pos;
785                                 break;
786                         case VK_RETURN:
787                                 outwnd_copy_marked_selection(hwnd);
788                                 break;
789                         case UP_FAST:  // special value we define
790                                 vpos -= 5;
791                                 break;
792                         case DOWN_FAST:  // special value we define
793                                 vpos += 5;
794                                 break;
795                         }
796                         
797                         if ( vpos < 0 ) vpos = 0;
798                         else if ( vpos > max_scroll_pos ) vpos = max_scroll_pos;
799                         si.cbSize = sizeof(SCROLLINFO);
800                         si.fMask = SIF_POS;
801                         si.nPos = vpos;
802                         SetScrollInfo(hwnd, SB_VERT, &si, 1 );
803                         ScrollWindow(hwnd, 0, (old_vpos-vpos)*nTextHeight, NULL, NULL);
804                         UpdateWindow(hOutputWnd);
805                         //InvalidateRect(hOutputWnd,NULL,0);
806                 }
807                 break;
808
809         case WM_SIZE:
810                 InvalidateRect(hOutputWnd,NULL,0);
811                 break;
812
813         case WM_PAINT:
814                 outwnd_paint(hwnd);
815                 break;
816
817         case WM_DESTROY:
818                 outwnd_disabled = 1;
819                 PostQuitMessage(0);
820                 break;
821
822         default:
823                 return DefWindowProc(hwnd, msg, wParam, lParam);
824                 break;
825         }
826
827         return 0;
828 }
829
830 void outwnd_copy_marked_selection(HWND hwnd)
831 {
832         HGLOBAL h_text;
833         int i, size = 1;
834         char *ptr;
835
836         if (marked_top == marked_bottom)
837         {
838                 size += marked_right - marked_left;
839
840         } else {
841                 i = marked_top;
842                 size += strlen(outtext[i++]) - marked_left + 2;
843
844                 while (i < marked_bottom)
845                         size += strlen(outtext[i++]) + 2;
846
847                 size += marked_right;
848         }
849
850         h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);
851         ptr = (char *) GlobalLock(h_text);
852
853         if (marked_top == marked_bottom)
854         {
855                 i = marked_right - marked_left;
856                 memcpy(ptr, outtext[marked_top] + marked_left, i);
857                 ptr[i] = 0;
858
859         } else {
860                 i = marked_top;
861                 strcpy(ptr, outtext[i] + marked_left);
862                 strcat(ptr, "\r\n");
863                 i++;
864
865                 while (i < marked_bottom)
866                 {
867                         strcat(ptr, outtext[i++]);
868                         strcat(ptr, "\r\n");
869                 }
870
871                 ptr[strlen(ptr) + marked_right] = 0;
872                 strncat(ptr, outtext[i], marked_right);
873         }
874
875         GlobalUnlock(h_text);
876         OpenClipboard(hwnd);
877         EmptyClipboard();
878         SetClipboardData(CF_TEXT, h_text);
879         CloseClipboard();
880
881         marked = 0;
882         InvalidateRect(hwnd, NULL, 0);
883 }
884
885 void outwnd_paint(HWND hwnd)
886 {
887         int i, n, x, y, len;
888         int old_nrows, scroll_pos;
889         HDC hdc;
890         PAINTSTRUCT ps; 
891         RECT client;
892         TEXTMETRIC tm;
893         HFONT newfont, oldfont;
894         HBRUSH newbrush, oldbrush;
895
896         Outwnd_changed = 0;
897
898         hdc = BeginPaint(hwnd, &ps);
899         GetClientRect(hOutputWnd, &client);
900         newfont = (HFONT)GetStockObject(ANSI_FIXED_FONT);
901         oldfont = (HFONT)SelectObject(hdc,newfont);
902         
903         GetTextMetrics(hdc, &tm);
904         nTextHeight = tm.tmHeight + tm.tmExternalLeading;
905         nTextWidth = tm.tmAveCharWidth;
906         old_nrows = nCharRows;
907         nCharRows = ((client.bottom-client.top)/nTextHeight)+1;
908
909         newbrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
910         oldbrush = (HBRUSH)SelectObject(hdc,newbrush);
911         
912         y = client.bottom - nTextHeight * nCharRows;  // starting y position at top
913         client_top_yoffset = y - nTextHeight;
914         scroll_pos = (max_scroll_pos - GetScrollPos( hOutputWnd, SB_VERT ));
915         cur_line_index = x = mprintf_last_line - scroll_pos - nCharRows;
916         if (x >= marked_top && x < marked_bottom)  // starting in marked area
917                 text_hilight(hdc);
918         else
919                 text_normal(hdc);
920         if (scroll_pos != old_scroll_pos) {
921                 if (!scroll_pos)        {
922                         char tmp[1024];
923                         sprintf( tmp, "Debug Spew");
924                         SetWindowText( hOutputWnd, tmp );
925
926                 } else {
927                         char tmp[1024];
928                         sprintf( tmp, "Debug Spew [Scrolled back %d lines]", scroll_pos );
929                         SetWindowText( hOutputWnd, tmp );
930                 }
931
932                 old_scroll_pos = scroll_pos;
933         }
934
935         i = nCharRows;
936         while (i--)
937         {
938                 n = mprintf_last_line - scroll_pos - i;
939                 if (n < 0)
940                         n += SCROLL_BUFFER_SIZE;
941         
942                 if (n >= 0 && n < SCROLL_BUFFER_SIZE)
943                 {
944                         len = strlen(outtext[n]);
945                         if (marked)
946                         {
947                                 if (n == marked_top && n == marked_bottom)  // special 1 line case
948                                 {
949                                         if (marked_left)
950                                                 TextOut(hdc, 0, y, outtext[n], marked_left);
951
952                                         text_hilight(hdc);
953                                         x = marked_left * nTextWidth;
954                                         TextOut(hdc, x, y, outtext[n] + marked_left, marked_right -
955                                                 marked_left);
956
957                                         text_normal(hdc);
958                                         x = marked_right * nTextWidth;
959                                         if (marked_right < len)
960                                                 TextOut(hdc, x, y, outtext[n] + marked_right, len - marked_right);
961
962                                         x = len * nTextWidth;
963                                         TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
964
965                                 } else if (n == marked_top)     {  // start marked on this line
966                                         if (marked_left)
967                                                 TextOut(hdc, 0, y, outtext[n], marked_left);
968
969                                         text_hilight(hdc);
970                                         x = marked_left * nTextWidth;
971
972                                         TextOut(hdc, x, y, outtext[n] + marked_left, len - marked_left);
973
974                                         x = len * nTextWidth;
975                                         if (marked_left < MAX_LINE_WIDTH)
976                                                 TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - marked_left);
977
978                                 } else if (n == marked_bottom)  {  // end marked on this line
979                                         if (marked_right)
980                                                 TextOut(hdc, 0, y, outtext[n], marked_right);
981
982                                         text_normal(hdc);
983                                         x = marked_right * nTextWidth;
984                                         if (marked_right < len)
985                                                 TextOut(hdc, x, y, outtext[n] + marked_right, len - marked_right);
986
987                                         x = len * nTextWidth;
988                                         TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
989
990                                 } else  {  // whole line marked
991                                         TextOut(hdc, 0, y, outtext[n], len);
992                                         x = len * nTextWidth;
993                                         TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
994                                 }
995
996                         } else {
997                                 TextOut(hdc, 0, y, outtext[n], len);
998                                 x = len * nTextWidth;
999                                 TextOut(hdc, x, y, spaces, MAX_LINE_WIDTH - len);
1000                         }
1001                 } else
1002                         TextOut(hdc, 0, y, spaces, MAX_LINE_WIDTH);
1003
1004                 y += nTextHeight;
1005         }
1006
1007         text_normal(hdc);
1008         SelectObject(hdc, oldfont);
1009         SelectObject(hdc, oldbrush);
1010         DeleteObject(newbrush);
1011
1012         if ( old_nrows != nCharRows )   {
1013                 SCROLLINFO si;
1014                 max_scroll_pos = SCROLL_BUFFER_SIZE-nCharRows - 1;
1015                 si.cbSize = sizeof(SCROLLINFO);
1016                 si.fMask = SIF_RANGE;
1017                 si.nMin = 0;
1018                 si.nMax = max_scroll_pos;
1019                 SetScrollInfo(hwnd, SB_VERT, &si, 1 );
1020         }
1021
1022         EndPaint(hwnd, &ps);
1023
1024 }
1025
1026 void outwnd_update_marking(LPARAM l_parm, HWND hwnd)
1027 {
1028         int x, y;
1029         RECT rect;
1030
1031         if (!scroll_wait)       {
1032                 y = (signed short) HIWORD(l_parm);
1033                 GetClientRect(hwnd, &rect);
1034                 
1035                 if (y < -150)   {
1036                         SendMessage(hwnd, WM_KEYDOWN, UP_FAST, 0);
1037                         scroll_wait = 1;
1038
1039                 } else if (y < 0)       {
1040                         SendMessage(hwnd, WM_KEYDOWN, VK_UP, 0);
1041                         scroll_wait = 1;
1042                 }
1043
1044                 if (y >= rect.bottom + 150)     {
1045                         SendMessage(hwnd, WM_KEYDOWN, DOWN_FAST, 0);
1046                         scroll_wait = 1;
1047
1048                 } else if (y >= rect.bottom)    {
1049                         SendMessage(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1050                         scroll_wait = 1;
1051                 }
1052         }
1053
1054         fix_marking_coords(x, y, l_parm);
1055         marked = 1;
1056         marked_top = marked_bottom = marking_started_y;
1057         marked_left = marking_started_x;
1058         marked_right = marking_started_x + 1;
1059
1060         if (y < marked_top)
1061         {
1062                 marked_top = y;
1063                 marked_left = x;
1064
1065         } else if (y > marked_bottom)   {
1066                 marked_bottom = y;
1067                 marked_right = x + 1;
1068
1069         } else {  // must be single line case
1070                 if (x < marked_left)
1071                         marked_left = x;
1072                 if (x >= marked_right)
1073                         marked_right = x + 1;
1074
1075                 if (marked_left >= (signed int) strlen(outtext[y]))
1076                 {
1077                         marked = 0;  // this isn't going to even show up
1078                         return;
1079                 }
1080         }
1081
1082         if (marked_left >= (signed int) strlen(outtext[marked_top]))
1083         {
1084                 marked_top++;
1085                 marked_left = 0;
1086         }
1087
1088         if (marked_right > (signed int) strlen(outtext[marked_bottom]))
1089                 marked_right = strlen(outtext[marked_bottom]);
1090
1091         if (marked && (marked_top > marked_bottom || (marked_top == marked_bottom &&
1092                 marked_left >= marked_right)))
1093         {
1094                 char msg[1024];
1095
1096                 sprintf(msg, "Marking limits invalid!\n"
1097                         "(%d,%d) to (%d,%d)", marked_left, marked_top, marked_right, marked_bottom);
1098
1099                 MessageBox(hwnd, msg, "Error", MB_OK | MB_ICONERROR);
1100                 marked = 0;
1101         }
1102
1103         if (marked != old_marked || marked_top != old_marked_top || marked_bottom !=
1104                 old_marked_bottom || marked_left != old_marked_left || marked_right !=
1105                 old_marked_right)
1106         {
1107                 old_marked = marked;
1108                 old_marked_left = marked_left;
1109                 old_marked_right = marked_right;
1110                 old_marked_top = marked_top;
1111                 old_marked_bottom = marked_bottom;
1112                 InvalidateRect(hwnd, NULL, 0);
1113         }
1114 }
1115
1116 BOOL outwnd_create(int display_under_freespace_window)
1117 {
1118         int x;
1119         WNDCLASSEX wclass;
1120         HINSTANCE hInst = GetModuleHandle(NULL);
1121         RECT rect;
1122         DWORD style;
1123
1124         wclass.hInstance                = hInst;
1125         wclass.lpszClassName    = szOutputClass;
1126         wclass.lpfnWndProc              = (WNDPROC) outwnd_handler;
1127         wclass.style                    = CS_BYTEALIGNCLIENT | CS_OWNDC;        //CS_DBLCLKS | CS_PARENTDC| CS_VREDRAW | CS_HREDRAW |;
1128         wclass.cbSize                   = sizeof(WNDCLASSEX);
1129         wclass.hIcon                    = LoadIcon(NULL, IDI_APPLICATION);
1130         wclass.hIconSm                  = LoadIcon(NULL, IDI_APPLICATION);
1131         wclass.hCursor                  = LoadCursor(NULL, IDC_ARROW);
1132         wclass.lpszMenuName             = NULL; //"FreeSpaceMenu";
1133         wclass.cbClsExtra               = 0;
1134         wclass.cbWndExtra               = 0;
1135         wclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);     //(HBRUSH)NULL;
1136
1137         if (!RegisterClassEx(&wclass))
1138                 return FALSE;
1139
1140         if (display_under_freespace_window)     {
1141                 style = WS_OVERLAPPEDWINDOW;;
1142
1143                 RECT client_rect;
1144                 client_rect.left = client_rect.top = 0;
1145                 client_rect.right = 640;
1146                 client_rect.bottom = 480;
1147                 AdjustWindowRect(&client_rect,WS_CAPTION | WS_SYSMENU,FALSE);
1148
1149                 int x = (GetSystemMetrics( SM_CXSCREEN )-(client_rect.right - client_rect.left))/2;
1150                 int y = 0;
1151                 if ( x < 0 ) x = 0;
1152
1153                 rect.left = x;
1154                 rect.top = y;
1155                 rect.right = x + client_rect.right - client_rect.left - 1;
1156                 rect.bottom = y + client_rect.bottom - client_rect.top - 1;
1157
1158                 if(!Is_standalone){
1159                         rect.top = rect.bottom;
1160                         rect.bottom = GetSystemMetrics(SM_CYSCREEN) - TASKBAR_HEIGHT - rect.top;
1161                         rect.right -= rect.left;
1162                 } else {
1163                         rect.left += 350;
1164                         rect.right = 550;
1165                         rect.bottom = 400;
1166                 }       
1167         } else {
1168                 style = WS_OVERLAPPEDWINDOW | WS_MINIMIZE;;
1169                 rect.top = rect.bottom = rect.left = rect.right = CW_USEDEFAULT;
1170         }
1171         
1172         //      Create Game Window
1173         hOutputWnd = CreateWindow(szOutputClass, "Debug Spew", style, rect.left,
1174                 rect.top, rect.right, rect.bottom, NULL, NULL, hInst, NULL);
1175
1176                 // Show it, but don't activate it.  If you activate it, it cause problems
1177         // with fullscreen startups in main window.
1178         SetWindowPos( hOutputWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW );
1179
1180         outwnd_disabled = FALSE;
1181         SetTimer(hOutputWnd, TIMER1, 50, NULL);
1182         for (x=0; x<MAX_LINE_WIDTH; x++)
1183                 spaces[x] = ' ';
1184
1185         spaces[MAX_LINE_WIDTH] = 0;
1186         return TRUE;
1187 }
1188
1189 DWORD outwnd_thread(int display_under_freespace_window)
1190 {
1191         MSG msg;
1192
1193         if (!outwnd_create(display_under_freespace_window))
1194                 return 0;
1195
1196         while (1)       {
1197                 if (WaitMessage())      {
1198                         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))   {
1199                                 TranslateMessage(&msg);
1200                                 DispatchMessage(&msg);
1201                         }
1202                 }
1203
1204                 if (outwnd_disabled) break;
1205         }
1206         return 0;
1207 }
1208
1209 void close_mono()
1210 {
1211 //      DeviceIoControl (mono_driver, (DWORD) IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0, &cbReturned, 0);
1212         if (hOutputThread)      {
1213                 CloseHandle(hOutputThread);
1214                 hOutputThread = NULL;
1215         }
1216         if (mono_driver)        {
1217                 CloseHandle(mono_driver);
1218                 mono_driver = NULL;
1219         }
1220 }
1221
1222 void outwnd_init(int display_under_freespace_window)
1223 {
1224 #ifndef NMONO
1225         if (!outwnd_inited)     {
1226                 outwnd_inited = TRUE;
1227                 hOutputThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)outwnd_thread, (LPVOID)display_under_freespace_window, 0, &OutputThreadID);
1228                 //SetThreadPriority(hOutputThread, THREAD_PRIORITY_TIME_CRITICAL);
1229
1230                 // set up the monochrome drivers
1231     if ( (mono_driver = CreateFile("\\\\.\\MONO", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == ((HANDLE)-1))  {
1232                  outwnd_printf2("Cannot get handle to monochrome driver.\n");
1233                  mono_init();
1234                 }
1235
1236          atexit(close_mono);
1237         }
1238 #endif
1239
1240 #ifndef NDEBUG
1241         if ( Log_fp == NULL ) {
1242                 Log_fp = fopen(Freespace_logfilename, "wb");
1243         }
1244 #endif 
1245 }
1246
1247 BOOL CALLBACK find_dlg_handler(HWND hwnd,UINT msg,WPARAM wParam, LPARAM lParam)
1248 {
1249         switch (msg)
1250         {
1251                 case WM_COMMAND:
1252                         switch (LOWORD(wParam)) {
1253                                 case IDOK:
1254                                         GetDlgItemText(hwnd, IDC_TEXT, find_text, 82);  // get the text to find
1255                                         EndDialog(hwnd, IDOK);
1256                                         return 1;
1257
1258                                 case IDCANCEL:
1259                                         EndDialog(hwnd, 0);
1260                                         return 1;
1261                         }
1262                         break;
1263
1264                 case WM_INITDIALOG:
1265                         SendDlgItemMessage(hwnd, IDC_TEXT, EM_LIMITTEXT, 80, 0);  // limit text to 80 chars
1266                         if (GetDlgCtrlID((HWND) wParam) != IDC_TEXT) {
1267                                 SetFocus(GetDlgItem(hwnd, IDC_TEXT));
1268                                 return FALSE;
1269                         }
1270
1271                         return TRUE;
1272         }
1273
1274         return 0;
1275 }
1276
1277 void find_text_in_outwindow(int n, int p)
1278 {
1279         char *ptr, *str;
1280
1281         str = outtext[n] + p;
1282         while (1)
1283         {
1284                 ptr = strstr(str, find_text);
1285                 if (ptr)
1286                 {
1287                         int scroll_pos, pos;
1288
1289                         find_pos = ptr - str;
1290                         find_line = n;
1291                         marked = 1;
1292                         marked_top = marked_bottom = find_line;
1293                         marked_left = find_pos;
1294                         marked_right = find_pos + strlen(find_text);
1295
1296                         scroll_pos = (max_scroll_pos - GetScrollPos(hOutputWnd, SB_VERT));
1297                         pos = mprintf_last_line - scroll_pos;
1298                         if (pos < 0)
1299                                 pos += SCROLL_BUFFER_SIZE;
1300
1301                         pos -= n;
1302                         if (pos < 0)
1303                                 pos += SCROLL_BUFFER_SIZE;
1304
1305                         Assert(pos >= 0);
1306                         if (pos >= nCharRows - 1)  // outside of window viewport, so scroll
1307                         {
1308                                 SCROLLINFO si;
1309
1310                                 pos = mprintf_last_line - n - nCharRows / 2;
1311                                 if (pos < 0)
1312                                         pos += SCROLL_BUFFER_SIZE;
1313
1314                                 if (pos > max_scroll_pos)
1315                                         pos = max_scroll_pos;
1316
1317                                 scroll_pos = max_scroll_pos - GetScrollPos(hOutputWnd, SB_VERT);
1318                                 si.cbSize = sizeof(SCROLLINFO);
1319                                 si.fMask = SIF_POS;
1320                                 si.nPos = max_scroll_pos - pos;
1321                                 SetScrollInfo(hOutputWnd, SB_VERT, &si, 1);
1322                                 ScrollWindow(hOutputWnd, 0, (scroll_pos - pos) * nTextHeight, NULL, NULL);
1323                         }
1324
1325                         InvalidateRect(hOutputWnd, NULL, 0);
1326                         UpdateWindow(hOutputWnd);
1327                         return;
1328                 }
1329
1330                 n--;
1331                 if (n < 0)
1332                         n += SCROLL_BUFFER_SIZE;
1333
1334                 if (n == mprintf_last_line)
1335                 {
1336                         MessageBox(hOutputWnd, "Search text not found", "Find Error", MB_OK | MB_ICONERROR);
1337                         find_line = -1;
1338                         return;
1339                 }
1340
1341                 str = outtext[n];
1342         }
1343 }
1344
1345 void outwnd_close()
1346 {
1347 #ifndef NDEBUG
1348         if ( Log_fp != NULL ) {
1349                 fclose(Log_fp);
1350                 Log_fp = NULL;
1351         }
1352 #endif
1353
1354 }
1355
1356
1357 #endif //NDEBUG
1358