Merge from trunk. This is slightly out of date as I did the merge

several days ago.

Subversion-branch: /branches/raven-branch
Subversion-revision: 2212
This commit is contained in:
Simon Howard 2010-12-10 22:44:01 +00:00
commit 8dab0a3e63
38 changed files with 1670 additions and 356 deletions

View file

@ -53,6 +53,7 @@ EXTRA_DIST= \
README.OPL \
TODO \
BUGS \
NOT-BUGS \
rpm.spec
MAINTAINERCLEANFILES = $(AUX_DIST_GEN)

48
NEWS
View file

@ -4,8 +4,19 @@
* The DOSbox OPL emulator (DBOPL) has been imported to replace
the older FMOPL code. The quality of OPL emulation is now
therefore much better.
* The game can now run in screen modes at any color depth (not
just 8-bit modes). This is mainly to work around problems with
Windows Vista/7, where 8-bit color modes don't always work
properly.
* Multiplayer servers now register themselves with an Internet
master server. Use the -search command line parameter to
find servers on the Internet to play on. You can also use
DoomSeeker (http://skulltag.net/doomseeker/) which supports
this functionality.
* When running in windowed mode, it is now possible to
dynamically resize the window by dragging the window borders.
* Names can be specified for servers with the -servername command
line parameter.
* There are now keyboard, mouse and joystick bindings to cycle
through available weapons, making play with joypads or mobile
devices (ie. without a proper keyboard) much more practical.
@ -15,19 +26,50 @@
port, for cards that don't use port 0x388.
* Up to 8 mouse buttons are now supported (including the
mousewheel).
* The Python scripts used for building Chocolate Doom now work
with Python 3 (but also continue to work with Python 2)
(thanks arin).
* The font used for the textscreen library can be forced by
setting the TEXTSCREEN_FONT environment variable to "small" or
"normal".
* There is now a NOT-BUGS file included that lists some common
Vanilla Doom bugs/limitations that you might encounter.
Compatibility:
* The -timer and -avg options now work the same as Vanilla when
playing back demos (thanks xttl)
* A texture lookup bug was fixed that caused the wrong sky to be
displayed in Spooky01.wad (thanks Porsche Monty).
* The HacX v1.2 IWAD file is now supported, and can be used
standalone without the need for the Doom II IWAD (thanks
atyth).
Bugs fixed:
* A workaround has been a bug in old versions of SDL_mixer
(v1.2.8 and earlier) that could cause the game to lock up.
Please upgrade to a newer version if you haven't already.
* It is now possible to use OPL emulation at 11025Hz sound
sampling rate (thanks to the new OPL emulator).
sampling rate, due to the new OPL emulator (thanks Porsche
Monty).
* The span renderer function (used for drawing floors and
ceilings) now behaves the same as Vanilla Doom, so screenshots
are pixel-perfect identical to Vanilla Doom (thanks Porsche
Monty).
* The zone memory system now aligns allocated memory to 8-byte
boundaries on 64-bit systems, which may fix crashes on systems
such as sparc64.
such as sparc64 (thanks Ryan Freeman and Edd Barrett).
* The configure script now checks for libm, fixing compile
problems on Fedora Linux.
problems on Fedora Linux (thanks Sander van Dijk).
* Sound distortion with certain music files when played back
using OPL (eg. Heretic title screen).
* Error in Windows when reading response files (thanks Porsche
Monty, xttl, Janizdreg).
* Windows Vista/7 8-bit color mode issues (the default is now to
run in 32-bit color depth on these versions) (thanks to
everybody who reported this and helped test the fix).
* Screen borders no longer flash when running on widescreen
monitors, if you choose a true-color screen mode (thanks
exp(x)).
1.4.0 (2010-07-10):

112
NOT-BUGS Normal file
View file

@ -0,0 +1,112 @@
The aim of Chocolate Doom is to behave as closely to Vanilla Doom as
possible. As a result, you may experience problems that you would
also experience when using Vanilla Doom. These are not "bugs" as
Chocolate Doom is behaving as intended.
This is not intended to be a comprehensive list of Vanilla Doom bugs.
For more information, consult the "engine bugs" page of the Doom Wiki.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== Game exits after title screen with message about game version ==
The game may exit after the title screen is shown, with a message like
the following:
Demo is from a different game version!
(read 106, should be 109)
*** You may need to upgrade your version of Doom to v1.9. ***
See: http://doomworld.com/files/patches.shtml
This appears to be v1.6/v1.666.
This usually indicates that your IWAD file that you are using to play
the game (usually named doom.wad or doom2.wad) is out of date.
Chocolate Doom only supports the v1.9 IWAD file.
To fix the problem, you must upgrade to the v1.9 IWAD file. The URL
in the message has downloadable upgrade patches that you can use to
upgrade.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== Game exits when accessing the options menu ==
The game may exit with the message "Bad V_DrawPatch" when accessing
the options menu, if you have your mouse sensitivity set high.
The Doom options menu has a slider that allows the mouse sensitivity
to be controlled; however, it has only a very limited range. It is
common for experienced players to set a mouse sensitivity that is much
higher than what can be set via the options menu. The setup program
allows a larger range of values to be set.
However, setting very high sensitivity values causes the game to exit
when accessing the options menu under Vanilla Doom. Because Chocolate
Doom aims to emulate Vanilla Doom as closely as possible, it does the
same thing.
One solution to the problem is to set a lower mouse sensitivity.
Alternatively, all of the settings in the options menu can be
controlled through Doom's key bindings anyway:
End game: F7
Messages on/off: F8
Graphic detail high/low: F5
Screen size smaller/larger: -/+
Sound volume menu: F4
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== Game exits with "Savegame buffer overrun" when saving the game ==
If you are playing on a particularly large level, it is possible that
when you save the game, the game will quit with the message "Savegame
buffer overrun".
Vanilla Doom has a limited size memory bufferthat it uses for saving
games. If you are playing on a large level, the buffer may be too
small for the entire savegame to fit. Chocolate Doom allows the limit
to be disabled: in the setup tool, go to the "compatibility" menu and
disable the "Vanilla savegame limit" option.
If this error happens to you, your game has not been lost! A file
named temp.dsg is saved; rename this to doomsav0.dsg to make it appear
in the first slot in the "load game" menu. (On Unix systems, you will
need to look in the .chocolate-doom/savegames directory in your home
directory)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== Game ends suddenly when recording a demo ==
If you are recording a very long demo, the game may exit suddenly.
Vanilla Doom has a limited size memory buffer that it uses to save the
demo into. When the buffer is full, the game exits. You can tell if
this happens, as the demo file will be around 131,072 bytes in size.
You can work around this by using the -maxdemo command line parameter
to specify a larger buffer size. Alternatively, the limit can be
disabled: in the setup tool, go to the compatibility menu and disable
the "Vanilla demo limit" option.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
== Game exits with a message about "visplanes" ==
The game may exit with one of these messages:
R_FindPlane: no more visplanes
R_DrawPlanes: visplane overflow (129)
This is known as the "visplane overflow" limit and is one of the most
well-known Vanilla Doom engine limits. You should only ever experience
this when trying to play an add-on level. The level you are trying to
play is too complex; it was most likely designed to work with a limit
removing source port.
More information can be found here:
http://rome.ro/lee_killough/editing/visplane.shtml

10
README
View file

@ -65,9 +65,13 @@ Here are some examples:
You are encouraged to sign up and contribute any useful information
you may have regarding the port!
* Chocolate Doom is not perfect. See the BUGS file for a list of
known issues. New bug reports can be submitted to the Chocolate
Doom bug tracker on Sourceforge. See:
* Chocolate Doom is not perfect. See the BUGS file for a list of
known issues. Because of the nature of the project, you may also
encounter Vanilla Doom bugs; these are intentionally present; see
the NOT-BUGS file for more information.
New bug reports can be submitted to the Chocolate Doom bug tracker
on Sourceforge. See:
http://sourceforge.net/projects/chocolate-doom

View file

@ -1,6 +1,4 @@
#!/usr/bin/python
#
# $Id: convert-icon 704 2006-10-18 00:51:11Z fraggle $
#!/usr/bin/env python
#
# Copyright(C) 2005 Simon Howard
#
@ -29,7 +27,7 @@ import re
try:
import Image
except ImportError:
print "WARNING: Could not update %s. Please install the Python Imaging library." % sys.argv[2]
print("WARNING: Could not update %s. Please install the Python Imaging library." % sys.argv[2])
sys.exit(0)
@ -71,4 +69,3 @@ def convert_image(filename, output_filename):
convert_image(sys.argv[1], sys.argv[2])

View file

@ -150,11 +150,8 @@ config_files = {}
show_vanilla_options = True
class Parameter:
def __cmp__(self, other):
if self.name < other.name:
return -1
else:
return 1
def __lt__(self, other):
return self.name < other.name
def __init__(self):
self.text = ""
@ -389,7 +386,7 @@ def print_template(template_file, content):
try:
for line in f:
line = line.replace("@content", content)
print line.rstrip()
print(line.rstrip())
finally:
f.close()
@ -407,7 +404,7 @@ def wiki_output(targets, template):
read_wikipages()
for t in targets:
print t.wiki_output()
print(t.wiki_output())
def plaintext_output(targets, template_file):
@ -419,13 +416,13 @@ def plaintext_output(targets, template_file):
print_template(template_file, content)
def usage():
print "Usage: %s [-V] [-c filename ]( -m | -w | -p ) <directory>" \
% sys.argv[0]
print " -c : Provide documentation for the specified configuration file"
print " -m : Manpage output"
print " -w : Wikitext output"
print " -p : Plaintext output"
print " -V : Don't show Vanilla Doom options"
print("Usage: %s [-V] [-c filename ]( -m | -w | -p ) <directory>" \
% sys.argv[0])
print(" -c : Provide documentation for the specified configuration file")
print(" -m : Manpage output")
print(" -w : Wikitext output")
print(" -p : Plaintext output")
print(" -V : Don't show Vanilla Doom options")
sys.exit(0)
# Parse command line

View file

@ -176,8 +176,8 @@ static void FillBuffer(int16_t *buffer, unsigned int nsamples)
for (i=0; i<nsamples; ++i)
{
buffer[i * 2] = (int16_t) (mix_buffer[i] * 2);
buffer[i * 2 + 1] = (int16_t) (mix_buffer[i] * 2);
buffer[i * 2] = (int16_t) mix_buffer[i];
buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
}
}

