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:
commit
8dab0a3e63
38 changed files with 1670 additions and 356 deletions
|
|
@ -53,6 +53,7 @@ EXTRA_DIST= \
|
|||
README.OPL \
|
||||
TODO \
|
||||
BUGS \
|
||||
NOT-BUGS \
|
||||
rpm.spec
|
||||
|
||||
MAINTAINERCLEANFILES = $(AUX_DIST_GEN)
|
||||
|
|
|
|||
48
NEWS
48
NEWS
|
|
@ -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
112
NOT-BUGS
Normal 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
10
README
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
|
||||
|
|
|
|||
25
man/docgen
25
man/docgen
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ DOC_FILES = README \
|
|||
INSTALL \
|
||||
NEWS \
|
||||
BUGS \
|
||||
NOT-BUGS \
|
||||
CMDLINE \
|
||||
TODO
|
||||
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
143
src/deh_io.c
143
src/deh_io.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -762,6 +762,8 @@ void M_DrawReadThis1(void)
|
|||
switch (gameversion)
|
||||
{
|
||||
case exe_doom_1_9:
|
||||
case exe_hacx:
|
||||
|
||||
if (gamemode == commercial)
|
||||
{
|
||||
// Doom 2
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
207
src/i_video.c
207
src/i_video.c
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ void NET_DedicatedServer(void)
|
|||
CheckForClientOptions();
|
||||
|
||||
NET_SV_Init();
|
||||
|
||||
NET_SV_AddModule(&net_sdl_module);
|
||||
NET_SV_RegisterWithMaster();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
769
src/net_query.c
769
src/net_query.c
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue