gzdoom/src/m_misc.cpp
Christoph Oelckers 6227906072 - Fixed: SNDINFO must be loaded before the textures. However, this required
some changes to the MAPINFO parser which tried to access the texture manager
  to check if the level name patches exist. That check had to be moved to
  where the intermission screen is set up.
- Fixed: 'bloodcolor' ignored the first parameter value when given a list
  of integers.
  Please note that this creates an incompatibility between old and new 
  versions so if you want to create something that works with both 2.2.0
  and current versions better use the string format version for the color
  parameter!
- Rewrote the DECORATE property parser so that the parser is completely
  separated from the property handlers. This should allow reuse of all 
  the handler code for a new format if Doomscript requires one.
- Fixed: PClass::InitializeActorInfo copied too many bytes if a subclass's
  defaults were larger than the parent's.
- Moved A_ChangeFlag to thingdef_codeptr.cpp.
- Moved translation related code from thingdef_properties.cpp to r_translate.cpp
  and rewrote the translation parser to use FScanner instead of strtol.
- replaced DECORATE's 'alpha default' by 'defaultalpha' for consistency.
  Since this was never used outside zdoom.pk3 it's not critical.
- Removed support for game specific pickup messages because the only thing
  this was ever used for - Raven's invulnerability item - has already been
  split up into a Heretic and Hexen version.

SVN r1240 (trunk)
2008-09-21 18:02:38 +00:00