View file

@ -25,6 +25,7 @@ DOC_FILES = README \
INSTALL \
NEWS \
BUGS \
NOT-BUGS \
CMDLINE \
TODO

View file

@ -3,10 +3,11 @@
import os
import re
import shutil
import struct
import sys
import tempfile
CAB_HEADER = "MSCE"
CAB_HEADER = "MSCE".encode("ascii")
ARCHITECTURES = {
"shx-sh3": 103,
@ -58,16 +59,10 @@ DIR_VARIABLES = {
}
def write_int16(f, value):
b1 = value & 0xff
b2 = (value >> 8) & 0xff
f.write("%c%c" % (b1, b2))
f.write(struct.pack("<H", value))
def write_int32(f, value):
b1 = value & 0xff
b2 = (value >> 8) & 0xff
b3 = (value >> 16) & 0xff
b4 = (value >> 24) & 0xff
f.write("%c%c%c%c" % (b1, b2, b3, b4))
f.write(struct.pack("<I", value))
# Pad a string with NUL characters so that it has a length that is
# a multiple of 4. At least one NUL is always added.
@ -208,7 +203,7 @@ class StringDictionary:
for i, s in self.string_list:
write_int16(stream, i)
write_int16(stream, len(s))
stream.write(s)
stream.write(s.encode("ascii"))
class DirectoryList:
def __init__(self, cab_header):
@ -252,7 +247,7 @@ class DirectoryList:
# dir_path = dir_path[1:]
dir_path = [ dir ]
dir_path = map(lambda x: dictionary.get(x), dir_path)
dir_path = list(map(lambda x: dictionary.get(x), dir_path))
self.directories[key] = self.index
self.directories_list.append((self.index, dir_path))
@ -334,7 +329,7 @@ class FileList:
write_int16(stream, file_no)
write_int32(stream, flags)
write_int16(stream, len(filename))
stream.write(filename)
stream.write(filename.encode("ascii"))
# TODO?
@ -412,7 +407,7 @@ class LinkList:
# Map dirs that make up the path to strings.
dictionary = self.cab_header.dictionary
dest_path = map(lambda x: dictionary.get(x), dest_path)
dest_path = list(map(lambda x: dictionary.get(x), dest_path))
self.links.append((self.index, target_type, target_id,
base_dir, dest_path))
@ -492,6 +487,7 @@ class CabHeaderFile:
section.write(stream)
pos = stream.tell()
if pos != old_pos + len(section):
print(section)
raise Exception("Section is %i bytes long, but %i written" % \
(len(section), pos - old_pos))
old_pos = pos
@ -574,7 +570,7 @@ class CabFile:
basename = self.__shorten_name(self.files[0], 0)
filename = os.path.join(dir, basename)
stream = file(filename, "w")
stream = open(filename, "wb")
self.cab_header.write(stream)
stream.close()
@ -625,17 +621,17 @@ def expand_path(filename):
# Expand $(xyz) path variables to their Windows equivalents:
def replace_var(match):
var_name = match.group(1)
var_name = match.group(1)
if not var_name in DIR_VARIABLES:
raise Exception("Unknown variable '%s'" % var_name)
else:
return DIR_VARIABLES[var_name]
if not var_name in DIR_VARIABLES:
raise Exception("Unknown variable '%s'" % var_name)
else:
return DIR_VARIABLES[var_name]
return re.sub(r"\$\((.*?)\)", replace_var, filename)
def read_config_file(filename):
f = file(filename)
f = open(filename)
data = f.readlines()
data = "".join(data)
@ -656,10 +652,10 @@ def print_dependencies(filename):
files_list = config["files"]
for dest, source_file in files_list.items():
print source_file
print(source_file)
if len(sys.argv) < 3:
print "Usage: %s <config file> <output file>" % sys.argv[0]
print("Usage: %s <config file> <output file>" % sys.argv[0])
sys.exit(0)
if sys.argv[1] == "-d":

View file

@ -33,6 +33,7 @@ net_dedicated.c net_dedicated.h \
net_io.c net_io.h \
net_packet.c net_packet.h \
net_sdl.c net_sdl.h \
net_query.c net_query.h \
net_server.c net_server.h \
net_structrw.c net_structrw.h \
z_native.c z_zone.h

View file

@ -47,6 +47,7 @@ static iwad_t iwads[] =
{ "doom.wad", doom, retail, "Doom" },
{ "doom1.wad", doom, shareware, "Doom Shareware" },
{ "chex.wad", doom, shareware, "Chex Quest" },
{ "hacx.wad", doom2, commercial, "Hacx" },
{ "heretic.wad", heretic, retail, "Heretic" },
{ "heretic1.wad", heretic, shareware, "Heretic Shareware" },
{ "hexen.wad", hexen, commercial, "Hexen" },

View file

@ -62,6 +62,7 @@ typedef enum
typedef enum
{
exe_doom_1_9, // Doom 1.9: used for shareware, registered and commercial
exe_hacx, // Hacx
exe_ultimate, // Ultimate Doom (retail)
exe_final, // Final Doom
exe_chex, // Chex Quest executable (based on Final Doom)

View file

@ -30,21 +30,63 @@
#include <string.h>
#include "i_system.h"
#include "w_wad.h"
#include "z_zone.h"
#include "deh_defs.h"
#include "deh_io.h"
typedef enum
{
DEH_INPUT_FILE,
DEH_INPUT_LUMP
} deh_input_type_t;
struct deh_context_s
{
FILE *stream;
deh_input_type_t type;
char *filename;
// If the input comes from a memory buffer, pointer to the memory
// buffer.
unsigned char *input_buffer;
size_t input_buffer_len;
unsigned int input_buffer_pos;
int lumpnum;
// If the input comes from a file, the file stream for reading
// data.
FILE *stream;
// Current line number that we have reached:
int linenum;
// Used by DEH_ReadLine:
boolean last_was_newline;
char *readbuffer;
int readbuffer_size;
};
static deh_context_t *DEH_NewContext(void)
{
deh_context_t *context;
context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
// Initial read buffer size of 128 bytes
context->readbuffer_size = 128;
context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
context->linenum = 0;
context->last_was_newline = true;
return context;
}
// Open a dehacked file for reading
// Returns NULL if open failed
@ -52,22 +94,41 @@ deh_context_t *DEH_OpenFile(char *filename)
{
FILE *fstream;
deh_context_t *context;
fstream = fopen(filename, "r");
if (fstream == NULL)
return NULL;
context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
context->stream = fstream;
// Initial read buffer size of 128 bytes
context = DEH_NewContext();
context->readbuffer_size = 128;
context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
context->filename = filename;
context->linenum = 0;
context->last_was_newline = true;
context->type = DEH_INPUT_FILE;
context->stream = fstream;
context->filename = strdup(filename);
return context;
}
// Open a WAD lump for reading.
deh_context_t *DEH_OpenLump(int lumpnum)
{
deh_context_t *context;
void *lump;
lump = W_CacheLumpNum(lumpnum, PU_STATIC);
context = DEH_NewContext();
context->type = DEH_INPUT_LUMP;
context->lumpnum = lumpnum;
context->input_buffer = lump;
context->input_buffer_len = W_LumpLength(lumpnum);
context->input_buffer_pos = 0;
context->filename = malloc(9);
strncpy(context->filename, lumpinfo[lumpnum].name, 8);
context->filename[8] = '\0';
return context;
}
@ -76,33 +137,67 @@ deh_context_t *DEH_OpenFile(char *filename)
void DEH_CloseFile(deh_context_t *context)
{
fclose(context->stream);
if (context->type == DEH_INPUT_FILE)
{
fclose(context->stream);
}
else if (context->type == DEH_INPUT_LUMP)
{
W_ReleaseLumpNum(context->lumpnum);
}
Z_Free(context->readbuffer);
Z_Free(context);
}
int DEH_GetCharFile(deh_context_t *context)
{
if (feof(context->stream))
{
// end of file
return -1;
}
return fgetc(context->stream);
}
int DEH_GetCharLump(deh_context_t *context)
{
int result;
if (context->input_buffer_pos >= context->input_buffer_len)
{
return -1;
}
result = context->input_buffer[context->input_buffer_pos];
++context->input_buffer_pos;
return result;
}
// Reads a single character from a dehacked file
int DEH_GetChar(deh_context_t *context)
{
int result;
// Read characters, but ignore carriage returns
// Essentially this is a DOS->Unix conversion
do
do
{
if (feof(context->stream))
switch (context->type)
{
// end of file
case DEH_INPUT_FILE:
result = DEH_GetCharFile(context);
break;
result = -1;
case DEH_INPUT_LUMP:
result = DEH_GetCharLump(context);
break;
}
else
{
result = fgetc(context->stream);
}
} while (result == '\r');
// Track the current line number
@ -111,9 +206,9 @@ int DEH_GetChar(deh_context_t *context)
{
++context->linenum;
}
context->last_was_newline = result == '\n';
return result;
}

View file

@ -30,6 +30,7 @@
#include "deh_defs.h"
deh_context_t *DEH_OpenFile(char *filename);
deh_context_t *DEH_OpenLump(int lumpnum);
void DEH_CloseFile(deh_context_t *context);
int DEH_GetChar(deh_context_t *context);
char *DEH_ReadLine(deh_context_t *context);

View file

@ -32,6 +32,7 @@
#include "doomtype.h"
#include "d_iwad.h"
#include "m_argv.h"
#include "w_wad.h"
#include "deh_defs.h"
#include "deh_io.h"
@ -246,9 +247,6 @@ static void DEH_ParseContext(deh_context_t *context)
DEH_Error(context, "This is not a valid dehacked patch file!");
}
deh_allow_long_strings = false;
deh_allow_long_cheats = false;
// Read the file
for (;;)
@ -260,7 +258,9 @@ static void DEH_ParseContext(deh_context_t *context)
// end of file?
if (line == NULL)
{
return;
}
while (line[0] != '\0' && isspace(line[0]))
++line;
@ -347,6 +347,48 @@ int DEH_LoadFile(char *filename)
return 1;
}
// Load dehacked file from WAD lump.
int DEH_LoadLump(int lumpnum)
{
deh_context_t *context;
// If it's in a lump, it's probably designed for a modern source port,
// so allow it to do long string and cheat replacements.
deh_allow_long_strings = true;
deh_allow_long_cheats = true;
context = DEH_OpenLump(lumpnum);
if (context == NULL)
{
fprintf(stderr, "DEH_LoadFile: Unable to open lump %i\n", lumpnum);
return 0;
}
DEH_ParseContext(context);
DEH_CloseFile(context);
return 1;
}
int DEH_LoadLumpByName(char *name)
{
int lumpnum;
lumpnum = W_CheckNumForName(name);
if (lumpnum == -1)
{
fprintf(stderr, "DEH_LoadLumpByName: '%s' lump not found\n", name);
return 0;
}
return DEH_LoadLump(lumpnum);
}
// Checks the command line for -deh argument
void DEH_Init(void)
@ -387,4 +429,3 @@ void DEH_Init(void)
}
}

