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