735 lines
14 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
//
// $Log:$
//
// DESCRIPTION:
// Default Config File.
// Screenshots.
//
//-----------------------------------------------------------------------------
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <malloc.h>
#include <time.h>
#include "doomtype.h"
#include "version.h"
#if defined(_WIN32)
#include <io.h>
#else
#include <unistd.h>
#endif
#include <ctype.h>
#include "doomdef.h"
#include "m_swap.h"
#include "m_argv.h"
#include "w_wad.h"
#include "c_cvars.h"
#include "c_dispatch.h"
#include "c_bind.h"
#include "i_system.h"
#include "i_video.h"
#include "v_video.h"
#include "r_defs.h"
#include "hu_stuff.h"
// State.
#include "doomstat.h"
// Data.
#include "m_misc.h"
#include "m_png.h"
#include "cmdlib.h"
#include "g_game.h"
#include "gi.h"
#include "gameconfigfile.h"
FGameConfigFile *GameConfig;
CVAR(Bool, screenshot_quiet, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR(String, screenshot_type, "png", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR(String, screenshot_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
EXTERN_CVAR(Bool, longsavemessages);
extern void FreeKeySections();
static long ParseCommandLine (const char *args, int *argc, char **argv);
//
// M_WriteFile
//
#ifndef O_BINARY
#define O_BINARY 0
#endif
bool M_WriteFile (char const *name, void *source, int length)
{
int handle;
int count;
handle = open ( name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
if (handle == -1)
return false;
count = write (handle, source, length);
close (handle);
if (count < length)
return false;
return true;
}
//
// M_ReadFile
//
int M_ReadFile (char const *name, BYTE **buffer)
{
int handle, count, length;
struct stat fileinfo;
BYTE *buf;
handle = open (name, O_RDONLY | O_BINARY, 0666);
if (handle == -1)
I_Error ("Couldn't read file %s", name);
if (fstat (handle,&fileinfo) == -1)
I_Error ("Couldn't read file %s", name);
length = fileinfo.st_size;
buf = new BYTE[length];
count = read (handle, buf, length);
close (handle);
if (count < length)
I_Error ("Couldn't read file %s", name);
*buffer = buf;
return length;
}
//---------------------------------------------------------------------------
//
// PROC M_FindResponseFile
//
//---------------------------------------------------------------------------
void M_FindResponseFile (void)
{
int i;
for (i = 1; i < Args->NumArgs(); i++)
{
if (Args->GetArg(i)[0] == '@')
{
char **argv;
char *file;
int argc;
int argcinresp;
FILE *handle;
int size;
long argsize;
int k;
int index;
// READ THE RESPONSE FILE INTO MEMORY
handle = fopen (Args->GetArg(i) + 1,"rb");
if (!handle)
{ // [RH] Make this a warning, not an error.
Printf ("No such response file (%s)!", Args->GetArg(i) + 1);
continue;
}
Printf ("Found response file %s!\n", Args->GetArg(i) + 1);
fseek (handle, 0, SEEK_END);
size = ftell (handle);
fseek (handle, 0, SEEK_SET);
file = new char[size+1];
fread (file, size, 1, handle);
file[size] = 0;
fclose (handle);
argsize = ParseCommandLine (file, &argcinresp, NULL);
argc = argcinresp + Args->NumArgs() - 1;
if (argc != 0)
{
argv = (char **)M_Malloc (argc*sizeof(char *) + argsize);
argv[i] = (char *)argv + argc*sizeof(char *);
ParseCommandLine (file, NULL, argv+i);
for (index = 0; index < i; ++index)
argv[index] = Args->GetArg (index);
for (index = i + 1, i += argcinresp; index < Args->NumArgs (); ++index)
argv[i++] = Args->GetArg (index);
Args->Destroy();
Args = new DArgs(i, argv);
}
delete[] file;
// DISPLAY ARGS
Printf ("%d command-line args:\n", Args->NumArgs ());
for (k = 1; k < Args->NumArgs (); k++)
Printf ("%s\n", Args->GetArg (k));
break;
}
}
}
// ParseCommandLine
//
// This is just like the version in c_dispatch.cpp, except it does not
// do cvar expansion.
static long ParseCommandLine (const char *args, int *argc, char **argv)
{
int count;
char *buffplace;
count = 0;
buffplace = NULL;
if (argv != NULL)
{
buffplace = argv[0];
}
for (;;)
{
while (*args <= ' ' && *args)
{ // skip white space
args++;
}
if (*args == 0)
{
break;
}
else if (*args == '\"')
{ // read quoted string
char stuff;
if (argv != NULL)
{
argv[count] = buffplace;
}
count++;
args++;
do
{
stuff = *args++;
if (stuff == '\\' && *args == '\"')
{
stuff = '\"', args++;
}
else if (stuff == '\"')
{
stuff = 0;
}
else if (stuff == 0)
{
args--;
}
if (argv != NULL)
{
*buffplace = stuff;
}
buffplace++;
} while (stuff);
}
else
{ // read unquoted string
const char *start = args++, *end;
while (*args && *args > ' ' && *args != '\"')
args++;
end = args;
if (argv != NULL)
{
argv[count] = buffplace;
while (start < end)
*buffplace++ = *start++;
*buffplace++ = 0;
}
else
{
buffplace += end - start + 1;
}
count++;
}
}
if (argc != NULL)
{
*argc = count;
}
return (long)(buffplace - (char *)0);
}
#ifdef unix
FString GetUserFile (const char *file, bool nodir)
{
char *home = getenv ("HOME");
if (home == NULL || *home == '\0')
I_FatalError ("Please set your HOME environment variable");
FString path = home;
if (path[path.Len()-1] != '/')
path += nodir ? "/" : "/"GAME_DIR;
else if (!nodir)
path += GAME_DIR;
if (!nodir)
{
struct stat info;
if (stat (path, &info) == -1)
{
if (mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
{
I_FatalError ("Failed to create %s directory:\n%s",
path.GetChars(), strerror (errno));
}
}
else
{
if (!S_ISDIR(info.st_mode))
{
I_FatalError ("%s must be a directory", path.GetChars());
}
}
}
path += '/';
path += file;
return path;
}
#endif
//
// M_SaveDefaults
//
bool M_SaveDefaults (const char *filename)
{
FString oldpath;
bool success;
if (filename != NULL)
{
oldpath = GameConfig->GetPathName();
GameConfig->ChangePathName (filename);
}
GameConfig->ArchiveGlobalData ();
if (GameNames[gameinfo.gametype] != NULL)
{
GameConfig->ArchiveGameData (GameNames[gameinfo.gametype]);
}
success = GameConfig->WriteConfigFile ();
if (filename != NULL)
{
GameConfig->ChangePathName (filename);
}
return success;
}
void M_SaveDefaultsFinal ()
{
while (!M_SaveDefaults (NULL) && I_WriteIniFailed ())
{
/* Loop until the config saves or I_WriteIniFailed() returns false */
}
delete GameConfig;
GameConfig = NULL;
}
CCMD (writeini)
{
const char *filename = (argv.argc() == 1) ? NULL : argv[1];
if (!M_SaveDefaults (filename))
{
Printf ("Writing config failed: %s\n", strerror(errno));
}
else
{
Printf ("Config saved.\n");
}
}
//
// M_LoadDefaults
//
void M_LoadDefaults ()
{
GameConfig = new FGameConfigFile;
GameConfig->DoGlobalSetup ();
atterm (FreeKeySections);
atterm (M_SaveDefaultsFinal);
}
//
// SCREEN SHOTS
//
typedef struct
{
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin;
unsigned short ymin;
unsigned short xmax;
unsigned short ymax;
unsigned short hdpi;
unsigned short vdpi;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
} pcx_t;
//
// WritePCXfile
//
void WritePCXfile (FILE *file, const BYTE *buffer, const PalEntry *palette,
ESSType color_type, int width, int height, int pitch)
{
BYTE temprow[MAXWIDTH * 3];
const BYTE *data;
int x, y;
int runlen;
int bytes_per_row_minus_one;
BYTE color;
pcx_t pcx;
pcx.manufacturer = 10; // PCX id
pcx.version = 5; // 256 (or more) colors
pcx.encoding = 1;
pcx.bits_per_pixel = 8; // 256 (or more) colors
pcx.xmin = 0;
pcx.ymin = 0;
pcx.xmax = LittleShort(width-1);
pcx.ymax = LittleShort(height-1);
pcx.hdpi = LittleShort(75);
pcx.vdpi = LittleShort(75);
memset (pcx.palette, 0, sizeof(pcx.palette));
pcx.reserved = 0;
pcx.color_planes = (color_type == SS_PAL) ? 1 : 3; // chunky image
pcx.bytes_per_line = width + (width & 1);
pcx.palette_type = 1; // not a grey scale
memset (pcx.filler, 0, sizeof(pcx.filler));
fwrite (&pcx, 128, 1, file);
bytes_per_row_minus_one = ((color_type == SS_PAL) ? width : width * 3) - 1;
// pack the image
for (y = height; y > 0; y--)
{
switch (color_type)
{
case SS_PAL:
data = buffer;
break;
case SS_RGB:
// Unpack RGB into separate planes.
for (int i = 0; i < width; ++i)
{
temprow[i ] = buffer[i*3];
temprow[i + width ] = buffer[i*3 + 1];
temprow[i + width * 2] = buffer[i*3 + 2];
}
data = temprow;
break;
case SS_BGRA:
// Unpack RGB into separate planes, discarding A.
for (int i = 0; i < width; ++i)
{
temprow[i ] = buffer[i*4 + 2];
temprow[i + width ] = buffer[i*4 + 1];
temprow[i + width * 2] = buffer[i*4];
}
data = temprow;
break;
default:
// Should never happen.
return;
}
buffer += pitch;
color = *data++;
runlen = 1;
for (x = bytes_per_row_minus_one; x > 0; x--)
{
if (*data == color)
{
runlen++;
}
else
{
if (runlen > 1 || color >= 0xc0)
{
while (runlen > 63)
{
putc (0xff, file);
putc (color, file);
runlen -= 63;
}
if (runlen > 0)
{
putc (0xc0 + runlen, file);
}
}
if (runlen > 0)
{
putc (color, file);
}
runlen = 1;
color = *data;
}
data++;
}
if (runlen > 1 || color >= 0xc0)
{
while (runlen > 63)
{
putc (0xff, file);
putc (color, file);
runlen -= 63;
}
if (runlen > 0)
{
putc (0xc0 + runlen, file);
}
}
if (runlen > 0)
{
putc (color, file);
}
if (width & 1)
putc (0, file);
}
// write the palette
if (color_type == SS_PAL)
{
putc (12, file); // palette ID byte
for (x = 0; x < 256; x++, palette++)
{
putc (palette->r, file);
putc (palette->g, file);
putc (palette->b, file);
}
}
}
//
// WritePNGfile
//
void WritePNGfile (FILE *file, const BYTE *buffer, const PalEntry *palette,
ESSType color_type, int width, int height, int pitch)
{
if (!M_CreatePNG (file, buffer, palette, color_type, width, height, pitch) ||
!M_AppendPNGText (file, "Software", GAMENAME DOTVERSIONSTR) ||
!M_FinishPNG (file))
{
Printf ("Could not create screenshot.\n");
}
}
//
// M_ScreenShot
//
static bool FindFreeName (FString &fullname, const char *extension)
{
FString lbmname;
int i;
for (i = 0; i <= 9999; i++)
{
const char *gamename = GameNames[gameinfo.gametype];
time_t now;
tm *tm;
time(&now);
tm = localtime(&now);
if (tm == NULL)
{
lbmname.Format ("%sScreenshot_%s_%04d.%s", fullname.GetChars(), gamename, i, extension);
}
else if (i == 0)
{
lbmname.Format ("%sScreenshot_%s_%04d%02d%02d_%02d%02d%02d.%s", fullname.GetChars(), gamename,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
extension);
}
else
{
lbmname.Format ("%sScreenshot_%s_%04d%02d%02d_%02d%02d%02d_%02d.%s", fullname.GetChars(), gamename,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
i, extension);
}
if (!FileExists (lbmname.GetChars()))
{
fullname = lbmname;
return true; // file doesn't exist
}
}
return false;
}
void M_ScreenShot (const char *filename)
{
FILE *file;
FString autoname;
bool writepcx = (stricmp (screenshot_type, "pcx") == 0); // PNG is the default
// find a file name to save it to
if (filename == NULL || filename[0] == '\0')
{
#ifndef unix
if (Args->CheckParm ("-cdrom"))
{
autoname = CDROM_DIR "\\";
}
else
#endif
{
int dirlen = (int)strlen (screenshot_dir);
if (dirlen == 0)
{
autoname = progdir;
}
else if (dirlen > 0)
{
autoname = screenshot_dir;
if (autoname[dirlen-1] != '/' && autoname[dirlen-1] != '\\')
{
autoname += '/';
}
}
}
if (!FindFreeName (autoname, writepcx ? "pcx" : "png"))
{
Printf ("M_ScreenShot: Delete some screenshots\n");
return;
}
}
else
{
autoname = filename;
DefaultExtension (autoname, writepcx ? ".pcx" : ".png");
}
CreatePath(screenshot_dir);
// save the screenshot
const BYTE *buffer;
int pitch;
ESSType color_type;
screen->GetScreenshotBuffer(buffer, pitch, color_type);
if (buffer != NULL)
{
PalEntry palette[256];
if (color_type == SS_PAL)
{
screen->GetFlashedPalette(palette);
}
file = fopen (autoname, "wb");
if (file == NULL)
{
Printf ("Could not open %s\n", autoname.GetChars());
screen->ReleaseScreenshotBuffer();
return;
}
if (writepcx)
{
WritePCXfile(file, buffer, palette, color_type,
screen->GetWidth(), screen->GetHeight(), pitch);
}
else
{
WritePNGfile(file, buffer, palette, color_type,
screen->GetWidth(), screen->GetHeight(), pitch);
}
fclose(file);
screen->ReleaseScreenshotBuffer();
if (!screenshot_quiet)
{
int slash = -1;
if (!longsavemessages) slash = autoname.LastIndexOfAny(":/\\");
Printf ("Captured %s\n", autoname.GetChars()+slash+1);
}
}
else
{
if (!screenshot_quiet)
{
Printf ("Could not create screenshot.\n");
}
}
}
CCMD (screenshot)
{
if (argv.argc() == 1)
G_ScreenShot (NULL);
else
G_ScreenShot (argv[1]);
}