View file

@ -41,6 +41,8 @@
void DEH_Init(void);
int DEH_LoadFile(char *filename);
int DEH_LoadLump(int lumpnum);
int DEH_LoadLumpByName(char *name);
boolean DEH_ParseAssignment(char *line, char **variable_name, char **value);

View file

@ -122,8 +122,6 @@ int startmap;
boolean autostart;
int startloadgame;
FILE* debugfile;
boolean advancedemo;
// Store demo, do not accept any inputs
@ -424,14 +422,6 @@ void D_DoomLoop (void)
if (demorecording)
G_BeginRecording ();
if (M_CheckParm ("-debugfile"))
{
char filename[20];
sprintf (filename,"debug%i.txt",consoleplayer);
printf ("debug output to: %s\n",filename);
debugfile = fopen (filename,"w");
}
TryRunTics();
I_SetWindowTitle(gamedescription);
@ -611,8 +601,12 @@ void D_StartTitle (void)
// These are from the original source: some of them are perhaps
// not used in any dehacked patches
static char *banners[] =
static char *banners[] =
{
// doom2.wad
" "
"DOOM 2: Hell on Earth v%i.%i"
" ",
// doom1.wad
" "
"DOOM Shareware Startup v%i.%i"
@ -629,10 +623,6 @@ static char *banners[] =
" "
"The Ultimate DOOM Startup v%i.%i"
" ",
// doom2.wad
" "
"DOOM 2: Hell on Earth v%i.%i"
" ",
// tnt.wad
" "
"DOOM 2: TNT - Evilution v%i.%i"
@ -833,6 +823,18 @@ static boolean CheckChex(char *iwadname)
chex_iwadname));
}
// Check if the IWAD file is the Hacx IWAD.
// Returns true if this is hacx.wad.
static boolean CheckHacx(char *iwadname)
{
char *hacx_iwadname = "hacx.wad";
return (strlen(iwadname) > strlen(hacx_iwadname)
&& !strcasecmp(iwadname + strlen(iwadname) - strlen(hacx_iwadname),
hacx_iwadname));
}
// print title for every printed line
char title[128];
@ -903,6 +905,7 @@ static struct
GameVersion_t version;
} gameversions[] = {
{"Doom 1.9", "1.9", exe_doom_1_9},
{"Hacx", "hacx", exe_hacx},
{"Ultimate Doom", "ultimate", exe_ultimate},
{"Final Doom", "final", exe_final},
{"Chex Quest", "chex", exe_chex},
@ -960,6 +963,12 @@ static void InitGameVersion(void)
gameversion = exe_chex;
}
else if (CheckHacx(iwadfile))
{
// hacx exe: identified by iwad filename
gameversion = exe_hacx;
}
else if (gamemode == shareware || gamemode == registered)
{
// original
@ -1060,6 +1069,20 @@ static void D_Endoom(void)
I_Endoom(endoom);
}
static void LoadHacxDeh(void)
{
// If this is the HACX IWAD, we need to load the DEHACKED lump.
if (gameversion == exe_hacx)
{
if (!DEH_LoadLumpByName("DEHACKED"))
{
I_Error("DEHACKED lump not found. Please check that this is the "
"Hacx v1.2 IWAD.");
}
}
}
//
// D_DoomMain
//
@ -1096,6 +1119,21 @@ void D_DoomMain (void)
// Never returns
}
//!
// @category net
//
// Query the Internet master server for a global list of active
// servers.
//
if (M_CheckParm("-search"))
{
printf("\nSearching for servers on Internet ...\n");
p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
printf("\n%i server(s) found.\n", p);
exit(0);
}
//!
// @arg <address>
// @category net
@ -1109,6 +1147,7 @@ void D_DoomMain (void)
if (p > 0)
{
NET_QueryAddress(myargv[p+1]);
exit(0);
}
//!
@ -1117,8 +1156,13 @@ void D_DoomMain (void)
// Search the local LAN for running servers.
//
if (M_CheckParm("-search"))
NET_LANQuery();
if (M_CheckParm("-localsearch"))
{
printf("\nSearching for servers on local LAN ...\n");
p = NET_LANQuery(NET_QueryPrintCallback, NULL);
printf("\n%i server(s) found.\n", p);
exit(0);
}
#endif
@ -1365,6 +1409,7 @@ void D_DoomMain (void)
D_IdentifyVersion();
InitGameVersion();
LoadChexDeh();
LoadHacxDeh();
D_SetGameDescription();
SetSaveGameDir(iwadfile);
@ -1443,10 +1488,9 @@ void D_DoomMain (void)
p = M_CheckParm ("-timer");
if (p && p < myargc-1 && deathmatch)
if (p && p < myargc-1)
{
timelimit = atoi(myargv[p+1]);
printf("timer: %i\n", timelimit);
}
//!
@ -1458,10 +1502,8 @@ void D_DoomMain (void)
p = M_CheckParm ("-avg");
if (p && p < myargc-1 && deathmatch)
if (p && p < myargc-1)
{
DEH_printf("Austin Virtual Gaming: Levels will end "
"after 20 minutes\n");
timelimit = 20;
}

View file

