1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: m_misc.c 1520 2020-05-05 03:29:56Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program 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 //
20 // $Log: m_misc.c,v $
21 // Revision 1.10  2004/04/18 12:40:14  hurdler
22 // Jive's request for saving screenshots
23 //
24 // Revision 1.9  2003/10/15 14:09:47  darkwolf95
25 // Fixed screenshots filename bug
26 //
27 // Revision 1.8  2001/03/03 06:17:33  bpereira
28 // Revision 1.7  2001/02/24 13:35:20  bpereira
29 //
30 // Revision 1.6  2001/01/25 22:15:42  bpereira
31 // added heretic support
32 //
33 // Revision 1.5  2000/10/08 13:30:01  bpereira
34 // Revision 1.4  2000/09/28 20:57:15  bpereira
35 // Revision 1.3  2000/04/16 18:38:07  bpereira
36 //
37 // Revision 1.2  2000/02/26 00:28:42  hurdler
38 // Mostly bug fix (see borislog.txt 23-2-2000, 24-2-2000)
39 //
40 //
41 // DESCRIPTION:
42 //      Default Config File.
43 //      PCX Screenshots.
44 //      File i/o
45 //      Common used routines
46 //
47 //-----------------------------------------------------------------------------
48 
49 
50 #include <fcntl.h>
51 #include <unistd.h>
52 
53 #include "doomincl.h"
54 #include "g_game.h"
55 #include "m_misc.h"
56 #include "hu_stuff.h"
57 #include "v_video.h"
58 #include "z_zone.h"
59 #include "g_input.h"
60 #include "i_video.h"
61 #include "d_main.h"
62 #include "m_argv.h"
63 #include "m_swap.h"
64 
65 #ifdef HWRENDER
66 #include "hardware/hw_main.h"
67 #endif
68 
69 // ==========================================================================
70 //                         FILE INPUT / OUTPUT
71 // ==========================================================================
72 
73 
74 //
75 // FIL_WriteFile
76 //
77 
FIL_WriteFile(char const * name,void * source,int length)78 boolean FIL_WriteFile ( char const*   name,
79                         void*         source,
80                         int           length )
81 {
82     int         handle;
83     int         count;
84 
85     handle = open ( name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
86 
87     if (handle == -1)
88         return false;
89 
90     count = write (handle, source, length);
91     close (handle);
92 
93     if (count < length)
94         return false;
95 
96     return true;
97 }
98 
99 //
100 // FIL_ReadFile : return length, 0 on error
101 //
102 //Fab:26-04-98:
103 //  appends a zero byte at the end
FIL_ReadFile(char const * name,byte ** buffer)104 int FIL_ReadFile ( char const*   name,
105                    byte**        buffer )
106 {
107     int    handle, count, length;
108     struct stat fileinfo;
109     byte   *buf;
110 
111     handle = open (name, O_RDONLY | O_BINARY, 0666);
112     if (handle == -1)
113         return 0;
114 
115     if (fstat (handle,&fileinfo) == -1)
116         return 0;
117 
118     length = fileinfo.st_size;
119     buf = Z_Malloc (length+1, PU_STATIC, 0);
120     count = read (handle, buf, length);
121     close (handle);
122 
123     if (count < length)
124         return 0;
125 
126     //Fab:26-04-98:append 0 byte for script text files
127     buf[length]=0;
128 
129     *buffer = buf;
130     return length;
131 }
132 
133 // Extended Read and Write of buffers.
134 
FIL_ExtFile_Open(ExtFIL_t * ft,char const * name,boolean write_flag)135 int FIL_ExtFile_Open ( ExtFIL_t * ft,  char const* name, boolean write_flag )
136 {
137     ft->stat_error = STAT_OPEN;
138     ft->bufcnt = 0;  // buffer empty
139     ft->handle =
140       open ( name,
141              ( (write_flag)? O_WRONLY | O_CREAT | O_TRUNC | O_BINARY // write
142                             :O_RDONLY | O_BINARY  // read
143              ), 0666);
144     if( ft->handle < 0) // file not found, or not created
145         ft->stat_error = ft->handle; // error
146     return ft->stat_error;
147 }
148 
FIL_ExtWriteFile(ExtFIL_t * ft,size_t length)149 int FIL_ExtWriteFile ( ExtFIL_t * ft, size_t length )
150 {
151     int count = write (ft->handle, ft->buffer, length);
152     if( count != length )  // did not write all of length (disk full)
153        ft->stat_error = ERR_RW;  // something negative, not -1
154     return ft->stat_error;
155 }
156 
FIL_ExtReadFile(ExtFIL_t * ft,size_t length)157 int FIL_ExtReadFile ( ExtFIL_t * ft, size_t length )
158 {
159     // check for done reading
160     if( ft->stat_error < STAT_OPEN )  // ERR or EOF
161         goto done;
162     // still have data to read
163     // append to existing data
164     int count = read (ft->handle, ft->buffer+ft->bufcnt, length);
165     // It is not an error if read returns less than asked, it may have
166     // been interupted or other things.  Return of 0 is end-of-file.
167     if( count == -1 ) // error
168     {
169         ft->stat_error = ERR_RW; // read err
170         goto done;
171     }
172 
173     ft->bufcnt += count;
174     if( count == 0 ) // EOF
175         ft->stat_error = STAT_EOF;
176 
177 done:
178     return ft->stat_error;
179 }
180 
FIL_ExtFile_Close(ExtFIL_t * ft)181 void FIL_ExtFile_Close ( ExtFIL_t * ft )
182 {
183     if( ft->handle >= 0 )  // protect against second call when errors
184     {
185         close (ft->handle);
186         ft->handle = -127;
187         ft->stat_error = STAT_CLOSED;
188     }
189 }
190 
191 
192 //
193 // checks if needed, and add default extension to filename
194 // in path[MAX_WADPATH]
FIL_DefaultExtension(char * path,const char * extension)195 void FIL_DefaultExtension (char *path, const char *extension)
196 {
197     char    *src;
198     // [WDJ] assume MAX_WADPATH buffer
199     int  plen = strlen(path);
200     if( plen > (MAX_WADPATH - 4) )   return;  // too long to add extension
201 
202   // search for '.' from end to begin, add .EXT only when not found
203     src = path + plen - 1;
204 
205     while (*src != '/' && src != path)
206     {
207         if (*src == '.')
208             return;                 // it has an extension
209         src--;
210     }
211 
212     strcat (path, extension);
213 }
214 
215 
216 // Point to start of the filename in longer string
FIL_Filename_of(char * nstr)217 char * FIL_Filename_of( char * nstr )
218 {
219     int i;
220     // point to start of filename only
221     for (i = strlen(nstr) - 1; i >= 0; i--)
222     {
223       if (nstr[i] == '\\' || nstr[i] == '/' || nstr[i] == ':')
224         break;
225     }
226     return &nstr[i+1];
227 }
228 
229 #if 0
230 // [WDJ] Unused, was only used in old W_AddFile, makes DOS assumptions
231 // Uppercase only
232 //  Creates a resource name (max 8 chars 0 padded) from a file path
233 //
234 void FIL_ExtractFileBase ( char*  path,  char* dest )
235 {
236     char*       src;
237     int         length;
238 
239     src = path + strlen(path) - 1;
240 
241     // back up until a \ or the start
242     while (src != path
243            && *(src-1) != '\\'
244            && *(src-1) != '/')
245     {
246         src--;
247     }
248 
249     // copy up to eight characters
250     memset (dest,0,8);
251     length = 0;
252 
253     while (*src && *src != '.')
254     {
255         if (++length == 9)
256             I_Error ("Filename base of %s >8 chars",path);
257 
258         *dest++ = toupper((int)*src++);
259     }
260 }
261 #endif
262 
263 
264 //  Returns true if a filename extension is found
265 //  There are no '.' in wad resource name
266 //
FIL_CheckExtension(const char * in)267 boolean FIL_CheckExtension (const char * in)
268 {
269     while (*in++)
270     {
271         if (*in=='.')
272             return true;
273     }
274 
275     return false;
276 }
277 
278 
279 // ==========================================================================
280 //                          CONFIGURATION
281 // ==========================================================================
282 // owned, malloc
283 char * configfile_main = NULL;
284 char * configfile_drawmode = NULL;
285 // true once config.cfg loaded, and values are present.
286 // Bit 0x80 is used to flag that some configfile was loaded.
287 // Bit 0x40 is used to flag empty CFG_main.
288 static byte config_loaded = 0;  // bit per cfg
289 static const byte  config_load_bit[ 4 ] = { 0, 0x01, 0x02, 0x04 };  // bit masks
290 
M_Set_configfile_main(const char * filename)291 void  M_Set_configfile_main( const char * filename )
292 {
293     free( configfile_main );
294     configfile_main = strdup( filename );
295     config_loaded &= ~config_load_bit[CFG_main];  // clear flag bit
296 }
297 
298 // This table will work even on compilers that do not
299 // combine identical const strings.
300 // index by drawmode : drawmode_e
301 const char * configfile_drawmode_designator[] = {
302   "--", // no drawmode configfile
303   "8p",  // DRM_8pal
304   "15",  // DRM_15
305   "16",  // DRM_16
306   "24",  // DRM_24
307   "32",  // DRM_32
308   "bp",  // DRM_explicit_bpp,
309   "n",   // DRM_native
310 #ifdef HWRENDER
311   "gl",  // DRM_opengl
312   "mg",  // DRM_minigl
313 #ifdef SMIF_WIN_NATIVE
314   "wg",  // DRM_glide
315   "wd",  // DRM_d3d
316 #endif
317 #endif
318 };
319 
320 // name is usually limited to 8 char
321 #define  NAMEBUF_LEN  24
322 
323 //  drawmode : drawmode_sel_t
M_Set_configfile_drawmode(byte drawmode)324 void  M_Set_configfile_drawmode( byte drawmode )
325 {
326     char cfgbuf[ MAX_WADPATH ];
327     char namebuf[ NAMEBUF_LEN ];
328 
329     // Form drawmode config filename.
330     // example: /home/user/.legacy/configgl.cfg
331     const char * cfdd_str = configfile_drawmode_designator[ drawmode ];
332     snprintf( namebuf, NAMEBUF_LEN-1, DRAWMODE_CONFIGFILENAME, cfdd_str );
333     cat_filename( cfgbuf, legacyhome, namebuf );
334 
335     free( configfile_drawmode );
336     configfile_drawmode = strdup( cfgbuf );
337     config_loaded &= ~config_load_bit[CFG_drawmode];  // clear flag bit
338 }
339 
340 
M_Have_configfile_drawmode(void)341 byte  M_Have_configfile_drawmode( void )
342 {
343     return  config_loaded & config_load_bit[ CFG_drawmode ];
344 }
345 
M_Set_configfile_drawmode_present(void)346 void  M_Set_configfile_drawmode_present( void )
347 {
348     config_loaded |= config_load_bit[ CFG_drawmode ];
349 }
350 
351 
352 // Save config file, without disturbing configfile settings.
Command_SaveConfig_f(void)353 void Command_SaveConfig_f (void)
354 {
355     char cfgname[MAX_WADPATH];
356     COM_args_t  carg;
357 
358     COM_Args( &carg );
359 
360     if (carg.num!=2)
361     {
362         CONS_Printf("saveconfig <filename[.cfg]> : save config to a file\n");
363         return;
364     }
365     strncpy(cfgname, carg.arg[1], MAX_WADPATH-1);
366     cfgname[MAX_WADPATH-1] = '\0';
367     if( cfgname[0] == 0 )  goto failed;
368 
369     FIL_DefaultExtension (cfgname,".cfg");
370 
371     M_SaveConfig( CFG_main, cfgname );
372 
373     // Also save the drawmode configfile, with NULL name check.
374     M_SaveConfig( CFG_drawmode, configfile_drawmode );
375 
376     // Check that the file now exists.
377     FILE * f = fopen (cfgname, "r");
378     if( ! f ) goto failed;
379     fclose( f );
380 
381     CONS_Printf("config saved as %s\n", cfgname );  // actual name saved
382     return;
383 
384 failed:
385     I_SoftError("Could not save game config file %s\n", cfgname );
386     return;
387 }
388 
389 // Replace config values from a new config file.
Command_LoadConfig_f(void)390 void Command_LoadConfig_f (void)
391 {
392     char cfgname[MAX_WADPATH];
393     COM_args_t  carg;
394     byte  namearg = 1;
395     char  other_flag = 0;
396 
397     COM_Args( &carg );
398     if( carg.num < 2 || carg.num > 3 )  goto bad_syntax;
399 
400     if( carg.arg[1][0] == '-' )
401     {
402         other_flag = carg.arg[1][1];
403         namearg = 2;
404     }
405     else if( carg.arg[2][0] == '-' )
406     {
407         other_flag = carg.arg[2][1];
408     }
409 
410     strncpy(cfgname, carg.arg[namearg], MAX_WADPATH-1);
411     cfgname[MAX_WADPATH-1] = '\0';
412     if( cfgname[0] == 0 )  goto failed;
413 
414     FIL_DefaultExtension (cfgname,".cfg");
415     //  for create, don't check if file exists
416 
417     // The original intent was to load a complete config file, replacing all.
418     other_flag = tolower( other_flag );
419     if( other_flag == 'a' )  // append
420     {
421         // Append config file settings.
422         if( access(cfgname, R_OK ) < 0 )  goto failed;
423         // Load additional config settings.  Do not save them to the main config.
424         M_LoadConfig( CFG_other, cfgname );
425     }
426     else
427     {
428         M_ClearConfig( CFG_main );
429         // Load config sets main config filename.
430         M_Set_configfile_main( cfgname );
431         // At program end, it will overwrite this config file with all the CFG_main settings.
432         M_LoadConfig( CFG_main, cfgname );
433     }
434     return;
435 
436 failed:
437     I_SoftError("Could not load game config file %s\n", cfgname );
438     return;
439 
440 bad_syntax:
441     CONS_Printf("loadconfig (-A) <filename[.cfg]> : load config from a file\n");
442     return;
443 }
444 
Command_ChangeConfig_f(void)445 void Command_ChangeConfig_f (void)
446 {
447     COM_args_t  carg;
448 
449     COM_Args( &carg );
450 
451     if (carg.num!=2)
452     {
453         CONS_Printf("changeconfig <filename[.cfg]> : save current config and load another\n");
454         return;
455     }
456 
457 #if 1
458     // Why be indirect when there is a direct method, with all the parameters.
459     M_SaveAllConfig();
460 #else
461     COM_BufAddText (va("saveconfig \"%s\"\n", configfile_main));
462 #endif
463 #if 1
464     // Replace the main config with a different config file.
465     M_ClearConfig( CFG_main );  // cleanup old values
466 #endif
467     // indirect invoke of LoadConfig above.
468     COM_BufAddText (va("loadconfig \"%s\"\n", carg.arg[1])); // -> configfile
469 }
470 
471 
472 // Clear the config
473 //   cfg : cv_config_e, source config file ident
M_ClearConfig(byte cfg)474 void M_ClearConfig( byte cfg )
475 {
476     config_loaded &= ~config_load_bit[cfg];  // clear flag bit
477     CV_Clear_Config( cfg );
478 }
479 
480 //
481 // Load a config file
482 //
483 //   cfg : cv_config_e, source config file ident
M_LoadConfig(byte cfg,const char * cfgfile)484 void M_LoadConfig( byte cfg, const char * cfgfile )
485 {
486     FILE * fr;
487 
488     //  cfgfile is initialised by d_main when searching for the wad ?!
489     if( ! cfgfile )  return;
490 
491     // Check that it can be opened.
492     fr = fopen ( cfgfile, "r" );
493     if( ! fr )
494     {
495         if( cfg == CFG_main )
496             config_loaded |= 0x40;  // set flag bit, so can create CFG_main later
497         return;
498     }
499     fclose( fr );
500 
501     COM_BufExecute( CFG_none );  // Clear buffer of any COM commands, before Loading
502 
503     // load config, make sure those commands doesnt require the screen..
504     CONS_Printf("\n");
505     COM_BufInsertText (va("exec \"%s\"\n", cfgfile));
506     COM_BufExecute( cfg );       // make sure initial settings are done
507 
508     // make sure I_Quit() will write back the correct config
509     // (do not write back the config if it crash before)
510     config_loaded |= 0x80 | config_load_bit[cfg];  // set flag bit, and specific bit
511 }
512 
513 
514 
515 //   cfg : cv_config_e
M_SaveConfig(byte cfg,const char * cfgfile)516 void M_SaveConfig( byte cfg, const char * cfgfile )
517 {
518     FILE * fw;
519     consvar_t * cv;
520     // When CFG_main, also save CFG_none vars
521     byte cfg2;
522 
523     if( ! cfgfile )
524         return;
525 
526     // make sure not to write back the config until
527     //  it's been correctly loaded
528     if( ! config_loaded )
529         return;
530 
531     // Write this config file if one was loaded,
532     // or if there are some values of that config now.
533     if( (cfg != CFG_main) // CFG_main always gets saved, has all CFG_none var too.
534         && ! (config_loaded & config_load_bit[cfg])  // cfg was NOT loaded
535         && ! CV_Config_check( cfg ) ) // NOT any of the cfg is (current or pushed).
536         return;
537 
538     fw = fopen (cfgfile, "w");
539     if (!fw)
540     {
541         I_SoftError("Could not save game config file %s\n", cfgfile);
542         return;
543     }
544 
545     // header message
546     fprintf (fw, "// Doom Legacy configuration file.\n");
547 
548     //FIXME: save key aliases if ever implemented..
549 
550     // Save CV variables
551     // The main configfile also gets the uninitialized variables (CFG_none).
552     // There are no CFG_null variables.
553     cfg2 = (cfg == CFG_main) ? CFG_none : CFG_null;
554     for( cv = CV_IteratorFirst(); cv ; cv = CV_Iterator( cv ) )
555     {
556         if( cv->flags & CV_SAVE )
557         {
558             const char * str = NULL;
559             byte cm = cv->state & CS_CONFIG;
560             if( (cm == cfg) || (cm == cfg2) )
561             {
562                 str = cv->string;
563             }
564             else if( cv->state & CS_PUSHED )
565             {
566                 str = CV_Get_Config_string( cv, cfg );
567             }
568             else  continue;  // not current and not pushed
569 
570             if( ! str ) continue;
571             // Save the cvar string
572             fprintf( fw, "%s \"%s\"\n", cv->name, str );
573         }
574     }
575 
576     if( cfg == CFG_main )
577         G_SaveKeySetting(fw);
578 
579     fclose (fw);
580 }
581 
582 //  Save all game config here
M_SaveAllConfig(void)583 void M_SaveAllConfig( void )
584 {
585     M_SaveConfig( CFG_main, configfile_main );
586 
587     M_SaveConfig( CFG_drawmode, configfile_drawmode );
588 }
589 
590 
591 
592 // ==========================================================================
593 //                            SCREEN SHOTS
594 // ==========================================================================
595 
596 // Make filename for screen shot.
597 // return 1 when have filename.
M_Make_Screenshot_Filename(char * lbmname,const char * ext)598 byte  M_Make_Screenshot_Filename( char * lbmname, const char * ext )
599 {
600     int i;
601     char wadname[MAX_WADPATH];
602     char * s;
603     char * wn;
604     char * vernum;
605     const char * savedir;
606 
607     // Defaults
608     savedir = ".";
609     switch( gamemode )
610     {
611      case heretic :
612        wn = "HRTC";
613        break;
614      case chexquest1 :
615        wn = "CHXQ";
616        break;
617      default :
618        wn = "DOOM";
619        break;
620     }
621 
622     if (cv_screenshot_dir.string[0]) // Hurdler: Jive's request (save file in other directory)
623     {
624         savedir = (const char *) cv_screenshot_dir.string;
625         for (i=1; ; i++) // seach the first "real" wad file (also skip iwad).
626         {
627             char * wadfile = startupwadfiles[i];
628             if( ! wadfile )  break;
629             // Examine extension
630             int pos = strlen(wadfile) - 4;
631             if ((pos >= 0) && !strncmp(&wadfile[pos], ".wad", 4))
632             {
633                 // Wad file name copied to screenshot name.
634                 strcpy(wadname, wadfile);
635                 wadname[pos] = '\0';  // eliminate wad extension
636                 wn = wadname;
637                 // Eliminate wad directory name
638                 s = strrchr(wn, '/');
639                 if( s )  wn = s + 1;
640 #if defined( SMIF_PC_DOS) || defined( WIN32 ) || defined( SMIF_OS2_NATIVE )
641                 s = strrchr(wn, '\\');  // DOS
642                 if( s )  wn = s + 1;
643 #endif
644                 break;
645             }
646         }
647         snprintf(lbmname, MAX_WADPATH-1, "%s/%s0000.%s", cv_screenshot_dir.string, wn, ext );
648         lbmname[MAX_WADPATH-1] = 0;
649     }
650     else
651     {
652         sprintf(lbmname, "%s0000.%s", wn, ext );
653     }
654 
655     vernum = strrchr( lbmname, '.') - 4;
656     if( ( strlen( lbmname ) > (MAX_WADPATH-2) )
657         || vernum == NULL )
658     {
659         CONS_Printf("Screenshot directory or name too long: %s\n", lbmname );
660         return 0;
661     }
662 
663     if( access( savedir, F_OK ) < 0 )
664     {
665         CONS_Printf("Screenshot directory error: %s\n", savedir);
666         return 0;
667     }
668 
669     // Find unused file name version.
670     for (i=0 ; i<10000; i++)
671     {
672         vernum[0] = '0' + ((i/1000) % 10);
673         vernum[1] = '0' + ((i/100) % 10);
674         vernum[2] = '0' + ((i/10) % 10);
675         vernum[3] = '0' + ((i/1) % 10);
676         if (access(lbmname, F_OK) == -1)
677             return 1;      // file doesn't exist
678     }
679 
680     CONS_Printf("Screenshot: Failed to find unused filename: %s\n", lbmname);
681     return 0;
682 }
683 
684 
685 // Graphic File types
686 // pcx
687 // ppm
688 // Targa
689 // bmp (windows and DOS)
690 // png (requires LIBPNG)
691 
692 #ifdef SMIF_PC_DOS
693 
694 // PCX file format
695 #pragma pack(1)
696 typedef struct
697 {
698     uint8_t       manufacturer;
699     uint8_t       version;
700     uint8_t       encoding;
701     uint8_t       bits_per_pixel;
702 
703     uint16_t      xmin;
704     uint16_t      ymin;
705     uint16_t      xmax;
706     uint16_t      ymax;
707 
708     uint16_t      hres;
709     uint16_t      vres;
710 
711     uint8_t       palette[48];
712 
713     uint8_t       reserved;
714     uint8_t       color_planes;
715     uint16_t      bytes_per_line;
716     uint16_t      palette_type;
717 
718     uint8_t       filler[58];
719     uint8_t       data;           // unbounded
720 } pcx_t;
721 #pragma pack()
722 
723 
724 //
725 // WritePCXfile
726 //
Write_PCXfile(const char * file_name,int width,int height,byte * data,byte * palette)727 boolean Write_PCXfile ( const char * file_name, int width, int height, byte* data, byte* palette )
728 {
729     int         i;
730     int         length;
731     boolean     br;
732     pcx_t*      pcx;
733     byte*       pack;
734 
735     pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL);
736 
737     pcx->manufacturer = 0x0a;           // PCX id
738     pcx->version = 5;                   // 256 color
739     pcx->encoding = 1;                  // uncompressed
740     pcx->bits_per_pixel = 8;            // 256 color
741     pcx->xmin = 0;
742     pcx->ymin = 0;
743     // [WDJ] The PCX format must be little-endian, must swap when big-endian
744     pcx->xmax = (uint16_t)( LE_SWAP16(width-1) );
745     pcx->ymax = (uint16_t)( LE_SWAP16(height-1) );
746     pcx->hres = (uint16_t)( LE_SWAP16(width) );
747     pcx->vres = (uint16_t)( LE_SWAP16(height) );
748     memset (pcx->palette,0,sizeof(pcx->palette));
749     pcx->color_planes = 1;              // chunky image
750     pcx->bytes_per_line = (uint16_t)( LE_SWAP16(width) );
751     pcx->palette_type = (uint16_t)( LE_SWAP16(1) );   // Color (2=grey scale)
752     memset (pcx->filler,0,sizeof(pcx->filler));
753 
754 
755     // pack the image
756     pack = &pcx->data;
757 
758     for (i=0 ; i<width*height ; i++)
759     {
760         if ( (*data & 0xc0) != 0xc0)
761             *pack++ = *data++;
762         else
763         {
764             *pack++ = 0xc1;
765             *pack++ = *data++;
766         }
767     }
768 
769     // write the palette
770     *pack++ = 0x0c;     // palette ID byte
771     for (i=0 ; i<768 ; i++)
772         *pack++ = *palette++;
773 
774     // write output file
775     length = pack - (byte *)pcx;
776     br = FIL_WriteFile (file_name, pcx, length);
777 
778     Z_Free (pcx);
779     return br;
780 }
781 
782 #endif
783 
784 
785 
786 // --------------------------------------------------------------------------
787 // save screenshots with TGA format
788 // --------------------------------------------------------------------------
789 // This will not be packed under Linux, GNU, or WIN32, unless it is explicit.
790 #pragma pack(1)
791 typedef struct {  // sizeof() = 18
792   byte      id_field_length;
793   byte      color_map_type;
794   byte      image_type;
795   int16_t   c_map_origin;
796   int16_t   c_map_length;
797   byte      c_map_depth;
798   int16_t   x_origin;
799   int16_t   y_origin;
800   uint16_t  width;
801   uint16_t  height;
802   byte      image_pix_size;  // 16, 24, 32
803   byte      image_descriptor;
804 } TGAHeader_t;
805 #pragma pack()
806 
Write_TGA(const char * filename,int width,int height,int bitpp,byte * colormap,byte * buffer,size_t size)807 boolean  Write_TGA( const char * filename, int width, int height, int bitpp, byte* colormap, byte* buffer, size_t size )
808 {
809     int fd;
810     size_t count = 0;
811     TGAHeader_t tga_hdr;
812 
813     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
814     if (fd < 0)
815         return false;
816 
817     memset(&tga_hdr, 0, sizeof(tga_hdr));
818     // TGA format is little-endian
819     tga_hdr.width = (uint16_t)( LE_SWAP16(width) );
820     tga_hdr.height = (uint16_t)( LE_SWAP16(height) );
821     tga_hdr.image_pix_size = bitpp;  // normal, 24 bits per pixel
822     tga_hdr.image_type = 2;  // Uncompressed, RGB
823     tga_hdr.image_descriptor = 0x20;  // bit 5, origin in upper left-hand corner
824 
825     if( colormap )
826     {
827         tga_hdr.image_type = 1;  // Uncompressed, colormap image
828         tga_hdr.color_map_type = 1;
829         tga_hdr.c_map_origin = 0;
830         tga_hdr.c_map_length = LE_SWAP16( 256 );
831         tga_hdr.c_map_depth = 24;
832     }
833 
834     count = write(fd, &tga_hdr, sizeof(TGAHeader_t));
835     if( count != sizeof(TGAHeader_t) )  goto write_fail;
836 
837     if( colormap )
838     {
839         count = write(fd, colormap, 256*3);
840         if( count != 256*3 )  goto write_fail;
841     }
842 
843     count = write(fd, buffer, size);
844     if( count != size )  goto write_fail;
845 
846     close(fd);
847     return true;
848 
849 write_fail:
850     close(fd);
851     return false;
852 }
853 
854 
855 // indexed by drawmode:  DRAW8PAL, DRAW15, DRAW16, DRAW24, DRAW32
856 
857 //
858 // M_ScreenShot
859 //
M_ScreenShot(void)860 void M_ScreenShot (void)
861 {
862     char   filename[MAX_WADPATH];
863     // vid : from video setup
864     byte*  bufs;  // source buffer ( screen[2] or hw_bufr )
865     byte*  hw_bufr = NULL;  // allocated
866     byte*  bufw = NULL;  // allocated
867     byte*  bp;
868     int i;
869     int num_pixels = vid.width * vid.height;
870     size_t  bufsize;
871     byte   wr_bytepp = 3;
872     byte   src_bitpp;
873     boolean  br = false;
874 
875 #ifdef HWRENDER
876     if (rendermode!=render_soft)
877     {
878         // Hardware draw.
879         // Save as Targa format.
880         hw_bufr = HWR_Get_Screenshot( & src_bitpp );
881         if( ! hw_bufr )  goto done;
882         bufs = hw_bufr;
883     }
884     else
885 #endif
886     {
887         // Software draw.
888         // munge planar buffer to bufs
889         bufs = screens[2];  // Take screenshot to screens[2]
890         I_ReadScreen (bufs);
891         src_bitpp = vid.bitpp;
892 
893         if( vid.ybytes != vid.width )
894         {
895             // eliminate padding in the buffer
896             byte *dest, *src;
897             dest = src = &bufs[0];
898             for( i=1; i<vid.height; i++ )
899             {
900                 src += vid.ybytes;
901                 dest += vid.widthbytes;
902                 // overlapping copy
903                 memmove(dest, src, vid.width);
904             }
905         }
906     }
907 
908 #ifdef SMIF_PC_DOS
909     if( (vid.drawmode == DRAW8PAL) && (cv_screenshot_type.EV == 8) )
910     {
911         // Save screenshot in PCX format
912 
913         if( ! M_Make_Screenshot_Filename( filename, "pcx" ) )
914             return;
915 
916         GenPrintf( EMSG_ver, "Save PCX: %s\n", filename );
917 
918         // save the pcx file
919         br = Write_PCXfile ( filename, vid.width, vid.height, bufs,
920                             W_CacheLumpName ("PLAYPAL",PU_CACHE));
921         goto done;
922     }
923 #endif
924 
925     // Software draw, Targa format.
926     // Targa allows 24 bit, 32 bit (with alpha), and 16 bit (5,5,5) formats.
927 
928     if( ! M_Make_Screenshot_Filename( filename, "tga" ) )
929         return;
930 
931     GenPrintf( EMSG_ver, "Save Targa: %s\n", filename );
932 //    printf("Write Targa %s, drawmode=%i, wr_bytepp= %i, bitpp= %i\n", filename, vid.drawmode, wr_bytepp, wr_bytepp*8 );
933     bufsize = (size_t)num_pixels * wr_bytepp;
934     bufw = malloc( bufsize );
935     if (!bufw)  goto done;
936 
937     bp = bufw;
938 
939     // conversions
940     switch( src_bitpp )
941     {
942      case 8:
943         {
944             // PAL 8 bit format.
945             // To Targa 8 bit, color mapped.
946             byte  pal24[256*3];
947             RGBA_t * pal32 = pLocalPalette;
948 
949             // Convert palette from RGBA to 24bit RGB.
950             bp = pal24;
951             for (i=0; i<256; i++)
952             {
953                 *(bp++) = pal32[i].s.blue;
954                 *(bp++) = pal32[i].s.green;
955                 *(bp++) = pal32[i].s.red;
956             }
957 
958             br = Write_TGA( filename, vid.width, vid.height, 8, pal24, bufs, num_pixels );
959             goto done;
960         }
961 
962 #ifdef ENABLE_DRAW15
963      case 15:
964         {
965             // Screen (5,5,5) format.
966             uint16_t * r16 = (uint16_t*) bufs;
967             if(cv_screenshot_type.EV == 1)  // Full
968             {
969                 for (i=0; i<num_pixels; i++)
970                 {
971                     // Convert 15bit 555 RGB to 24 bit RGB.
972                     uint16_t rgb555 = *(r16++);
973                     *(bp++) = (rgb555 & 0x001F) << 3;
974                     *(bp++) = (rgb555 & 0x03E0) >> (5-3);
975                     *(bp++) = (rgb555 & 0x7C00) >> (10-3);
976                 }
977                 wr_bytepp = 3;
978             }
979             else
980             {   // compact
981                 // To Targa 16 bit, (5,5,5) format.
982                 uint16_t * w16 = (uint16_t*) bufw;
983                 for (i=0; i<num_pixels; i++)
984                 {
985                     // Convert 15bit 555 RGB to Targa 15 bit RGB.
986                     // Set alpha channel (0x8000)
987                     *(w16++) = *(r16++) | 0x8000;
988                 }
989                 wr_bytepp = 2;
990             }
991         }
992         break;
993 #endif
994 #if defined( ENABLE_DRAW16 ) || defined( HWRENDER )
995      // HWRENDER Glide uses this.
996      case 16:
997         {
998             // Screen (5,6,5) format.
999             uint16_t * r16 = (uint16_t*) bufs;
1000             if(cv_screenshot_type.EV == 1)  // Full
1001             {
1002                 // To Targa 24 bit format.
1003                 for (i=0; i<num_pixels; i++)
1004                 {
1005                     // Convert 16bit 565 RGB to 24 bit RGB.
1006                     uint16_t rgb565 = *(r16++);
1007                     *(bp++) = (rgb565 & 0x001F) << 3;
1008                     *(bp++) = (rgb565 & 0x07E0) >> (5-2);
1009                     *(bp++) = (rgb565 & 0xF800) >> (11-3);
1010                 }
1011                 wr_bytepp = 3;
1012             }
1013             else
1014             {   // compact
1015                 // To Targa 16 bit, (5,5,5) format.
1016                 uint16_t * w16 = (uint16_t*) bufw;
1017                 for (i=0; i<num_pixels; i++)
1018                 {
1019                     // Convert 16bit 565 RGB to Targa 15 bit RGB.
1020                     uint16_t rgb565 = *(r16++);
1021                     // Set alpha channel (0x8000)
1022                     *(w16++) = (rgb565 & 0x001F) | ((rgb565 >> 1) & 0x7FE0) | 0x8000;
1023                 }
1024                 wr_bytepp = 2;
1025             }
1026         }
1027         break;
1028 #endif
1029 #if defined( ENABLE_DRAW24 ) || defined( HWRENDER )
1030      // HWRENDER OpenGL uses this.
1031      case 24:
1032         // Screen 3 byte format.
1033         // Already in Targa 3 byte format.
1034         memcpy( bufw, bufs, bufsize );
1035         break;
1036 #endif
1037 #ifdef ENABLE_DRAW32
1038      case 32:
1039         // Screen 4 byte format.
1040         for (i=0; i<num_pixels; i++)
1041         {
1042             *(bp++) = *(bufs++);
1043             *(bp++) = *(bufs++);
1044             *(bp++) = *(bufs++);
1045             bufs++;  // alpha
1046         }
1047         break;
1048 #endif
1049      default:
1050         goto done;
1051     }
1052 
1053     br = Write_TGA( filename, vid.width, vid.height, wr_bytepp*8, NULL, bufw, bufsize );
1054 
1055 done:
1056     if( bufw )  free(bufw);
1057     if( hw_bufr )  free(hw_bufr);
1058 
1059     if( br )
1060         CONS_Printf("screen shot %s saved\n", filename);
1061     else
1062         //CONS_Printf("Couldn't create screen shot\n");
1063         CONS_Printf("%s\n", filename);
1064 }
1065 
1066 
1067 // ==========================================================================
1068 //                        MISC STRING FUNCTIONS
1069 // ==========================================================================
1070 
1071 
1072 //  Temporary varargs for COM_Buf and CONS_Printf usage
1073 //  COM_BufAddText( va( "format", args ) )
1074 //
1075 // Buffer returned by va(), for every caller
1076 #define VA_BUF_SIZE 1024
1077 static char  va_buffer[VA_BUF_SIZE];
1078 //
va(const char * format,...)1079 char*   va(const char *format, ...)
1080 {
1081     va_list      argptr;
1082 
1083     va_start(argptr, format);
1084     vsnprintf(va_buffer, VA_BUF_SIZE, format, argptr);
1085     va_buffer[VA_BUF_SIZE-1] = '\0'; // term, when length limited
1086     va_end(argptr);
1087 
1088     return va_buffer;
1089 }
1090 
1091 
1092 // creates a copy of a string, null-terminated
1093 // returns ptr to the new duplicate string
1094 //
Z_StrDup(const char * in)1095 char *Z_StrDup (const char *in)
1096 {
1097     char    *out;
1098 
1099     out = Z_Malloc (strlen(in)+1, PU_STATIC, NULL);
1100     strcpy (out, in);
1101     return out;
1102 }
1103 
1104 // dest must be filename buffer of MAX_WADPATH
1105 // If directory dn does not end in '/', then a separator will be included.
cat_filename(char * dest,const char * dn,const char * fn)1106 void cat_filename( char * dest, const char * dn, const char * fn )
1107 {
1108     const char * format = "%s%s";
1109     int dnlen = strlen( dn );
1110     if( dnlen )
1111     {
1112         // if directory does not have '/' then include one in format
1113         char ch = dn[ dnlen-1 ]; // last char
1114         if( ! ( ch == '/' || ch == '\\' ))   format = "%s/%s";
1115     }
1116     snprintf(dest, MAX_WADPATH-1, format, dn, fn);
1117     dest[MAX_WADPATH-1] = '\0';
1118 }
1119 
1120 #if 0
1121 // [WDJ] No longer used
1122 // s1=s2+s3+s1
1123 void strcatbf(char *s1, const char *s2, const char *s3)
1124 {
1125     char tmp[1024];
1126 
1127     strcpy(tmp,s1);
1128     strcpy(s1,s2);
1129     strcat(s1,s3);
1130     strcat(s1,tmp);
1131 }
1132 #endif
1133 
1134