ref: 1b53d785acee74cfb4a5d9e08ca27f25d07e9b14
parent: 031dbfb4ccd727ef660f72774cb767885fd9eae6
parent: 51b9d1fccd8c885d8349b307b3553bdf5a638027
author: Fabian Greffrath <fabian@greffrath.com>
date: Mon Sep 21 03:46:37 EDT 2015
Merge branch 'master' into sdl2-branch
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,9 @@
msvc/doom.vcproj \
msvc/heretic.vcproj \
msvc/hexen.vcproj \
+ msvc/strife.vcproj \
msvc/inttypes.h \
+ msvc/libopl.vcproj \
msvc/libpcsound.vcproj \
msvc/libtextscreen.vcproj \
msvc/README \
@@ -15,6 +17,8 @@
msvc/server.vcproj \
msvc/setup.vcproj \
msvc/stdint.h \
+ msvc/win_opendir.c \
+ msvc/win_opendir.h \
msvc/win32.rc
CODEBLOCKS_FILES= \
@@ -24,6 +28,8 @@
codeblocks/game-res.rc \
codeblocks/heretic.cbp \
codeblocks/hexen.cbp \
+ codeblocks/strife.cbp \
+ codeblocks/libopl.cbp \
codeblocks/libpcsound.cbp \
codeblocks/libtextscreen.cbp \
codeblocks/README \
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,47 @@
+2.2.1 (2015-09-10):
-2.2.0 (2015-05-??):
+ Chocolate Doom has not seen a great deal of "stable" patch
+ releases in its history. While the development tree sees major new
+ features and changes, the purpose of this release, and hopefully
+ others to follow like it, is to repair some deficiencies that
+ existed in 2.2.0.
+
+ General:
+ * Preferences for the OS X launcher are now stored with a unique
+ name to not conflict with other applications. (thanks Xeriphas1994)
+ * Unix desktop entry files are now brought up to full desktop
+ entry specification compliance. (thanks chungy, Fabian)
+ * Unix AppData entries are now included, allowing software centers
+ to display detailed information about the engines. (thanks chungy)
+ * Partial XDG base directory specification compliance on Unix
+ systems now exist to search for IWAD paths. One benefit is that
+ $HOME/.local/share/games/doom is now a valid location to store
+ and automatically find IWADs. (thanks chungy)
+
+ Build systems:
+ * The Microsoft Visual Studio build system was not fully
+ functional in 2.2.0 and has been fixed. (thanks Linguica)
+ * The autoconf build system checks for windres only for Windows
+ toolchains. Some Linux distributions mistakingly include the
+ program in their native toolchains. (thanks Fabian)
+ * A compiler hint for packed structs has been added, which
+ otherwise broke the games when built under recent GCC releases
+ for Windows. (thanks Fabian)
+
+ Doom:
+ * The GOG.com releases of The Ultimate Doom, Doom II, and Final
+ Doom are now detected and supported on Windows. (thanks chungy)
+ * An integer overflow was used in spawn angle calculation,
+ undefined C behavior which broke with Clang optimization.
+ (thanks David Majnemer for insight)
+
+ Setup tool:
+ * The help URL for the level warp menu now points to the proper
+ wiki page, rather than the multiplayer page.
+ * The manifest has been updated for Windows 10 compatibility.
+ (thanks chungy)
+
+2.2.0 (2015-06-09):
* The Hexen four level demo IWAD is now supported. Thanks to
Fabian Greffrath for his careful investigation and emulation of
--- a/README.Strife
+++ b/README.Strife
@@ -28,31 +28,23 @@
been employed to disassemble and decompile the executable, which was cross-
referenced against the Linux DOOM and DOS Heretic sources and painstakingly
combed over multiple times, instruction-by-instruction, to ensure that the
-resulting Chocolate-Doom-based executable is as close as possible to the
+resulting Chocolate Doom-based executable is as close as possible to the
original.
* Is it Legal? *
-Reverse engineering is a protected activity so long as the original code is
-not used directly in the product. Due to the vast amount of information lost
-through the process of compilation, and the need to refactor large portions
-of code in order to eliminate non-portable idioms or to adapt them properly to
-Chocolate Doom's framework, the resulting code behaves the same, but is not
-the *same* code.
+Chocolate Strife was originally reverse-engineered from the DOS Strife
+binaries. Although reverse engineering is legally a protected activity, this
+nonetheless left some open questions about its legal status.
-In addition, James Monroe and John Carmack have both stated that they have no
-objections to the project. Because they are the original authors of the code,
-and neither Rogue nor their publisher, Velocity, Inc., exist any longer as legal
-entities, this is as close to legal permission as can be obtained.
+In 2014, a new commercial release of Strife was published (Strife: Veteran
+Edition) based on the Chocolate Strife code, and developed by the authors of
+Chocolate Strife under commercial license. The release of Strife: Veteran
+Edition, along with its GPL-licensed source code, constitutes tacit approval
+for the legal status of Chocolate Strife by its current copyright holder.
-The transformed results of the disassembly have been combined with the
-raven-branch version of the Chocolate Doom source port by Simon 'fraggle'
-Howard, with his direct assistance, and have been released for the benefit of
-the community under the GNU General Public License v2.0. See the file "COPYING"
-for more details
-
* Is it Perfect? *
Almost, but not entirely! That's where you come in. Help us by reporting any
@@ -126,48 +118,4 @@
Aside from Chocolate Doom, portions of the code are derived from the Eternity
Engine, Copyright 2011 Team Eternity, as published under the GNU GPL.
-
-
-* New in v2.0 Release *
-
-+ No infinite loop if menus are up during a screen fade.
-
-+ Peasant death sound during intro plays at normal volume level.
-
-+ Torches no longer produce pipping sounds (special thanks to fraggle).
-
-+ Fade to black occurs at the start of slideshow sequences as in vanilla.
-
-+ Network game support, with up to 8 players.
-
-+ No more infinite horizon effect if screen is resized while looking up or
- down.
-
-+ Corrected default internal Acolyte and Beggar dialogue strings.
-
-+ Proper save game directory behavior (same as other Choco ports).
-
-+ Vanilla behavior for crush-and-raise floor types (special thanks to Gez).
-
-+ Bug fix for all crushing floor types.
-
-+ Broken power coupling awards erroneous quest flag #32, as in vanilla.
-
-+ Complete support for emulation of Strife v1.31, including abililty to save
- on multiple save slots.
-
-+ Telefrags insta-kill as in vanilla (player cannot heal 10000 damage even with
- a full inventory of health items).
-
-+ Frags are displayed on the keys popup during deathmatch.
-
-+ Multiplayer chat features, including ability to set player names, now match
- vanilla behavior.
-
-+ Support for -random parameter.
-
-+ Vanilla behavior when pressing pause on menus with scroll bars (special
- thanks to fraggle and Alexandre Xavier).
-
-+ Merged into Chocolate Doom trunk for v2.0 release.
--- a/codeblocks/config.h
+++ b/codeblocks/config.h
@@ -9,13 +9,13 @@
#define PACKAGE_NAME "Chocolate Doom"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 2.1.0"
+#define PACKAGE_STRING "Chocolate Doom 2.2.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "chocolate-doom"
/* Define to the version of this package. */
-#define PACKAGE_VERSION "2.1.0"
+#define PACKAGE_VERSION "2.2.1"
/* Change this when you create your awesome forked version */
#define PROGRAM_PREFIX "chocolate-"
@@ -24,7 +24,7 @@
#define STDC_HEADERS 1
/* Version number of package */
-#define VERSION "2.1.0"
+#define VERSION "2.2.1"
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/codeblocks/game-res.rc
+++ b/codeblocks/game-res.rc
@@ -1,8 +1,8 @@
1 ICON "../data/doom.ico"
1 VERSIONINFO
-PRODUCTVERSION 2,1,0,0
-FILEVERSION 2,1,0,0
+PRODUCTVERSION 2,2,1,0
+FILEVERSION 2,2,1,0
FILETYPE 1
{
BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
{
BLOCK "040904E4"
{
- VALUE "FileVersion", "2.1.0"
- VALUE "FileDescription", "2.1.0"
+ VALUE "FileVersion", "2.2.1"
+ VALUE "FileDescription", "2.2.1"
VALUE "InternalName", "Chocolate Doom"
VALUE "CompanyName", "Chocolate Doom"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate Doom"
- VALUE "ProductVersion", "2.1.0"
+ VALUE "ProductVersion", "2.2.1"
}
}
BLOCK "VarFileInfo"
--- a/codeblocks/setup-res.rc
+++ b/codeblocks/setup-res.rc
@@ -1,8 +1,8 @@
1 ICON "../data/setup.ico"
1 VERSIONINFO
-PRODUCTVERSION 2,1,0,0
-FILEVERSION 2,1,0,0
+PRODUCTVERSION 2,2,1,0
+FILEVERSION 2,2,1,0
FILETYPE 1
{
BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
{
BLOCK "040904E4"
{
- VALUE "FileVersion", "2.1.0"
+ VALUE "FileVersion", "2.2.1"
VALUE "FileDescription", "Chocolate Doom Setup"
VALUE "InternalName", "chocolate-setup"
VALUE "CompanyName", "Chocolate Doom"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate Doom Setup"
- VALUE "ProductVersion", "2.1.0"
+ VALUE "ProductVersion", "2.2.1"
}
}
BLOCK "VarFileInfo"
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,12 @@
-AC_INIT(Chocolate Doom, 2.1.0, fraggle@gmail.com, chocolate-doom)
+AC_INIT(Chocolate Doom, 2.2.1, fraggle@gmail.com, chocolate-doom)
PACKAGE_SHORTNAME=${PACKAGE_NAME% Doom}
PACKAGE_SHORTDESC="Conservative source port"
-PACKAGE_COPYRIGHT="Copyright (C) 1993-2013"
+PACKAGE_COPYRIGHT="Copyright (C) 1993-2015"
PACKAGE_LICENSE="GNU General Public License, version 2"
PACKAGE_MAINTAINER="Simon Howard"
PACKAGE_URL="http://www.chocolate-doom.org/"
+PACKAGE_ISSUES="https://github.com/chocolate-doom/chocolate-doom/issues"
AC_CONFIG_AUX_DIR(autotools)
@@ -103,7 +104,15 @@
AC_CHECK_LIB(amd64, amd64_iopl)
])
-AC_CHECK_TOOL(WINDRES, windres, )
+case $host in
+ *cygwin* | *mingw* )
+ AC_CHECK_TOOL(WINDRES, windres, )
+ ;;
+ *)
+ WINDRES=
+ ;;
+esac
+
AC_CHECK_TOOL(STRIP, strip, )
AM_CONDITIONAL(HAVE_WINDRES, test "$WINDRES" != "")
@@ -143,6 +152,7 @@
AC_SUBST(PACKAGE_LICENSE)
AC_SUBST(PACKAGE_MAINTAINER)
AC_SUBST(PACKAGE_URL)
+AC_SUBST(PACKAGE_ISSUES)
dnl Shut up the datarootdir warnings.
AC_DEFUN([AC_DATAROOTDIR_CHECKED])
@@ -160,11 +170,14 @@
rpm.spec
data/Makefile
src/Makefile
+src/doom.appdata.xml
src/doom.desktop
src/doom-screensaver.desktop
src/doom/Makefile
+src/heretic.appdata.xml
src/heretic.desktop
src/heretic/Makefile
+src/hexen.appdata.xml
src/hexen.desktop
src/hexen/Makefile
src/resource.rc
@@ -172,6 +185,7 @@
src/setup/Makefile
src/setup/setup.desktop
src/setup/setup-manifest.xml
+src/strife.appdata.xml
src/strife.desktop
src/strife/Makefile
textscreen/Makefile
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,4 +1,5 @@
MANPAGE_GEN_FILES = environ.man \
+ iwad_paths.man \
doom.template \
heretic.template \
hexen.template \
--- a/man/doom.template
+++ b/man/doom.template
@@ -10,6 +10,8 @@
to behave as similar to the original DOS version of Doom as is possible.
.br
@content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
.SH ENVIRONMENT
This section describes environment variables that control Chocolate Doom's
behavior.
--- a/man/environ.man
+++ b/man/environ.man
@@ -1,9 +1,6 @@
.TP
\fBDOOMWADDIR\fR, \fBDOOMWADPATH\fR
-These environment variables provide paths to search for Doom .WAD files when
-looking for a game IWAD file or a PWAD file specified with the `\-file' option.
-\fBDOOMWADDIR\fR specifies a single path in which to look for WAD files,
-while \fBDOOMWWADPATH\fR specifies a colon-separated list of paths to search.
+See the section, \fBIWAD SEARCH PATHS\fR above.
.TP
\fBPCSOUND_DRIVER\fR
When running in PC speaker sound effect mode, this environment variable
--- a/man/heretic.template
+++ b/man/heretic.template
@@ -11,6 +11,8 @@
possible.
.br
@content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
.SH ENVIRONMENT
This section describes environment variables that control Chocolate Heretic's
behavior.
--- a/man/hexen.template
+++ b/man/hexen.template
@@ -11,6 +11,8 @@
possible.
.br
@content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
.SH ENVIRONMENT
This section describes environment variables that control Chocolate Hexen's
behavior.
--- /dev/null
+++ b/man/iwad_paths.man
@@ -1,0 +1,48 @@
+To play, an IWAD file is needed. This is a large file containing all of the
+levels, graphics, sound effects, music and other material that make up the
+game. IWAD files are named according to the game; the standard names are:
+.TP
+\fBdoom.wad, doom1.wad, doom2.wad, tnt.wad, plutonia.wad\fR
+Doom, Doom II, Final Doom
+.TP
+\fBheretic.wad, heretic1.wad, hexen.wad, strife1.wad\fR
+Heretic, Hexen and Strife (commercial Doom engine games).
+.TP
+\fBhacx.wad, chex.wad\fR
+Hacx and Chex Quest - more obscure games based on the Doom engine.
+.TP
+\fBfreedm.wad, freedoom1.wad, freedoom2.wad\fR
+The Freedoom open content IWAD files.
+.LP
+The following directory paths are searched in order to find an IWAD:
+.TP
+\fBCurrent working directory\fR
+Any IWAD files found in the current working directory will be used in
+preference to IWADs found in any other directories.
+.TP
+\fBDOOMWADDIR\fR
+This environment variable can be set to contain a path to a single directory
+in which to look for IWAD files. This environment variable is supported by
+most Doom source ports.
+.TP
+\fBDOOMWADPATH\fR
+This environment variable, if set, can contain a colon-separated list of
+directories in which to look for IWAD files, or alternatively full paths to
+specific IWAD files.
+.TP
+\fB$HOME/.local/share/games/doom\fR
+Writeable directory in the user's home directory. The path can be overridden
+using the \fBXDG_DATA_HOME\fR environment variable (see the XDG Base Directory
+Specification).
+.TP
+\fB/usr/local/share/games/doom, /usr/share/games/doom\fR
+System-wide locations that can be accessed by all users. The path
+\fB/usr/share/games/doom\fR is a standard path that is supported by most
+Doom source ports. These paths can be overridden using the \fBXDG_DATA_DIRS\fR
+environment variable (see the XDG Base Directory Specification).
+.LP
+The above can be overridden on a one-time basis by using the \fB\-iwad\fR
+command line parameter to provide the path to an IWAD file to use. This
+parameter can also be used to specify the name of a particular IWAD to use
+from one of the above paths. For example, '\fB-iwad doom.wad\fR' will search
+the above paths for the file \fBdoom.wad\fR to use.
--- a/man/strife.template
+++ b/man/strife.template
@@ -13,6 +13,8 @@
@content
+.SH IWAD SEARCH PATHS
+@include iwad_paths.man
.SH ENVIRONMENT
This section describes environment variables that control Chocolate Strife's
behavior.
--- a/msvc/config.h
+++ b/msvc/config.h
@@ -11,19 +11,19 @@
#define PACKAGE_NAME "Chocolate Doom"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 2.1.0"
+#define PACKAGE_STRING "Chocolate Doom 2.2.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "chocolate-doom"
/* Define to the version of this package. */
-#define PACKAGE_VERSION "2.1.0"
+#define PACKAGE_VERSION "2.2.1"
/* Change this when you create your awesome forked version */
#define PROGRAM_PREFIX "chocolate-"
/* Version number of package */
-#define VERSION "2.1.0"
+#define VERSION "2.2.1"
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/msvc/win32.rc
+++ b/msvc/win32.rc
@@ -25,8 +25,8 @@
#endif
1 VERSIONINFO
-PRODUCTVERSION 2,1,0,0
-FILEVERSION 2,1,0,0
+PRODUCTVERSION 2,2,1,0
+FILEVERSION 2,2,1,0
FILETYPE 1
BEGIN
BLOCK "StringFileInfo"
@@ -34,12 +34,12 @@
BLOCK "040904E4"
BEGIN
VALUE "FileVersion", "1.0.0"
- VALUE "FileDescription", "Chocolate Doom 2.1.0"
+ VALUE "FileDescription", "Chocolate Doom 2.2.1"
VALUE "InternalName", "chocolate-doom"
VALUE "CompanyName", "fraggle@gmail.com"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate Doom"
- VALUE "ProductVersion", "2.1.0"
+ VALUE "ProductVersion", "2.2.1"
END
END
BLOCK "VarFileInfo"
--- a/opl/dbopl.h
+++ b/opl/dbopl.h
@@ -195,6 +195,7 @@
void Chip__Chip(Chip *self);
void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val );
void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output );
+void Chip__GenerateBlock3(Chip *self, Bitu total, Bit32s* output );
// haleyjd 09/09/10: Not standard C.
#ifdef _MSC_VER
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -65,15 +65,16 @@
// Initialize the specified driver and detect an OPL chip. Returns
// true if an OPL is detected.
-static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
+static opl_init_result_t InitDriver(opl_driver_t *_driver,
+ unsigned int port_base)
{
- int result1, result2;
+ opl_init_result_t result1, result2;
// Initialize the driver.
if (!_driver->init_func(port_base))
{
- return 0;
+ return OPL_INIT_NONE;
}
// The driver was initialized okay, so we now have somewhere
@@ -86,12 +87,12 @@
result1 = OPL_Detect();
result2 = OPL_Detect();
- if (!result1 || !result2)
+ if (result1 == OPL_INIT_NONE || result2 == OPL_INIT_NONE)
{
printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
_driver->shutdown_func();
driver = NULL;
- return 0;
+ return OPL_INIT_NONE;
}
init_stage_reg_writes = 0;
@@ -103,15 +104,15 @@
// Find a driver automatically by trying each in the list.
-static int AutoSelectDriver(unsigned int port_base)
+static opl_init_result_t AutoSelectDriver(unsigned int port_base)
{
int i;
- int result;
+ opl_init_result_t result;
for (i=0; drivers[i] != NULL; ++i)
{
result = InitDriver(drivers[i], port_base);
- if (result)
+ if (result != OPL_INIT_NONE)
{
return result;
}
@@ -119,13 +120,13 @@
printf("OPL_Init: Failed to find a working driver.\n");
- return 0;
+ return OPL_INIT_NONE;
}
-// Initialize the OPL library. Returns true if initialized
-// successfully.
+// Initialize the OPL library. Return value indicates type of OPL chip
+// detected, if any.
-int OPL_Init(unsigned int port_base)
+opl_init_result_t OPL_Init(unsigned int port_base)
{
char *driver_name;
int i;
@@ -150,7 +151,7 @@
{
printf("OPL_Init: Failed to initialize "
"driver: '%s'.\n", driver_name);
- return 0;
+ return OPL_INIT_NONE;
}
}
}
@@ -157,7 +158,7 @@
printf("OPL_Init: unknown driver: '%s'.\n", driver_name);
- return 0;
+ return OPL_INIT_NONE;
}
else
{
@@ -278,7 +279,7 @@
// Detect the presence of an OPL chip
-int OPL_Detect(void)
+opl_init_result_t OPL_Detect(void)
{
int result1, result2;
int i;
@@ -323,11 +324,17 @@
result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3);
if (result1 == 0x00)
{
- return 2;
+ return OPL_INIT_OPL3;
}
- return 1;
+ else
+ {
+ return OPL_INIT_OPL2;
+ }
}
- return 0;
+ else
+ {
+ return OPL_INIT_NONE;
+ }
}
// Initialize registers on startup
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -23,8 +23,17 @@
typedef void (*opl_callback_t)(void *data);
+// Result from OPL_Init(), indicating what type of OPL chip was detected,
+// if any.
typedef enum
{
+ OPL_INIT_NONE,
+ OPL_INIT_OPL2,
+ OPL_INIT_OPL3,
+} opl_init_result_t;
+
+typedef enum
+{
OPL_REGISTER_PORT = 0,
OPL_DATA_PORT = 1,
OPL_REGISTER_PORT_OPL3 = 2
@@ -66,7 +75,7 @@
// Initialize the OPL subsystem.
-int OPL_Init(unsigned int port_base);
+opl_init_result_t OPL_Init(unsigned int port_base);
// Shut down the OPL subsystem.
@@ -99,7 +108,7 @@
// Perform a detection sequence to determine that an
// OPL chip is present.
-int OPL_Detect(void);
+opl_init_result_t OPL_Detect(void);
// Initialize all registers, performed on startup.
--- a/pkg/osx/Info.plist.in
+++ b/pkg/osx/Info.plist.in
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>CFBundleIdentifier</key>
+ <string>org.chocolate-doom.launcher</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
@@ -26,6 +28,10 @@
<string>NSApplication</string>
<key>NSMainNibFile</key>
<string>launcher</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright (C) 1993-2015, id Software and Raven Software, Simon Howard, James Haley, Samuel Villarreal and other contributors.
+
+Licensed under the GNU GPL v2.</string>
<!-- file associations: -->
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -14,5 +14,6 @@
chocolate-setup
*.exe
*.desktop
+*.appdata.xml
tags
TAGS
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -217,6 +217,25 @@
doom-screensaver.desktop.in \
manifest.xml
+appdatadir = $(prefix)/share/appdata
+appdata_DATA = \
+ @PROGRAM_PREFIX@doom.appdata.xml \
+ @PROGRAM_PREFIX@heretic.appdata.xml \
+ @PROGRAM_PREFIX@hexen.appdata.xml \
+ @PROGRAM_PREFIX@strife.appdata.xml
+
+@PROGRAM_PREFIX@doom.appdata.xml : doom.appdata.xml
+ cp doom.appdata.xml $@
+
+@PROGRAM_PREFIX@heretic.appdata.xml : heretic.appdata.xml
+ cp heretic.appdata.xml $@
+
+@PROGRAM_PREFIX@hexen.appdata.xml : hexen.appdata.xml
+ cp hexen.appdata.xml $@
+
+@PROGRAM_PREFIX@strife.appdata.xml : strife.appdata.xml
+ cp strife.appdata.xml $@
+
appdir = $(prefix)/share/applications
app_DATA = \
@PROGRAM_PREFIX@doom.desktop \
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -142,22 +142,53 @@
},
};
-// Value installed by the Collector's Edition when it is installed
+// Values installed by the GOG.com and Collector's Edition versions
-static registry_value_t collectors_edition_value =
+static registry_value_t root_path_keys[] =
{
- HKEY_LOCAL_MACHINE,
- SOFTWARE_KEY "\\Activision\\DOOM Collector's Edition\\v1.0",
- "INSTALLPATH",
+ // Doom Collector's Edition
+
+ {
+ HKEY_LOCAL_MACHINE,
+ SOFTWARE_KEY "\\Activision\\DOOM Collector's Edition\\v1.0",
+ "INSTALLPATH",
+ },
+
+ // Ultimate Doom
+
+ {
+ HKEY_LOCAL_MACHINE,
+ SOFTWARE_KEY "\\GOG.com\\Games\\1435827232",
+ "PATH",
+ },
+
+ // Doom II
+
+ {
+ HKEY_LOCAL_MACHINE,
+ SOFTWARE_KEY "\\GOG.com\\Games\\1435848814",
+ "PATH",
+ },
+
+ // Final Doom
+
+ {
+ HKEY_LOCAL_MACHINE,
+ SOFTWARE_KEY "\\GOG.com\\Games\\1435848742",
+ "PATH",
+ },
};
// Subdirectories of the above install path, where IWADs are installed.
-static char *collectors_edition_subdirs[] =
+static char *root_path_subdirs[] =
{
+ ".",
"Doom2",
"Final Doom",
"Ultimate Doom",
+ "TNT",
+ "Plutonia",
};
// Location where Steam is installed
@@ -268,30 +299,34 @@
}
}
-// Check for Doom: Collector's Edition
+// Check for GOG.com and Doom: Collector's Edition
-static void CheckCollectorsEdition(void)
+static void CheckInstallRootPaths(void)
{
- char *install_path;
- char *subpath;
unsigned int i;
- install_path = GetRegistryString(&collectors_edition_value);
-
- if (install_path == NULL)
+ for (i=0; i<arrlen(root_path_keys); ++i)
{
- return;
- }
+ char *install_path;
+ char *subpath;
+ unsigned int j;
- for (i=0; i<arrlen(collectors_edition_subdirs); ++i)
- {
- subpath = M_StringJoin(install_path, DIR_SEPARATOR_S,
- collectors_edition_subdirs[i], NULL);
+ install_path = GetRegistryString(&root_path_keys[i]);
- AddIWADDir(subpath);
- }
+ if (install_path == NULL)
+ {
+ continue;
+ }
- free(install_path);
+ for (j=0; j<arrlen(root_path_subdirs); ++j)
+ {
+ subpath = M_StringJoin(install_path, DIR_SEPARATOR_S,
+ root_path_subdirs[j], NULL);
+ AddIWADDir(subpath);
+ }
+
+ free(install_path);
+ }
}
@@ -509,47 +544,29 @@
return mission;
}
-//
-// Add directories from the list in the DOOMWADPATH environment variable.
-//
-
-static void AddDoomWadPath(void)
+// Add IWAD directories parsed from splitting a path string containing
+// paths separated by PATH_SEPARATOR. 'suffix' is a string to concatenate
+// to the end of the paths before adding them.
+static void AddIWADPath(char *path, char *suffix)
{
- char *doomwadpath;
- char *p;
+ char *left, *p;
- // Check the DOOMWADPATH environment variable.
+ path = M_StringDuplicate(path);
- doomwadpath = getenv("DOOMWADPATH");
-
- if (doomwadpath == NULL)
- {
- return;
- }
-
- doomwadpath = M_StringDuplicate(doomwadpath);
-
- // Add the initial directory
-
- AddIWADDir(doomwadpath);
-
// Split into individual dirs within the list.
+ left = path;
- p = doomwadpath;
-
for (;;)
{
- p = strchr(p, PATH_SEPARATOR);
-
+ p = strchr(left, PATH_SEPARATOR);
if (p != NULL)
{
- // Break at the separator and store the right hand side
+ // Break at the separator and use the left hand side
// as another iwad dir
-
*p = '\0';
- p += 1;
- AddIWADDir(p);
+ AddIWADDir(M_StringJoin(left, suffix, NULL));
+ left = p + 1;
}
else
{
@@ -556,9 +573,68 @@
break;
}
}
+
+ AddIWADDir(M_StringJoin(left, suffix, NULL));
+
+ free(path);
}
+// Add standard directories where IWADs are located on Unix systems.
+// To respect the freedesktop.org specification we support overriding
+// using standard environment variables. See the XDG Base Directory
+// Specification:
+// <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>
+static void AddXdgDirs(void)
+{
+ char *env, *tmp_env;
+ // Quote:
+ // > $XDG_DATA_HOME defines the base directory relative to which
+ // > user specific data files should be stored. If $XDG_DATA_HOME
+ // > is either not set or empty, a default equal to
+ // > $HOME/.local/share should be used.
+ env = getenv("XDG_DATA_HOME");
+ tmp_env = NULL;
+
+ if (env == NULL)
+ {
+ char *homedir = getenv("HOME");
+ if (homedir == NULL)
+ {
+ homedir = "/";
+ }
+
+ tmp_env = M_StringJoin(homedir, "/.local/share", NULL);
+ env = tmp_env;
+ }
+
+ // We support $XDG_DATA_HOME/games/doom (which will usually be
+ // ~/.local/share/games/doom) as a user-writeable extension to
+ // the usual /usr/share/games/doom location.
+ AddIWADDir(M_StringJoin(env, "/games/doom", NULL));
+ free(tmp_env);
+
+ // Quote:
+ // > $XDG_DATA_DIRS defines the preference-ordered set of base
+ // > directories to search for data files in addition to the
+ // > $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS
+ // > should be seperated with a colon ':'.
+ // >
+ // > If $XDG_DATA_DIRS is either not set or empty, a value equal to
+ // > /usr/local/share/:/usr/share/ should be used.
+ env = getenv("XDG_DATA_DIRS");
+ if (env == NULL)
+ {
+ // (Trailing / omitted from paths, as it is added below)
+ env = "/usr/local/share:/usr/share";
+ }
+
+ // The "standard" location for IWADs on Unix that is supported by most
+ // source ports is /usr/share/games/doom - we support this through the
+ // XDG_DATA_DIRS mechanism, through which it can be overridden.
+ AddIWADPath(env, "/games/doom");
+}
+
//
// Build a list of IWAD files
//
@@ -565,7 +641,7 @@
static void BuildIWADDirList(void)
{
- char *doomwaddir;
+ char *env;
if (iwad_dirs_built)
{
@@ -573,28 +649,28 @@
}
// Look in the current directory. Doom always does this.
-
AddIWADDir(".");
// Add DOOMWADDIR if it is in the environment
+ env = getenv("DOOMWADDIR");
+ if (env != NULL)
+ {
+ AddIWADDir(env);
+ }
- doomwaddir = getenv("DOOMWADDIR");
-
- if (doomwaddir != NULL)
+ // Add dirs from DOOMWADPATH:
+ env = getenv("DOOMWADPATH");
+ if (env != NULL)
{
- AddIWADDir(doomwaddir);
- }
+ AddIWADPath(env, "");
+ }
- // Add dirs from DOOMWADPATH
-
- AddDoomWadPath();
-
#ifdef _WIN32
// Search the registry and find where IWADs have been installed.
CheckUninstallStrings();
- CheckCollectorsEdition();
+ CheckInstallRootPaths();
CheckSteamEdition();
CheckDOSDefaults();
@@ -603,12 +679,7 @@
CheckSteamGUSPatches();
#else
-
- // Standard places where IWAD files are installed under Unix.
-
- AddIWADDir("/usr/share/games/doom");
- AddIWADDir("/usr/local/share/games/doom");
-
+ AddXdgDirs();
#endif
// Don't run this function again.
--- a/src/d_mode.c
+++ b/src/d_mode.c
@@ -120,6 +120,9 @@
GameMission_t mission;
GameVersion_t version;
} valid_versions[] = {
+ { doom, exe_doom_1_666 },
+ { doom, exe_doom_1_7 },
+ { doom, exe_doom_1_8 },
{ doom, exe_doom_1_9 },
{ doom, exe_hacx },
{ doom, exe_ultimate },
--- a/src/deh_io.c
+++ b/src/deh_io.c
@@ -120,7 +120,7 @@
context->input_buffer_pos = 0;
context->filename = malloc(9);
- M_StringCopy(context->filename, lumpinfo[lumpnum].name, 9);
+ M_StringCopy(context->filename, lumpinfo[lumpnum]->name, 9);
return context;
}
--- /dev/null
+++ b/src/doom.appdata.xml.in
@@ -1,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+ <id>@PROGRAM_PREFIX@doom.desktop</id>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+ <url type="homepage">@PACKAGE_URL@</url>
+ <url type="bugtracker">@PACKAGE_ISSUES@</url>
+ <description>
+ <p>
+ @PACKAGE_SHORTNAME@ Doom is a conservative,
+ historically-accurate Doom source port, which is compatible with
+ the thousands of mods and levels that were made before the Doom
+ source code was released. Unlike other source ports, the goal
+ is to preserve the original look, feel, limitations, and bugs of
+ the original DOS executable.
+ </p>
+ <p>
+ Full support for single- and multi-player games is provided, for
+ all of the original Doom games, Chex Quest, and Hacx. Unlike
+ the original executable, network play is implemented on the IP
+ network stack, allowing it to function on modern LANs and the
+ Internet.
+ </p>
+ </description>
+ <screenshots>
+ <screenshot type="default">
+ <image>http://www.chocolate-doom.org/wiki/images/9/97/GNOME_FreeDM_DEMO4.png</image>
+ <caption>FreeDM, DM05: Metal</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/a/a6/GNOME_Doom_II_DEMO2.png</image>
+ <caption>Doom II, Level 5: The Waste Tunnels</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/4/41/GNOME_Doomsday_of_UAC.png</image>
+ <caption>Doomsday of UAC (uac_dead.wad)</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/2/2a/GNOME_Freedoom_DTWID_DEMO3.png</image>
+ <caption>Doom the Way id Did, on Freedoom. Level 3-2: City of Corpses</caption>
+ </screenshot>
+ </screenshots>
+</component>
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -607,6 +607,10 @@
" "
"DOOM 2: Hell on Earth v%i.%i"
" ",
+ // doom2.wad v1.666
+ " "
+ "DOOM 2: Hell on Earth v%i.%i66"
+ " ",
// doom1.wad
" "
"DOOM Shareware Startup v%i.%i"
@@ -619,6 +623,10 @@
" "
"DOOM System Startup v%i.%i"
" ",
+ // Doom v1.666
+ " "
+ "DOOM System Startup v%i.%i66"
+ " "
// doom.wad (Ultimate DOOM)
" "
"The Ultimate DOOM Startup v%i.%i"
@@ -731,12 +739,12 @@
for (i=0; i<numlumps; ++i)
{
- if (!strncasecmp(lumpinfo[i].name, "MAP01", 8))
+ if (!strncasecmp(lumpinfo[i]->name, "MAP01", 8))
{
gamemission = doom2;
break;
}
- else if (!strncasecmp(lumpinfo[i].name, "E1M1", 8))
+ else if (!strncasecmp(lumpinfo[i]->name, "E1M1", 8))
{
gamemission = doom;
break;
@@ -945,8 +953,12 @@
static void InitGameVersion(void)
{
+ byte *demolump;
+ char demolumpname[6];
+ int demoversion;
int p;
int i;
+ boolean status;
//!
// @arg <version>
@@ -998,13 +1010,46 @@
gameversion = exe_hacx;
}
- else if (gamemode == shareware || gamemode == registered)
+ else if (gamemode == shareware || gamemode == registered
+ || (gamemode == commercial && gamemission == doom2))
{
// original
-
gameversion = exe_doom_1_9;
- // TODO: Detect IWADs earlier than Doom v1.9.
+ // Detect version from demo lump
+ for (i = 1; i <= 3; ++i)
+ {
+ M_snprintf(demolumpname, 6, "demo%i", i);
+ if (W_CheckNumForName(demolumpname) > 0)
+ {
+ demolump = W_CacheLumpName(demolumpname, PU_STATIC);
+ demoversion = demolump[0];
+ W_ReleaseLumpName(demolumpname);
+ status = true;
+ switch (demoversion)
+ {
+ case 106:
+ gameversion = exe_doom_1_666;
+ break;
+ case 107:
+ gameversion = exe_doom_1_7;
+ break;
+ case 108:
+ gameversion = exe_doom_1_8;
+ break;
+ case 109:
+ gameversion = exe_doom_1_9;
+ break;
+ default:
+ status = false;
+ break;
+ }
+ if (status)
+ {
+ break;
+ }
+ }
+ }
}
else if (gamemode == retail)
{
@@ -1012,20 +1057,13 @@
}
else if (gamemode == commercial)
{
- if (gamemission == doom2)
- {
- gameversion = exe_doom_1_9;
- }
- else
- {
- // Final Doom: tnt or plutonia
- // Defaults to emulating the first Final Doom executable,
- // which has the crash in the demo loop; however, having
- // this as the default should mean that it plays back
- // most demos correctly.
+ // Final Doom: tnt or plutonia
+ // Defaults to emulating the first Final Doom executable,
+ // which has the crash in the demo loop; however, having
+ // this as the default should mean that it plays back
+ // most demos correctly.
- gameversion = exe_final;
- }
+ gameversion = exe_final;
}
}
@@ -1493,7 +1531,7 @@
if (D_AddFile(file))
{
- M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+ M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
sizeof(demolumpname));
}
else
@@ -1528,7 +1566,7 @@
for (i = numiwadlumps; i < numlumps; ++i)
{
- if (!strncmp(lumpinfo[i].name, "DEHACKED", 8))
+ if (!strncmp(lumpinfo[i]->name, "DEHACKED", 8))
{
DEH_LoadLump(i, false, false);
loaded++;
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -614,7 +614,8 @@
// The "Sky never changes in Doom II" bug was fixed in
// the id Anthology version of doom2.exe for Final Doom.
- if ((gamemode == commercial) && (gameversion == exe_final2))
+ if ((gamemode == commercial)
+ && (gameversion == exe_final2 || gameversion == exe_chex))
{
char *skytexturename;
@@ -1168,26 +1169,26 @@
fixed_t xa, ya;
signed int an;
- an = (ANG45 * ((signed int) mthing->angle / 45));
- // Right-shifting a negative signed integer is implementation-defined,
- // so divide instead.
- an /= 1 << ANGLETOFINESHIFT;
+ // This calculation overflows in Vanilla Doom, but here we deliberately
+ // avoid integer overflow as it is undefined behavior, so the value of
+ // 'an' will always be positive.
+ an = (ANG45 >> ANGLETOFINESHIFT) * ((signed int) mthing->angle / 45);
switch (an)
{
- case -4096:
+ case 4096: // -4096:
xa = finetangent[2048]; // finecosine[-4096]
ya = finetangent[0]; // finesine[-4096]
break;
- case -3072:
+ case 5120: // -3072:
xa = finetangent[3072]; // finecosine[-3072]
ya = finetangent[1024]; // finesine[-3072]
break;
- case -2048:
+ case 6144: // -2048:
xa = finesine[0]; // finecosine[-2048]
ya = finetangent[2048]; // finesine[-2048]
break;
- case -1024:
+ case 7168: // -1024:
xa = finesine[1024]; // finecosine[-1024]
ya = finetangent[3072]; // finesine[-1024]
break;
@@ -1195,7 +1196,6 @@
case 1024:
case 2048:
case 3072:
- case 4096:
xa = finecosine[an];
ya = finesine[an];
break;
--- a/src/doom/hu_stuff.c
+++ b/src/doom/hu_stuff.c
@@ -48,7 +48,7 @@
#define HU_TITLE2 (mapnames_commercial[gamemap-1])
#define HU_TITLEP (mapnames_commercial[gamemap-1 + 32])
#define HU_TITLET (mapnames_commercial[gamemap-1 + 64])
-#define HU_TITLE_CHEX (mapnames[gamemap - 1])
+#define HU_TITLE_CHEX (mapnames_chex[(gameepisode-1)*9+gamemap-1])
#define HU_TITLEHEIGHT 1
#define HU_TITLEX 0
#define HU_TITLEY (167 - SHORT(hu_font[0]->height))
@@ -163,6 +163,60 @@
"NEWLEVEL"
};
+char* mapnames_chex[] = // Chex Quest names.
+{
+
+ HUSTR_E1M1,
+ HUSTR_E1M2,
+ HUSTR_E1M3,
+ HUSTR_E1M4,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+ HUSTR_E1M5,
+
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL",
+ "NEWLEVEL"
+};
+
// List of names for levels in commercial IWADs
// (doom2.wad, plutonia.wad, tnt.wad). These are stored in a
// single large array; WADs like pl2.wad have a MAP33, and rely on
@@ -351,10 +405,7 @@
break;
}
- // Chex.exe always uses the episode 1 level title
- // eg. E2M1 gives the title for E1M1
-
- if (gameversion == exe_chex)
+ if (logical_gamemission == doom && gameversion == exe_chex)
{
s = HU_TITLE_CHEX;
}
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -751,6 +751,9 @@
switch (gameversion)
{
+ case exe_doom_1_666:
+ case exe_doom_1_7:
+ case exe_doom_1_8:
case exe_doom_1_9:
case exe_hacx:
@@ -1074,7 +1077,7 @@
// Doom 1.9 had two menus when playing Doom 1
// All others had only one
- if (gameversion == exe_doom_1_9 && gamemode != commercial)
+ if (gameversion <= exe_doom_1_9 && gamemode != commercial)
{
choice = 0;
M_SetupNextMenu(&ReadDef2);
--- a/src/doom/p_doors.c
+++ b/src/doom/p_doors.c
@@ -392,6 +392,12 @@
}
// if the sector has an active thinker, use it
+
+ if (line->sidenum[side^1] == -1)
+ {
+ I_Error("EV_VerticalDoor: DR special type on 1-sided linedef");
+ }
+
sec = sides[ line->sidenum[side^1]] .sector;
if (sec->specialdata)
--- a/src/doom/r_data.c
+++ b/src/doom/r_data.c
@@ -832,7 +832,7 @@
if (flatpresent[i])
{
lump = firstflat + i;
- flatmemory += lumpinfo[lump].size;
+ flatmemory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
}
@@ -869,7 +869,7 @@
for (j=0 ; j<texture->patchcount ; j++)
{
lump = texture->patches[j].patch;
- texturememory += lumpinfo[lump].size;
+ texturememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump , PU_CACHE);
}
}
@@ -898,7 +898,7 @@
for (k=0 ; k<8 ; k++)
{
lump = firstspritelump + sf->lump[k];
- spritememory += lumpinfo[lump].size;
+ spritememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump , PU_CACHE);
}
}
--- a/src/doom/r_things.c
+++ b/src/doom/r_things.c
@@ -208,22 +208,22 @@
// filling in the frames for whatever is found
for (l=start+1 ; l<end ; l++)
{
- if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+ if (!strncasecmp(lumpinfo[l]->name, spritename, 4))
{
- frame = lumpinfo[l].name[4] - 'A';
- rotation = lumpinfo[l].name[5] - '0';
+ frame = lumpinfo[l]->name[4] - 'A';
+ rotation = lumpinfo[l]->name[5] - '0';
if (modifiedgame)
- patched = W_GetNumForName (lumpinfo[l].name);
+ patched = W_GetNumForName (lumpinfo[l]->name);
else
patched = l;
R_InstallSpriteLump (patched, frame, rotation, false);
- if (lumpinfo[l].name[6])
+ if (lumpinfo[l]->name[6])
{
- frame = lumpinfo[l].name[6] - 'A';
- rotation = lumpinfo[l].name[7] - '0';
+ frame = lumpinfo[l]->name[6] - 'A';
+ rotation = lumpinfo[l]->name[7] - '0';
R_InstallSpriteLump (l, frame, rotation, true);
}
}
--- a/src/doom/s_sound.c
+++ b/src/doom/s_sound.c
@@ -46,7 +46,7 @@
// Distance tp origin when sounds should be maxed out.
// This should relate to movement clipping resolution
// (see BLOCKMAP handling).
-// In the source code release: (160*FRACUNIT). Changed back to the
+// In the source code release: (160*FRACUNIT). Changed back to the
// Vanilla value of 200 (why was this changed?)
#define S_CLOSE_DIST (200 * FRACUNIT)
@@ -59,7 +59,6 @@
#define S_STEREO_SWING (96 * FRACUNIT)
-#define NORM_PITCH 128
#define NORM_PRIORITY 64
#define NORM_SEP 128
@@ -73,7 +72,9 @@
// handle of the sound being played
int handle;
-
+
+ int pitch;
+
} channel_t;
// The set of channels available
@@ -85,7 +86,7 @@
int sfxVolume = 8;
-// Maximum volume of music.
+// Maximum volume of music.
int musicVolume = 8;
@@ -95,7 +96,7 @@
// Whether songs are mus_paused
-static boolean mus_paused;
+static boolean mus_paused;
// Music currently being played
@@ -112,10 +113,25 @@
//
void S_Init(int sfxVolume, int musicVolume)
-{
+{
int i;
- I_SetOPLDriverVer(opl_v_new);
+ if (gameversion == exe_doom_1_666)
+ {
+ if (logical_gamemission == doom)
+ {
+ I_SetOPLDriverVer(opl_doom1_1_666);
+ }
+ else
+ {
+ I_SetOPLDriverVer(opl_doom2_1_666);
+ }
+ }
+ else
+ {
+ I_SetOPLDriverVer(opl_doom_1_9);
+ }
+
I_PrecacheSounds(S_sfx, NUMSFX);
S_SetSfxVolume(sfxVolume);
@@ -141,6 +157,12 @@
S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
}
+ // Doom defaults to pitch-shifting off.
+ if (snd_pitchshift == -1)
+ {
+ snd_pitchshift = 0;
+ }
+
I_AtExit(S_Shutdown, true);
}
@@ -237,10 +259,10 @@
{
mnum = spmus[gamemap-1];
}
- }
+ }
S_ChangeMusic(mnum, true);
-}
+}
void S_StopSound(mobj_t *origin)
{
@@ -265,7 +287,7 @@
{
// channel number to use
int cnum;
-
+
channel_t* c;
// Find an open channel
@@ -296,7 +318,7 @@
if (cnum == snd_channels)
{
- // FUCK! No lower priority. Sorry, Charlie.
+ // FUCK! No lower priority. Sorry, Charlie.
return -1;
}
else
@@ -337,12 +359,12 @@
// From _GG1_ p.428. Appox. eucledian distance fast.
approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
-
+
if (gamemap != 8 && approx_dist > S_CLIPPING_DIST)
{
return 0;
}
-
+
// angle of source to listener
angle = R_PointToAngle2(listener->x,
listener->y,
@@ -384,12 +406,27 @@
// distance effect
*vol = (snd_SfxVolume
* ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
- / S_ATTENUATOR;
+ / S_ATTENUATOR;
}
-
+
return (*vol > 0);
}
+// clamp supplied integer to the range 0 <= x <= 255.
+
+static int Clamp(int x)
+{
+ if (x < 0)
+ {
+ return 0;
+ }
+ else if (x > 255)
+ {
+ return 255;
+ }
+ return x;
+}
+
void S_StartSound(void *origin_p, int sfx_id)
{
sfxinfo_t *sfx;
@@ -396,6 +433,7 @@
mobj_t *origin;
int rc;
int sep;
+ int pitch;
int cnum;
int volume;
@@ -411,9 +449,11 @@
sfx = &S_sfx[sfx_id];
// Initialize sound parameters
+ pitch = NORM_PITCH;
if (sfx->link)
{
volume += sfx->volume;
+ pitch = sfx->pitch;
if (volume < 1)
{
@@ -438,7 +478,7 @@
if (origin->x == players[consoleplayer].mo->x
&& origin->y == players[consoleplayer].mo->y)
- {
+ {
sep = NORM_SEP;
}
@@ -446,12 +486,23 @@
{
return;
}
- }
+ }
else
{
sep = NORM_SEP;
}
+ // hacks to vary the sfx pitches
+ if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit)
+ {
+ pitch += 8 - (M_Random()&15);
+ }
+ else if (sfx_id != sfx_itemup && sfx_id != sfx_tink)
+ {
+ pitch += 16 - (M_Random()&31);
+ }
+ pitch = Clamp(pitch);
+
// kill old sound
S_StopSound(origin);
@@ -474,8 +525,9 @@
sfx->lumpnum = I_GetSfxLumpNum(sfx);
}
- channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep);
-}
+ channels[cnum].pitch = pitch;
+ channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep, channels[cnum].pitch);
+}
//
// Stop and resume music, during game PAUSE.
@@ -549,7 +601,7 @@
c->origin,
&volume,
&sep);
-
+
if (!audible)
{
S_StopChannel(cnum);
@@ -576,7 +628,7 @@
{
I_Error("Attempt to set music volume at %d",
volume);
- }
+ }
I_SetMusicVolume(volume);
}
--- a/src/doom/st_stuff.c
+++ b/src/doom/st_stuff.c
@@ -528,7 +528,8 @@
{
musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1;
- if (((buf[0]-'0')*10 + buf[1]-'0') > 35)
+ if (((buf[0]-'0')*10 + buf[1]-'0') > 35
+ && gameversion >= exe_doom_1_8)
plyr->message = DEH_String(STSTR_NOMUS);
else
S_ChangeMusic(musnum, 1);
@@ -617,13 +618,20 @@
{
epsd = buf[0] - '0';
map = buf[1] - '0';
- }
- // Chex.exe always warps to episode 1.
+ // Chex.exe always warps to episode 1.
- if (gameversion == exe_chex)
- {
- epsd = 1;
+ if (gameversion == exe_chex)
+ {
+ if (epsd > 1)
+ {
+ epsd = 1;
+ }
+ if (map > 5)
+ {
+ map = 5;
+ }
+ }
}
// Catch invalid maps.
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -51,8 +51,14 @@
//
#ifdef __GNUC__
+
+#ifdef __clang__
#define PACKEDATTR __attribute__((packed))
#else
+#define PACKEDATTR __attribute__((packed,gcc_struct))
+#endif
+
+#else
#define PACKEDATTR
#endif
@@ -66,7 +72,7 @@
#include <inttypes.h>
-#ifdef __cplusplus
+#if defined(__cplusplus) || defined(__bool_true_false_are_defined)
// Use builtin bool type with C++.
--- a/src/gusconf.c
+++ b/src/gusconf.c
@@ -192,6 +192,7 @@
data = Z_Malloc(len + 1, PU_STATIC, NULL);
W_ReadLump(lumpnum, data);
+ data[len] = '\0';
return data;
}
--- /dev/null
+++ b/src/heretic.appdata.xml.in
@@ -1,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+ <id>@PROGRAM_PREFIX@heretic.desktop</id>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+ <url type="homepage">@PACKAGE_URL@</url>
+ <url type="bugtracker">@PACKAGE_ISSUES@</url>
+ <description>
+ <p>
+ @PACKAGE_SHORTNAME@ Heretic is a conservative,
+ historically-accurate Heretic source port, which is compatible
+ with mods and levels that were made before the Heretic source
+ code was released. Unlike other source ports, the goal is to
+ preserve the original look, feel, limitations, and bugs of the
+ original DOS executable.
+ </p>
+ <p>
+ Full support for single- and multi-player games is provided.
+ Unlike the original executable, network play is implemented on
+ the IP network stack, allowing it to function on modern LANs and
+ the Internet.
+ </p>
+ </description>
+ <screenshots>
+ <screenshot type="default">
+ <image>http://www.chocolate-doom.org/wiki/images/9/93/GNOME_Heretic_E5M4.png</image>
+ <caption>Level E5M4: Courtyard</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/1/14/GNOME_Heretic_Shareware_DEMO3.png</image>
+ <caption>Shareware Level E1M9: The Graveyard</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/3/34/GNOME_Heretic_E4M1.png</image>
+ <caption>Level E4M1: Catafalque</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/4/42/GNOME_Heretic_Shareware_DEMO1.png</image>
+ <caption>Shareware Level E1M3: The Gatehouse</caption>
+ </screenshot>
+ </screenshots>
+</component>
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -62,7 +62,6 @@
boolean debugmode; // checkparm of -debug
boolean ravpic; // checkparm of -ravpic
boolean cdrom; // true if cd-rom mode active
-boolean singletics; // debug flag to cancel adaptiveness
boolean noartiskip; // whether shift-enter skips an artifact
skill_t startskill;
@@ -1019,7 +1018,7 @@
if (D_AddFile(file))
{
- M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+ M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
sizeof(demolumpname));
}
else
--- a/src/heretic/g_game.c
+++ b/src/heretic/g_game.c
@@ -102,7 +102,6 @@
int consoleplayer; // player taking events and displaying
int displayplayer; // view being displayed
-int gametic;
int levelstarttic; // gametic at level start
int totalkills, totalitems, totalsecret; // for intermission
--- a/src/heretic/r_data.c
+++ b/src/heretic/r_data.c
@@ -671,7 +671,7 @@
if (flatpresent[i])
{
lump = firstflat + i;
- flatmemory += lumpinfo[lump].size;
+ flatmemory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
@@ -701,7 +701,7 @@
for (j = 0; j < texture->patchcount; j++)
{
lump = texture->patches[j].patch;
- texturememory += lumpinfo[lump].size;
+ texturememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
}
@@ -731,7 +731,7 @@
for (k = 0; k < 8; k++)
{
lump = firstspritelump + sf->lump[k];
- spritememory += lumpinfo[lump].size;
+ spritememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
}
--- a/src/heretic/r_things.c
+++ b/src/heretic/r_things.c
@@ -175,15 +175,15 @@
// scan the lumps, filling in the frames for whatever is found
//
for (l = start + 1; l < end; l++)
- if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+ if (!strncasecmp(lumpinfo[l]->name, spritename, 4))
{
- frame = lumpinfo[l].name[4] - 'A';
- rotation = lumpinfo[l].name[5] - '0';
+ frame = lumpinfo[l]->name[4] - 'A';
+ rotation = lumpinfo[l]->name[5] - '0';
R_InstallSpriteLump(l, frame, rotation, false);
- if (lumpinfo[l].name[6])
+ if (lumpinfo[l]->name[6])
{
- frame = lumpinfo[l].name[6] - 'A';
- rotation = lumpinfo[l].name[7] - '0';
+ frame = lumpinfo[l]->name[6] - 'A';
+ rotation = lumpinfo[l]->name[7] - '0';
R_InstallSpriteLump(l, frame, rotation, true);
}
}
--- a/src/heretic/s_sound.c
+++ b/src/heretic/s_sound.c
@@ -268,10 +268,8 @@
sep = 512 - sep;
}
- // TODO: Play pitch-shifted sounds as in Vanilla Heretic
-
- channel[i].pitch = (byte) (127 + (M_Random() & 7) - (M_Random() & 7));
- channel[i].handle = I_StartSound(&S_sfx[sound_id], i, vol, sep);
+ channel[i].pitch = (byte) (NORM_PITCH + (M_Random() & 7) - (M_Random() & 7));
+ channel[i].handle = I_StartSound(&S_sfx[sound_id], i, vol, sep, channel[i].pitch);
channel[i].mo = origin;
channel[i].sound_id = sound_id;
channel[i].priority = priority;
@@ -327,9 +325,8 @@
S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
}
- // TODO: Pitch shifting.
- channel[i].pitch = (byte) (127 - (M_Random() & 3) + (M_Random() & 3));
- channel[i].handle = I_StartSound(&S_sfx[sound_id], i, volume, 128);
+ channel[i].pitch = (byte) (NORM_PITCH - (M_Random() & 3) + (M_Random() & 3));
+ channel[i].handle = I_StartSound(&S_sfx[sound_id], i, volume, 128, channel[i].pitch);
channel[i].mo = origin;
channel[i].sound_id = sound_id;
channel[i].priority = 1; //super low priority.
@@ -516,7 +513,7 @@
void S_Init(void)
{
- I_SetOPLDriverVer(opl_v_old);
+ I_SetOPLDriverVer(opl_doom2_1_666);
soundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL);
if (snd_Channels > 8)
{
@@ -526,6 +523,12 @@
S_SetMaxVolume(true);
I_AtExit(S_ShutDown, true);
+
+ // Heretic defaults to pitch-shifting on
+ if (snd_pitchshift == -1)
+ {
+ snd_pitchshift = 1;
+ }
I_PrecacheSounds(S_sfx, NUMSFX);
}
--- /dev/null
+++ b/src/hexen.appdata.xml.in
@@ -1,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+ <id>@PROGRAM_PREFIX@hexen.desktop</id>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+ <url type="homepage">@PACKAGE_URL@</url>
+ <url type="bugtracker">@PACKAGE_ISSUES@</url>
+ <description>
+ <p>
+ @PACKAGE_SHORTNAME@ Hexen is a conservative,
+ historically-accurate Hexen source port, which is compatible
+ with mods and levels that were made before the Hexen source code
+ was released. Unlike other source ports, the goal is to
+ preserve the original look, feel, limitations, and bugs of the
+ original DOS executable.
+ </p>
+ <p>
+ Full support for single- and multi-player games is provided.
+ Unlike the original executable, network play is implemented on
+ the IP network stack, allowing it to function on modern LANs and
+ the Internet.
+ </p>
+ </description>
+ <screenshots>
+ <screenshot type="default">
+ <image>http://www.chocolate-doom.org/wiki/images/0/0f/GNOME_Hexen_Guardian_of_Fire.png</image>
+ <caption>Level "Guardian of Fire"</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/5/5c/GNOME_Hexen_Effluvium.png</image>
+ <caption>Level "Effluvium"</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/c/c1/GNOME_Hexen_Dragon_Chapel.png</image>
+ <caption>Level "Dragon Chapel"</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/a/a7/GNOME_Hexen_Darkmere.png</image>
+ <caption>Level "Darkmere"</caption>
+ </screenshot>
+ </screenshots>
+</component>
--- a/src/hexen/g_game.c
+++ b/src/hexen/g_game.c
@@ -88,7 +88,6 @@
int consoleplayer; // player taking events and displaying
int displayplayer; // view being displayed
-int gametic;
int levelstarttic; // gametic at level start
char demoname[32];
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -103,7 +103,6 @@
boolean ravpic; // checkparm of -ravpic
boolean cdrom = false; // true if cd-rom mode active
boolean cmdfrag; // true if a CMD_FRAG packet should be sent out
-boolean singletics; // debug flag to cancel adaptiveness
boolean artiskip; // whether shift-enter skips an artifact
int maxzone = 0x800000; // Maximum allocated for zone heap (8meg default)
skill_t startskill;
@@ -270,15 +269,26 @@
maxplayers = 4;
}
- // The v1.0 IWAD file is missing a bunch of lumps.
- if (gamemode != shareware && W_CheckNumForName("CLUS1MSG") == -1)
+ // The v1.0 IWAD file is missing a bunch of lumps that can cause the game
+ // to crash, so we exit with an error if the user tries to play with it.
+ // But we provide an override command line flag if they really want to
+ // do it.
+
+ //!
+ // If provided, the check for the v1.0 IWAD file is disabled, even though
+ // it will almost certainly cause the game to crash.
+ //
+ // @category compat
+ //
+
+ if (!M_ParmExists("-v10override")
+ && gamemode != shareware && W_CheckNumForName("CLUS1MSG") < 0)
{
- printf(
- "** WARNING: You are playing with the Hexen v1.0 IWAD. This\n"
- "** isn't supported by " PACKAGE_NAME ", and you may find that\n"
- "** the game will crash. Please upgrade to the v1.1 IWAD file.\n"
- "** See here for more information:\n"
- "** http://www.doomworld.com/classicdoom/info/patches.php\n");
+ I_Error(
+ "You are trying to use the Hexen v1.0 IWAD. This isn't\n"
+ "supported by " PACKAGE_NAME ". Please upgrade to the v1.1\n"
+ "IWAD file. See here for more information:\n"
+ " http://www.doomworld.com/classicdoom/info/patches.php");
}
}
@@ -653,7 +663,7 @@
if (W_AddFile(file) != NULL)
{
- M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+ M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
sizeof(demolumpname));
}
else
--- a/src/hexen/h2def.h
+++ b/src/hexen/h2def.h
@@ -169,7 +169,7 @@
typedef union
{
- int i;
+ intptr_t i;
struct mobj_s *m;
struct player_s *p;
} specialval_t;
--- a/src/hexen/r_data.c
+++ b/src/hexen/r_data.c
@@ -627,7 +627,7 @@
if (flatpresent[i])
{
lump = firstflat + i;
- flatmemory += lumpinfo[lump].size;
+ flatmemory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
@@ -658,7 +658,7 @@
for (j = 0; j < texture->patchcount; j++)
{
lump = texture->patches[j].patch;
- texturememory += lumpinfo[lump].size;
+ texturememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
}
@@ -688,7 +688,7 @@
for (k = 0; k < 8; k++)
{
lump = firstspritelump + sf->lump[k];
- spritememory += lumpinfo[lump].size;
+ spritememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
}
--- a/src/hexen/r_things.c
+++ b/src/hexen/r_things.c
@@ -178,15 +178,15 @@
// scan the lumps, filling in the frames for whatever is found
//
for (l = start + 1; l < end; l++)
- if (!strncmp(lumpinfo[l].name, namelist[i], 4))
+ if (!strncmp(lumpinfo[l]->name, namelist[i], 4))
{
- frame = lumpinfo[l].name[4] - 'A';
- rotation = lumpinfo[l].name[5] - '0';
+ frame = lumpinfo[l]->name[4] - 'A';
+ rotation = lumpinfo[l]->name[5] - '0';
R_InstallSpriteLump(l, frame, rotation, false);
- if (lumpinfo[l].name[6])
+ if (lumpinfo[l]->name[6])
{
- frame = lumpinfo[l].name[6] - 'A';
- rotation = lumpinfo[l].name[7] - '0';
+ frame = lumpinfo[l]->name[6] - 'A';
+ rotation = lumpinfo[l]->name[7] - '0';
R_InstallSpriteLump(l, frame, rotation, true);
}
}
--- a/src/hexen/s_sound.c
+++ b/src/hexen/s_sound.c
@@ -498,17 +498,16 @@
// vol = SoundCurve[dist];
}
-#if 0
-// TODO
- if (S_sfx[sound_id].changePitch)
+ // if the sfxinfo_t is marked as 'can be pitch shifted'
+ if (S_sfx[sound_id].pitch)
{
- Channel[i].pitch = (byte) (127 + (M_Random() & 7) - (M_Random() & 7));
+ Channel[i].pitch = (byte) (NORM_PITCH + (M_Random() & 7) - (M_Random() & 7));
}
else
{
- Channel[i].pitch = 127;
+ Channel[i].pitch = NORM_PITCH;
}
-#endif
+
if (S_sfx[sound_id].lumpnum == 0)
{
S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
@@ -517,7 +516,8 @@
Channel[i].handle = I_StartSound(&S_sfx[sound_id],
i,
vol,
- sep /* , Channel[i].pitch] */);
+ sep,
+ Channel[i].pitch);
Channel[i].sound_id = sound_id;
Channel[i].priority = priority;
Channel[i].volume = volume;
@@ -771,7 +771,7 @@
if (sep > 192)
sep = 512 - sep;
}
- I_UpdateSoundParams(i, vol, sep /*, Channel[i].pitch */);
+ I_UpdateSoundParams(i, vol, sep);
priority = S_sfx[Channel[i].sound_id].priority;
priority *= PRIORITY_MAX_ADJUST - (dist / DIST_ADJUST);
Channel[i].priority = priority;
@@ -787,7 +787,7 @@
void S_Init(void)
{
- I_SetOPLDriverVer(opl_v_old);
+ I_SetOPLDriverVer(opl_doom2_1_666);
SoundCurve = W_CacheLumpName("SNDCURVE", PU_STATIC);
// SoundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL);
@@ -798,6 +798,12 @@
I_SetMusicVolume(snd_MusicVolume * 8);
I_AtExit(S_ShutDown, true);
+
+ // Hexen defaults to pitch-shifting on
+ if (snd_pitchshift == -1)
+ {
+ snd_pitchshift = 1;
+ }
I_PrecacheSounds(S_sfx, NUMSFX);
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -307,7 +307,7 @@
124, 124, 125, 125, 126, 126, 127, 127
};
-static opl_driver_ver_t opl_drv_ver = opl_v_new;
+static opl_driver_ver_t opl_drv_ver = opl_doom_1_9;
static boolean music_initialized = false;
//static boolean musicpaused = false;
@@ -349,9 +349,15 @@
// Configuration file variable, containing the port number for the
// adlib chip.
+char *snd_dmxoption = "";
int opl_io_port = 0x388;
-int opl_type = 0;
+// If true, OPL sound channels are reversed to their correct arrangement
+// (as intended by the MIDI standard) rather than the backwards one
+// used by DMX due to a bug.
+
+static boolean opl_stereo_correct = false;
+
// Load instrument table from GENMIDI lump:
static boolean LoadInstrumentTable(void)
@@ -470,7 +476,7 @@
*rover = voice;
voice->next = NULL;
- if (next != NULL && double_voice && opl_drv_ver == opl_v_old)
+ if (next != NULL && double_voice && opl_drv_ver != opl_doom_1_9)
{
VoiceKeyOff(next);
ReleaseVoice(next);
@@ -770,13 +776,32 @@
ReleaseVoice(result);
}
-// Alternate version of ReplaceExistingVoice() used when emulating old
-// versions of the DMX library used in Heretic and Hexen.
+// Alternate versions of ReplaceExistingVoice() used when emulating old
+// versions of the DMX library used in Doom 1.666, Heretic and Hexen.
-static void ReplaceExistingVoiceOld(opl_channel_data_t *channel)
+static void ReplaceExistingVoiceDoom1(void)
{
opl_voice_t *rover;
opl_voice_t *result;
+
+ result = voice_alloced_list;
+
+ for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+ {
+ if (rover->channel > result->channel)
+ {
+ result = rover;
+ }
+ }
+
+ VoiceKeyOff(result);
+ ReleaseVoice(result);
+}
+
+static void ReplaceExistingVoiceDoom2(opl_channel_data_t *channel)
+{
+ opl_voice_t *rover;
+ opl_voice_t *result;
opl_voice_t *roverend;
int i;
int priority;
@@ -918,6 +943,11 @@
{
opl_voice_t *voice;
+ if (!opl_opl3mode && opl_drv_ver == opl_doom1_1_666)
+ {
+ instrument_voice = 0;
+ }
+
// Find a voice to use for this new note.
voice = GetFreeVoice();
@@ -962,7 +992,7 @@
{
genmidi_instr_t *instrument;
opl_channel_data_t *channel;
- unsigned int note, key, volume;
+ unsigned int note, key, volume, voicenum;
boolean double_voice;
/*
@@ -1009,43 +1039,66 @@
double_voice = (SHORT(instrument->flags) & GENMIDI_FLAG_2VOICE) != 0;
- if (opl_drv_ver == opl_v_old)
+ switch (opl_drv_ver)
{
- if (voice_alloced_num == num_opl_voices)
- {
- ReplaceExistingVoiceOld(channel);
- }
- if (voice_alloced_num == num_opl_voices - 1 && double_voice)
- {
- ReplaceExistingVoiceOld(channel);
- }
+ case opl_doom1_1_666:
+ voicenum = double_voice + 1;
+ if (!opl_opl3mode)
+ {
+ voicenum = 1;
+ }
+ while (voice_alloced_num > num_opl_voices - voicenum)
+ {
+ ReplaceExistingVoiceDoom1();
+ }
- // Find and program a voice for this instrument. If this
- // is a double voice instrument, we must do this twice.
+ // Find and program a voice for this instrument. If this
+ // is a double voice instrument, we must do this twice.
- if (double_voice)
- {
- VoiceKeyOn(channel, instrument, 1, note, key, volume);
- }
+ if (double_voice)
+ {
+ VoiceKeyOn(channel, instrument, 1, note, key, volume);
+ }
- VoiceKeyOn(channel, instrument, 0, note, key, volume);
- }
- else
- {
- if (voice_free_list == NULL)
- {
- ReplaceExistingVoice();
- }
+ VoiceKeyOn(channel, instrument, 0, note, key, volume);
+ break;
+ case opl_doom2_1_666:
+ if (voice_alloced_num == num_opl_voices)
+ {
+ ReplaceExistingVoiceDoom2(channel);
+ }
+ if (voice_alloced_num == num_opl_voices - 1 && double_voice)
+ {
+ ReplaceExistingVoiceDoom2(channel);
+ }
- // Find and program a voice for this instrument. If this
- // is a double voice instrument, we must do this twice.
+ // Find and program a voice for this instrument. If this
+ // is a double voice instrument, we must do this twice.
- VoiceKeyOn(channel, instrument, 0, note, key, volume);
+ if (double_voice)
+ {
+ VoiceKeyOn(channel, instrument, 1, note, key, volume);
+ }
- if (double_voice)
- {
- VoiceKeyOn(channel, instrument, 1, note, key, volume);
- }
+ VoiceKeyOn(channel, instrument, 0, note, key, volume);
+ break;
+ default:
+ case opl_doom_1_9:
+ if (voice_free_list == NULL)
+ {
+ ReplaceExistingVoice();
+ }
+
+ // Find and program a voice for this instrument. If this
+ // is a double voice instrument, we must do this twice.
+
+ VoiceKeyOn(channel, instrument, 0, note, key, volume);
+
+ if (double_voice)
+ {
+ VoiceKeyOn(channel, instrument, 1, note, key, volume);
+ }
+ break;
}
}
@@ -1086,6 +1139,16 @@
unsigned int reg_pan;
unsigned int i;
+ // The DMX library has the stereo channels backwards, maybe because
+ // Paul Radek had a Soundblaster card with the channels reversed, or
+ // perhaps it was just a bug in the OPL3 support that was never
+ // finished. By default we preserve this bug, but we also provide a
+ // secret DMXOPTION to fix it.
+ if (opl_stereo_correct)
+ {
+ pan = 144 - pan;
+ }
+
if (opl_opl3mode)
{
if (pan >= 96)
@@ -1650,19 +1713,28 @@
static boolean I_OPL_InitMusic(void)
{
- int opl_chip_type;
+ char *dmxoption;
+ opl_init_result_t chip_type;
OPL_SetSampleRate(snd_samplerate);
- opl_chip_type = OPL_Init(opl_io_port);
- if (!opl_chip_type)
+ chip_type = OPL_Init(opl_io_port);
+ if (chip_type == OPL_INIT_NONE)
{
printf("Dude. The Adlib isn't responding.\n");
return false;
}
- if (opl_chip_type == 2 && opl_type)
+ // The DMXOPTION variable must be set to enable OPL3 support.
+ // As an extension, we also allow it to be set from the config file.
+ dmxoption = getenv("DMXOPTION");
+ if (dmxoption == NULL)
{
+ dmxoption = snd_dmxoption != NULL ? snd_dmxoption : "";
+ }
+
+ if (chip_type == OPL_INIT_OPL3 && strstr(dmxoption, "-opl3") != NULL)
+ {
opl_opl3mode = 1;
num_opl_voices = OPL_NUM_VOICES * 2;
}
@@ -1671,6 +1743,10 @@
opl_opl3mode = 0;
num_opl_voices = OPL_NUM_VOICES;
}
+
+ // Secret, undocumented DMXOPTION that reverses the stereo channels
+ // into their correct orientation.
+ opl_stereo_correct = strstr(dmxoption, "-reverse") != NULL;
// Initialize all registers.
--- a/src/i_pcsound.c
+++ b/src/i_pcsound.c
@@ -174,7 +174,8 @@
static int I_PCS_StartSound(sfxinfo_t *sfxinfo,
int channel,
int vol,
- int sep)
+ int sep,
+ int pitch)
{
int result;
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -752,7 +752,7 @@
for (lumpnum = 0; lumpnum < numlumps; ++lumpnum)
{
- strncpy(name, lumpinfo[lumpnum].name, 8);
+ strncpy(name, lumpinfo[lumpnum]->name, 8);
name[8] = '\0';
if (!IsMusicLump(lumpnum))
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -52,6 +52,7 @@
sfxinfo_t *sfxinfo;
Mix_Chunk chunk;
int use_count;
+ int pitch;
allocated_sound_t *prev, *next;
};
@@ -59,7 +60,7 @@
static boolean sound_initialized = false;
-static sfxinfo_t *channels_playing[NUM_CHANNELS];
+static allocated_sound_t *channels_playing[NUM_CHANNELS];
static int mixer_freq;
static Uint16 mixer_format;
@@ -136,10 +137,6 @@
AllocatedSoundUnlink(snd);
- // Unlink from higher-level code.
-
- snd->sfxinfo->driver_data = NULL;
-
// Keep track of the amount of allocated sound data:
allocated_sounds_size -= snd->chunk.alen;
@@ -200,7 +197,7 @@
// Allocate a block for a new sound effect.
-static Mix_Chunk *AllocateSound(sfxinfo_t *sfxinfo, size_t len)
+static allocated_sound_t *AllocateSound(sfxinfo_t *sfxinfo, size_t len)
{
allocated_sound_t *snd;
@@ -231,14 +228,11 @@
snd->chunk.alen = len;
snd->chunk.allocated = 1;
snd->chunk.volume = MIX_MAX_VOLUME;
+ snd->pitch = NORM_PITCH;
snd->sfxinfo = sfxinfo;
snd->use_count = 0;
- // driver_data pointer points to the allocated_sound structure.
-
- sfxinfo->driver_data = snd;
-
// Keep track of how much memory all these cached sounds are using...
allocated_sounds_size += len;
@@ -245,7 +239,7 @@
AllocatedSoundLink(snd);
- return &snd->chunk;
+ return snd;
}
// Lock a sound, to indicate that it may not be freed.
@@ -279,15 +273,77 @@
//printf("-- %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count);
}
-// When a sound stops, check if it is still playing. If it is not,
+// Search through the list of allocated sounds and return the one that matches
+// the supplied sfxinfo entry and pitch level.
+
+static allocated_sound_t * GetAllocatedSoundBySfxInfoAndPitch(sfxinfo_t *sfxinfo, int pitch)
+{
+ allocated_sound_t * p = allocated_sounds_head;
+
+ while (p != NULL)
+ {
+ if (p->sfxinfo == sfxinfo && p->pitch == pitch)
+ {
+ return p;
+ }
+ p = p->next;
+ }
+
+ return NULL;
+}
+
+// Allocate a new sound chunk and pitch-shift an existing sound up-or-down
+// into it.
+
+static allocated_sound_t * PitchShift(allocated_sound_t *insnd, int pitch)
+{
+ allocated_sound_t * outsnd;
+ Sint16 *inp, *outp;
+ Sint16 *srcbuf, *dstbuf;
+ Uint32 srclen, dstlen;
+
+ srcbuf = (Sint16 *)insnd->chunk.abuf;
+ srclen = insnd->chunk.alen;
+
+ // determine ratio pitch:NORM_PITCH and apply to srclen, then invert.
+ // This is an approximation of vanilla behaviour based on measurements
+ dstlen = (int)((1 + (1 - (float)pitch / NORM_PITCH)) * srclen);
+
+ // ensure that the new buffer is an even length
+ if ((dstlen % 2) == 0)
+ {
+ dstlen++;
+ }
+
+ outsnd = AllocateSound(insnd->sfxinfo, dstlen);
+
+ if (!outsnd)
+ {
+ return NULL;
+ }
+
+ outsnd->pitch = pitch;
+ dstbuf = (Sint16 *)outsnd->chunk.abuf;
+
+ // loop over output buffer. find corresponding input cell, copy over
+ for (outp = dstbuf; outp < dstbuf + dstlen/2; ++outp)
+ {
+ inp = srcbuf + (int)((float)(outp - dstbuf) / dstlen * srclen);
+ *outp = *inp;
+ }
+
+ return outsnd;
+}
+
+// When a sound stops, check if it is still playing. If it is not,
// we can mark the sound data as CACHE to be freed back for other
// means.
static void ReleaseSoundOnChannel(int channel)
{
- sfxinfo_t *sfxinfo = channels_playing[channel];
+ allocated_sound_t *snd = channels_playing[channel];
- if (sfxinfo == NULL)
+ if (snd == NULL)
{
return;
}
@@ -294,7 +350,14 @@
channels_playing[channel] = NULL;
- UnlockAllocatedSound(sfxinfo->driver_data);
+ UnlockAllocatedSound(snd);
+
+ // if the sound is a pitch-shift and it's not in use, immediately
+ // free it
+ if (snd->pitch != NORM_PITCH && snd->use_count <= 0)
+ {
+ FreeAllocatedSound(snd);
+ }
}
#ifdef HAVE_LIBSAMPLERATE
@@ -343,6 +406,7 @@
// uint32_t alen;
int retn;
int16_t *expanded;
+ allocated_sound_t *snd;
Mix_Chunk *chunk;
src_data.input_frames = length;
@@ -374,13 +438,14 @@
// alen = src_data.output_frames_gen * 4;
- chunk = AllocateSound(sfxinfo, src_data.output_frames_gen * 4);
+ snd = AllocateSound(sfxinfo, src_data.output_frames_gen * 4);
- if (chunk == NULL)
+ if (snd == NULL)
{
return false;
}
+ chunk = &snd->chunk;
expanded = (int16_t *) chunk->abuf;
// Convert the result back into 16-bit integers.
@@ -533,11 +598,12 @@
int length)
{
SDL_AudioCVT convertor;
+ allocated_sound_t *snd;
Mix_Chunk *chunk;
uint32_t expanded_length;
-
- // Calculate the length of the expanded version of the sample.
+ // Calculate the length of the expanded version of the sample.
+
expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate);
// Double up twice: 8 -> 16 bit and mono -> stereo
@@ -546,13 +612,15 @@
// Allocate a chunk in which to expand the sound
- chunk = AllocateSound(sfxinfo, expanded_length);
+ snd = AllocateSound(sfxinfo, expanded_length);
- if (chunk == NULL)
+ if (snd == NULL)
{
return false;
}
+ chunk = &snd->chunk;
+
// If we can, use the standard / optimized SDL conversion routines.
if (samplerate <= mixer_freq
@@ -696,11 +764,12 @@
#ifdef DEBUG_DUMP_WAVS
{
char filename[16];
+ allocated_sound_t * snd;
M_snprintf(filename, sizeof(filename), "%s.wav",
- DEH_String(S_sfx[sound].name));
- WriteWAV(filename, sound_chunks[sound].abuf,
- sound_chunks[sound].alen, mixer_freq);
+ DEH_String(sfxinfo->name));
+ snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH);
+ WriteWAV(filename, snd->chunk.abuf, snd->chunk.alen,mixer_freq);
}
#endif
@@ -786,8 +855,7 @@
static boolean LockSound(sfxinfo_t *sfxinfo)
{
// If the sound isn't loaded, load it now
-
- if (sfxinfo->driver_data == NULL)
+ if (GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH) == NULL)
{
if (!CacheSFX(sfxinfo))
{
@@ -795,7 +863,7 @@
}
}
- LockAllocatedSound(sfxinfo->driver_data);
+ LockAllocatedSound(GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH));
return true;
}
@@ -857,7 +925,7 @@
// is set, but currently not used by mixing.
//
-static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep)
+static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch)
{
allocated_sound_t *snd;
@@ -875,19 +943,47 @@
if (!LockSound(sfxinfo))
{
- return -1;
+ return -1;
}
- snd = sfxinfo->driver_data;
+ snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, pitch);
+ if (snd == NULL)
+ {
+ allocated_sound_t *newsnd;
+ // fetch the base sound effect, un-pitch-shifted
+ snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH);
+
+ if (snd == NULL)
+ {
+ return -1;
+ }
+
+ if (snd_pitchshift)
+ {
+ newsnd = PitchShift(snd, pitch);
+
+ if (newsnd)
+ {
+ LockAllocatedSound(newsnd);
+ UnlockAllocatedSound(snd);
+ snd = newsnd;
+ }
+ }
+ }
+ else
+ {
+ LockAllocatedSound(snd);
+ }
+
// play sound
- Mix_PlayChannelTimed(channel, &snd->chunk, 0, -1);
+ Mix_PlayChannel(channel, &snd->chunk, 0);
- channels_playing[channel] = sfxinfo;
+ channels_playing[channel] = snd;
// set separation, etc.
-
+
I_SDL_UpdateSoundParams(channel, vol, sep);
return channel;
@@ -919,7 +1015,7 @@
return Mix_Playing(handle);
}
-//
+//
// Periodically called to update the sound system
//
@@ -935,7 +1031,7 @@
{
// Sound has finished playing on this channel,
// but sound data has not been released to cache
-
+
ReleaseSoundOnChannel(i);
}
}
@@ -942,7 +1038,7 @@
}
static void I_SDL_ShutdownSound(void)
-{
+{
if (!sound_initialized)
{
return;
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -48,6 +48,11 @@
char *snd_musiccmd = "";
+// Whether to vary the pitch of sound effects
+// Each game will set the default differently
+
+int snd_pitchshift = -1;
+
// Low-level sound and music modules we are using
static sound_module_t *sound_module;
@@ -68,7 +73,6 @@
extern opl_driver_ver_t opl_drv_ver;
extern int opl_io_port;
-extern int opl_type;
// For native music module:
@@ -309,12 +313,12 @@
}
}
-int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep)
+int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch)
{
if (sound_module != NULL)
{
CheckVolumeSeparation(&vol, &sep);
- return sound_module->StartSound(sfxinfo, channel, vol, sep);
+ return sound_module->StartSound(sfxinfo, channel, vol, sep, pitch);
}
else
{
@@ -346,7 +350,7 @@
{
if (sound_module != NULL && sound_module->CacheSounds != NULL)
{
- sound_module->CacheSounds(sounds, num_sounds);
+ sound_module->CacheSounds(sounds, num_sounds);
}
}
@@ -433,6 +437,7 @@
void I_BindSoundVariables(void)
{
+ extern char *snd_dmxoption;
extern int use_libsamplerate;
extern float libsamplerate_scale;
@@ -444,10 +449,11 @@
M_BindIntVariable("snd_mport", &snd_mport);
M_BindIntVariable("snd_maxslicetime_ms", &snd_maxslicetime_ms);
M_BindStringVariable("snd_musiccmd", &snd_musiccmd);
+ M_BindStringVariable("snd_dmxoption", &snd_dmxoption);
M_BindIntVariable("snd_samplerate", &snd_samplerate);
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
- M_BindIntVariable("opl_type", &opl_type);
+ M_BindIntVariable("snd_pitchshift", &snd_pitchshift);
M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path);
M_BindStringVariable("gus_patch_path", &gus_patch_path);
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -22,6 +22,8 @@
#include "doomtype.h"
+// so that the individual game logic and sound driver code agree
+#define NORM_PITCH 127
//
// SoundFX struct.
@@ -32,7 +34,7 @@
{
// tag name, used for hexen.
char *tagname;
-
+
// lump name. If we are running with use_sfx_prefix=true, a
// 'DS' (or 'DP' for PC speaker sounds) is prepended to this.
@@ -44,7 +46,7 @@
// referenced sound if a link
sfxinfo_t *link;
- // pitch if a link
+ // pitch if a link (Doom), whether to pitch-shift (Hexen)
int pitch;
// volume if a link
@@ -56,7 +58,7 @@
int usefulness;
// lump number of sfx
- int lumpnum;
+ int lumpnum;
// Maximum number of channels that the sound can be played on
// (Heretic)
@@ -76,13 +78,13 @@
// lump number of music
int lumpnum;
-
+
// music data
void *data;
// music handle once registered
void *handle;
-
+
} musicinfo_t;
typedef enum
@@ -133,7 +135,7 @@
// Start a sound on a given channel. Returns the channel id
// or -1 on failure.
- int (*StartSound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep);
+ int (*StartSound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch);
// Stop the sound playing on the given channel.
@@ -154,7 +156,7 @@
int I_GetSfxLumpNum(sfxinfo_t *sfxinfo);
void I_UpdateSound(void);
void I_UpdateSoundParams(int channel, int vol, int sep);
-int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep);
+int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch);
void I_StopSound(int channel);
boolean I_SoundIsPlaying(int channel);
void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds);
@@ -231,13 +233,15 @@
extern int snd_cachesize;
extern int snd_maxslicetime_ms;
extern char *snd_musiccmd;
+extern int snd_pitchshift;
void I_BindSoundVariables(void);
// DMX version to emulate for OPL emulation:
typedef enum {
- opl_v_old, // Hexen, Heretic
- opl_v_new // Doom, Strife
+ opl_doom1_1_666, // Doom 1 v1.666
+ opl_doom2_1_666, // Doom 2 v1.666, Hexen, Heretic
+ opl_doom_1_9 // Doom v1.9, Strife
} opl_driver_ver_t;
void I_SetOPLDriverVer(opl_driver_ver_t ver);
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -801,6 +801,14 @@
CONFIG_VARIABLE_INT(snd_maxslicetime_ms),
//!
+ // If non-zero, sound effects will have their pitch varied up or
+ // down by a random amount during play. If zero, sound effects
+ // play back at their default pitch. The default is zero.
+ //
+
+ CONFIG_VARIABLE_INT(snd_pitchshift),
+
+ //!
// External command to invoke to perform MIDI playback. If set to
// the empty string, SDL_mixer's internal MIDI playback is used.
// This only has any effect when snd_musicdevice is set to General
@@ -809,16 +817,18 @@
CONFIG_VARIABLE_STRING(snd_musiccmd),
//!
+ // Value to set for the DMXOPTION environment variable. If this contains
+ // "-opl3", output for an OPL3 chip is generated when in OPL MIDI
+ // playback mode.
+ //
+ CONFIG_VARIABLE_STRING(snd_dmxoption),
+
+ //!
// The I/O port to use to access the OPL chip. Only relevant when
// using native OPL music playback.
//
CONFIG_VARIABLE_INT_HEX(opl_io_port),
-
- //!
- // OPL chip type.
- //
- CONFIG_VARIABLE_INT(opl_type),
//!
// @game doom heretic strife
--- a/src/setup/display.h
+++ b/src/setup/display.h
@@ -21,5 +21,6 @@
extern int show_endoom;
extern int graphical_startup;
+extern int png_screenshots;
#endif /* #ifndef SETUP_DISPLAY_H */
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -716,71 +716,77 @@
// GUI
//
-static void AddJoystickControl(txt_table_t *table, char *label, int *var)
+static void AddJoystickControl(TXT_UNCAST_ARG(table), char *label, int *var)
{
+ TXT_CAST_ARG(txt_table_t, table);
txt_joystick_input_t *joy_input;
joy_input = TXT_NewJoystickInput(var);
- TXT_AddWidget(table, TXT_NewLabel(label));
- TXT_AddWidget(table, joy_input);
+ TXT_AddWidgets(table,
+ TXT_NewLabel(label),
+ joy_input,
+ TXT_TABLE_EMPTY,
+ NULL);
}
void ConfigJoystick(void)
{
txt_window_t *window;
- txt_table_t *button_table, *axis_table;
- txt_table_t *joystick_table;
window = TXT_NewWindow("Gamepad/Joystick configuration");
-
+ TXT_SetTableColumns(window, 6);
+ TXT_SetColumnWidths(window, 18, 10, 2, 14, 10, 0);
TXT_SetWindowHelpURL(window, WINDOW_HELP_URL);
TXT_AddWidgets(window,
- joystick_table = TXT_NewTable(2),
- TXT_NewSeparator("Axes"),
- axis_table = TXT_NewTable(2),
- TXT_NewSeparator("Buttons"),
- button_table = TXT_NewTable(4),
- NULL);
-
- TXT_SetColumnWidths(joystick_table, 13, 40);
-
- TXT_AddWidgets(joystick_table,
TXT_NewLabel("Controller"),
joystick_button = TXT_NewButton("zzzz"),
- NULL);
+ TXT_TABLE_EOL,
- TXT_SetColumnWidths(axis_table, 20, 15);
-
- TXT_AddWidgets(axis_table,
+ TXT_NewSeparator("Axes"),
TXT_NewLabel("Forward/backward"),
y_axis_widget = TXT_NewJoystickAxis(&joystick_y_axis,
&joystick_y_invert,
JOYSTICK_AXIS_VERTICAL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_EMPTY,
+ TXT_TABLE_EMPTY,
+
TXT_NewLabel("Turn left/right"),
- x_axis_widget = TXT_NewJoystickAxis(&joystick_x_axis,
- &joystick_x_invert,
- JOYSTICK_AXIS_HORIZONTAL),
+ x_axis_widget =
+ TXT_NewJoystickAxis(&joystick_x_axis,
+ &joystick_x_invert,
+ JOYSTICK_AXIS_HORIZONTAL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_EMPTY,
+ TXT_TABLE_EMPTY,
+
TXT_NewLabel("Strafe left/right"),
TXT_NewJoystickAxis(&joystick_strafe_axis,
&joystick_strafe_invert,
JOYSTICK_AXIS_HORIZONTAL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_EMPTY,
+ TXT_TABLE_EMPTY,
+
+ TXT_NewSeparator("Buttons"),
NULL);
- TXT_SetColumnWidths(button_table, 16, 12, 14, 11);
+ AddJoystickControl(window, "Fire/Attack", &joybfire);
+ AddJoystickControl(window, "Strafe Left", &joybstrafeleft);
- AddJoystickControl(button_table, "Fire/Attack", &joybfire);
- AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft);
+ AddJoystickControl(window, "Use", &joybuse);
+ AddJoystickControl(window, "Strafe Right", &joybstraferight);
- AddJoystickControl(button_table, "Use", &joybuse);
- AddJoystickControl(button_table, "Strafe Right", &joybstraferight);
+ AddJoystickControl(window, "Previous weapon", &joybprevweapon);
+ AddJoystickControl(window, "Strafe", &joybstrafe);
- AddJoystickControl(button_table, "Previous weapon", &joybprevweapon);
- AddJoystickControl(button_table, "Strafe", &joybstrafe);
+ AddJoystickControl(window, "Next weapon", &joybnextweapon);
- AddJoystickControl(button_table, "Next weapon", &joybnextweapon);
-
// High values of joybspeed are used to activate the "always run mode"
// trick in Vanilla Doom. If this has been enabled, not only is the
// joybspeed value meaningless, but the control itself is useless.
@@ -787,15 +793,15 @@
if (joybspeed < 20)
{
- AddJoystickControl(button_table, "Speed", &joybspeed);
+ AddJoystickControl(window, "Speed", &joybspeed);
}
if (gamemission == hexen || gamemission == strife)
{
- AddJoystickControl(button_table, "Jump", &joybjump);
+ AddJoystickControl(window, "Jump", &joybjump);
}
- AddJoystickControl(button_table, "Activate menu", &joybmenu);
+ AddJoystickControl(window, "Activate menu", &joybmenu);
TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL);
TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
--- a/src/setup/keyboard.c
+++ b/src/setup/keyboard.c
@@ -146,8 +146,9 @@
// Add a label and keyboard input to the specified table.
-static void AddKeyControl(txt_table_t *table, char *name, int *var)
+static void AddKeyControl(TXT_UNCAST_ARG(table), char *name, int *var)
{
+ TXT_CAST_ARG(txt_table_t, table);
txt_key_input_t *key_input;
TXT_AddWidget(table, TXT_NewLabel(name));
@@ -157,20 +158,26 @@
TXT_SignalConnect(key_input, "set", KeySetCallback, var);
}
-static void AddSectionLabel(txt_table_t *table, char *title, boolean add_space)
+static void AddSectionLabel(TXT_UNCAST_ARG(table), char *title,
+ boolean add_space)
{
+ TXT_CAST_ARG(txt_table_t, table);
char buf[64];
if (add_space)
{
- TXT_AddWidgets(table, TXT_NewStrut(0, 1), TXT_NewStrut(0, 1),
- NULL);
+ TXT_AddWidgets(table,
+ TXT_NewStrut(0, 1),
+ TXT_TABLE_EOL,
+ NULL);
}
M_snprintf(buf, sizeof(buf), " - %s - ", title);
- TXT_AddWidgets(table, TXT_NewLabel(buf), TXT_NewStrut(0, 0),
- NULL);
+ TXT_AddWidgets(table,
+ TXT_NewLabel(buf),
+ TXT_TABLE_EOL,
+ NULL);
}
static void ConfigExtraKeys(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
{
@@ -244,7 +251,8 @@
AddKeyControl(table, "Chaos Device", &key_arti_teleport);
AddKeyControl(table, "Banishment Device", &key_arti_teleportother);
AddKeyControl(table, "Porkalator", &key_arti_egg);
- AddKeyControl(table, "Icon of the Defender", &key_arti_invulnerability);
+ AddKeyControl(table, "Icon of the Defender",
+ &key_arti_invulnerability);
}
}
else
@@ -262,8 +270,8 @@
AddKeyControl(table, "Weapon 6", &key_weapon6);
AddKeyControl(table, "Weapon 7", &key_weapon7);
AddKeyControl(table, "Weapon 8", &key_weapon8);
- AddKeyControl(table, "Previous weapon", &key_prevweapon);
- AddKeyControl(table, "Next weapon", &key_nextweapon);
+ AddKeyControl(table, "Previous weapon", &key_prevweapon);
+ AddKeyControl(table, "Next weapon", &key_nextweapon);
}
static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
@@ -353,9 +361,6 @@
void ConfigKeyboard(void)
{
txt_window_t *window;
- txt_table_t *movement_table;
- txt_table_t *action_table;
- txt_table_t *dialogs_table;
txt_checkbox_t *run_control;
always_run = joybspeed >= 20;
@@ -364,54 +369,57 @@
TXT_SetWindowHelpURL(window, WINDOW_HELP_URL);
- TXT_AddWidgets(window,
- TXT_NewSeparator("Movement"),
- movement_table = TXT_NewTable(4),
+ // The window is on a 5-column grid layout that looks like:
+ // Label | Control | | Label | Control
+ // There is a small gap between the two conceptual "columns" of
+ // controls, just for spacing.
+ TXT_SetTableColumns(window, 5);
+ TXT_SetColumnWidths(window, 15, 8, 2, 15, 8);
- TXT_NewSeparator("Action"),
- action_table = TXT_NewTable(4),
- dialogs_table = TXT_NewTable(2),
+ TXT_AddWidget(window, TXT_NewSeparator("Movement"));
+ AddKeyControl(window, "Move Forward", &key_up);
+ TXT_AddWidget(window, TXT_TABLE_EMPTY);
+ AddKeyControl(window, "Strafe Left", &key_strafeleft);
- TXT_NewSeparator("Misc."),
- run_control = TXT_NewCheckBox("Always run", &always_run),
- TXT_NewInvertedCheckBox("Use native keyboard mapping",
- &vanilla_keyboard_mapping),
- NULL);
+ AddKeyControl(window, "Move Backward", &key_down);
+ TXT_AddWidget(window, TXT_TABLE_EMPTY);
+ AddKeyControl(window, "Strafe Right", &key_straferight);
- TXT_SetColumnWidths(movement_table, 15, 8, 15, 8);
+ AddKeyControl(window, "Turn Left", &key_left);
+ TXT_AddWidget(window, TXT_TABLE_EMPTY);
+ AddKeyControl(window, "Speed On", &key_speed);
- TXT_SignalConnect(run_control, "changed", UpdateJoybSpeed, NULL);
+ AddKeyControl(window, "Turn Right", &key_right);
+ TXT_AddWidget(window, TXT_TABLE_EMPTY);
+ AddKeyControl(window, "Strafe On", &key_strafe);
- AddKeyControl(movement_table, "Move Forward", &key_up);
- AddKeyControl(movement_table, " Strafe Left", &key_strafeleft);
- AddKeyControl(movement_table, "Move Backward", &key_down);
- AddKeyControl(movement_table, " Strafe Right", &key_straferight);
- AddKeyControl(movement_table, "Turn Left", &key_left);
- AddKeyControl(movement_table, " Speed On", &key_speed);
- AddKeyControl(movement_table, "Turn Right", &key_right);
- AddKeyControl(movement_table, " Strafe On", &key_strafe);
-
if (gamemission == hexen || gamemission == strife)
{
- AddKeyControl(movement_table, "Jump", &key_jump);
+ AddKeyControl(window, "Jump", &key_jump);
}
- TXT_SetColumnWidths(action_table, 15, 8, 15, 8);
+ TXT_AddWidget(window, TXT_NewSeparator("Action"));
+ AddKeyControl(window, "Fire/Attack", &key_fire);
+ TXT_AddWidget(window, TXT_TABLE_EMPTY);
+ AddKeyControl(window, "Use", &key_use);
- AddKeyControl(action_table, "Fire/Attack", &key_fire);
- AddKeyControl(action_table, " Use", &key_use);
-
- // Other key bindings are stored in separate sub-dialogs:
-
- TXT_SetColumnWidths(dialogs_table, 24, 24);
-
- TXT_AddWidgets(dialogs_table,
+ TXT_AddWidgets(window,
TXT_NewButton2("More controls...", ConfigExtraKeys, NULL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_TABLE_EMPTY,
TXT_NewButton2("Other keys...", OtherKeysDialog, NULL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+
+ TXT_NewSeparator("Misc."),
+ run_control = TXT_NewCheckBox("Always run", &always_run),
+ TXT_TABLE_EOL,
+ TXT_NewInvertedCheckBox("Use native keyboard mapping",
+ &vanilla_keyboard_mapping),
+ TXT_TABLE_EOL,
NULL);
+ TXT_SignalConnect(run_control, "changed", UpdateJoybSpeed, NULL);
TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
-
}
void BindKeyboardVariables(void)
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -91,6 +91,8 @@
show_endoom = 0;
dclick_use = 0;
novert = 1;
+ snd_dmxoption = "-opl3 -reverse";
+ png_screenshots = 1;
}
static int MainMenuKeyPress(txt_window_t *window, int key, void *user_data)
--- a/src/setup/mode.c
+++ b/src/setup/mode.c
@@ -140,6 +140,9 @@
if (gamemission == strife)
{
+ // Strife has a different default value than the other games
+ screenblocks = 10;
+
M_BindStringVariable("back_flat", &back_flat);
M_BindStringVariable("nickname", &nickname);
--- a/src/setup/mouse.c
+++ b/src/setup/mouse.c
@@ -67,8 +67,9 @@
}
}
-static void AddMouseControl(txt_table_t *table, char *label, int *var)
+static void AddMouseControl(TXT_UNCAST_ARG(table), char *label, int *var)
{
+ TXT_CAST_ARG(txt_table_t, table);
txt_mouse_input_t *mouse_input;
TXT_AddWidget(table, TXT_NewLabel(label));
@@ -92,7 +93,7 @@
buttons_table = TXT_NewTable(2),
NULL);
- TXT_SetColumnWidths(buttons_table, 29, 5);
+ TXT_SetColumnWidths(buttons_table, 24, 5);
AddMouseControl(buttons_table, "Move backward", &mousebbackward);
AddMouseControl(buttons_table, "Use", &mousebuse);
@@ -111,35 +112,28 @@
void ConfigMouse(void)
{
txt_window_t *window;
- txt_table_t *motion_table;
- txt_table_t *buttons_table;
window = TXT_NewWindow("Mouse configuration");
+ TXT_SetTableColumns(window, 2);
+
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
TXT_SetWindowHelpURL(window, WINDOW_HELP_URL);
TXT_AddWidgets(window,
TXT_NewCheckBox("Enable mouse", &usemouse),
+ TXT_TABLE_OVERFLOW_RIGHT,
TXT_NewInvertedCheckBox("Allow vertical mouse movement",
&novert),
+ TXT_TABLE_OVERFLOW_RIGHT,
TXT_NewCheckBox("Grab mouse in windowed mode",
&grabmouse),
+ TXT_TABLE_OVERFLOW_RIGHT,
TXT_NewCheckBox("Double click acts as \"use\"",
&dclick_use),
+ TXT_TABLE_OVERFLOW_RIGHT,
TXT_NewSeparator("Mouse motion"),
- motion_table = TXT_NewTable(2),
-
- TXT_NewSeparator("Buttons"),
- buttons_table = TXT_NewTable(2),
- TXT_NewButton2("More controls...",
- ConfigExtraButtons,
- NULL),
- NULL);
-
- TXT_SetColumnWidths(motion_table, 27, 5);
-
- TXT_AddWidgets(motion_table,
TXT_NewLabel("Speed"),
TXT_NewSpinControl(&mouseSensitivity, 1, 256),
TXT_NewLabel("Acceleration"),
@@ -146,15 +140,16 @@
TXT_NewFloatSpinControl(&mouse_acceleration, 1.0, 5.0),
TXT_NewLabel("Acceleration threshold"),
TXT_NewSpinControl(&mouse_threshold, 0, 32),
+
+ TXT_NewSeparator("Buttons"),
NULL);
- TXT_SetColumnWidths(buttons_table, 27, 5);
+ AddMouseControl(window, "Fire/Attack", &mousebfire);
+ AddMouseControl(window, "Move forward", &mousebforward);
+ AddMouseControl(window, "Strafe on", &mousebstrafe);
- AddMouseControl(buttons_table, "Fire/Attack", &mousebfire);
- AddMouseControl(buttons_table, "Move forward", &mousebforward);
- AddMouseControl(buttons_table, "Strafe on", &mousebstrafe);
-
- TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction());
+ TXT_AddWidget(window,
+ TXT_NewButton2("More controls...", ConfigExtraButtons, NULL));
}
void BindMouseVariables(void)
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@ -37,6 +37,7 @@
#define MULTI_START_HELP_URL "http://www.chocolate-doom.org/setup-multi-start"
#define MULTI_JOIN_HELP_URL "http://www.chocolate-doom.org/setup-multi-join"
#define MULTI_CONFIG_HELP_URL "http://www.chocolate-doom.org/setup-multi-config"
+#define LEVEL_WARP_HELP_URL "http://www.chocolate-doom.org/setup-level-warp"
#define NUM_WADS 10
#define NUM_EXTRA_PARAMS 10
@@ -388,7 +389,6 @@
static void LevelSelectDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
{
txt_window_t *window;
- txt_table_t *table;
txt_button_t *button;
const iwad_t *iwad;
char buf[10];
@@ -403,7 +403,7 @@
if (warptype == WARP_ExMy)
{
episodes = D_GetNumEpisodes(iwad->mission, iwad->mode);
- table = TXT_NewTable(episodes);
+ TXT_SetTableColumns(window, episodes);
// ExMy levels
@@ -418,7 +418,7 @@
if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, x, y))
{
- TXT_AddWidget(table, NULL);
+ TXT_AddWidget(window, NULL);
continue;
}
@@ -428,11 +428,11 @@
SetExMyWarp, (void *) (x * 10 + y));
TXT_SignalConnect(button, "pressed",
CloseLevelSelectDialog, window);
- TXT_AddWidget(table, button);
+ TXT_AddWidget(window, button);
if (warpepisode == x && warpmap == y)
{
- TXT_SelectWidget(table, button);
+ TXT_SelectWidget(window, button);
}
}
}
@@ -439,7 +439,7 @@
}
else
{
- table = TXT_NewTable(6);
+ TXT_SetTableColumns(window, 6);
for (i=0; i<60; ++i)
{
@@ -450,7 +450,7 @@
if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, 1, l))
{
- TXT_AddWidget(table, NULL);
+ TXT_AddWidget(window, NULL);
continue;
}
@@ -460,16 +460,14 @@
SetMAPxyWarp, (void *) l);
TXT_SignalConnect(button, "pressed",
CloseLevelSelectDialog, window);
- TXT_AddWidget(table, button);
+ TXT_AddWidget(window, button);
if (warpmap == l)
{
- TXT_SelectWidget(table, button);
+ TXT_SelectWidget(window, button);
}
}
}
-
- TXT_AddWidget(window, table);
}
static void IWADSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
@@ -710,37 +708,33 @@
static void StartGameMenu(char *window_title, int multiplayer)
{
txt_window_t *window;
- txt_table_t *gameopt_table;
- txt_table_t *advanced_table;
txt_widget_t *iwad_selector;
window = TXT_NewWindow(window_title);
- TXT_SetWindowHelpURL(window, MULTI_START_HELP_URL);
+ TXT_SetTableColumns(window, 2);
+ TXT_SetColumnWidths(window, 12, 6);
- TXT_AddWidgets(window,
- gameopt_table = TXT_NewTable(2),
- TXT_NewSeparator("Monster options"),
- TXT_NewInvertedCheckBox("Monsters enabled", &nomonsters),
- TXT_NewCheckBox("Fast monsters", &fast),
- TXT_NewCheckBox("Respawning monsters", &respawn),
- TXT_NewSeparator("Advanced"),
- advanced_table = TXT_NewTable(2),
- NULL);
+ if (multiplayer)
+ {
+ TXT_SetWindowHelpURL(window, MULTI_START_HELP_URL);
+ }
+ else
+ {
+ TXT_SetWindowHelpURL(window, LEVEL_WARP_HELP_URL);
+ }
TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction());
TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction(multiplayer));
- TXT_SetColumnWidths(gameopt_table, 12, 6);
+ TXT_AddWidgets(window,
+ TXT_NewLabel("Game"),
+ iwad_selector = IWADSelector(),
+ NULL);
- TXT_AddWidgets(gameopt_table,
- TXT_NewLabel("Game"),
- iwad_selector = IWADSelector(),
- NULL);
-
if (gamemission == hexen)
{
txt_dropdown_list_t *cc_dropdown;
- TXT_AddWidgets(gameopt_table,
+ TXT_AddWidgets(window,
TXT_NewLabel("Character class "),
cc_dropdown = TXT_NewDropdownList(&character_class,
character_classes, 3),
@@ -751,16 +745,16 @@
TXT_SignalConnect(cc_dropdown, "changed", UpdateWarpType, NULL);
}
- TXT_AddWidgets(gameopt_table,
- TXT_NewLabel("Skill"),
- skillbutton = TXT_NewDropdownList(&skill, doom_skills, 5),
- TXT_NewLabel("Level warp"),
- warpbutton = TXT_NewButton2("????", LevelSelectDialog, NULL),
- NULL);
+ TXT_AddWidgets(window,
+ TXT_NewLabel("Skill"),
+ skillbutton = TXT_NewDropdownList(&skill, doom_skills, 5),
+ TXT_NewLabel("Level warp"),
+ warpbutton = TXT_NewButton2("?", LevelSelectDialog, NULL),
+ NULL);
if (multiplayer)
{
- TXT_AddWidgets(gameopt_table,
+ TXT_AddWidgets(window,
TXT_NewLabel("Game type"),
GameTypeDropdown(),
TXT_NewLabel("Time limit"),
@@ -768,23 +762,36 @@
TXT_NewLabel("minutes"),
NULL),
NULL);
+ }
- TXT_AddWidget(window,
- TXT_NewInvertedCheckBox("Register with master server",
- &privateserver));
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Monster options"),
+ TXT_NewInvertedCheckBox("Monsters enabled", &nomonsters),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewCheckBox("Fast monsters", &fast),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewCheckBox("Respawning monsters", &respawn),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ NULL);
- TXT_AddWidgets(advanced_table,
+ if (multiplayer)
+ {
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Advanced"),
TXT_NewLabel("UDP port"),
TXT_NewIntInputBox(&udpport, 5),
+ TXT_NewInvertedCheckBox("Register with master server",
+ &privateserver),
+ TXT_TABLE_OVERFLOW_RIGHT,
NULL);
}
- TXT_AddWidget(window,
- TXT_NewButton2("Add extra parameters...",
- OpenExtraParamsWindow, NULL));
+ TXT_AddWidgets(window,
+ TXT_NewButton2("Add extra parameters...",
+ OpenExtraParamsWindow, NULL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ NULL);
- TXT_SetColumnWidths(advanced_table, 12, 6);
-
TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
UpdateWarpType(NULL, NULL);
@@ -985,24 +992,15 @@
void JoinMultiGame(void)
{
txt_window_t *window;
- txt_table_t *gameopt_table;
- txt_table_t *serveropt_table;
txt_inputbox_t *address_box;
window = TXT_NewWindow("Join multiplayer game");
+ TXT_SetTableColumns(window, 2);
+ TXT_SetColumnWidths(window, 12, 12);
+
TXT_SetWindowHelpURL(window, MULTI_JOIN_HELP_URL);
- TXT_AddWidgets(window,
- gameopt_table = TXT_NewTable(2),
- TXT_NewSeparator("Server"),
- serveropt_table = TXT_NewTable(1),
- TXT_NewStrut(0, 1),
- TXT_NewButton2("Add extra parameters...", OpenExtraParamsWindow, NULL),
- NULL);
-
- TXT_SetColumnWidths(gameopt_table, 12, 12);
-
- TXT_AddWidgets(gameopt_table,
+ TXT_AddWidgets(window,
TXT_NewLabel("Game"),
IWADSelector(),
NULL);
@@ -1009,7 +1007,7 @@
if (gamemission == hexen)
{
- TXT_AddWidgets(gameopt_table,
+ TXT_AddWidgets(window,
TXT_NewLabel("Character class "),
TXT_NewDropdownList(&character_class,
character_classes, 3),
@@ -1016,15 +1014,21 @@
NULL);
}
- TXT_AddWidgets(serveropt_table,
- TXT_NewHorizBox(
- TXT_NewLabel("Connect to address: "),
- address_box = TXT_NewInputBox(&connect_address, 30),
- NULL),
+ TXT_AddWidgets(window,
+ TXT_NewSeparator("Server"),
+ TXT_NewLabel("Connect to address: "),
+ address_box = TXT_NewInputBox(&connect_address, 30),
+
TXT_NewButton2("Find server on Internet...",
FindInternetServer, NULL),
+ TXT_TABLE_OVERFLOW_RIGHT,
TXT_NewButton2("Find server on local network...",
FindLANServer, NULL),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewStrut(0, 1),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewButton2("Add extra parameters...",
+ OpenExtraParamsWindow, NULL),
NULL);
TXT_SelectWidget(window, address_box);
@@ -1101,7 +1105,7 @@
window = TXT_NewWindow("Multiplayer Configuration");
TXT_SetWindowHelpURL(window, MULTI_CONFIG_HELP_URL);
- TXT_AddWidgets(window,
+ TXT_AddWidgets(window,
TXT_NewStrut(0, 1),
TXT_NewHorizBox(TXT_NewLabel("Player name: "),
TXT_NewInputBox(&net_player_name, 25),
@@ -1124,7 +1128,7 @@
TXT_NewInputBox(&chat_macros[(i + 1) % 10], 40),
NULL);
}
-
+
TXT_AddWidget(window, table);
}
--- a/src/setup/setup-manifest.xml.in
+++ b/src/setup/setup-manifest.xml.in
@@ -32,6 +32,7 @@
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Vista -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <!-- 8 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <!-- 8.1 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- 10 -->
</application>
</compatibility>
--- a/src/setup/setup.desktop.in
+++ b/src/setup/setup.desktop.in
@@ -4,4 +4,5 @@
Icon=@PROGRAM_PREFIX@setup
Type=Application
Comment=Setup tool for @PACKAGE_SHORTNAME@
-Categories=Settings
+Categories=Settings;
+Keywords=first;person;shooter;doom;heretic;hexen;strife;vanilla;
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -59,6 +59,13 @@
"Native MIDI",
};
+typedef enum
+{
+ OPLMODE_OPL2,
+ OPLMODE_OPL3,
+ NUM_OPLMODES,
+} oplmode_t;
+
static char *opltype_strings[] =
{
"OPL2",
@@ -76,6 +83,8 @@
int snd_cachesize = 64 * 1024 * 1024;
int snd_maxslicetime_ms = 28;
char *snd_musiccmd = "";
+int snd_pitchshift = 0;
+char *snd_dmxoption = "";
static int numChannels = 8;
static int sfxVolume = 8;
@@ -88,7 +97,6 @@
static char *timidity_cfg_path = NULL;
static char *gus_patch_path = NULL;
static int gus_ram_kb = 1024;
-static int opl_type = 0;
// DOS specific variables: these are unused but should be maintained
// so that the config file can be shared between chocolate
@@ -103,6 +111,7 @@
static int snd_sfxmode;
static int snd_musicmode;
+static int snd_oplmode;
static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
{
@@ -134,43 +143,80 @@
snd_musicdevice = SNDDEVICE_GUS;
break;
}
+
+ switch (snd_oplmode)
+ {
+ default:
+ case OPLMODE_OPL2:
+ snd_dmxoption = "";
+ break;
+
+ case OPLMODE_OPL3:
+ snd_dmxoption = "-opl3";
+ break;
+ }
}
+static txt_dropdown_list_t *OPLTypeSelector(void)
+{
+ txt_dropdown_list_t *result;
+
+ if (snd_dmxoption != NULL && strstr(snd_dmxoption, "-opl3") != NULL)
+ {
+ snd_oplmode = OPLMODE_OPL3;
+ }
+ else
+ {
+ snd_oplmode = OPLMODE_OPL2;
+ }
+
+ result = TXT_NewDropdownList(&snd_oplmode, opltype_strings, 2);
+
+ TXT_SignalConnect(result, "changed", UpdateSndDevices, NULL);
+
+ return result;
+}
+
static void UpdateExtraTable(TXT_UNCAST_ARG(widget),
TXT_UNCAST_ARG(extra_table))
{
TXT_CAST_ARG(txt_table_t, extra_table);
+ TXT_ClearTable(extra_table);
+
switch (snd_musicmode)
{
- case MUSICMODE_OPL:
- TXT_InitTable(extra_table, 2);
- TXT_SetColumnWidths(extra_table, 19, 4);
- TXT_AddWidgets(extra_table,
- TXT_NewLabel("OPL type"),
- TXT_NewDropdownList(&opl_type, opltype_strings, 2),
- NULL);
- break;
+ case MUSICMODE_OPL:
+ TXT_AddWidgets(extra_table,
+ TXT_NewLabel("OPL type"),
+ OPLTypeSelector(),
+ NULL);
+ break;
- case MUSICMODE_GUS:
- TXT_InitTable(extra_table, 1);
- TXT_AddWidgets(extra_table,
- TXT_NewLabel("GUS patch path:"),
- TXT_NewFileSelector(&gus_patch_path, 30,
- "Select path to GUS patches",
- TXT_DIRECTORY),
- NULL);
- break;
+ case MUSICMODE_GUS:
+ TXT_AddWidgets(extra_table,
+ TXT_NewLabel("GUS patch path:"),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewFileSelector(&gus_patch_path, 34,
+ "Select path to GUS patches",
+ TXT_DIRECTORY),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ NULL);
+ break;
- case MUSICMODE_NATIVE:
- TXT_InitTable(extra_table, 1);
- TXT_AddWidgets(extra_table,
- TXT_NewLabel("Timidity configuration file:"),
- TXT_NewFileSelector(&timidity_cfg_path, 30,
- "Select Timidity config file",
- cfg_extension),
- NULL);
- break;
+ case MUSICMODE_NATIVE:
+ TXT_AddWidgets(extra_table,
+ TXT_NewLabel("Timidity configuration file:"),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewFileSelector(&timidity_cfg_path, 34,
+ "Select Timidity config file",
+ cfg_extension),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ NULL);
+ break;
+
+ default:
+ break;
}
}
@@ -177,8 +223,6 @@
void ConfigSound(void)
{
txt_window_t *window;
- txt_table_t *sfx_table;
- txt_table_t *music_table;
txt_table_t *extra_table;
txt_dropdown_list_t *sfx_mode_control;
txt_dropdown_list_t *music_mode_control;
@@ -233,20 +277,15 @@
// Build the window
window = TXT_NewWindow("Sound configuration");
-
TXT_SetWindowHelpURL(window, WINDOW_HELP_URL);
+ TXT_SetTableColumns(window, 2);
+ TXT_SetColumnWidths(window, 19, 15);
TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP,
TXT_SCREEN_W / 2, 5);
TXT_AddWidgets(window,
- TXT_NewSeparator("Sound effects"),
- sfx_table = TXT_NewTable(2),
- NULL);
-
- TXT_SetColumnWidths(sfx_table, 19, 15);
-
- TXT_AddWidgets(sfx_table,
+ TXT_NewSeparator("Sound effects"),
TXT_NewLabel("Sound effects"),
sfx_mode_control = TXT_NewDropdownList(&snd_sfxmode,
sfxmode_strings,
@@ -257,25 +296,28 @@
TXT_NewSpinControl(&sfxVolume, 0, 15),
NULL);
+ // Only show for games that implemented pitch shifting:
+ if (gamemission == doom || gamemission == heretic || gamemission == hexen)
+ {
+ TXT_AddWidgets(window,
+ TXT_NewCheckBox("Pitch-shifted sounds",
+ &snd_pitchshift),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ NULL);
+ }
+
if (gamemission == strife)
{
- TXT_AddWidgets(sfx_table,
+ TXT_AddWidgets(window,
TXT_NewLabel("Voice volume"),
TXT_NewSpinControl(&voiceVolume, 0, 15),
+ TXT_NewCheckBox("Show text with voices", &show_talk),
+ TXT_TABLE_OVERFLOW_RIGHT,
NULL);
- TXT_AddWidget(window,
- TXT_NewCheckBox("Show text with voices", &show_talk));
}
TXT_AddWidgets(window,
- TXT_NewSeparator("Music"),
- music_table = TXT_NewTable(2),
- extra_table = TXT_NewTable(1),
- NULL);
-
- TXT_SetColumnWidths(music_table, 19, 15);
-
- TXT_AddWidgets(music_table,
+ TXT_NewSeparator("Music"),
TXT_NewLabel("Music"),
music_mode_control = TXT_NewDropdownList(&snd_musicmode,
musicmode_strings,
@@ -282,8 +324,11 @@
NUM_MUSICMODES),
TXT_NewLabel("Music volume"),
TXT_NewSpinControl(&musicVolume, 0, 15),
+ extra_table = TXT_NewTable(2),
+ TXT_TABLE_OVERFLOW_RIGHT,
NULL);
+ TXT_SetColumnWidths(extra_table, 19, 15);
TXT_SignalConnect(sfx_mode_control, "changed", UpdateSndDevices, NULL);
TXT_SignalConnect(music_mode_control, "changed", UpdateSndDevices, NULL);
@@ -316,11 +361,13 @@
M_BindIntVariable("snd_mport", &snd_mport);
M_BindIntVariable("snd_maxslicetime_ms", &snd_maxslicetime_ms);
M_BindStringVariable("snd_musiccmd", &snd_musiccmd);
+ M_BindStringVariable("snd_dmxoption", &snd_dmxoption);
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
- M_BindIntVariable("opl_type", &opl_type);
+ M_BindIntVariable("snd_pitchshift", &snd_pitchshift);
+
if (gamemission == strife)
{
M_BindIntVariable("voice_volume", &voiceVolume);
@@ -329,6 +376,10 @@
timidity_cfg_path = M_StringDuplicate("");
gus_patch_path = M_StringDuplicate("");
+
+ // All versions of Heretic and Hexen did pitch-shifting.
+ // Most versions of Doom did not and Strife never did.
+ snd_pitchshift = gamemission == heretic || gamemission == hexen;
// Default sound volumes - different games use different values.
--- a/src/setup/sound.h
+++ b/src/setup/sound.h
@@ -20,4 +20,6 @@
void ConfigSound(void);
void BindSoundVariables(void);
+extern char *snd_dmxoption;
+
#endif /* #ifndef SETUP_SOUND_H */
--- a/src/setup/txt_joyaxis.c
+++ b/src/setup/txt_joyaxis.c
@@ -367,7 +367,7 @@
joystick_axis->joystick = SDL_JoystickOpen(joystick_index);
if (joystick_axis->joystick == NULL)
{
- // TODO: OpenErrorWindow();
+ TXT_MessageBox(NULL, "Please configure a controller first!");
return;
}
@@ -461,7 +461,7 @@
TXT_DrawString(buf);
- for (i=strlen(buf); i<JOYSTICK_AXIS_WIDTH; ++i)
+ for (i = strlen(buf); i < joystick_axis->widget.w; ++i)
{
TXT_DrawString(" ");
}
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -189,7 +189,7 @@
static void OpenErrorWindow(void)
{
- TXT_MessageBox(NULL, "Please configure a joystick first!");
+ TXT_MessageBox(NULL, "Please configure a controller first!");
}
static void OpenPromptWindow(txt_joystick_input_t *joystick_input)
@@ -218,7 +218,7 @@
// Open the prompt window
- window = TXT_MessageBox(NULL, "Press the new joystick button...");
+ window = TXT_MessageBox(NULL, "Press the new button on the controller...");
TXT_SDL_SetEventCallback(EventCallback, joystick_input);
TXT_SignalConnect(window, "closed", PromptWindowClosed, joystick);
@@ -296,7 +296,8 @@
return 0;
}
-static void TXT_JoystickInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
+static void TXT_JoystickInputMousePress(TXT_UNCAST_ARG(widget),
+ int x, int y, int b)
{
TXT_CAST_ARG(txt_joystick_input_t, widget);
--- /dev/null
+++ b/src/strife.appdata.xml.in
@@ -1,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<component type="desktop">
+ <id>@PROGRAM_PREFIX@strife.desktop</id>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <developer_name>@PACKAGE_MAINTAINER@</developer_name>
+ <url type="homepage">@PACKAGE_URL@</url>
+ <url type="bugtracker">@PACKAGE_ISSUES@</url>
+ <description>
+ <p>
+ @PACKAGE_SHORTNAME@ Strife is a conservative,
+ historically-accurate recreation of the Strife engine. It is
+ completely compatible with the original game and mods created
+ with the original engine in mind. Made with a great reverse
+ engineering effort, it has the goal of preserving the original
+ look, feel, limitations, and bugs of the original DOS
+ executable.
+ </p>
+ <p>
+ Full support for single- and multi-player games is provided.
+ Unlike the original executable, network play is implemented on
+ the IP network stack, allowing it to function on modern LANs and
+ the Internet.
+ </p>
+ </description>
+ <screenshots>
+ <screenshot type="default">
+ <image>http://www.chocolate-doom.org/wiki/images/b/b2/GNOME_Strife_Rowan.png</image>
+ <caption>Talking to Rowan</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/1/1f/GNOME_Strife_Town.png</image>
+ <caption>The Town</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/8/8a/GNOME_Strife_Opening.png</image>
+ <caption>Opening Cinematic</caption>
+ </screenshot>
+ <screenshot>
+ <image>http://www.chocolate-doom.org/wiki/images/c/c4/GNOME_Strife_Sewage.png</image>
+ <caption>In the sewage</caption>
+ </screenshot>
+ </screenshots>
+</component>
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -1629,7 +1629,7 @@
if (D_AddFile (file))
{
- M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name,
+ M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
sizeof(demolumpname));
}
else
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -124,7 +124,6 @@
int consoleplayer; // player taking events and displaying
int displayplayer; // view being displayed
-int gametic;
int levelstarttic; // gametic at level start
int totalkills, /*totalitems,*/ totalsecret; // for intermission
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@ -938,7 +938,7 @@
if (flatpresent[i])
{
lump = firstflat + i;
- flatmemory += lumpinfo[lump].size;
+ flatmemory += lumpinfo[lump]->size;
W_CacheLumpNum(lump, PU_CACHE);
}
}
@@ -975,7 +975,7 @@
for (j=0 ; j<texture->patchcount ; j++)
{
lump = texture->patches[j].patch;
- texturememory += lumpinfo[lump].size;
+ texturememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump , PU_CACHE);
}
}
@@ -1004,7 +1004,7 @@
for (k=0 ; k<8 ; k++)
{
lump = firstspritelump + sf->lump[k];
- spritememory += lumpinfo[lump].size;
+ spritememory += lumpinfo[lump]->size;
W_CacheLumpNum(lump , PU_CACHE);
}
}
--- a/src/strife/r_things.c
+++ b/src/strife/r_things.c
@@ -211,22 +211,22 @@
// filling in the frames for whatever is found
for (l=start+1 ; l<end ; l++)
{
- if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+ if (!strncasecmp(lumpinfo[l]->name, spritename, 4))
{
- frame = lumpinfo[l].name[4] - 'A';
- rotation = lumpinfo[l].name[5] - '0';
+ frame = lumpinfo[l]->name[4] - 'A';
+ rotation = lumpinfo[l]->name[5] - '0';
if (modifiedgame)
- patched = W_GetNumForName (lumpinfo[l].name);
+ patched = W_GetNumForName (lumpinfo[l]->name);
else
patched = l;
R_InstallSpriteLump (patched, frame, rotation, false);
- if (lumpinfo[l].name[6])
+ if (lumpinfo[l]->name[6])
{
- frame = lumpinfo[l].name[6] - 'A';
- rotation = lumpinfo[l].name[7] - '0';
+ frame = lumpinfo[l]->name[6] - 'A';
+ rotation = lumpinfo[l]->name[7] - '0';
R_InstallSpriteLump (l, frame, rotation, true);
}
}
--- a/src/strife/s_sound.c
+++ b/src/strife/s_sound.c
@@ -60,7 +60,6 @@
#define S_STEREO_SWING (96 * FRACUNIT)
-#define NORM_PITCH 128
#define NORM_PRIORITY 64
#define NORM_SEP 128
@@ -74,6 +73,8 @@
// handle of the sound being played
int handle;
+
+ int pitch;
} channel_t;
@@ -137,7 +138,7 @@
{
int i;
- I_SetOPLDriverVer(opl_v_new);
+ I_SetOPLDriverVer(opl_doom_1_9);
I_PrecacheSounds(S_sfx, NUMSFX);
S_SetSfxVolume(sfxVolume);
@@ -408,6 +409,7 @@
mobj_t *origin;
int rc;
int sep;
+ int pitch;
int cnum;
int volume;
@@ -454,7 +456,7 @@
if (origin->x == players[consoleplayer].mo->x
&& origin->y == players[consoleplayer].mo->y)
- {
+ {
sep = NORM_SEP;
}
@@ -462,11 +464,12 @@
{
return;
}
- }
+ }
else
{
sep = NORM_SEP;
}
+ pitch = NORM_PITCH;
// kill old sound [STRIFE] - nope!
//S_StopSound(origin);
@@ -490,7 +493,7 @@
sfx->lumpnum = I_GetSfxLumpNum(sfx);
}
- channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep);
+ channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep, pitch);
}
@@ -615,9 +618,9 @@
// get a channel for the voice
i_voicehandle = S_GetChannel(NULL, &voice->sfx, true);
-
+
channels[i_voicehandle].handle
- = I_StartSound(&voice->sfx, i_voicehandle, snd_VoiceVolume, NORM_SEP);
+ = I_StartSound(&voice->sfx, i_voicehandle, snd_VoiceVolume, NORM_SEP, NORM_PITCH);
}
}
@@ -693,7 +696,7 @@
c->origin,
&volume,
&sep);
-
+
if (!audible)
{
S_StopChannel(cnum);
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -719,7 +719,7 @@
}
void WritePNGfile(char *filename, byte *data,
- int width, int height,
+ int inwidth, int inheight,
byte *palette)
{
png_structp ppng;
@@ -726,8 +726,14 @@
png_infop pinfo;
png_colorp pcolor;
FILE *handle;
- int i;
+ int i, j;
+ int width, height;
+ byte *rowbuf;
+ // scale up to accommodate aspect ratio correction
+ width = inwidth * 5;
+ height = inheight * 6;
+
handle = fopen(filename, "wb");
if (!handle)
{
@@ -773,9 +779,26 @@
png_write_info(ppng, pinfo);
- for (i = 0; i < SCREENHEIGHT; i++)
+ rowbuf = malloc(width);
+
+ if (rowbuf)
{
- png_write_row(ppng, data + i*SCREENWIDTH);
+ for (i = 0; i < SCREENHEIGHT; i++)
+ {
+ // expand the row 5x
+ for (j = 0; j < SCREENWIDTH; j++)
+ {
+ memset(rowbuf + j * 5, *(data + i*SCREENWIDTH + j), 5);
+ }
+
+ // write the row 6 times
+ for (j = 0; j < 6; j++)
+ {
+ png_write_row(ppng, rowbuf);
+ }
+ }
+
+ free(rowbuf);
}
png_write_end(ppng, pinfo);
--- a/src/w_checksum.c
+++ b/src/w_checksum.c
@@ -33,7 +33,7 @@
int i;
int result;
- for (i=0; i<num_open_wadfiles; ++i)
+ for (i = 0; i < num_open_wadfiles; ++i)
{
if (open_wadfiles[i] == handle)
{
@@ -77,11 +77,11 @@
// Go through each entry in the WAD directory, adding information
// about each entry to the SHA1 hash.
- for (i=0; i<numlumps; ++i)
+ for (i = 0; i < numlumps; ++i)
{
- ChecksumAddLump(&sha1_context, &lumpinfo[i]);
+ ChecksumAddLump(&sha1_context, lumpinfo[i]);
}
-
+
SHA1_Final(digest, &sha1_context);
}
--- a/src/w_main.c
+++ b/src/w_main.c
@@ -16,8 +16,10 @@
// Common code to parse command line, identifying WAD files to load.
//
+#include "config.h"
#include "doomfeatures.h"
#include "d_iwad.h"
+#include "i_system.h"
#include "m_argv.h"
#include "w_main.h"
#include "w_merge.h"
@@ -26,7 +28,6 @@
// Parse the command line, merging WAD files that are sppecified.
// Returns true if at least one file was added.
-
boolean W_ParseCommandLine(void)
{
boolean modifiedgame = false;
@@ -194,5 +195,46 @@
// W_PrintDirectory();
return modifiedgame;
+}
+
+// Lump names that are unique to particular game types. This lets us check
+// the user is not trying to play with the wrong executable, eg.
+// chocolate-doom -iwad hexen.wad.
+static const struct
+{
+ GameMission_t mission;
+ char *lumpname;
+} unique_lumps[] = {
+ { doom, "POSSA1" },
+ { heretic, "IMPXA1" },
+ { hexen, "ETTNA1" },
+ { strife, "AGRDA1" },
+};
+
+void W_CheckCorrectIWAD(GameMission_t mission)
+{
+ int i;
+ lumpindex_t lumpnum;
+
+ for (i = 0; i < arrlen(unique_lumps); ++i)
+ {
+ if (mission != unique_lumps[i].mission)
+ {
+ lumpnum = W_CheckNumForName(unique_lumps[i].lumpname);
+
+ if (lumpnum >= 0)
+ {
+ I_Error("\nYou are trying to use a %s IWAD file with "
+ "the %s%s binary.\nThis isn't going to work.\n"
+ "You probably want to use the %s%s binary.",
+ D_SuggestGameName(unique_lumps[i].mission,
+ indetermined),
+ PROGRAM_PREFIX,
+ D_GameMissionString(mission),
+ PROGRAM_PREFIX,
+ D_GameMissionString(unique_lumps[i].mission));
+ }
+ }
+ }
}
--- a/src/w_main.h
+++ b/src/w_main.h
@@ -18,7 +18,10 @@
#ifndef W_MAIN_H
#define W_MAIN_H
+#include "d_mode.h"
+
boolean W_ParseCommandLine(void);
+void W_CheckCorrectIWAD(GameMission_t mission);
#endif /* #ifndef W_MAIN_H */
--- a/src/w_merge.c
+++ b/src/w_merge.c
@@ -39,7 +39,7 @@
typedef struct
{
- lumpinfo_t *lumps;
+ lumpinfo_t **lumps;
int numlumps;
} searchlist_t;
@@ -74,7 +74,7 @@
for (i=0; i<list->numlumps; ++i)
{
- if (!strncasecmp(list->lumps[i].name, name, 8))
+ if (!strncasecmp(list->lumps[i]->name, name, 8))
return i;
}
@@ -352,7 +352,7 @@
for (i=0; i<iwad_sprites.numlumps; ++i)
{
- AddSpriteLump(&iwad_sprites.lumps[i]);
+ AddSpriteLump(iwad_sprites.lumps[i]);
}
// Add all sprites from the PWAD
@@ -360,7 +360,7 @@
for (i=0; i<pwad_sprites.numlumps; ++i)
{
- AddSpriteLump(&pwad_sprites.lumps[i]);
+ AddSpriteLump(pwad_sprites.lumps[i]);
}
}
@@ -386,13 +386,13 @@
static void DoMerge(void)
{
section_t current_section;
- lumpinfo_t *newlumps;
+ lumpinfo_t **newlumps;
int num_newlumps;
int lumpindex;
int i, n;
-
+
// Can't ever have more lumps than we already have
- newlumps = malloc(sizeof(lumpinfo_t) * numlumps);
+ newlumps = calloc(numlumps, sizeof(lumpinfo_t *));
num_newlumps = 0;
// Add IWAD lumps
@@ -400,7 +400,7 @@
for (i=0; i<iwad.numlumps; ++i)
{
- lumpinfo_t *lump = &iwad.lumps[i];
+ lumpinfo_t *lump = iwad.lumps[i];
switch (current_section)
{
@@ -414,7 +414,7 @@
current_section = SECTION_SPRITES;
}
- newlumps[num_newlumps++] = *lump;
+ newlumps[num_newlumps++] = lump;
break;
@@ -432,7 +432,7 @@
newlumps[num_newlumps++] = pwad_flats.lumps[n];
}
- newlumps[num_newlumps++] = *lump;
+ newlumps[num_newlumps++] = lump;
// back to normal reading
current_section = SECTION_NORMAL;
@@ -448,7 +448,7 @@
if (lumpindex < 0)
{
- newlumps[num_newlumps++] = *lump;
+ newlumps[num_newlumps++] = lump;
}
}
@@ -460,11 +460,11 @@
if (!strncasecmp(lump->name, "S_END", 8))
{
- // add all the pwad sprites
+ // add all the PWAD sprites
for (n=0; n<pwad_sprites.numlumps; ++n)
{
- if (SpriteLumpNeeded(&pwad_sprites.lumps[n]))
+ if (SpriteLumpNeeded(pwad_sprites.lumps[n]))
{
newlumps[num_newlumps++] = pwad_sprites.lumps[n];
}
@@ -471,7 +471,7 @@
}
// copy the ending
- newlumps[num_newlumps++] = *lump;
+ newlumps[num_newlumps++] = lump;
// back to normal reading
current_section = SECTION_NORMAL;
@@ -483,7 +483,7 @@
if (SpriteLumpNeeded(lump))
{
- newlumps[num_newlumps++] = *lump;
+ newlumps[num_newlumps++] = lump;
}
}
@@ -496,7 +496,7 @@
for (i=0; i<pwad.numlumps; ++i)
{
- lumpinfo_t *lump = &pwad.lumps[i];
+ lumpinfo_t *lump = pwad.lumps[i];
switch (current_section)
{
@@ -515,7 +515,7 @@
{
// Don't include the headers of sections
- newlumps[num_newlumps++] = *lump;
+ newlumps[num_newlumps++] = lump;
}
break;
@@ -550,7 +550,6 @@
free(lumpinfo);
lumpinfo = newlumps;
numlumps = num_newlumps;
-
}
void W_PrintDirectory(void)
@@ -560,8 +559,8 @@
// debug
for (i=0; i<numlumps; ++i)
{
- for (n=0; n<8 && lumpinfo[i].name[n] != '\0'; ++n)
- putchar(lumpinfo[i].name[n]);
+ for (n=0; n<8 && lumpinfo[i]->name[n] != '\0'; ++n)
+ putchar(lumpinfo[i]->name[n]);
putchar('\n');
}
}
@@ -579,7 +578,7 @@
if (W_AddFile(filename) == NULL)
return;
- // iwad is at the start, pwad was appended to the end
+ // IWAD is at the start, PWAD was appended to the end
iwad.lumps = lumpinfo;
iwad.numlumps = old_numlumps;
@@ -606,25 +605,23 @@
{
int i;
- // Go through the IWAD list given, replacing lumps with lumps of
+ // Go through the IWAD list given, replacing lumps with lumps of
// the same name from the PWAD
-
for (i=0; i<list->numlumps; ++i)
{
int index;
- index = FindInList(&pwad, list->lumps[i].name);
+ index = FindInList(&pwad, list->lumps[i]->name);
if (index > 0)
{
- memcpy(&list->lumps[i], &pwad.lumps[index],
+ memcpy(list->lumps[i], pwad.lumps[index],
sizeof(lumpinfo_t));
}
}
-
}
-// Merge sprites and flats in the way NWT does with its -af and -as
+// Merge sprites and flats in the way NWT does with its -af and -as
// command-line options.
void W_NWTMergeFile(char *filename, int flags)
@@ -638,7 +635,7 @@
if (W_AddFile(filename) == NULL)
return;
- // iwad is at the start, pwad was appended to the end
+ // IWAD is at the start, PWAD was appended to the end
iwad.lumps = lumpinfo;
iwad.numlumps = old_numlumps;
@@ -645,13 +642,13 @@
pwad.lumps = lumpinfo + old_numlumps;
pwad.numlumps = numlumps - old_numlumps;
-
+
// Setup sprite/flat lists
SetupLists();
// Merge in flats?
-
+
if (flags & W_NWT_MERGE_FLATS)
{
W_NWTAddLumps(&iwad_flats);
@@ -690,7 +687,7 @@
return;
}
- // iwad is at the start, pwad was appended to the end
+ // IWAD is at the start, PWAD was appended to the end
iwad.lumps = lumpinfo;
iwad.numlumps = old_numlumps;
@@ -697,7 +694,7 @@
pwad.lumps = lumpinfo + old_numlumps;
pwad.numlumps = numlumps - old_numlumps;
-
+
// Setup sprite/flat lists
SetupLists();
@@ -706,12 +703,12 @@
for (i=0; i<iwad_sprites.numlumps; ++i)
{
- if (FindInList(&pwad, iwad_sprites.lumps[i].name) >= 0)
+ if (FindInList(&pwad, iwad_sprites.lumps[i]->name) >= 0)
{
// Replace this entry with an empty string. This is what
// nwt -merge does.
- M_StringCopy(iwad_sprites.lumps[i].name, "", 8);
+ M_StringCopy(iwad_sprites.lumps[i]->name, "", 8);
}
}
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -26,8 +26,6 @@
#include "doomtype.h"
-#include "config.h"
-#include "d_iwad.h"
#include "i_swap.h"
#include "i_system.h"
#include "i_video.h"
@@ -57,16 +55,17 @@
//
// Location of each lump on disk.
-lumpinfo_t *lumpinfo;
+lumpinfo_t **lumpinfo;
unsigned int numlumps = 0;
// Hash table for fast lookups
-static lumpinfo_t **lumphash;
+static lumpindex_t *lumphash;
// Variables for the reload hack: filename of the PWAD to reload, and the
// lumps from WADs before the reload file, so we can resent numlumps and
// load the file again.
static wad_file_t *reloadhandle = NULL;
+static lumpinfo_t *reloadlumps = NULL;
static char *reloadname = NULL;
static int reloadlump = -1;
@@ -87,46 +86,6 @@
return result;
}
-// Increase the size of the lumpinfo[] array to the specified size.
-static void ExtendLumpInfo(int newnumlumps)
-{
- lumpinfo_t *newlumpinfo;
- unsigned int i;
-
- newlumpinfo = calloc(newnumlumps, sizeof(lumpinfo_t));
-
- if (newlumpinfo == NULL)
- {
- I_Error ("Couldn't realloc lumpinfo");
- }
-
- // Copy over lumpinfo_t structures from the old array. If any of
- // these lumps have been cached, we need to update the user
- // pointers to the new location.
- for (i = 0; i < numlumps && i < newnumlumps; ++i)
- {
- memcpy(&newlumpinfo[i], &lumpinfo[i], sizeof(lumpinfo_t));
-
- if (newlumpinfo[i].cache != NULL)
- {
- Z_ChangeUser(newlumpinfo[i].cache, &newlumpinfo[i].cache);
- }
-
- // We shouldn't be generating a hash table until after all WADs have
- // been loaded, but just in case...
- if (lumpinfo[i].next != NULL)
- {
- int nextlumpnum = lumpinfo[i].next - lumpinfo;
- newlumpinfo[i].next = &newlumpinfo[nextlumpnum];
- }
- }
-
- // All done.
- free(lumpinfo);
- lumpinfo = newlumpinfo;
- numlumps = newnumlumps;
-}
-
//
// LUMP BASED ROUTINES.
//
@@ -143,14 +102,14 @@
wad_file_t *W_AddFile (char *filename)
{
wadinfo_t header;
- lumpinfo_t *lump_p;
- unsigned int i;
+ lumpindex_t i;
wad_file_t *wad_file;
int length;
int startlump;
filelump_t *fileinfo;
filelump_t *filerover;
- int newnumlumps;
+ lumpinfo_t *filelumps;
+ int numfilelumps;
// If the filename begins with a ~, it indicates that we should use the
// reload hack.
@@ -180,15 +139,6 @@
return NULL;
}
- // If this is the reload file, we need to save the file handle so that we
- // can close it later on when we do a reload.
- if (reloadname)
- {
- reloadhandle = wad_file;
- }
-
- newnumlumps = numlumps;
-
if (strcasecmp(filename+strlen(filename)-3 , "wad" ) )
{
// single lump file
@@ -206,9 +156,9 @@
// extension).
M_ExtractFileBase (filename, fileinfo->name);
- newnumlumps++;
+ numfilelumps = 1;
}
- else
+ else
{
// WAD file
W_Read(wad_file, 0, &header, sizeof(header));
@@ -221,8 +171,8 @@
I_Error ("Wad file %s doesn't have IWAD "
"or PWAD id\n", filename);
}
-
- // ???modifiedgame = true;
+
+ // ???modifiedgame = true;
}
header.numlumps = LONG(header.numlumps);
@@ -231,26 +181,36 @@
fileinfo = Z_Malloc(length, PU_STATIC, 0);
W_Read(wad_file, header.infotableofs, fileinfo, length);
- newnumlumps += header.numlumps;
+ numfilelumps = header.numlumps;
}
// Increase size of numlumps array to accomodate the new file.
+ filelumps = calloc(numfilelumps, sizeof(lumpinfo_t));
+ if (filelumps == NULL)
+ {
+ I_Error("Failed to allocate array for lumps from new file.");
+ }
+
startlump = numlumps;
- ExtendLumpInfo(newnumlumps);
+ numlumps += numfilelumps;
+ lumpinfo = realloc(lumpinfo, numlumps * sizeof(lumpinfo_t *));
+ if (lumpinfo == NULL)
+ {
+ I_Error("Failed to increase lumpinfo[] array size.");
+ }
- lump_p = &lumpinfo[startlump];
-
filerover = fileinfo;
- for (i=startlump; i<numlumps; ++i)
+ for (i = startlump; i < numlumps; ++i)
{
- lump_p->wad_file = wad_file;
- lump_p->position = LONG(filerover->filepos);
- lump_p->size = LONG(filerover->size);
+ lumpinfo_t *lump_p = &filelumps[i - startlump];
+ lump_p->wad_file = wad_file;
+ lump_p->position = LONG(filerover->filepos);
+ lump_p->size = LONG(filerover->size);
lump_p->cache = NULL;
- strncpy(lump_p->name, filerover->name, 8);
+ strncpy(lump_p->name, filerover->name, 8);
+ lumpinfo[i] = lump_p;
- ++lump_p;
++filerover;
}
@@ -262,6 +222,14 @@
lumphash = NULL;
}
+ // If this is the reload file, we need to save some details about the
+ // file so that we can close it later on when we do a reload.
+ if (reloadname)
+ {
+ reloadhandle = wad_file;
+ reloadlumps = filelumps;
+ }
+
return wad_file;
}
@@ -282,10 +250,9 @@
// Returns -1 if name not found.
//
-int W_CheckNumForName (char* name)
+lumpindex_t W_CheckNumForName(char* name)
{
- lumpinfo_t *lump_p;
- int i;
+ lumpindex_t i;
// Do we have a hash table yet?
@@ -292,28 +259,28 @@
if (lumphash != NULL)
{
int hash;
-
+
// We do! Excellent.
hash = W_LumpNameHash(name) % numlumps;
-
- for (lump_p = lumphash[hash]; lump_p != NULL; lump_p = lump_p->next)
+
+ for (i = lumphash[hash]; i != -1; i = lumpinfo[i]->next)
{
- if (!strncasecmp(lump_p->name, name, 8))
+ if (!strncasecmp(lumpinfo[i]->name, name, 8))
{
- return lump_p - lumpinfo;
+ return i;
}
}
- }
+ }
else
{
// We don't have a hash table generate yet. Linear search :-(
- //
+ //
// scan backwards so patch lump files take precedence
- for (i=numlumps-1; i >= 0; --i)
+ for (i = numlumps - 1; i >= 0; --i)
{
- if (!strncasecmp(lumpinfo[i].name, name, 8))
+ if (!strncasecmp(lumpinfo[i]->name, name, 8))
{
return i;
}
@@ -332,12 +299,12 @@
// W_GetNumForName
// Calls W_CheckNumForName, but bombs out if not found.
//
-int W_GetNumForName (char* name)
+lumpindex_t W_GetNumForName(char* name)
{
- int i;
+ lumpindex_t i;
i = W_CheckNumForName (name);
-
+
if (i < 0)
{
I_Error ("W_GetNumForName: %s not found!", name);
@@ -351,7 +318,7 @@
// W_LumpLength
// Returns the buffer size needed to load the given lump.
//
-int W_LumpLength (unsigned int lump)
+int W_LumpLength(lumpindex_t lump)
{
if (lump >= numlumps)
{
@@ -358,7 +325,7 @@
I_Error ("W_LumpLength: %i >= numlumps", lump);
}
- return lumpinfo[lump].size;
+ return lumpinfo[lump]->size;
}
@@ -368,29 +335,29 @@
// Loads the lump into the given buffer,
// which must be >= W_LumpLength().
//
-void W_ReadLump(unsigned int lump, void *dest)
+void W_ReadLump(lumpindex_t lump, void *dest)
{
int c;
lumpinfo_t *l;
-
+
if (lump >= numlumps)
{
- I_Error ("W_ReadLump: %i >= numlumps", lump);
+ I_Error ("W_ReadLump: %i >= numlumps", lump);
}
- l = lumpinfo+lump;
-
- I_BeginRead ();
-
+ l = lumpinfo[lump];
+
+ I_BeginRead();
+
c = W_Read(l->wad_file, l->position, dest, l->size);
if (c < l->size)
{
- I_Error ("W_ReadLump: only read %i of %i on lump %i",
- c, l->size, lump);
+ I_Error("W_ReadLump: only read %i of %i on lump %i",
+ c, l->size, lump);
}
- I_EndRead ();
+ I_EndRead();
}
@@ -408,7 +375,7 @@
// when no longer needed (do not use Z_ChangeTag).
//
-void *W_CacheLumpNum(int lumpnum, int tag)
+void *W_CacheLumpNum(lumpindex_t lumpnum, int tag)
{
byte *result;
lumpinfo_t *lump;
@@ -418,7 +385,7 @@
I_Error ("W_CacheLumpNum: %i >= numlumps", lumpnum);
}
- lump = &lumpinfo[lumpnum];
+ lump = lumpinfo[lumpnum];
// Get the pointer to return. If the lump is in a memory-mapped
// file, we can just return a pointer to within the memory-mapped
@@ -470,7 +437,7 @@
// complicated ...
//
-void W_ReleaseLumpNum(int lumpnum)
+void W_ReleaseLumpNum(lumpindex_t lumpnum)
{
lumpinfo_t *lump;
@@ -479,7 +446,7 @@
I_Error ("W_ReleaseLumpNum: %i >= numlumps", lumpnum);
}
- lump = &lumpinfo[lumpnum];
+ lump = lumpinfo[lumpnum];
if (lump->wad_file->mapped != NULL)
{
@@ -566,7 +533,7 @@
void W_GenerateHashTable(void)
{
- unsigned int i;
+ lumpindex_t i;
// Free the old hash table, if there is one:
if (lumphash != NULL)
@@ -577,19 +544,23 @@
// Generate hash table
if (numlumps > 0)
{
- lumphash = Z_Malloc(sizeof(lumpinfo_t *) * numlumps, PU_STATIC, NULL);
- memset(lumphash, 0, sizeof(lumpinfo_t *) * numlumps);
+ lumphash = Z_Malloc(sizeof(lumpindex_t) * numlumps, PU_STATIC, NULL);
- for (i=0; i<numlumps; ++i)
+ for (i = 0; i < numlumps; ++i)
{
+ lumphash[i] = -1;
+ }
+
+ for (i = 0; i < numlumps; ++i)
+ {
unsigned int hash;
- hash = W_LumpNameHash(lumpinfo[i].name) % numlumps;
+ hash = W_LumpNameHash(lumpinfo[i]->name) % numlumps;
// Hook into the hash table
- lumpinfo[i].next = lumphash[hash];
- lumphash[hash] = &lumpinfo[i];
+ lumpinfo[i]->next = lumphash[hash];
+ lumphash[hash] = i;
}
}
@@ -605,7 +576,7 @@
void W_Reload(void)
{
char *filename;
- int i;
+ lumpindex_t i;
if (reloadname == NULL)
{
@@ -612,12 +583,12 @@
return;
}
- // We must release any lumps being held in the PWAD we're about to reload:
+ // We must free any lumps being cached from the PWAD we're about to reload:
for (i = reloadlump; i < numlumps; ++i)
{
- if (lumpinfo[i].cache != NULL)
+ if (lumpinfo[i]->cache != NULL)
{
- W_ReleaseLumpNum(i);
+ Z_Free(lumpinfo[i]->cache);
}
}
@@ -628,6 +599,8 @@
filename = reloadname;
W_CloseFile(reloadhandle);
+ free(reloadlumps);
+
reloadname = NULL;
reloadlump = -1;
reloadhandle = NULL;
@@ -637,46 +610,5 @@
// The WAD directory has changed, so we have to regenerate the
// fast lookup hashtable:
W_GenerateHashTable();
-}
-
-// Lump names that are unique to particular game types. This lets us check
-// the user is not trying to play with the wrong executable, eg.
-// chocolate-doom -iwad hexen.wad.
-static const struct
-{
- GameMission_t mission;
- char *lumpname;
-} unique_lumps[] = {
- { doom, "POSSA1" },
- { heretic, "IMPXA1" },
- { hexen, "ETTNA1" },
- { strife, "AGRDA1" },
-};
-
-void W_CheckCorrectIWAD(GameMission_t mission)
-{
- int i;
- int lumpnum;
-
- for (i = 0; i < arrlen(unique_lumps); ++i)
- {
- if (mission != unique_lumps[i].mission)
- {
- lumpnum = W_CheckNumForName(unique_lumps[i].lumpname);
-
- if (lumpnum >= 0)
- {
- I_Error("\nYou are trying to use a %s IWAD file with "
- "the %s%s binary.\nThis isn't going to work.\n"
- "You probably want to use the %s%s binary.",
- D_SuggestGameName(unique_lumps[i].mission,
- indetermined),
- PROGRAM_PREFIX,
- D_GameMissionString(mission),
- PROGRAM_PREFIX,
- D_GameMissionString(unique_lumps[i].mission));
- }
- }
- }
}
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -23,8 +23,6 @@
#include <stdio.h>
#include "doomtype.h"
-#include "d_mode.h"
-
#include "w_file.h"
@@ -37,6 +35,7 @@
//
typedef struct lumpinfo_s lumpinfo_t;
+typedef int lumpindex_t;
struct lumpinfo_s
{
@@ -47,33 +46,30 @@
void *cache;
// Used for hash table lookups
-
- lumpinfo_t *next;
+ lumpindex_t next;
};
-extern lumpinfo_t *lumpinfo;
+extern lumpinfo_t **lumpinfo;
extern unsigned int numlumps;
-wad_file_t *W_AddFile (char *filename);
-void W_Reload (void);
+wad_file_t *W_AddFile(char *filename);
+void W_Reload(void);
-int W_CheckNumForName (char* name);
-int W_GetNumForName (char* name);
+lumpindex_t W_CheckNumForName(char *name);
+lumpindex_t W_GetNumForName(char *name);
-int W_LumpLength (unsigned int lump);
-void W_ReadLump (unsigned int lump, void *dest);
+int W_LumpLength(lumpindex_t lump);
+void W_ReadLump(lumpindex_t lump, void *dest);
-void* W_CacheLumpNum (int lump, int tag);
-void* W_CacheLumpName (char* name, int tag);
+void *W_CacheLumpNum(lumpindex_t lump, int tag);
+void *W_CacheLumpName(char *name, int tag);
-void W_GenerateHashTable(void);
+void W_GenerateHashTable(void);
extern unsigned int W_LumpNameHash(const char *s);
-void W_ReleaseLumpNum(int lump);
-void W_ReleaseLumpName(char *name);
-
-void W_CheckCorrectIWAD(GameMission_t mission);
+void W_ReleaseLumpNum(lumpindex_t lump);
+void W_ReleaseLumpName(char *name);
#endif
--- a/textscreen/examples/guitest.c
+++ b/textscreen/examples/guitest.c
@@ -103,7 +103,7 @@
toplabel = TXT_NewLabel("This is a multiline label.\n"
"A single label object contains \n"
- "all three of these lines.\n");
+ "all three of these lines.");
TXT_AddWidget(window, toplabel);
TXT_SetWidgetAlign(toplabel, TXT_HORIZ_CENTER);
@@ -110,7 +110,7 @@
//TXT_AddWidget(window, TXT_NewScrollPane(15, 4, table));
TXT_AddWidget(window, table);
- for (i=0; i<5; ++i)
+ for (i=0; i<3; ++i)
{
TXT_snprintf(buf, sizeof(buf), "Option %i in a table:", i + 1);
TXT_AddWidget(table, TXT_NewLabel(buf));
@@ -119,6 +119,17 @@
TXT_snprintf(buf, sizeof(buf), " Button %i-2 ", i + 1);
TXT_AddWidget(table, TXT_NewButton(buf));
}
+
+ TXT_AddWidgets(table,
+ TXT_NewLabel("Still the same table, but:\n"
+ "This label magically overflows\n"
+ "across multiple cells! Cool, huh? "),
+ TXT_TABLE_OVERFLOW_RIGHT,
+ TXT_NewButton("Do nothing"),
+ TXT_TABLE_OVERFLOW_DOWN,
+ TXT_TABLE_OVERFLOW_DOWN,
+ TXT_NewButton("Also nothing"),
+ NULL);
TXT_AddWidget(window, TXT_NewStrut(0, 1));
value_label = TXT_NewLabel("");
--- a/textscreen/txt_checkbox.c
+++ b/textscreen/txt_checkbox.c
@@ -65,7 +65,7 @@
TXT_SetWidgetBG(checkbox);
TXT_DrawString(checkbox->label);
- for (i=strlen(checkbox->label); i < w-5; ++i)
+ for (i=strlen(checkbox->label); i < w-4; ++i)
{
TXT_DrawString(" ");
}
--- a/textscreen/txt_scrollpane.c
+++ b/textscreen/txt_scrollpane.c
@@ -402,7 +402,8 @@
if ((key == KEY_UPARROW || key == KEY_DOWNARROW
|| key == KEY_LEFTARROW || key == KEY_RIGHTARROW
- || key == KEY_PGUP || key == KEY_PGDN)
+ || key == KEY_PGUP || key == KEY_PGDN
+ || key == KEY_TAB)
&& scrollpane->child->widget_class == &txt_table_class)
{
if (PageSelectedWidget(scrollpane, key))
--- a/textscreen/txt_table.c
+++ b/textscreen/txt_table.c
@@ -26,6 +26,20 @@
#include "txt_strut.h"
#include "txt_table.h"
+txt_widget_t txt_table_overflow_right;
+txt_widget_t txt_table_overflow_down;
+txt_widget_t txt_table_eol;
+txt_widget_t txt_table_empty;
+
+// Returns true if the given widget in the table's widgets[] array refers
+// to an actual widget - not NULL, or one of the special overflow pointers.
+static int IsActualWidget(txt_widget_t *widget)
+{
+ return widget != NULL
+ && widget != &txt_table_overflow_right
+ && widget != &txt_table_overflow_down;
+}
+
// Remove all entries from a table
void TXT_ClearTable(TXT_UNCAST_ARG(table))
@@ -39,12 +53,12 @@
for (i=table->columns; i<table->num_widgets; ++i)
{
- if (table->widgets[i] != NULL)
+ if (IsActualWidget(table->widgets[i]))
{
TXT_DestroyWidget(table->widgets[i]);
}
}
-
+
// Shrink the table to just the column strut widgets
table->num_widgets = table->columns;
@@ -62,8 +76,104 @@
return (table->num_widgets + table->columns - 1) / table->columns;
}
-static void CalcRowColSizes(txt_table_t *table,
- unsigned int *row_heights,
+// Most widgets occupy just one cell of a table, but if the special
+// overflow constants are used, they can occupy multiple cells.
+// This function figures out for a widget in a given cell, which
+// cells it should actually occupy (always a rectangle).
+static void CellOverflowedSize(txt_table_t *table, int x, int y,
+ int *w, int *h)
+{
+ txt_widget_t *widget;
+ int x1, y1;
+
+ if (!IsActualWidget(table->widgets[y * table->columns + x]))
+ {
+ *w = 0; *h = 0;
+ return;
+ }
+
+ *w = table->columns - x;
+ *h = 0;
+ for (y1 = y; y1 < TableRows(table); ++y1)
+ {
+ // Every overflow cell must point to either (x, y) or another
+ // overflow cell. This means the first in every row must be
+ // txt_table_overflow_down.
+
+ if (y1 * table->columns + x >= table->num_widgets)
+ {
+ break;
+ }
+
+ widget = table->widgets[y1 * table->columns + x];
+
+ if (y1 != y && widget != &txt_table_overflow_down)
+ {
+ break;
+ }
+
+ for (x1 = x + 1; x1 < x + *w; ++x1)
+ {
+ if (y1 * table->columns + x1 >= table->num_widgets)
+ {
+ break;
+ }
+
+ // Can be either type of overflow, except on the first row.
+ // Otherwise we impose a limit on the width.
+ widget = table->widgets[y1 * table->columns + x1];
+ if (widget != &txt_table_overflow_right
+ && (widget != &txt_table_overflow_down || y1 == y))
+ {
+ *w = x1 - x;
+ break;
+ }
+ }
+
+ ++*h;
+ }
+}
+
+static int IsOverflowingCell(txt_table_t *table, int x, int y)
+{
+ int w, h;
+ CellOverflowedSize(table, x, y, &w, &h);
+ return w > 1 || h > 1;
+}
+
+// Using the given column/row size tables, calculate the size of the given
+// widget, storing the result in (w, h).
+static void CalculateWidgetDimensions(txt_table_t *table,
+ int x, int y,
+ unsigned int *column_widths,
+ unsigned int *row_heights,
+ unsigned int *w, unsigned int *h)
+{
+ txt_widget_t *widget;
+ int cell_w, cell_h;
+ int x1, y1;
+
+ widget = table->widgets[y * table->columns + x];
+
+ // Find which cells this widget occupies.
+ CellOverflowedSize(table, x, y, &cell_w, &cell_h);
+
+ // Add up column / row widths / heights to get the actual dimensions.
+ *w = 0;
+ for (x1 = x; x1 < x + cell_w; ++x1)
+ {
+ *w += column_widths[x1];
+ }
+
+ *h = 0;
+ for (y1 = y; y1 < y + cell_h; ++y1)
+ {
+ *h += row_heights[y1];
+ }
+}
+
+static void CalcRowColSizes(txt_table_t *table,
+ unsigned int *row_heights,
unsigned int *col_widths)
{
int x, y;
@@ -74,11 +184,11 @@
memset(col_widths, 0, sizeof(int) * table->columns);
- for (y=0; y<rows; ++y)
+ for (y = 0; y < rows; ++y)
{
row_heights[y] = 0;
- for (x=0; x<table->columns; ++x)
+ for (x = 0; x < table->columns; ++x)
{
if (y * table->columns + x >= table->num_widgets)
break;
@@ -85,11 +195,20 @@
widget = table->widgets[y * table->columns + x];
- // NULL represents an empty spacer
-
- if (widget != NULL)
+ if (IsActualWidget(widget))
{
TXT_CalcWidgetSize(widget);
+ }
+
+ // In the first pass we ignore overflowing cells.
+ if (IsOverflowingCell(table, x, y))
+ {
+ continue;
+ }
+
+ // NULL represents an empty spacer
+ if (IsActualWidget(widget))
+ {
if (widget->h > row_heights[y])
row_heights[y] = widget->h;
if (widget->w > col_widths[x])
@@ -97,6 +216,37 @@
}
}
}
+
+ // In the second pass, we go through again and process overflowing
+ // widgets, to ensure that they will fit.
+ for (y = 0; y < rows; ++y)
+ {
+ for (x = 0; x < table->columns; ++x)
+ {
+ unsigned int w, h;
+
+ if (y * table->columns + x >= table->num_widgets)
+ break;
+
+ widget = table->widgets[y * table->columns + x];
+ if (!IsActualWidget(widget))
+ {
+ continue;
+ }
+
+ // Expand column width and row heights as needed.
+ CalculateWidgetDimensions(table, x, y, col_widths, row_heights,
+ &w, &h);
+ if (w < widget->w)
+ {
+ col_widths[x] += widget->w - w;
+ }
+ if (h < widget->h)
+ {
+ row_heights[y] += widget->h - h;
+ }
+ }
+ }
}
static void TXT_CalcTableSize(TXT_UNCAST_ARG(table))
@@ -132,36 +282,67 @@
free(column_widths);
}
+static void FillRowToEnd(txt_table_t *table)
+{
+ while ((table->num_widgets % table->columns) != 0)
+ {
+ TXT_AddWidget(table, &txt_table_overflow_right);
+ }
+}
+
void TXT_AddWidget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget))
{
TXT_CAST_ARG(txt_table_t, table);
TXT_CAST_ARG(txt_widget_t, widget);
+ int is_separator;
+ int i;
- if (table->num_widgets > 0)
+ // Convenience alias for NULL:
+ if (widget == &txt_table_empty)
{
- txt_widget_t *last_widget;
+ widget = NULL;
+ }
+ else if (widget == &txt_table_eol)
+ {
+ FillRowToEnd(table);
+ return;
+ }
- last_widget = table->widgets[table->num_widgets - 1];
+ // We have special handling for the separator widget:
+ is_separator = IsActualWidget(widget)
+ && widget->widget_class == &txt_separator_class;
- if (widget != NULL && last_widget != NULL
- && widget->widget_class == &txt_separator_class
- && last_widget->widget_class == &txt_separator_class)
+ // If we add two separators consecutively, the new separator replaces the
+ // first. This allows us to override the "implicit" separator that is
+ // added at the top of a window when it is created.
+ if (is_separator)
+ {
+ for (i = table->num_widgets - 1; i >= 0; --i)
{
- // The previous widget added was a separator; replace
- // it with this one.
- //
- // This way, if the first widget added to a window is
- // a separator, it replaces the "default" separator that
- // the window itself adds on creation.
+ txt_widget_t *last_widget;
+ last_widget = table->widgets[i];
- table->widgets[table->num_widgets - 1] = widget;
-
- TXT_DestroyWidget(last_widget);
-
- return;
+ if (IsActualWidget(last_widget)
+ && widget->widget_class == &txt_separator_class
+ && last_widget->widget_class == &txt_separator_class)
+ {
+ table->widgets[i] = widget;
+ TXT_DestroyWidget(last_widget);
+ return;
+ }
+ else if (last_widget != &txt_table_overflow_right)
+ {
+ break;
+ }
}
}
+ // Separators begin on a new line.
+ if (is_separator)
+ {
+ FillRowToEnd(table);
+ }
+
table->widgets = realloc(table->widgets,
sizeof(txt_widget_t *) * (table->num_widgets + 1));
table->widgets[table->num_widgets] = widget;
@@ -168,11 +349,16 @@
++table->num_widgets;
// Maintain parent pointer.
-
- if (widget != NULL)
+ if (IsActualWidget(widget))
{
widget->parent = &table->widget;
}
+
+ // Separators always take up the entire line.
+ if (is_separator)
+ {
+ FillRowToEnd(table);
+ }
}
// Add multiple widgets to a table.
@@ -217,7 +403,7 @@
if (i >= 0 && i < table->num_widgets)
{
widget = table->widgets[i];
- return widget != NULL
+ return IsActualWidget(widget)
&& TXT_SelectableWidget(widget)
&& widget->visible;
}
@@ -280,7 +466,7 @@
{
cur_widget = table->widgets[i];
- if (table->widget.focused && cur_widget != NULL)
+ if (table->widget.focused && IsActualWidget(cur_widget))
{
TXT_SetWidgetFocus(cur_widget, 0);
}
@@ -313,7 +499,7 @@
if (selected >= 0 && selected < table->num_widgets)
{
- if (table->widgets[selected] != NULL
+ if (IsActualWidget(table->widgets[selected])
&& TXT_SelectableWidget(table->widgets[selected])
&& TXT_WidgetKeyPress(table->widgets[selected], key))
{
@@ -321,6 +507,29 @@
}
}
+ if (key == KEY_TAB)
+ {
+ int dir;
+ int i;
+
+ dir = TXT_GetModifierState(TXT_MOD_SHIFT) ? -1 : 1;
+
+ // Cycle through all widgets until we find one that can be selected.
+ for (i = table->selected_y * table->columns + table->selected_x + dir;
+ i >= 0 && i < table->num_widgets;
+ i += dir)
+ {
+ if (IsActualWidget(table->widgets[i])
+ && TXT_SelectableWidget(table->widgets[i]))
+ {
+ ChangeSelection(table, i % table->columns, i / table->columns);
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
if (key == KEY_DOWNARROW)
{
int new_x, new_y;
@@ -330,7 +539,7 @@
for (new_y = table->selected_y + 1; new_y < rows; ++new_y)
{
new_x = FindSelectableColumn(table, new_y, table->selected_x);
-
+
if (new_x >= 0)
{
// Found a selectable widget in this column!
@@ -339,7 +548,7 @@
return 1;
}
- }
+ }
}
if (key == KEY_UPARROW)
@@ -360,7 +569,7 @@
return 1;
}
- }
+ }
}
if (key == KEY_LEFTARROW)
@@ -428,13 +637,16 @@
}
}
-static void LayoutCell(txt_table_t *table, int x, int y, int col_width,
+static void LayoutCell(txt_table_t *table, int x, int y,
int draw_x, int draw_y)
{
txt_widget_t *widget;
+ int col_width;
widget = table->widgets[y * table->columns + x];
+ col_width = widget->w;
+
// Adjust x position based on alignment property
switch (widget->align)
@@ -445,7 +657,7 @@
case TXT_HORIZ_CENTER:
TXT_CalcWidgetSize(widget);
-
+
// Separators are always drawn left-aligned.
if (widget->widget_class != &txt_separator_class)
@@ -452,17 +664,16 @@
{
draw_x += (col_width - widget->w) / 2;
}
-
+
break;
case TXT_HORIZ_RIGHT:
TXT_CalcWidgetSize(widget);
-
+
if (widget->widget_class != &txt_separator_class)
{
draw_x += col_width - widget->w;
}
-
break;
}
@@ -481,6 +692,7 @@
TXT_CAST_ARG(txt_table_t, table);
unsigned int *column_widths;
unsigned int *row_heights;
+ txt_widget_t *widget;
int draw_x, draw_y;
int x, y;
int i;
@@ -505,9 +717,9 @@
}
// Draw all cells
-
+
draw_y = table->widget.y;
-
+
for (y=0; y<rows; ++y)
{
draw_x = table->widget.x;
@@ -519,10 +731,14 @@
if (i >= table->num_widgets)
break;
- if (table->widgets[i] != NULL)
+ widget = table->widgets[i];
+
+ if (IsActualWidget(widget))
{
- LayoutCell(table, x, y, column_widths[x],
- draw_x, draw_y);
+ CalculateWidgetDimensions(table, x, y,
+ column_widths, row_heights,
+ &widget->w, &widget->h);
+ LayoutCell(table, x, y, draw_x, draw_y);
}
draw_x += column_widths[x];
@@ -552,7 +768,7 @@
{
widget = table->widgets[i];
- if (widget != NULL)
+ if (IsActualWidget(widget))
{
TXT_GotoXY(widget->x, widget->y);
TXT_DrawWidget(widget);
@@ -574,7 +790,7 @@
// NULL widgets are spacers
- if (widget != NULL)
+ if (IsActualWidget(widget))
{
if (x >= widget->x && x < (signed) (widget->x + widget->w)
&& y >= widget->y && y < (signed) (widget->y + widget->h))
@@ -617,7 +833,7 @@
for (i = 0; i < table->num_widgets; ++i)
{
- if (table->widgets[i] != NULL
+ if (IsActualWidget(table->widgets[i])
&& TXT_SelectableWidget(table->widgets[i]))
{
ChangeSelection(table, i % table->columns, i / table->columns);
@@ -641,7 +857,7 @@
if (i < table->num_widgets)
{
- if (table->widgets[i] != NULL)
+ if (IsActualWidget(table->widgets[i]))
{
TXT_SetWidgetFocus(table->widgets[i], focused);
}
@@ -777,6 +993,11 @@
if (index >= 0 && index < table->num_widgets)
{
result = table->widgets[index];
+
+ if (!IsActualWidget(result))
+ {
+ result = NULL;
+ }
}
if (result != NULL && result->widget_class == &txt_table_class)
@@ -798,7 +1019,7 @@
for (i=0; i<table->num_widgets; ++i)
{
- if (table->widgets[i] == NULL)
+ if (!IsActualWidget(table->widgets[i]))
{
continue;
}
@@ -830,6 +1051,66 @@
// Not found.
return 0;
+}
+
+void TXT_SetTableColumns(TXT_UNCAST_ARG(table), int new_columns)
+{
+ TXT_CAST_ARG(txt_table_t, table);
+ txt_widget_t **new_widgets;
+ txt_widget_t *widget;
+ int new_num_widgets;
+ int i, j, x;
+
+ // We need as many full rows as are in the current list, plus the
+ // remainder from the last row.
+ new_num_widgets = (table->num_widgets / table->columns) * new_columns
+ + (table->num_widgets % table->columns);
+ new_widgets = calloc(new_num_widgets, sizeof(txt_widget_t *));
+
+ // Reset and add one by one from the old table.
+ new_num_widgets = 0;
+
+ for (i = 0; i < table->num_widgets; ++i)
+ {
+ widget = table->widgets[i];
+ x = i % table->columns;
+
+ if (x < new_columns)
+ {
+ new_widgets[new_num_widgets] = widget;
+ ++new_num_widgets;
+ }
+ else if (IsActualWidget(widget))
+ {
+ TXT_DestroyWidget(widget);
+ }
+
+ // When we reach the last column of a row, we must pad it out with
+ // extra widgets to reach the next row.
+ if (x == table->columns - 1)
+ {
+ for (j = table->columns; j < new_columns; ++j)
+ {
+ // First row? We need to add struts that are used to apply
+ // the column widths.
+ if (i < table->columns)
+ {
+ widget = &TXT_NewStrut(0, 0)->widget;
+ }
+ else
+ {
+ widget = &txt_table_overflow_right;
+ }
+ new_widgets[new_num_widgets] = widget;
+ ++new_num_widgets;
+ }
+ }
+ }
+
+ free(table->widgets);
+ table->widgets = new_widgets;
+ table->num_widgets = new_num_widgets;
+ table->columns = new_columns;
}
// Sets the widths of columns in a table.
--- a/textscreen/txt_table.h
+++ b/textscreen/txt_table.h
@@ -22,6 +22,33 @@
*/
/**
+ * Magic value that if used in a table, will indicate that the cell is
+ * empty and the widget in the cell to the left can overflow into it.
+ */
+
+#define TXT_TABLE_OVERFLOW_RIGHT (&txt_table_overflow_right)
+
+/**
+ * Magic value that if used in a table, will indicate that the cell is
+ * empty and the widget in the cell above it can overflow down into it.
+ */
+
+#define TXT_TABLE_OVERFLOW_DOWN (&txt_table_overflow_down)
+
+/**
+ * Magic value that if given to @ref TXT_AddWidget(), will pad out all
+ * columns until the end of line.
+ */
+#define TXT_TABLE_EOL (&txt_table_eol)
+
+/**
+ * Indicates an empty space to @ref TXT_AddWidgets(). Equivalent to
+ * TXT_AddWidget(table, NULL), except that NULL is used by TXT_AddWidgets()
+ * to indicate the end of input.
+ */
+#define TXT_TABLE_EMPTY (&txt_table_empty)
+
+/**
* Table widget.
*
* A table is a widget that contains other widgets. It may have
@@ -45,21 +72,22 @@
// Widgets in this table
// The widget at (x,y) in the table is widgets[columns * y + x]
-
txt_widget_t **widgets;
int num_widgets;
// Number of columns
-
int columns;
- // Currently selected
-
+ // Currently selected:
int selected_x;
int selected_y;
};
extern txt_widget_class_t txt_table_class;
+extern txt_widget_t txt_table_overflow_right;
+extern txt_widget_t txt_table_overflow_down;
+extern txt_widget_t txt_table_eol;
+extern txt_widget_t txt_table_empty;
void TXT_InitTable(txt_table_t *table, int columns);
@@ -143,6 +171,22 @@
*/
int TXT_SelectWidget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget));
+
+/**
+ * Change the number of columns in the table.
+ *
+ * Existing widgets in the table will be preserved, unless the change
+ * reduces the number of columns, in which case the widgets from the
+ * 'deleted' columns will be freed.
+ *
+ * This function can be useful for changing the number of columns in
+ * a window, which by default are tables containing a single column.
+ *
+ * @param table The table.
+ * @param new_columns The new number of columns.
+ */
+
+void TXT_SetTableColumns(TXT_UNCAST_ARG(table), int new_columns);
/**
* Set the widths of the columns of the table.
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -27,6 +27,12 @@
#include "txt_separator.h"
#include "txt_window.h"
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#endif
+
void TXT_SetWindowAction(txt_window_t *window,
txt_horiz_align_t position,
txt_window_action_t *action)
@@ -507,8 +513,17 @@
window->help_url = help_url;
}
+#ifdef _WIN32
+
void TXT_OpenURL(char *url)
{
+ ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
+}
+
+#else
+
+void TXT_OpenURL(char *url)
+{
char *cmd;
size_t cmd_len;
@@ -515,9 +530,7 @@
cmd_len = strlen(url) + 30;
cmd = malloc(cmd_len);
-#if defined(_WIN32)
- TXT_snprintf(cmd, cmd_len, "cmd /c start \"%s\"", url);
-#elif defined(__MACOSX__)
+#if defined(__MACOSX__)
TXT_snprintf(cmd, cmd_len, "open \"%s\"", url);
#else
// The Unix situation sucks as usual, but the closest thing to a
@@ -535,6 +548,8 @@
system(cmd);
free(cmd);
}
+
+#endif /* #ifndef _WIN32 */
void TXT_OpenWindowHelpURL(txt_window_t *window)
{