@ -499,6 +499,7 @@ boolean D_InitNetGame(net_connect_data_t *connect_data,
if (i > 0)
{
addr = NET_FindLANServer();
NET_SV_RegisterWithMaster();
if (addr == NULL)
{
@ -617,12 +618,22 @@ void D_CheckNetGame (void)
// Show players here; the server might have specified a time limit
if (timelimit > 0)
if (timelimit > 0 && deathmatch)
{
DEH_printf("Levels will end after %d minute", timelimit);
if (timelimit > 1)
printf("s");
printf(".\n");
// Gross hack to work like Vanilla:
if (timelimit == 20 && M_CheckParm("-avg"))
{
DEH_printf("Austin Virtual Gaming: Levels will end "
"after 20 minutes\n");
}
else
{
DEH_printf("Levels will end after %d minute", timelimit);
if (timelimit > 1)
printf("s");
printf(".\n");
}
}
}
@ -634,9 +645,6 @@ void D_CheckNetGame (void)
//
void D_QuitNetGame (void)
{
if (debugfile)
fclose (debugfile);
#ifdef FEATURE_MULTIPLAYER
NET_SV_Shutdown();

View file

@ -255,7 +255,6 @@ extern int maxammo[NUMAMMO];
// File handling stuff.
extern char * savegamedir;
extern char basedefault[1024];
extern FILE* debugfile;
// if true, load all graphics at level load
extern boolean precache;

View file

@ -762,6 +762,8 @@ void M_DrawReadThis1(void)
switch (gameversion)
{
case exe_doom_1_9:
case exe_hacx:
if (gamemode == commercial)
{
// Doom 2

View file

@ -755,17 +755,7 @@ P_SetupLevel
// Make sure all sounds are stopped before Z_FreeTags.
S_Start ();
#if 0 // UNUSED
if (debugfile)
{
Z_FreeTags (PU_LEVEL, INT_MAX);
Z_FileDumpHeap (debugfile);
}
else
#endif
Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
// UNUSED W_Profile ();
P_InitThinkers ();

View file

@ -1389,10 +1389,9 @@ void P_SpawnSpecials (void)
if (W_CheckNumForName(DEH_String("texture2")) >= 0)
episode = 2;
// See if -TIMER was specified.
if (timelimit > 0)
if (timelimit > 0 && deathmatch)
{
levelTimer = true;
levelTimeCount = timelimit * 60 * TICRATE;
@ -1401,7 +1400,7 @@ void P_SpawnSpecials (void)
{
levelTimer = false;
}
// Init special SECTORs.
sector = sectors;
for (i=0 ; i<numsectors ; i++, sector++)

View file

@ -413,6 +413,7 @@ R_GetColumn
static void GenerateTextureHashTable(void)
{
texture_t **rover;
int i;
int key;
@ -429,12 +430,25 @@ static void GenerateTextureHashTable(void)
textures[i]->index = i;
// Hook into hash table
// Vanilla Doom does a linear search of the texures array
// and stops at the first entry it finds. If there are two
// entries with the same name, the first one in the array
// wins. The new entry must therefore be added at the end
// of the hash chain, so that earlier entries win.
key = W_LumpNameHash(textures[i]->name) % numtextures;
textures[i]->next = textures_hashtable[key];
textures_hashtable[key] = textures[i];
rover = &textures_hashtable[key];
while (*rover != NULL)
{
rover = &(*rover)->next;
}
// Hook into hash table
textures[i]->next = NULL;
*rover = textures[i];
}
}

View file

@ -53,6 +53,8 @@
#define MAX_SOUND_SLICE_TIME 70 /* ms */
#define NUM_CHANNELS 16
static boolean setpanning_workaround = false;
static boolean sound_initialized = false;
static sfxinfo_t *channels_playing[NUM_CHANNELS];
@ -632,10 +634,19 @@ static void I_SDL_UpdateSoundParams(int handle, int vol, int sep)
if (right < 0) right = 0;
else if (right > 255) right = 255;
// SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
// function. A workaround is to call Mix_UnregisterAllEffects for
// the channel before calling it. This is undesirable as it may lead
// to the channel volumes resetting briefly.
if (setpanning_workaround)
{
Mix_UnregisterAllEffects(handle);
}
Mix_SetPanning(handle, left, right);
}
//
// Starting a sound means adding it
// to the current list of active sounds
@ -822,8 +833,34 @@ static boolean I_SDL_InitSound(boolean _use_sfx_prefix)
}
#endif
// SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
// function that can cause the game to lock up. If we're using an old
// version, we need to apply a workaround. But the workaround has its
// own drawbacks ...
{
const SDL_version *mixer_version;
int v;
mixer_version = Mix_Linked_Version();
v = SDL_VERSIONNUM(mixer_version->major,
mixer_version->minor,
mixer_version->patch);
if (v <= SDL_VERSIONNUM(1, 2, 8))
{
setpanning_workaround = true;
fprintf(stderr, "\n"
"ATTENTION: You are using an old version of SDL_mixer!\n"
" This version has a bug that may cause "
"your sound to stutter.\n"
" Please upgrade to a newer version!\n"
"\n");
}
}
Mix_AllocateChannels(NUM_CHANNELS);
SDL_PauseAudio(0);
sound_initialized = true;

View file

@ -139,6 +139,12 @@ static SDL_Surface *screen;
static char *window_title = "";
// Intermediate 8-bit buffer that we draw to instead of 'screen'.
// This is used when we are rendering in 32-bit screen mode.
// When in a real 8-bit screen mode, screenbuffer == screen.
static SDL_Surface *screenbuffer;
// palette
static SDL_Color palette[256];
@ -172,6 +178,10 @@ static boolean native_surface;
static int screen_width = SCREENWIDTH;
static int screen_height = SCREENHEIGHT;
// Color depth.
int screen_bpp = 8;
// Automatically adjust video settings if the selected mode is
// not a valid video mode.
@ -911,17 +921,18 @@ static boolean BlitArea(int x1, int y1, int x2, int y2)
return true;
}
x_offset = (screen->w - screen_mode->width) / 2;
y_offset = (screen->h - screen_mode->height) / 2;
x_offset = (screenbuffer->w - screen_mode->width) / 2;
y_offset = (screenbuffer->h - screen_mode->height) / 2;
if (SDL_LockSurface(screen) >= 0)
if (SDL_LockSurface(screenbuffer) >= 0)
{
I_InitScale(I_VideoBuffer,
(byte *) screen->pixels + (y_offset * screen->pitch)
+ x_offset,
(byte *) screenbuffer->pixels
+ (y_offset * screen->pitch)
+ x_offset,
screen->pitch);
result = screen_mode->DrawScreen(x1, y1, x2, y2);
SDL_UnlockSurface(screen);
SDL_UnlockSurface(screenbuffer);
}
else
{
@ -1055,19 +1066,37 @@ void I_FinishUpdate (void)
// draw to screen
BlitArea(0, 0, SCREENWIDTH, SCREENHEIGHT);
// If we have a palette to set, the act of setting the palette
// updates the screen
if (palette_to_set)
{
SDL_SetColors(screen, palette, 0, 256);
SDL_SetColors(screenbuffer, palette, 0, 256);
palette_to_set = false;
// In native 8-bit mode, if we have a palette to set, the act
// of setting the palette updates the screen
if (screenbuffer == screen)
{
return;
}
}
else
// In 8in32 mode, we must blit from the fake 8-bit screen buffer
// to the real screen before doing a screen flip.
if (screenbuffer != screen)
{
SDL_Flip(screen);
SDL_Rect dst_rect;
// Center the buffer within the full screen space.
dst_rect.x = (screen->w - screenbuffer->w) / 2;
dst_rect.y = (screen->h - screenbuffer->h) / 2;
SDL_BlitSurface(screenbuffer, NULL, screen, &dst_rect);
}
SDL_Flip(screen);
}
@ -1348,15 +1377,61 @@ static void AutoAdjustWindowed(void)
}
}
// Auto-adjust to a valid color depth.
static void AutoAdjustColorDepth(void)
{
SDL_Rect **modes;
SDL_PixelFormat format;
const SDL_VideoInfo *info;
int flags;
if (fullscreen)
{
flags = SDL_FULLSCREEN;
}
else
{
flags = 0;
}
format.BitsPerPixel = screen_bpp;
format.BytesPerPixel = (screen_bpp + 7) / 8;
// Are any screen modes supported at the configured color depth?
modes = SDL_ListModes(&format, flags);
// If not, we must autoadjust to something sensible.
if (modes == NULL)
{
printf("I_InitGraphics: %ibpp color depth not supported.\n",
screen_bpp);
info = SDL_GetVideoInfo();
if (info != NULL && info->vfmt != NULL)
{
screen_bpp = info->vfmt->BitsPerPixel;
}
}
}
// If the video mode set in the configuration file is not available,
// try to choose a different mode.
static void I_AutoAdjustSettings(void)
{
int old_screen_w, old_screen_h;
int old_screen_w, old_screen_h, old_screen_bpp;
old_screen_w = screen_width;
old_screen_h = screen_height;
old_screen_bpp = screen_bpp;
// Possibly adjust color depth.
AutoAdjustColorDepth();
// If we are running fullscreen, try to autoadjust to a valid fullscreen
// mode. If this is impossible, switch to windowed.
@ -1375,10 +1450,11 @@ static void I_AutoAdjustSettings(void)
// Have the settings changed? Show a message.
if (screen_width != old_screen_w || screen_height != old_screen_h)
if (screen_width != old_screen_w || screen_height != old_screen_h
|| screen_bpp != old_screen_bpp)
{
printf("I_InitGraphics: Auto-adjusted to %ix%i.\n",
screen_width, screen_height);
printf("I_InitGraphics: Auto-adjusted to %ix%ix%ibpp.\n",
screen_width, screen_height, screen_bpp);
printf("NOTE: Your video settings have been adjusted. "
"To disable this behavior,\n"
@ -1565,6 +1641,33 @@ static void CheckCommandLine(void)
screen_height = atoi(myargv[i + 1]);
}
//!
// @category video
// @arg <bpp>
//
// Specify the color depth of the screen, in bits per pixel.
//
i = M_CheckParm("-bpp");
if (i > 0)
{
screen_bpp = atoi(myargv[i + 1]);
}
// Because we love Eternity:
//!
// @category video
//
// Set the color depth of the screen to 32 bits per pixel.
//
if (M_CheckParm("-8in32"))
{
screen_bpp = 32;
}
//!
// @category video
// @arg <WxY>
@ -1752,7 +1855,12 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
// Set the video mode.
flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
flags |= SDL_SWSURFACE | SDL_DOUBLEBUF;
if (screen_bpp == 8)
{
flags |= SDL_HWPALETTE;
}
if (fullscreen)
{
@ -1763,13 +1871,19 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
flags |= SDL_RESIZABLE;
}
screen = SDL_SetVideoMode(w, h, 8, flags);
screen = SDL_SetVideoMode(w, h, screen_bpp, flags);
if (screen == NULL)
{
I_Error("Error setting video mode: %s\n", SDL_GetError());
I_Error("Error setting video mode %ix%ix%ibpp: %s\n",
w, h, screen_bpp, SDL_GetError());
}
// Blank out the full screen area in case there is any junk in
// the borders that won't otherwise be overwritten.
SDL_FillRect(screen, NULL, 0);
// If mode was not set, it must be set now that we know the
// screen size.
@ -1791,6 +1905,22 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
}
}
// Create the screenbuffer surface; if we have a real 8-bit palettized
// screen, then we can use the screen as the screenbuffer.
if (screen->format->BitsPerPixel == 8)
{
screenbuffer = screen;
}
else
{
screenbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
mode->width, mode->height, 8,
0, 0, 0, 0);
SDL_FillRect(screenbuffer, NULL, 0);
}
// Save screen mode.
screen_mode = mode;
@ -1908,24 +2038,13 @@ void I_InitGraphics(void)
// Start with a clear black screen
// (screen will be flipped after we set the palette)
if (SDL_LockSurface(screen) >= 0)
{
byte *screenpixels;
int y;
screenpixels = (byte *) screen->pixels;
for (y=0; y<screen->h; ++y)
memset(screenpixels + screen->pitch * y, 0, screen->w);
SDL_UnlockSurface(screen);
}
SDL_FillRect(screenbuffer, NULL, 0);
// Set the palette
doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
I_SetPalette(doompal);
SDL_SetColors(screen, palette, 0, 256);
SDL_SetColors(screenbuffer, palette, 0, 256);
CreateCursors();
@ -1947,7 +2066,8 @@ void I_InitGraphics(void)
// Likewise if the screen pitch is not the same as the width
// If we have to multiply, drawing is done to a separate 320x200 buf
native_surface = !SDL_MUSTLOCK(screen)
native_surface = screen == screenbuffer
&& !SDL_MUSTLOCK(screen)
&& screen_mode == &mode_scale_1x
&& screen->pitch == SCREENWIDTH
&& aspect_ratio_correct;
@ -2018,5 +2138,26 @@ void I_BindVideoVariables(void)
M_BindVariable("usegamma", &usegamma);
M_BindVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping);
M_BindVariable("novert", &novert);
// Windows Vista or later? Set screen color depth to
// 32 bits per pixel, as 8-bit palettized screen modes
// don't work properly in recent versions.
#if defined(_WIN32) && !defined(_WIN32_WCE)
{
OSVERSIONINFOEX version_info;
ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((OSVERSIONINFO *) &version_info);
if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
&& version_info.dwMajorVersion >= 6)
{
screen_bpp = 32;
}
}
#endif
}

View file

@ -116,6 +116,7 @@ void I_EnableLoadingDisk(void);
extern char *video_driver;
extern boolean screenvisible;
extern float mouse_acceleration;
extern int mouse_threshold;
extern int vanilla_keyboard_mapping;

View file

@ -88,7 +88,7 @@ static void LoadResponseFile(int argv_index)
response_filename = myargv[argv_index] + 1;
// Read the response file into memory
handle = fopen(response_filename, "r");
handle = fopen(response_filename, "rb");
if (handle == NULL)
{

View file

@ -554,6 +554,12 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_INT(screen_height),
//!
// Color depth of the screen, in bits.
//
CONFIG_VARIABLE_INT(screen_bpp),
//!
// If this is non-zero, the mouse will be "grabbed" when running
// in windowed mode so that it can be used as an input device.

View file

@ -74,8 +74,8 @@ void NET_DedicatedServer(void)
CheckForClientOptions();
NET_SV_Init();
NET_SV_AddModule(&net_sdl_module);
NET_SV_RegisterWithMaster();
while (true)
{

View file

@ -128,6 +128,14 @@ typedef enum
NET_PACKET_TYPE_QUERY_RESPONSE,
} net_packet_type_t;
typedef enum
{
NET_MASTER_PACKET_TYPE_ADD,
NET_MASTER_PACKET_TYPE_ADD_RESPONSE,
NET_MASTER_PACKET_TYPE_QUERY,
NET_MASTER_PACKET_TYPE_QUERY_RESPONSE
} net_master_packet_type_t;
// Settings specified when the client connects to the server.
typedef struct

View file

@ -137,9 +137,16 @@ static void NET_CL_FreeAddress(net_addr_t *addr)
static net_addr_t *NET_CL_ResolveAddress(char *address)
{
client_addr.module = &net_loop_client_module;
if (address == NULL)
{
client_addr.module = &net_loop_client_module;
return &client_addr;
return &client_addr;
}
else
{
return NULL;
}
}
net_module_t net_loop_client_module =
@ -206,8 +213,15 @@ static void NET_SV_FreeAddress(net_addr_t *addr)
static net_addr_t *NET_SV_ResolveAddress(char *address)
{
server_addr.module = &net_loop_server_module;
return &server_addr;
if (address == NULL)
{
server_addr.module = &net_loop_server_module;
return &server_addr;
}
else
{
return NULL;
}
}
net_module_t net_loop_server_module =

View file

@ -37,50 +37,166 @@
#include "net_structrw.h"
#include "net_sdl.h"
typedef struct
// DNS address of the Internet master server.
#define MASTER_SERVER_ADDRESS "master.chocolate-doom.org"
// Time to wait for a response before declaring a timeout.
#define QUERY_TIMEOUT_SECS 2
// Number of query attempts to make before giving up on a server.
#define QUERY_MAX_ATTEMPTS 3
typedef enum
{
QUERY_TARGET_SERVER, // Normal server target.
QUERY_TARGET_MASTER, // The master server.
QUERY_TARGET_BROADCAST // Send a broadcast query
} query_target_type_t;
typedef enum
{
QUERY_TARGET_QUEUED, // Query not yet sent
QUERY_TARGET_QUERIED, // Query sent, waiting response
QUERY_TARGET_RESPONDED, // Response received
QUERY_TARGET_NO_RESPONSE
} query_target_state_t;
typedef struct
{
query_target_type_t type;
query_target_state_t state;
net_addr_t *addr;
net_querydata_t data;
} queryresponse_t;
unsigned int ping_time;
unsigned int query_time;
unsigned int query_attempts;
boolean printed;
} query_target_t;
// Transmit a query packet
static boolean registered_with_master = false;
static net_context_t *query_context;
static queryresponse_t *responders;
static int num_responses;
static query_target_t *targets;
static int num_targets;
// Add a new address to the list of hosts that has responded
static boolean query_loop_running = false;
static boolean printed_header = false;
static queryresponse_t *AddResponder(net_addr_t *addr,
net_querydata_t *data)
// Resolve the master server address.
net_addr_t *NET_Query_ResolveMaster(net_context_t *context)
{
queryresponse_t *response;
net_addr_t *addr;
responders = realloc(responders,
sizeof(queryresponse_t) * (num_responses + 1));
addr = NET_ResolveAddress(context, MASTER_SERVER_ADDRESS);
response = &responders[num_responses];
response->addr = addr;
response->data = *data;
++num_responses;
if (addr == NULL)
{
fprintf(stderr, "Warning: Failed to resolve address "
"for master server: %s\n", MASTER_SERVER_ADDRESS);
}
return response;
return addr;
}
// Returns true if the reply is from a host that has not previously
// responded.
// Send a registration packet to the master server to register
// ourselves with the global list.
static boolean CheckResponder(net_addr_t *addr)
void NET_Query_AddToMaster(net_addr_t *master_addr)
{
net_packet_t *packet;
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_ADD);
NET_SendPacket(master_addr, packet);
NET_FreePacket(packet);
}
// Process a packet received from the master server.
void NET_Query_MasterResponse(net_packet_t *packet)
{
unsigned int packet_type;
unsigned int result;
if (!NET_ReadInt16(packet, &packet_type)
|| !NET_ReadInt16(packet, &result))
{
return;
}
if (packet_type == NET_MASTER_PACKET_TYPE_ADD_RESPONSE)
{
if (result != 0)
{
// Only show the message once.
if (!registered_with_master)
{
printf("Registered with master server at %s\n",
MASTER_SERVER_ADDRESS);
registered_with_master = true;
}
}
else
{
// Always show rejections.
printf("Failed to register with master server at %s\n",
MASTER_SERVER_ADDRESS);
}
}
}
// Send a query to the master server.
static void NET_Query_SendMasterQuery(net_addr_t *addr)
{
net_packet_t *packet;
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_QUERY);
NET_SendPacket(addr, packet);
NET_FreePacket(packet);
}
// Given the specified address, find the target associated. If no
// target is found, and 'create' is true, a new target is created.
static query_target_t *GetTargetForAddr(net_addr_t *addr, boolean create)
{
query_target_t *target;
int i;
for (i=0; i<num_responses; ++i)
for (i=0; i<num_targets; ++i)
{
if (responders[i].addr == addr)
if (targets[i].addr == addr)
{
return false;
return &targets[i];
}
}
return true;
if (!create)
{
return NULL;
}
targets = realloc(targets, sizeof(query_target_t) * (num_targets + 1));
target = &targets[num_targets];
target->type = QUERY_TARGET_SERVER;
target->state = QUERY_TARGET_QUEUED;
target->printed = false;
target->query_attempts = 0;
target->addr = addr;
++num_targets;
return target;
}
// Transmit a query packet
@ -104,20 +220,442 @@ static void NET_Query_SendQuery(net_addr_t *addr)
NET_FreePacket(request);
}
static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet,
net_query_callback_t callback,
void *user_data)
{
unsigned int packet_type;
net_querydata_t querydata;
query_target_t *target;
// Read the header
if (!NET_ReadInt16(packet, &packet_type)
|| packet_type != NET_PACKET_TYPE_QUERY_RESPONSE)
{
return;
}
// Read query data
if (!NET_ReadQueryData(packet, &querydata))
{
return;
}
// Find the target that responded, or potentially add a new target
// if it was not already known (for LAN broadcast search)
target = GetTargetForAddr(addr, true);
if (target->state != QUERY_TARGET_RESPONDED)
{
target->state = QUERY_TARGET_RESPONDED;
memcpy(&target->data, &querydata, sizeof(net_querydata_t));
// Calculate RTT.
target->ping_time = I_GetTimeMS() - target->query_time;
// Invoke callback to signal that we have a new address.
callback(addr, &target->data, target->ping_time, user_data);
}
}
// Parse a response packet from the master server.
static void NET_Query_ParseMasterResponse(net_addr_t *master_addr,
net_packet_t *packet)
{
unsigned int packet_type;
query_target_t *target;
char *addr_str;
net_addr_t *addr;
// Read the header. We are only interested in query responses.
if (!NET_ReadInt16(packet, &packet_type)
|| packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE)
{
return;
}
// Read a list of strings containing the addresses of servers
// that the master knows about.
for (;;)
{
addr_str = NET_ReadString(packet);
if (addr_str == NULL)
{
break;
}
// Resolve address and add to targets list if it is not already
// there.
addr = NET_ResolveAddress(query_context, addr_str);
if (addr != NULL)
{
GetTargetForAddr(addr, true);
}
}
// Mark the master as having responded.
target = GetTargetForAddr(master_addr, true);
target->state = QUERY_TARGET_RESPONDED;
}
static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet,
net_query_callback_t callback,
void *user_data)
{
query_target_t *target;
// This might be the master server responding.
target = GetTargetForAddr(addr, false);
if (target != NULL && target->type == QUERY_TARGET_MASTER)
{
NET_Query_ParseMasterResponse(addr, packet);
}
else
{
NET_Query_ParseResponse(addr, packet, callback, user_data);
}
}
static void NET_Query_GetResponse(net_query_callback_t callback,
void *user_data)
{
net_addr_t *addr;
net_packet_t *packet;
if (NET_RecvPacket(query_context, &addr, &packet))
{
NET_Query_ParsePacket(addr, packet, callback, user_data);
NET_FreePacket(packet);
}
}
// Find a target we have not yet queried and send a query.
static void SendOneQuery(void)
{
unsigned int now;
unsigned int i;
now = I_GetTimeMS();
for (i = 0; i < num_targets; ++i)
{
// Not queried yet?
// Or last query timed out without a response?
if (targets[i].state == QUERY_TARGET_QUEUED
|| (targets[i].state == QUERY_TARGET_QUERIED
&& now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000))
{
break;
}
}
if (i >= num_targets)
{
return;
}
// Found a target to query. Send a query; how to do this depends on
// the target type.
switch (targets[i].type)
{
case QUERY_TARGET_SERVER:
NET_Query_SendQuery(targets[i].addr);
break;
case QUERY_TARGET_BROADCAST:
NET_Query_SendQuery(NULL);
break;
case QUERY_TARGET_MASTER:
NET_Query_SendMasterQuery(targets[i].addr);
break;
}
//printf("Queried %s\n", NET_AddrToString(targets[i].addr));
targets[i].state = QUERY_TARGET_QUERIED;
targets[i].query_time = I_GetTimeMS();
++targets[i].query_attempts;
}
// Time out servers that have been queried and not responded.
static void CheckTargetTimeouts(void)
{
unsigned int i;
unsigned int now;
now = I_GetTimeMS();
for (i = 0; i < num_targets; ++i)
{
// We declare a target to be "no response" when we've sent
// multiple query packets to it (QUERY_MAX_ATTEMPTS) and
// received no response to any of them.
if (targets[i].state == QUERY_TARGET_QUERIED
&& targets[i].query_attempts >= QUERY_MAX_ATTEMPTS
&& now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000)
{
targets[i].state = QUERY_TARGET_NO_RESPONSE;
}
}
}
// If all targets have responded or timed out, returns true.
static boolean AllTargetsDone(void)
{
unsigned int i;
for (i = 0; i < num_targets; ++i)
{
if (targets[i].state != QUERY_TARGET_RESPONDED
&& targets[i].state != QUERY_TARGET_NO_RESPONSE)
{
return false;
}
}
return true;
}
// Stop the query loop
static void NET_Query_ExitLoop(void)
{
query_loop_running = false;
}
// Loop waiting for responses.
// The specified callback is invoked when a new server responds.
static void NET_Query_QueryLoop(net_query_callback_t callback,
void *user_data)
{
query_loop_running = true;
while (query_loop_running && !AllTargetsDone())
{
// Send a query. This will only send a single query.
// Because of the delay below, this is therefore rate limited.
SendOneQuery();
// Check for a response
NET_Query_GetResponse(callback, user_data);
// Don't thrash the CPU
I_Sleep(50);
CheckTargetTimeouts();
}
}
void NET_Query_Init(void)
{
query_context = NET_NewContext();
NET_AddModule(query_context, &net_sdl_module);
net_sdl_module.InitClient();
targets = NULL;
num_targets = 0;
printed_header = false;
}
// Callback that exits the query loop when the first server is found.
static void NET_Query_ExitCallback(net_addr_t *addr, net_querydata_t *data,
unsigned int ping_time, void *user_data)
{
NET_Query_ExitLoop();
}
// Search the targets list and find a target that has responded.
// If none have responded, returns NULL.
static query_target_t *FindFirstResponder(void)
{
unsigned int i;
for (i = 0; i < num_targets; ++i)
{
if (targets[i].type == QUERY_TARGET_SERVER
&& targets[i].state == QUERY_TARGET_RESPONDED)
{
return &targets[i];
}
}
return NULL;
}
// Return a count of the number of responses.
static int GetNumResponses(void)
{
unsigned int i;
int result;
result = 0;
for (i = 0; i < num_targets; ++i)
{
if (targets[i].type == QUERY_TARGET_SERVER
&& targets[i].state == QUERY_TARGET_RESPONDED)
{
++result;
}
}
return result;
}
void NET_QueryAddress(char *addr_str)
{
net_addr_t *addr;
query_target_t *target;
NET_Query_Init();
addr = NET_ResolveAddress(query_context, addr_str);
if (addr == NULL)
{
I_Error("NET_QueryAddress: Host '%s' not found!", addr_str);
}
// Add the address to the list of targets.
target = GetTargetForAddr(addr, true);
printf("\nQuerying '%s'...\n", addr_str);
// Run query loop.
NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);
// Check if the target responded.
if (target->state == QUERY_TARGET_RESPONDED)
{
NET_QueryPrintCallback(addr, &target->data, target->ping_time, NULL);
}
else
{
I_Error("No response from '%s'", addr_str);
}
}
net_addr_t *NET_FindLANServer(void)
{
query_target_t *target;
query_target_t *responder;
NET_Query_Init();
// Add a broadcast target to the list.
target = GetTargetForAddr(NULL, true);
target->type = QUERY_TARGET_BROADCAST;
// Run the query loop, and stop at the first target found.
NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);
responder = FindFirstResponder();
if (responder != NULL)
{
return responder->addr;
}
else
{
return NULL;
}
}
int NET_LANQuery(net_query_callback_t callback, void *user_data)
{
query_target_t *target;
NET_Query_Init();
// Add a broadcast target to the list.
target = GetTargetForAddr(NULL, true);
target->type = QUERY_TARGET_BROADCAST;
NET_Query_QueryLoop(callback, user_data);
return GetNumResponses();
}
int NET_MasterQuery(net_query_callback_t callback, void *user_data)
{
net_addr_t *master;
query_target_t *target;
NET_Query_Init();
// Resolve master address and add to targets list.
master = NET_Query_ResolveMaster(query_context);
if (master == NULL)
{
return 0;
}
target = GetTargetForAddr(master, true);
target->type = QUERY_TARGET_MASTER;
NET_Query_QueryLoop(callback, user_data);
// Check that we got a response from the master, and display
// a warning if we didn't.
if (target->state == QUERY_TARGET_NO_RESPONSE)
{
fprintf(stderr, "NET_MasterQuery: no response from master server.\n");
}
return GetNumResponses();
}
static void formatted_printf(int wide, char *s, ...)
{
va_list args;
int i;
va_start(args, s);
i = vprintf(s, args);
va_end(args);
while (i < wide)
while (i < wide)
{
putchar(' ');
++i;
}
}
}
static char *GameDescription(GameMode_t mode, GameMission_t mission)
@ -146,6 +684,8 @@ static void PrintHeader(void)
{
int i;
putchar('\n');
formatted_printf(5, "Ping");
formatted_printf(18, "Address");
formatted_printf(8, "Players");
puts("Description");
@ -155,168 +695,37 @@ static void PrintHeader(void)
putchar('\n');
}
static void PrintResponse(queryresponse_t *response)
{
formatted_printf(18, "%s: ", NET_AddrToString(response->addr));
formatted_printf(8, "%i/%i", response->data.num_players,
response->data.max_players);
// Callback function that just prints information in a table.
if (response->data.gamemode != indetermined)
void NET_QueryPrintCallback(net_addr_t *addr,
net_querydata_t *data,
unsigned int ping_time,
void *user_data)
{
// If this is the first server, print the header.
if (!printed_header)
{
printf("(%s) ", GameDescription(response->data.gamemode,
response->data.gamemission));
PrintHeader();
printed_header = true;
}
if (response->data.server_state)
formatted_printf(5, "%4i", ping_time);
formatted_printf(18, "%s: ", NET_AddrToString(addr));
formatted_printf(8, "%i/%i", data->num_players,
data->max_players);
if (data->gamemode != indetermined)
{
printf("(%s) ", GameDescription(data->gamemode,
data->gamemission));
}
if (data->server_state)
{
printf("(game running) ");
}
NET_SafePuts(response->data.description);
}
static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet)
{
unsigned int packet_type;
net_querydata_t querydata;
queryresponse_t *response;
// Have we already received a packet from this host?
if (!CheckResponder(addr))
{
return;
}
// Read the header
if (!NET_ReadInt16(packet, &packet_type)
|| packet_type != NET_PACKET_TYPE_QUERY_RESPONSE)
{
return;
}
// Read query data
if (!NET_ReadQueryData(packet, &querydata))
{
return;
}
if (num_responses <= 0)
{
// If this is the first response, print the table header
PrintHeader();
}
response = AddResponder(addr, &querydata);
PrintResponse(response);
}
static void NET_Query_GetResponse(void)
{
net_addr_t *addr;
net_packet_t *packet;
if (NET_RecvPacket(query_context, &addr, &packet))
{
NET_Query_ParsePacket(addr, packet);
NET_FreePacket(packet);
}
}
static net_addr_t *NET_Query_QueryLoop(net_addr_t *addr,
boolean find_one)
{
int start_time;
int last_send_time;
last_send_time = -1;
start_time = I_GetTimeMS();
while (I_GetTimeMS() < start_time + 5000)
{
// Send a query once every second
if (last_send_time < 0 || I_GetTimeMS() > last_send_time + 1000)
{
NET_Query_SendQuery(addr);
last_send_time = I_GetTimeMS();
}
// Check for a response
NET_Query_GetResponse();
// Found a response?
if (find_one && num_responses > 0)
break;
// Don't thrash the CPU
I_Sleep(100);
}
if (num_responses > 0)
return responders[0].addr;
else
return NULL;
}
void NET_Query_Init(void)
{
query_context = NET_NewContext();
NET_AddModule(query_context, &net_sdl_module);
net_sdl_module.InitClient();
responders = NULL;
num_responses = 0;
}
void NET_QueryAddress(char *addr)
{
net_addr_t *net_addr;
NET_Query_Init();
net_addr = NET_ResolveAddress(query_context, addr);
if (net_addr == NULL)
{
I_Error("NET_QueryAddress: Host '%s' not found!", addr);
}
printf("\nQuerying '%s'...\n\n", addr);
if (!NET_Query_QueryLoop(net_addr, true))
{
I_Error("No response from '%s'", addr);
}
exit(0);
}
net_addr_t *NET_FindLANServer(void)
{
NET_Query_Init();
return NET_Query_QueryLoop(NULL, true);
}
void NET_LANQuery(void)
{
NET_Query_Init();
printf("\nSearching for servers on local LAN ...\n\n");
if (!NET_Query_QueryLoop(NULL, false))
{
I_Error("No servers found");
}
exit(0);
NET_SafePuts(data->description);
}

View file

@ -27,9 +27,22 @@
#include "net_defs.h"
typedef void (*net_query_callback_t)(net_addr_t *addr,
net_querydata_t *querydata,
unsigned int ping_time,
void *user_data);
extern int NET_LANQuery(net_query_callback_t callback, void *user_data);
extern int NET_MasterQuery(net_query_callback_t callback, void *user_data);
extern void NET_QueryAddress(char *addr);
extern void NET_LANQuery(void);
extern net_addr_t *NET_FindLANServer(void);
extern void NET_QueryPrintCallback(net_addr_t *addr, net_querydata_t *data,
unsigned int ping_time, void *user_data);
extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context);
extern void NET_Query_AddToMaster(net_addr_t *master_addr);
extern void NET_Query_MasterResponse(net_packet_t *packet);
#endif /* #ifndef NET_QUERY_H */

View file

@ -41,10 +41,15 @@
#include "net_io.h"
#include "net_loop.h"
#include "net_packet.h"
#include "net_query.h"
#include "net_server.h"
#include "net_sdl.h"
#include "net_structrw.h"
// How often to refresh our registration with the master server.
#define MASTER_REFRESH_PERIOD 20 * 60 /* 20 minutes */
typedef enum
{
// waiting for the game to start
@ -128,6 +133,11 @@ static unsigned int sv_gamemode;
static unsigned int sv_gamemission;
static net_gamesettings_t sv_settings;
// For registration with master server:
static net_addr_t *master_server = NULL;
static unsigned int master_refresh_time;
// receive window
static unsigned int recvwindow_start;
@ -1067,6 +1077,7 @@ void NET_SV_SendQueryResponse(net_addr_t *addr)
{
net_packet_t *reply;
net_querydata_t querydata;
int p;
// Version
@ -1086,9 +1097,22 @@ void NET_SV_SendQueryResponse(net_addr_t *addr)
querydata.gamemode = sv_gamemode;
querydata.gamemission = sv_gamemission;
// Server description. This is currently hard-coded.
//!
// @arg <name>
//
// When starting a network server, specify a name for the server.
//
querydata.description = "Chocolate Doom server";
p = M_CheckParm("-servername");
if (p > 0 && p + 1 < myargc)
{
querydata.description = myargv[p + 1];
}
else
{
querydata.description = "Unnamed server";
}
// Send it and we're done.
@ -1106,6 +1130,14 @@ static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
net_client_t *client;
unsigned int packet_type;
// Response from master server?
if (addr != NULL && addr == master_server)
{
NET_Query_MasterResponse(packet);
return;
}
// Find which client this packet came from
client = NET_SV_FindClient(addr);
@ -1511,6 +1543,32 @@ void NET_SV_Init(void)
server_initialized = true;
}
void NET_SV_RegisterWithMaster(void)
{
//!
// When running a server, don't register with the global master server.
//
// @category net
//
if (!M_CheckParm("-privateserver"))
{
master_server = NET_Query_ResolveMaster(server_context);
}
else
{
master_server = NULL;
}
// Send request.
if (master_server != NULL)
{
NET_Query_AddToMaster(master_server);
master_refresh_time = I_GetTimeMS();
}
}
// Run server code to check for new packets/send packets as the server
// requires
@ -1525,12 +1583,21 @@ void NET_SV_Run(void)
return;
}
while (NET_RecvPacket(server_context, &addr, &packet))
while (NET_RecvPacket(server_context, &addr, &packet))
{
NET_SV_Packet(packet, addr);
NET_FreePacket(packet);
}
// Possibly refresh our registration with the master server.
if (master_server != NULL
&& I_GetTimeMS() - master_refresh_time > MASTER_REFRESH_PERIOD * 1000)
{
NET_Query_AddToMaster(master_server);
master_refresh_time = I_GetTimeMS();
}
// "Run" any clients that may have things to do, independent of responses
// to received packets

View file

@ -41,5 +41,9 @@ void NET_SV_Shutdown(void);
void NET_SV_AddModule(net_module_t *module);
// Register server with master server.
void NET_SV_RegisterWithMaster(void);
#endif /* #ifndef NET_SERVER_H */

View file

@ -32,6 +32,27 @@
#include "display.h"
typedef struct
{
char *description;
int bpp;
} pixel_depth_t;
// List of supported pixel depths.
static pixel_depth_t pixel_depths[] =
{
{ "8-bit", 8 },
{ "16-bit", 16 },
{ "24-bit", 24 },
{ "32-bit", 32 },
};
// List of strings containing supported pixel depths.
static char **supported_bpps;
static int num_supported_bpps;
typedef struct
{
int w, h;
@ -78,6 +99,7 @@ static int aspect_ratio_correct = 1;
static int fullscreen = 1;
static int screen_width = 320;
static int screen_height = 200;
static int screen_bpp = 8;
static int startup_delay = 1000;
static int graphical_startup = 1;
static int show_endoom = 1;
@ -90,6 +112,10 @@ static int usegamma = 0;
static int selected_screen_width = 0, selected_screen_height;
// Index into the supported_bpps of the selected pixel depth.
static int selected_bpp = 0;
static int system_video_env_set;
// Set the SDL_VIDEODRIVER environment variable
@ -133,6 +159,153 @@ void SetDisplayDriver(void)
}
}
// Query SDL as to whether any fullscreen modes are available for the
// specified pixel depth.
static int PixelDepthSupported(int bpp)
{
SDL_PixelFormat format;
SDL_Rect **modes;
format.BitsPerPixel = bpp;
format.BytesPerPixel = (bpp + 7) / 8;
modes = SDL_ListModes(&format, SDL_FULLSCREEN);
return modes != NULL;
}
// Query SDL and populate the supported_bpps array.
static void IdentifyPixelDepths(void)
{
unsigned int i;
unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
if (supported_bpps != NULL)
{
free(supported_bpps);
}
supported_bpps = malloc(sizeof(char *) * num_depths);
num_supported_bpps = 0;
// Check each bit depth to determine if modes are available.
for (i = 0; i < num_depths; ++i)
{
// If modes are available, add this bit depth to the list.
if (PixelDepthSupported(pixel_depths[i].bpp))
{
supported_bpps[num_supported_bpps] = pixel_depths[i].description;
++num_supported_bpps;
}
}
// No supported pixel depths? That's kind of a problem. Add 8bpp
// as a fallback.
if (num_supported_bpps == 0)
{
supported_bpps[0] = pixel_depths[0].description;
++num_supported_bpps;
}
}
// Get the screen pixel depth corresponding to what selected_bpp is set to.
static int GetSelectedBPP(void)
{
unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
unsigned int i;
// Find which pixel depth is selected, and set screen_bpp.
for (i = 0; i < num_depths; ++i)
{
if (pixel_depths[i].description == supported_bpps[selected_bpp])
{
return pixel_depths[i].bpp;
}
}
// Default fallback value.
return 8;
}
// Get the index into supported_bpps of the specified pixel depth string.
static int GetSupportedBPPIndex(char *description)
{
unsigned int i;
for (i = 0; i < num_supported_bpps; ++i)
{
if (supported_bpps[i] == description)
{
return i;
}
}
// Shouldn't happen; fall back to the first in the list.
return 0;
}
// Set selected_bpp to match screen_bpp.
static int TrySetSelectedBPP(void)
{
unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
unsigned int i;
// Search pixel_depths, find the bpp that corresponds to screen_bpp,
// then set selected_bpp to match.
for (i = 0; i < num_depths; ++i)
{
if (pixel_depths[i].bpp == screen_bpp)
{
selected_bpp = GetSupportedBPPIndex(pixel_depths[i].description);
return 1;
}
}
return 0;
}
static void SetSelectedBPP(void)
{
const SDL_VideoInfo *info;
if (TrySetSelectedBPP())
{
return;
}
// screen_bpp does not match any supported pixel depth. Query SDL
// to find out what it recommends using.
info = SDL_GetVideoInfo();
if (info != NULL && info->vfmt != NULL)
{
screen_bpp = info->vfmt->BitsPerPixel;
}
// Try again.
if (!TrySetSelectedBPP())
{
// Give up and just use the first in the list.
selected_bpp = 0;
screen_bpp = GetSelectedBPP();
}
}
static void ModeSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(mode))
{
TXT_CAST_ARG(screen_mode_t, mode);
@ -177,6 +350,7 @@ static int GoodFullscreenMode(screen_mode_t *mode)
static void BuildFullscreenModesList(void)
{
SDL_PixelFormat format;
SDL_Rect **modes;
screen_mode_t *m1;
screen_mode_t *m2;
@ -194,7 +368,10 @@ static void BuildFullscreenModesList(void)
// Get a list of fullscreen modes and find out how many
// modes are in the list.
modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
format.BitsPerPixel = screen_bpp;
format.BytesPerPixel = (screen_bpp + 7) / 8;
modes = SDL_ListModes(&format, SDL_FULLSCREEN);
if (modes == NULL || modes == (SDL_Rect **) -1)
{
@ -317,8 +494,25 @@ static void GenerateModesTable(TXT_UNCAST_ARG(widget),
vidmode = FindBestMode(modes);
screen_width = modes[vidmode].w;
screen_height = modes[vidmode].h;
if (vidmode > 0)
{
screen_width = modes[vidmode].w;
screen_height = modes[vidmode].h;
}
}
// Callback invoked when the BPP selector is changed.
static void UpdateBPP(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(modes_table))
{
TXT_CAST_ARG(txt_table_t, modes_table);
screen_bpp = GetSelectedBPP();
// Rebuild list of fullscreen modes.
BuildFullscreenModesList();
GenerateModesTable(NULL, modes_table);
}
#if defined(_WIN32) && !defined(_WIN32_WCE)
@ -372,6 +566,11 @@ static void UpdateVideoDriver(TXT_UNCAST_ARG(widget),
RestartTextscreen();
// Rebuild the list of supported pixel depths.
IdentifyPixelDepths();
SetSelectedBPP();
// Rebuild the video modes list
BuildFullscreenModesList();
@ -385,8 +584,16 @@ void ConfigDisplay(void)
{
txt_window_t *window;
txt_table_t *modes_table;
txt_table_t *bpp_table;
txt_checkbox_t *fs_checkbox;
txt_checkbox_t *ar_checkbox;
txt_dropdown_list_t *bpp_selector;
// What color depths are supported? Generate supported_bpps array
// and set selected_bpp to match the current value of screen_bpp.
IdentifyPixelDepths();
SetSelectedBPP();
// First time in? Initialise selected_screen_{width,height}
@ -442,6 +649,7 @@ void ConfigDisplay(void)
TXT_AddWidgets(window,
TXT_NewSeparator("Screen mode"),
bpp_table = TXT_NewTable(2),
modes_table,
TXT_NewSeparator("Misc."),
NULL);
@ -458,6 +666,15 @@ void ConfigDisplay(void)
TXT_NewCheckBox("Show ENDOOM screen", &show_endoom));
}
TXT_AddWidgets(bpp_table,
TXT_NewLabel("Color depth: "),
bpp_selector = TXT_NewDropdownList(&selected_bpp,
supported_bpps,
num_supported_bpps),
NULL);
TXT_SignalConnect(bpp_selector, "changed", UpdateBPP, modes_table);
TXT_SignalConnect(fs_checkbox, "changed", GenerateModesTable, modes_table);
TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);
@ -486,5 +703,25 @@ void BindDisplayVariables(void)
M_BindVariable("graphical_startup", &graphical_startup);
}
// Windows Vista or later? Set screen color depth to
// 32 bits per pixel, as 8-bit palettized screen modes
// don't work properly in recent versions.
#if defined(_WIN32) && !defined(_WIN32_WCE)
{
OSVERSIONINFOEX version_info;
ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((OSVERSIONINFO *) &version_info);
if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
&& version_info.dwMajorVersion >= 6)
{
screen_bpp = 32;
}
}
#endif
}

View file

@ -65,8 +65,8 @@ static int joystick_y_invert = 0;
static txt_button_t *joystick_button;
static int *all_joystick_buttons[] = {
&joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
&joybuse, &joybstrafe, &joybjump
&joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
&joybuse, &joybstrafe, &joybprevweapon, &joybnextweapon, &joybjump
};
//

View file

@ -119,6 +119,22 @@ static SDL_Color ega_colors[] =
#endif
static txt_font_t *FontForName(char *name)
{
if (!strcmp(name, "small"))
{
return &small_font;
}
else if (!strcmp(name, "normal"))
{
return &main_font;
}
else
{
return NULL;
}
}
//
// Select the font to use, based on screen resolution
//
@ -129,9 +145,22 @@ static SDL_Color ega_colors[] =
static void ChooseFont(void)
{
SDL_Rect **modes;
char *env;
int i;
font = &main_font;
// Allow normal selection to be overridden from an environment variable:
env = getenv("TEXTSCREEN_FONT");
if (env != NULL)
{
font = FontForName(env);
if (font != NULL)
{
return;
}
}
// Check all modes
@ -140,6 +169,8 @@ static void ChooseFont(void)
// If in doubt and we can't get a list, always prefer to
// fall back to the normal font:
font = &main_font;
if (modes == NULL || modes == (SDL_Rect **) -1 || *modes == NULL)
{
#ifdef _WIN32_WCE