ref: 6f48bab893205636faf89ade2625b55c90d8c9bd
parent: 30f853b65820280931e06e07fd5f624313670305
parent: 939cbfeb115338cf7868e516019a4ae832b1ebde
author: Simon Howard <fraggle@soulsphere.org>
date: Mon Jan 2 09:42:49 EST 2017
Merge remote-tracking branch 'origin/master' into sdl2-branch
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,82 +1,94 @@
-## HEAD
+## 2.3.0 (2016-12-29)
### General
- * Bash completion scripts are included. (thanks Fabian)
- * Support the *.lmp file format in the OS X launcher (thanks Jon)
- * Added emulation for pitch-shifting as in early versions of Doom,
- Heretic, and Hexen. (thanks Jon)
- * Write out aspect-correct 1600×1200 PNGs. (thanks Jon)
- * OPL emulation is more accurate. (thanks Nuke.YKT)
- * Futher emulation of DMX bugs with GUS cards. (thanks Nuke.YKT)
- * Emulation of the disk icon has returned. (thanks Fabian, Jon)
- * Checksum calculations were fixed on big endian systems, allowing
+ * Bash completion scripts are included (thanks Fabian)
+ * The OS X launcher now supports the .lmp file format (thanks Jon)
+ * Pitch-shifting from early versions of Doom, Heretic, and Hexen.
+ is now supported (thanks Jon)
+ * Aspect ratio-corrected 1600×1200 PNGs are now written (thanks Jon)
+ * OPL emulation is more accurate (thanks Nuke.YKT)
+ * DMX bugs with GUS cards are now better emulated (thanks Nuke.YKT)
+ * The disk activity floppy disk icon is now shown (thanks Fabian, Jon)
+ * Checksum calculations are fixed on big endian systems, allowing
multiplayer games to be played in mixed little/big-endian
- environments. (thanks GhostlyDeath, njankowski)
+ environments (thanks GhostlyDeath, njankowski)
* The NES30, SNES30, and SFC30 gamepads are detected and configured
- automatically by the Setup tool. The automap can also be
- configured to a joystick button. (thanks Jon)
- * The vanilla limit of 4046 lumps per WAD is now enforced. (thanks
+ automatically by the Setup tool. The automap can also be configured
+ to a joystick button (thanks Jon)
+ * The vanilla limit of 4046 lumps per WAD is now enforced (thanks
Jon, Quasar, Edward-san)
- * Solidsegs overflow is emulated like in vanilla. (thanks Quasar)
- * Heretic/Hexen demo support has expanded. "-demoextend" allows
- demos to last longer than a single level; "-shortticfix" adjusts
- low-resolution turning to match Doom's handling; "-maxdemo" and
- "-longtics" support. (thanks CapnClever)
+ * Solidsegs overflow is emulated like in vanilla (thanks Quasar)
+ * Multiple capitalizations are now tried when searching for WAD files,
+ for convenience when running on case sensitive filesystems (thanks
+ Fabian).
+ * A new command line argument, `-strictdemos`, was added, to allow
+ more careful control over demo format extensions. Such extensions
+ are now forbidden in WAD files and warning messages are shown.
### Build systems
- * Improved compatibility with BSD Make. (thanks R.Rebello)
+ * There is better compatibility with BSD Make (thanks R.Rebello)
* “./configure --with-PACKAGE” checks were repaired to behave
- logically, rather than disabling the feature. (thanks R.Rebello)
- * Default to installing the games to ${bindir}, such as
- /usr/local/bin, rather than /usr/local/games. (thanks chungy)
- * Support Visual Studio 2015. (thanks Azarien)
- * Allow SDL headers and libraries to exist in the Microsoft Visual
- Studio project directory. (thanks Quasar)
- * Repaired the CodeBlocks projects by removing non-existent files
+ logically, rather than disabling the feature (thanks R.Rebello)
+ * Games are now installed to ${bindir} by default, eg.
+ /usr/local/bin, rather than /usr/local/games (thanks chungy)
+ * Visual Studio 2015 is now supported (thanks Azarien)
+ * SDL headers and libraries can now exist in the Microsoft Visual
+ Studio project directory (thanks Quasar)
+ * CodeBlocks projects were repaired by removing non-existent files
from the project files (thanks krystalgamer)
### Doom
- * Chex Quest’s level warp cheat (LEESNYDER##) was changed to behave
- like the original EXE. (thanks Nuke.YKT)
- * Allow starting multiplayer Chex Quest games.
- * Allow Freedoom: Phase 1 ≤ 0.10.1 to be loaded with mods, with
- -gameversion older than ultimate. (thanks Fabian, chungy)
+ * Chex Quest’s level warp cheat (LEESNYDER##) now behaves more like
+ like the original EXE (thanks Nuke.YKT)
+ * It's now possible to start multiplayer Chex Quest games.
+ * Freedoom: Phase 1 <= 0.10.1 can now be loaded with mods, with
+ -gameversion older than ultimate (thanks Fabian, chungy)
* The IWAD order preference for GOG.com installs matches vanilla
- Final Doom: doom2, plutonia, tnt, doom. (thanks chungy)
- * Added safety checks against write failures when saving a game,
- such as when the directory is read-only. Try falling back to a
- temporary directory and reporting an error instead. (thanks
+ Final Doom: doom2, plutonia, tnt, doom (thanks chungy)
+ * There are better safety checks against write failures when saving
+ a game, such as when the directory is read-only (thanks
terrorcide)
- * Versions 1.666, 1.7, and 1.8 are emulated. (thanks Nuke.YKT)
+ * Versions 1.666, 1.7, and 1.8 are emulated (thanks Nuke.YKT)
+ * Crashes are now handled more gracefully when a linedef references
+ nonexistent sidedefs (thanks Fabian)
### Heretic
- * Added map names for Episode 6, fixing a crash after completing a
- level in this episode. (thanks J.Benaim)
- * Added unlimited demo/savegame support. (thanks CapnClever)
+ * Map names were added for Episode 6, fixing a crash after completing
+ a level in this episode (thanks J.Benaim)
+ * Support for unlimited demo/savegames was added (thanks CapnClever)
+ * Demo support is expanded: "-demoextend" allows demos to last longer
+ than a single level; "-shortticfix" adjusts low-resolution turning
+ to match Doom's handling, and there is now "-maxdemo" and "-longtics"
+ support (thanks CapnClever)
### Hexen
- * The MRJONES cheat code returns an identical string as vanilla, and
- enables fully reproducable builds. (thanks Fabian)
- * Fixed an issue where the game crashed while killing the
- Wraithverge in 64-bit builds. (thanks J.Benaim)
- * Added unlimited demo/savegame support. (thanks CapnClever)
- * Mouse buttons for strafe left/right and move backward, as well as
- 'Double click acts as "use"' mouse option, now function properly.
- (thanks CapnClever)
+ * The MRJONES cheat code returns an identical string to vanilla, and
+ enables fully reproducible builds (thanks Fabian)
+ * An issue was fixed where the game crashed while killing the
+ Wraithverge in 64-bit builds (thanks J.Benaim)
+ * Support for unlimited demo/savegames was added (thanks CapnClever)
+ * Mouse buttons for strafe left/right and move backward were added,
+ as well as a "Double click acts as use" mouse option (thanks
+ CapnClever)
+ * Demo support is expanded: "-demoextend" allows demos to last longer
+ than a single level; "-shortticfix" adjusts low-resolution turning
+ to match Doom's handling, and there is now "-maxdemo" and "-longtics"
+ support (thanks CapnClever)
### Strife
- * Support added for automatic loading of the IWAD from the GOG.com
- release of Strife: Veteran Edition on Windows. (thanks chungy)
- * Jumping can be bound to a mouse button. (thanks Gez)
- * Gibbing logic was changed to match vanilla behavior. (thanks Quasar)
- * Several constants differences from vanilla were fixed. (thanks
+ * Support was added for automatic loading of the IWAD from the GOG.com
+ release of Strife: Veteran Edition on Windows (thanks chungy)
+ * Jumping can now be bound to a mouse button (thanks Gez)
+ * Gibbing logic was changed to match vanilla behavior (thanks Quasar)
+ * Several constants differences from vanilla were fixed (thanks
Nuke.YKT, Quasar)
* When using -iwad, voices.wad from the IWAD’s directory is prefered
- over auto-detected DOS/Steam/GOG.com installs. (thanks Quasar)
+ over auto-detected DOS/Steam/GOG.com installs (thanks Quasar)
### libtextscreen
- * Simplified the API for creating and managing tables and columns.
- * Allow cycling through tables with tab key.
+ * The API for creating and managing tables and columns was simplified.
+ * It's now possible to cycle through tables with the tab key.
+ * Windows can now have multiple columns.
## 2.2.1 (2015-09-10)
--- a/PHILOSOPHY.md
+++ b/PHILOSOPHY.md
@@ -28,9 +28,12 @@
* DOS Heretic 1.3.
* DOS Hexen 1.1.
* DOS Strife 1.31.
+ * DOS Chex Quest.
-“Vanilla” does not include ports (either official or unofficial), such
-as console ports, Doom 95 or Doom 3: BFG Edition.
+Compatibility with older versions of the DOS binaries is also a
+secondary goal (though not pre-release versions). Other ports (either
+official or unofficial) are out of scope: this includes console ports,
+non-DOS ports, Doom 95 and Doom 3: BFG Edition.
# Compatibility
@@ -46,7 +49,7 @@
Chocolate Doom as a faithful substitute.
* Demo compatibility: Doom was one of the first games to develop a
‘speedrunning’ community, and thousands of recordings of Doom
- gameplay (.lmp demo files) exist in the Compet-N archive. Chocolate
+ gameplay (`.lmp` demo files) exist in the Compet-N archive. Chocolate
Doom aims for the highest standard of demo compatibility with
Vanilla Doom, a goal that is often complicated by obscure behavior
that can be difficult to reproduce.
@@ -67,8 +70,8 @@
treated as such. Some examples are:
* The novert setting, which reproduces the functionality of
- novert.exe.
- * The -deh parameter, which loads Dehacked patches like dehacked.exe
+ `novert.exe`.
+ * The `-deh` parameter, which loads Dehacked patches like dehacked.exe
does under DOS. Chocolate Doom imposes the same limitations that
Vanilla Dehacked does.
@@ -97,8 +100,8 @@
3. Supporting obsolete technology is not a goal: it’s considered
acceptable that Chocolate Doom does not support every type of
hardware from 1993. However, Chocolate Doom should aim to recreate
- the functionality in a modern way. Examples of technology that
- isn’t supported are:
+ the functionality in a modern way. Examples of technologies that
+ aren’t supported are:
- No support for IPX networks, but TCP/IP is supported instead.
- No support for dial-up/serial connections; modern operating
@@ -111,9 +114,9 @@
- There are new key bindings for actions that can’t be rebound with
Vanilla Doom, because it’s useful for portability to machines
that don’t have a full keyboard.
- - There are additional mouse and joystick key bindings that let you
- perform actions with them that can only be done with the keyboard
- in Vanilla Doom.
+ - There are additional mouse and joystick button bindings that let
+ you perform actions with them that can only be done with the
+ keyboard in Vanilla Doom.
- Chocolate Doom includes some hacks to support the Doom 3: BFG
Edition IWAD files. The assumption is that being able to at least
play is better than nothing, even if it’s not Vanilla behavior.
@@ -145,9 +148,20 @@
- The startup messages in Chocolate Doom are not identical to
Vanilla Doom and are not necessarily in the same order.
- - Vanilla Doom has command line options named -comdev, -shdev and
- -regdev used by id internally for development; these have been
- removed.
+ - Vanilla Doom has command line options named `-comdev`, `-shdev`
+ and `-regdev` used by id internally for development; these have
+ been removed.
+
+ 8. Expansions to the vanilla demo formats are allowed, to make
+ recording and playback of vanilla gameplay more convenient, with
+ the following restrictions:
+
+ - Such expansions are not supported in WAD files (they are not
+ an editing feature for WAD authors to use).
+ - Support for these features can be completely disabled using the
+ `-strictdemos` command line argument.
+ - A warning is shown to the user on the console (stdout) when a
+ demo using one of these features is recorded or played back.
A good litmus test of when it’s acceptable to break from Vanilla
behavior is to ask the question: “Although this is Vanilla behavior,
--- a/README.md
+++ b/README.md
@@ -101,4 +101,4 @@
file for more information.
* Please send any feedback, questions or suggestions to
- fraggle@gmail.com. Thanks!
+ chocolate-doom-dev-list@chocolate-doom.org. Thanks!
--- 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.2.1"
+#define PACKAGE_STRING "Chocolate Doom 2.3.0"
/* 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.2.1"
+#define PACKAGE_VERSION "2.3.0"
/* 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.2.1"
+#define VERSION "2.3.0"
/* 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,2,1,0
-FILEVERSION 2,2,1,0
+PRODUCTVERSION 2,3,0,0
+FILEVERSION 2,3,0,0
FILETYPE 1
{
BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
{
BLOCK "040904E4"
{
- VALUE "FileVersion", "2.2.1"
- VALUE "FileDescription", "2.2.1"
+ VALUE "FileVersion", "2.3.0"
+ VALUE "FileDescription", "2.3.0"
VALUE "InternalName", "Chocolate Doom"
VALUE "CompanyName", "Chocolate Doom"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate Doom"
- VALUE "ProductVersion", "2.2.1"
+ VALUE "ProductVersion", "2.3.0"
}
}
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,2,1,0
-FILEVERSION 2,2,1,0
+PRODUCTVERSION 2,3,0,0
+FILEVERSION 2,3,0,0
FILETYPE 1
{
BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
{
BLOCK "040904E4"
{
- VALUE "FileVersion", "2.2.1"
+ VALUE "FileVersion", "2.3.0"
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.2.1"
+ VALUE "ProductVersion", "2.3.0"
}
}
BLOCK "VarFileInfo"
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,5 @@
-AC_INIT(Chocolate Doom, 2.999.0, fraggle@gmail.com, chocolate-doom)
+AC_INIT(Chocolate Doom, 2.999.0, chocolate-doom-dev-list@chocolate-doom.org,
+ chocolate-doom)
PACKAGE_SHORTNAME=${PACKAGE_NAME% Doom}
PACKAGE_SHORTDESC="Conservative source port"
--- 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.2.1"
+#define PACKAGE_STRING "Chocolate Doom 2.3.0"
/* 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.2.1"
+#define PACKAGE_VERSION "2.3.0"
/* Change this when you create your awesome forked version */
#define PROGRAM_PREFIX "chocolate-"
/* Version number of package */
-#define VERSION "2.2.1"
+#define VERSION "2.3.0"
/* 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,2,1,0
-FILEVERSION 2,2,1,0
+PRODUCTVERSION 2,3,0,0
+FILEVERSION 2,3,0,0
FILETYPE 1
BEGIN
BLOCK "StringFileInfo"
@@ -34,12 +34,12 @@
BLOCK "040904E4"
BEGIN
VALUE "FileVersion", "1.0.0"
- VALUE "FileDescription", "Chocolate Doom 2.2.1"
+ VALUE "FileDescription", "Chocolate Doom 2.3.0"
VALUE "InternalName", "chocolate-doom"
VALUE "CompanyName", "fraggle@gmail.com"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate Doom"
- VALUE "ProductVersion", "2.2.1"
+ VALUE "ProductVersion", "2.3.0"
END
END
BLOCK "VarFileInfo"
--- a/src/d_loop.c
+++ b/src/d_loop.c
@@ -816,3 +816,85 @@
{
loop_interface = i;
}
+
+// TODO: Move nonvanilla demo functions into a dedicated file.
+#include "m_misc.h"
+#include "w_wad.h"
+
+static boolean StrictDemos(void)
+{
+ //!
+ // @category demo
+ //
+ // When recording or playing back demos, disable any extensions
+ // of the vanilla demo format - record demos as vanilla would do,
+ // and play back demos as vanilla would do.
+ //
+ return M_ParmExists("-strictdemos");
+}
+
+// If the provided conditional value is true, we're trying to record
+// a demo file that will include a non-vanilla extension. The function
+// will return true if the conditional is true and it's allowed to use
+// this extension (no extensions are allowed if -strictdemos is given
+// on the command line). A warning is shown on the console using the
+// provided string describing the non-vanilla expansion.
+boolean D_NonVanillaRecord(boolean conditional, char *feature)
+{
+ if (!conditional || StrictDemos())
+ {
+ return false;
+ }
+
+ printf("Warning: Recording a demo file with a non-vanilla extension "
+ "(%s). Use -strictdemos to disable this extension.\n",
+ feature);
+
+ return true;
+}
+
+// Returns true if the given lump number corresponds to data from a .lmp
+// file, as opposed to a WAD.
+static boolean IsDemoFile(int lumpnum)
+{
+ char *lower;
+ boolean result;
+
+ lower = M_StringDuplicate(lumpinfo[lumpnum]->wad_file->path);
+ M_ForceLowercase(lower);
+ result = M_StringEndsWith(lower, ".lmp");
+ free(lower);
+
+ return result;
+}
+
+// If the provided conditional value is true, we're trying to play back
+// a demo that includes a non-vanilla extension. We return true if the
+// conditional is true and it's allowed to use this extension, checking
+// that:
+// - The -strictdemos command line argument is not provided.
+// - The given lumpnum identifying the demo to play back identifies a
+// demo that comes from a .lmp file, not a .wad file.
+// - Before proceeding, a warning is shown to the user on the console.
+boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
+ char *feature)
+{
+ if (!conditional || StrictDemos())
+ {
+ return false;
+ }
+
+ if (!IsDemoFile(lumpnum))
+ {
+ printf("Warning: WAD contains demo with a non-vanilla extension "
+ "(%s)\n", feature);
+ return false;
+ }
+
+ printf("Warning: Playing back a demo file with a non-vanilla extension "
+ "(%s). Use -strictdemos to disable this extension.\n",
+ feature);
+
+ return true;
+}
+
--- a/src/d_loop.h
+++ b/src/d_loop.h
@@ -77,5 +77,12 @@
extern boolean singletics;
extern int gametic, ticdup;
+// Check if it is permitted to record a demo with a non-vanilla feature.
+boolean D_NonVanillaRecord(boolean conditional, char *feature);
+
+// Check if it is permitted to play back a demo with a non-vanilla feature.
+boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
+ char *feature);
+
#endif
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -1440,19 +1440,6 @@
D_IdentifyVersion();
InitGameVersion();
- //!
- // @category mod
- //
- // Disable automatic loading of Dehacked patches for certain
- // IWAD files.
- //
- if (!M_ParmExists("-nodeh"))
- {
- // Some IWADs have dehacked patches that need to be loaded for
- // them to be played properly.
- LoadIwadDeh();
- }
-
// Check which IWAD variant we are using.
if (W_CheckNumForName("FREEDOOM") >= 0)
@@ -1469,6 +1456,19 @@
else if (W_CheckNumForName("DMENUPIC") >= 0)
{
gamevariant = bfgedition;
+ }
+
+ //!
+ // @category mod
+ //
+ // Disable automatic loading of Dehacked patches for certain
+ // IWAD files.
+ //
+ if (!M_ParmExists("-nodeh"))
+ {
+ // Some IWADs have dehacked patches that need to be loaded for
+ // them to be played properly.
+ LoadIwadDeh();
}
// Doom 3: BFG Edition includes modified versions of the classic
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -2057,6 +2057,8 @@
{
int i;
+ demo_p = demobuffer;
+
//!
// @category demo
//
@@ -2063,16 +2065,12 @@
// Record a high resolution "Doom 1.91" demo.
//
- longtics = M_CheckParm("-longtics") != 0;
+ longtics = D_NonVanillaRecord(M_ParmExists("-longtics"),
+ "Doom 1.91 demo format");
// If not recording a longtics demo, record in low res
-
lowres_turn = !longtics;
-
- demo_p = demobuffer;
-
- // Save the right version code for this demo
-
+
if (longtics)
{
*demo_p++ = DOOM_191_VERSION;
@@ -2128,6 +2126,8 @@
return "v1.8";
case 109:
return "v1.9";
+ case 111:
+ return "v1.91 hack demo?";
default:
break;
}
@@ -2147,27 +2147,29 @@
}
}
-void G_DoPlayDemo (void)
-{
- skill_t skill;
- int i, episode, map;
+void G_DoPlayDemo (void)
+{
+ skill_t skill;
+ int i, lumpnum, episode, map;
int demoversion;
-
- gameaction = ga_nothing;
- demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
+ lumpnum = W_GetNumForName(defdemoname);
+ gameaction = ga_nothing;
+ demobuffer = W_CacheLumpNum(lumpnum, PU_STATIC);
+ demo_p = demobuffer;
+
demoversion = *demo_p++;
- if (demoversion == G_VanillaVersionCode())
+ longtics = false;
+
+ // Longtics demos use the modified format that is generated by cph's
+ // hacked "v1.91" doom exe. This is a non-vanilla extension.
+ if (D_NonVanillaPlayback(demoversion == DOOM_191_VERSION, lumpnum,
+ "Doom 1.91 demo format"))
{
- longtics = false;
- }
- else if (demoversion == DOOM_191_VERSION)
- {
- // demo recorded with cph's modified "v1.91" doom exe
longtics = true;
}
- else
+ else if (demoversion != G_VanillaVersionCode())
{
char *message = "Demo is from a different game version!\n"
"(read %i, should be %i)\n"
@@ -2181,7 +2183,7 @@
I_Error(message, demoversion, G_VanillaVersionCode(),
DemoVersionDescription(demoversion));
}
-
+
skill = *demo_p++;
episode = *demo_p++;
map = *demo_p++;
--- a/src/heretic/g_game.c
+++ b/src/heretic/g_game.c
@@ -1791,7 +1791,8 @@
// Record or playback a demo with high resolution turning.
//
- longtics = M_ParmExists("-longtics");
+ longtics = D_NonVanillaRecord(M_ParmExists("-longtics"),
+ "vvHeretic longtics demo");
// If not recording a longtics demo, record in low res
@@ -1837,7 +1838,7 @@
// 0x02 = -nomonsters
*demo_p = 1; // assume player one exists
- if (respawnparm)
+ if (D_NonVanillaRecord(respawnparm, "vvHeretic -respawn header flag"))
{
*demo_p |= DEMOHEADER_RESPAWN;
}
@@ -1845,7 +1846,7 @@
{
*demo_p |= DEMOHEADER_LONGTICS;
}
- if (nomonsters)
+ if (D_NonVanillaRecord(nomonsters, "vvHeretic -nomonsters header flag"))
{
*demo_p |= DEMOHEADER_NOMONSTERS;
}
@@ -1877,20 +1878,33 @@
void G_DoPlayDemo(void)
{
skill_t skill;
- int i, episode, map;
+ int i, lumpnum, episode, map;
gameaction = ga_nothing;
- demobuffer = demo_p = W_CacheLumpName(defdemoname, PU_STATIC);
+ lumpnum = W_GetNumForName(defdemoname);
+ demobuffer = W_CacheLumpNum(lumpnum, PU_STATIC);
+ demo_p = demobuffer;
skill = *demo_p++;
episode = *demo_p++;
map = *demo_p++;
- // Read special parameter bits: see G_RecordDemo() for details.
- longtics = (*demo_p & DEMOHEADER_LONGTICS) != 0;
-
- // don't overwrite arguments from the command line
- respawnparm |= (*demo_p & DEMOHEADER_RESPAWN) != 0;
- nomonsters |= (*demo_p & DEMOHEADER_NOMONSTERS) != 0;
+ // vvHeretic allows extra options to be stored in the upper bits of
+ // the player 1 present byte. However, this is a non-vanilla extension.
+ if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_LONGTICS) != 0,
+ lumpnum, "vvHeretic longtics demo"))
+ {
+ longtics = true;
+ }
+ if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_RESPAWN) != 0,
+ lumpnum, "vvHeretic -respawn header flag"))
+ {
+ respawnparm = true;
+ }
+ if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_NOMONSTERS) != 0,
+ lumpnum, "vvHeretic -nomonsters header flag"))
+ {
+ nomonsters = true;
+ }
for (i = 0; i < MAXPLAYERS; i++)
playeringame[i] = (*demo_p++) != 0;
--- a/src/heretic/p_enemy.c
+++ b/src/heretic/p_enemy.c
@@ -845,8 +845,8 @@
r1 = P_SubRandom();
r2 = P_SubRandom();
- mo = P_SpawnMobj(actor->x + (r1 << 11),
- actor->y + (r2 << 11), actor->z,
+ mo = P_SpawnMobj(actor->x + (r2 << 11),
+ actor->y + (r1 << 11), actor->z,
MT_BLOOD);
mo->momx = P_SubRandom() << 10;
mo->momy = P_SubRandom() << 10;
@@ -920,9 +920,9 @@
r1 = P_SubRandom();
r2 = P_SubRandom();
r3 = P_SubRandom();
- P_SpawnMobj(actor->x + (r1 << 10),
+ P_SpawnMobj(actor->x + (r3 << 10),
actor->y + (r2 << 10),
- actor->z + (r3 << 10), MT_PUFFY);
+ actor->z + (r1 << 10), MT_PUFFY);
}
}
@@ -1769,8 +1769,8 @@
r2 = P_SubRandom();
actor->z = actor->floorz;
- mo = P_SpawnMobj(actor->x + (r1 << 10),
- actor->y + (r2 << 10), ONFLOORZ,
+ mo = P_SpawnMobj(actor->x + (r2 << 10),
+ actor->y + (r1 << 10), ONFLOORZ,
MT_MNTRFX3);
mo->target = actor->target;
mo->momx = 1; // Force block checking
@@ -2416,11 +2416,12 @@
void A_SpawnTeleGlitter(mobj_t * actor)
{
mobj_t *mo;
- int r;
+ int r1, r2;
- r = P_Random();
- mo = P_SpawnMobj(actor->x + ((r & 31) - 16) * FRACUNIT,
- actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
+ r1 = P_Random();
+ r2 = P_Random();
+ mo = P_SpawnMobj(actor->x + ((r2 & 31) - 16) * FRACUNIT,
+ actor->y + ((r1 & 31) - 16) * FRACUNIT,
actor->subsector->sector->floorheight, MT_TELEGLITTER);
mo->momz = FRACUNIT / 4;
}
@@ -2434,11 +2435,12 @@
void A_SpawnTeleGlitter2(mobj_t * actor)
{
mobj_t *mo;
- int r;
+ int r1, r2;
- r = P_Random();
- mo = P_SpawnMobj(actor->x + ((r & 31) - 16) * FRACUNIT,
- actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
+ r1 = P_Random();
+ r2 = P_Random();
+ mo = P_SpawnMobj(actor->x + ((r2 & 31) - 16) * FRACUNIT,
+ actor->y + ((r1 & 31) - 16) * FRACUNIT,
actor->subsector->sector->floorheight, MT_TELEGLITTER2);
mo->momz = FRACUNIT / 4;
}
--- a/src/hexen/a_action.c
+++ b/src/hexen/a_action.c
@@ -1175,9 +1175,9 @@
r1 = P_Random();
r2 = P_Random();
r3 = P_Random();
- mo = P_SpawnMobj(actor->x + ((r1 - 128) << 12),
+ mo = P_SpawnMobj(actor->x + ((r3 - 128) << 12),
actor->y + ((r2 - 128) << 12),
- actor->z + (r3 * actor->height / 256),
+ actor->z + (r1 * actor->height / 256),
MT_ZARMORCHUNK);
P_SetMobjState(mo, mo->info->spawnstate + i);
if (mo)
--- a/src/hexen/g_game.c
+++ b/src/hexen/g_game.c
@@ -1959,7 +1959,8 @@
// Record or playback a demo with high resolution turning.
//
- longtics = M_ParmExists("-longtics");
+ longtics = D_NonVanillaRecord(M_ParmExists("-longtics"),
+ "vvHeretic longtics demo");
// If not recording a longtics demo, record in low res
@@ -2006,7 +2007,7 @@
// 0x02 = -nomonsters
*demo_p = 1; // assume player one exists
- if (respawnparm)
+ if (D_NonVanillaRecord(respawnparm, "vvHeretic -respawn header flag"))
{
*demo_p |= DEMOHEADER_RESPAWN;
}
@@ -2014,7 +2015,7 @@
{
*demo_p |= DEMOHEADER_LONGTICS;
}
- if (nomonsters)
+ if (D_NonVanillaRecord(nomonsters, "vvHeretic -nomonsters header flag"))
{
*demo_p |= DEMOHEADER_NOMONSTERS;
}
@@ -2050,20 +2051,36 @@
void G_DoPlayDemo(void)
{
skill_t skill;
- int i, episode, map;
+ int i, lumpnum, episode, map;
gameaction = ga_nothing;
- demobuffer = demo_p = W_CacheLumpName(defdemoname, PU_STATIC);
+ lumpnum = W_GetNumForName(defdemoname);
+ demobuffer = W_CacheLumpNum(lumpnum, PU_STATIC);
+ demo_p = demobuffer;
skill = *demo_p++;
episode = *demo_p++;
map = *demo_p++;
- // Read special parameter bits: see G_RecordDemo() for details.
- longtics = (*demo_p & DEMOHEADER_LONGTICS) != 0;
-
- // don't overwrite arguments from the command line
- respawnparm |= (*demo_p & DEMOHEADER_RESPAWN) != 0;
- nomonsters |= (*demo_p & DEMOHEADER_NOMONSTERS) != 0;
+ // When recording we store some extra options inside the upper bits
+ // of the player 1 present byte. However, this is a non-vanilla extension.
+ // Note references to vvHeretic here; these are the extensions used by
+ // vvHeretic, which we're just reusing for Hexen demos too. There is no
+ // vvHexen.
+ if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_LONGTICS) != 0,
+ lumpnum, "vvHeretic longtics demo"))
+ {
+ longtics = true;
+ }
+ if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_RESPAWN) != 0,
+ lumpnum, "vvHeretic -respawn header flag"))
+ {
+ respawnparm = true;
+ }
+ if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_NOMONSTERS) != 0,
+ lumpnum, "vvHeretic -nomonsters header flag"))
+ {
+ nomonsters = true;
+ }
for (i = 0; i < maxplayers; i++)
{
--- a/src/hexen/p_enemy.c
+++ b/src/hexen/p_enemy.c
@@ -1481,13 +1481,14 @@
void A_MntrFloorFire(mobj_t * actor)
{
mobj_t *mo;
- int r;
+ int r1, r2;
- r = P_SubRandom();
+ r1 = P_SubRandom();
+ r2 = P_SubRandom();
actor->z = actor->floorz;
- mo = P_SpawnMobj(actor->x + (r << 10),
- actor->y + (P_SubRandom() << 10), ONFLOORZ,
+ mo = P_SpawnMobj(actor->x + (r2 << 10),
+ actor->y + (r1 << 10), ONFLOORZ,
MT_MNTRFX3);
mo->target = actor->target;
mo->momx = 1; // Force block checking
@@ -2411,10 +2412,12 @@
void A_SerpentSpawnGibs(mobj_t * actor)
{
mobj_t *mo;
- int r = P_Random();
+ int r1, r2;
- mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
- actor->y + ((P_Random() - 128) << 12),
+ r1 = P_Random();
+ r2 = P_Random();
+ mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
+ actor->y + ((r1 - 128) << 12),
actor->floorz + FRACUNIT, MT_SERPENT_GIB1);
if (mo)
{
@@ -2422,9 +2425,10 @@
mo->momy = (P_Random() - 128) << 6;
mo->floorclip = 6 * FRACUNIT;
}
- r = P_Random();
- mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
- actor->y + ((P_Random() - 128) << 12),
+ r1 = P_Random();
+ r2 = P_Random();
+ mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
+ actor->y + ((r1 - 128) << 12),
actor->floorz + FRACUNIT, MT_SERPENT_GIB2);
if (mo)
{
@@ -2432,9 +2436,10 @@
mo->momy = (P_Random() - 128) << 6;
mo->floorclip = 6 * FRACUNIT;
}
- r = P_Random();
- mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
- actor->y + ((P_Random() - 128) << 12),
+ r1 = P_Random();
+ r2 = P_Random();
+ mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
+ actor->y + ((r1 - 128) << 12),
actor->floorz + FRACUNIT, MT_SERPENT_GIB3);
if (mo)
{
@@ -2797,9 +2802,9 @@
r2 = P_SubRandom();
r3 = P_SubRandom();
- mo = P_SpawnMobj(actor->x + (r1 << 12), actor->y
+ mo = P_SpawnMobj(actor->x + (r3 << 12), actor->y
+ (r2 << 12),
- actor->z + (r3 << 11),
+ actor->z + (r1 << 11),
MT_BISHOPPAINBLUR);
if (mo)
{
@@ -3048,9 +3053,9 @@
r1 = P_Random();
r2 = P_Random();
r3 = P_Random();
- mo = P_SpawnMobj(actor->x + ((r1 - 128) << 14),
+ mo = P_SpawnMobj(actor->x + ((r3 - 128) << 14),
actor->y + ((r2 - 128) << 14),
- actor->z + ((r3 - 128) << 12),
+ actor->z + ((r1 - 128) << 12),
MT_DRAGON_FX2);
if (mo)
{
@@ -4865,10 +4870,10 @@
r2 = P_Random();
r3 = P_Random();
mo = P_SpawnMobj(actor->x +
- (((r1 - 128) * actor->radius) >> 7),
+ (((r3 - 128) * actor->radius) >> 7),
actor->y +
(((r2 - 128) * actor->radius) >> 7),
- actor->z + (r3 * actor->height / 255),
+ actor->z + (r1 * actor->height / 255),
MT_ICECHUNK);
P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
if (mo)
@@ -4885,10 +4890,10 @@
r2 = P_Random();
r3 = P_Random();
mo = P_SpawnMobj(actor->x +
- (((r1 - 128) * actor->radius) >> 7),
+ (((r3 - 128) * actor->radius) >> 7),
actor->y +
(((r2 - 128) * actor->radius) >> 7),
- actor->z + (r3 * actor->height / 255),
+ actor->z + (r1 * actor->height / 255),
MT_ICECHUNK);
P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
if (mo)
--- a/src/hexen/p_mobj.c
+++ b/src/hexen/p_mobj.c
@@ -1830,9 +1830,12 @@
void P_BloodSplatter2(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator)
{
mobj_t *mo;
+ int r1, r2;
- mo = P_SpawnMobj(x + ((P_Random() - 128) << 11),
- y + ((P_Random() - 128) << 11), z, MT_AXEBLOOD);
+ r1 = P_Random();
+ r2 = P_Random();
+ mo = P_SpawnMobj(x + ((r2 - 128) << 11),
+ y + ((r1 - 128) << 11), z, MT_AXEBLOOD);
mo->target = originator;
}
--- a/src/hexen/p_pspr.c
+++ b/src/hexen/p_pspr.c
@@ -868,9 +868,9 @@
r1 = P_Random();
r2 = P_Random();
r3 = P_Random();
- P_SpawnMobj(actor->x + ((r1 - 128) << 12), actor->y
+ P_SpawnMobj(actor->x + ((r3 - 128) << 12), actor->y
+ ((r2 - 128) << 12),
- actor->z + ((r3 - 128) << 11), MT_FSWORD_FLAME);
+ actor->z + ((r1 - 128) << 11), MT_FSWORD_FLAME);
}
}
@@ -1003,8 +1003,8 @@
}
r1 = P_Random();
r2 = P_Random();
- mo = P_SpawnMobj(actor->x + ((r1 - 128) * actor->radius / 256),
- actor->y + ((r2 - 128) * actor->radius / 256),
+ mo = P_SpawnMobj(actor->x + ((r2 - 128) * actor->radius / 256),
+ actor->y + ((r1 - 128) * actor->radius / 256),
actor->z + deltaZ, MT_LIGHTNING_ZAP);
if (mo)
{
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -101,10 +101,6 @@
typedef struct
{
- // Data for each channel.
-
- opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK];
-
// Track iterator used to read new events.
midi_track_iter_t *iter;
@@ -149,7 +145,8 @@
unsigned int note_volume;
// The current volume (register value) that has been set for this channel.
- unsigned int reg_volume;
+ unsigned int car_volume;
+ unsigned int mod_volume;
// Pan.
unsigned int reg_pan;
@@ -156,10 +153,6 @@
// Priority.
unsigned int priority;
-
- // Next in linked list; a voice is always either in the
- // free list or the allocated list.
- opl_voice_t *next;
};
// Operators used by the different voices.
@@ -325,12 +318,17 @@
// Voices:
static opl_voice_t voices[OPL_NUM_VOICES * 2];
-static opl_voice_t *voice_free_list;
-static opl_voice_t *voice_alloced_list;
+static opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2];
+static opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2];
+static int voice_free_num;
static int voice_alloced_num;
static int opl_opl3mode;
static int num_opl_voices;
+// Data for each channel.
+
+static opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK];
+
// Track data for playing tracks:
static opl_track_data_t *tracks;
@@ -366,17 +364,10 @@
{
byte *lump;
- lump = W_CacheLumpName("GENMIDI", PU_STATIC);
+ lump = W_CacheLumpName(DEH_String("genmidi"), PU_STATIC);
- // Check header
+ // DMX does not check header
- if (strncmp((char *) lump, GENMIDI_HEADER, strlen(GENMIDI_HEADER)) != 0)
- {
- W_ReleaseLumpName("GENMIDI");
-
- return false;
- }
-
main_instrs = (genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER));
percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS;
main_instr_names =
@@ -391,11 +382,11 @@
static opl_voice_t *GetFreeVoice(void)
{
opl_voice_t *result;
- opl_voice_t **rover;
+ int i;
// None available?
- if (voice_free_list == NULL)
+ if (voice_free_num == 0)
{
return NULL;
}
@@ -402,86 +393,66 @@
// Remove from free list
- result = voice_free_list;
- voice_free_list = voice_free_list->next;
+ result = voice_free_list[0];
- // Add to allocated list
+ voice_free_num--;
- rover = &voice_alloced_list;
-
- while (*rover != NULL)
+ for (i = 0; i < voice_free_num; i++)
{
- rover = &(*rover)->next;
+ voice_free_list[i] = voice_free_list[i + 1];
}
- *rover = result;
- result->next = NULL;
+ // Add to allocated list
- voice_alloced_num++;
+ voice_alloced_list[voice_alloced_num++] = result;
return result;
}
-// Remove a voice from the allocated voices list.
+// Release a voice back to the freelist.
-static void RemoveVoiceFromAllocedList(opl_voice_t *voice)
+static void VoiceKeyOff(opl_voice_t *voice);
+
+static void ReleaseVoice(int index)
{
- opl_voice_t **rover;
+ opl_voice_t *voice;
+ boolean double_voice;
+ int i;
- rover = &voice_alloced_list;
-
- // Search the list until we find the voice, then remove it.
-
- while (*rover != NULL)
+ // Doom 2 1.666 OPL crash emulation.
+ if (index >= voice_alloced_num)
{
- if (*rover == voice)
- {
- *rover = voice->next;
- voice->next = NULL;
- voice_alloced_num--;
- break;
- }
- rover = &(*rover)->next;
+ voice_alloced_num = 0;
+ voice_free_num = 0;
+ return;
}
-}
-// Release a voice back to the freelist.
+ voice = voice_alloced_list[index];
-static void VoiceKeyOff(opl_voice_t *voice);
+ VoiceKeyOff(voice);
-static void ReleaseVoice(opl_voice_t *voice)
-{
- opl_voice_t **rover;
- opl_voice_t *next;
- boolean double_voice;
-
voice->channel = NULL;
voice->note = 0;
double_voice = voice->current_instr_voice != 0;
- next = voice->next;
-
+
// Remove from alloced list.
- RemoveVoiceFromAllocedList(voice);
+ voice_alloced_num--;
- // Search to the end of the freelist (This is how Doom behaves!)
-
- rover = &voice_free_list;
-
- while (*rover != NULL)
+ for (i = index; i < voice_alloced_num; i++)
{
- rover = &(*rover)->next;
+ voice_alloced_list[i] = voice_alloced_list[i + 1];
}
- *rover = voice;
- voice->next = NULL;
+ // Search to the end of the freelist (This is how Doom behaves!)
- if (next != NULL && double_voice && opl_drv_ver != opl_doom_1_9)
+ voice_free_list[voice_free_num++] = voice;
+
+ if (double_voice && opl_drv_ver < opl_doom_1_9)
{
- VoiceKeyOff(next);
- ReleaseVoice(next);
+ ReleaseVoice(index);
}
}
@@ -488,7 +459,7 @@
// Load data to the specified operator
static void LoadOperatorData(int operator, genmidi_op_t *data,
- boolean max_level)
+ boolean max_level, unsigned int *volume)
{
int level;
@@ -495,13 +466,19 @@
// The scale and level fields must be combined for the level register.
// For the carrier wave we always set the maximum level.
- level = (data->scale & 0xc0) | (data->level & 0x3f);
+ level = data->scale;
if (max_level)
{
level |= 0x3f;
}
+ else
+ {
+ level |= data->level;
+ }
+ *volume = level;
+
OPL_WriteRegister(OPL_REGS_LEVEL + operator, level);
OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);
OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack);
@@ -540,8 +517,10 @@
// is set in SetVoiceVolume (below). If we are not using
// modulating mode, we must set both to minimum volume.
- LoadOperatorData(voice->op2 | voice->array, &data->carrier, true);
- LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating);
+ LoadOperatorData(voice->op2 | voice->array, &data->carrier, true,
+ &voice->car_volume);
+ LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating,
+ &voice->mod_volume);
// Set feedback register that control the connection between the
// two operators. Turn on bits in the upper nybble; I think this
@@ -550,10 +529,6 @@
OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array,
data->feedback | voice->reg_pan);
- // Hack to force a volume update.
-
- voice->reg_volume = 999;
-
// Calculate voice priority.
voice->priority = 0x0f - (data->carrier.attack >> 4)
@@ -584,12 +559,12 @@
// Update the volume register(s) if necessary.
- if (car_volume != voice->reg_volume)
+ if (car_volume != (voice->car_volume & 0x3f))
{
- voice->reg_volume = car_volume | (opl_voice->carrier.scale & 0xc0);
+ voice->car_volume = car_volume | (voice->car_volume & 0xc0);
OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array,
- voice->reg_volume);
+ voice->car_volume);
// If we are using non-modulated feedback mode, we must set the
// volume for both voices.
@@ -597,14 +572,21 @@
if ((opl_voice->feedback & 0x01) != 0
&& opl_voice->modulator.level != 0x3f)
{
- mod_volume = 0x3f - opl_voice->modulator.level;
- if (mod_volume >= car_volume)
+ mod_volume = opl_voice->modulator.level;
+ if (mod_volume < car_volume)
{
mod_volume = car_volume;
}
- OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array,
- mod_volume |
- (opl_voice->modulator.scale & 0xc0));
+
+ mod_volume |= voice->mod_volume & 0xc0;
+
+ if(mod_volume != voice->mod_volume)
+ {
+ voice->mod_volume = mod_volume;
+ OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array,
+ mod_volume |
+ (opl_voice->modulator.scale & 0xc0));
+ }
}
}
}
@@ -627,9 +609,10 @@
int i;
// Start with an empty free list.
+
+ voice_free_num = num_opl_voices;
+ voice_alloced_num = 0;
- voice_free_list = NULL;
-
// Initialize each voice.
for (i = 0; i < num_opl_voices; ++i)
@@ -642,7 +625,7 @@
// Add this voice to the freelist.
- ReleaseVoice(&voices[i]);
+ voice_free_list[i] = &voices[i];
}
}
@@ -653,7 +636,7 @@
static void I_OPL_SetMusicVolume(int volume)
{
- unsigned int i, j;
+ unsigned int i;
if (current_music_volume == volume)
{
@@ -666,20 +649,16 @@
// Update the volume of all voices.
- for (i = 0; i < num_tracks; ++i)
+ for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
{
- for (j = 0; j < MIDI_CHANNELS_PER_TRACK; ++j)
+ if (i == 15)
{
- if (j == 15)
- {
- SetChannelVolume(&tracks[i].channels[j], volume, false);
- }
- else
- {
- SetChannelVolume(&tracks[i].channels[j],
- tracks[i].channels[j].volume_base, false);
- }
+ SetChannelVolume(&channels[i], volume, false);
}
+ else
+ {
+ SetChannelVolume(&channels[i], channels[i].volume_base, false);
+ }
}
}
@@ -706,7 +685,7 @@
channel_num = 9;
}
- return &track->channels[channel_num];
+ return &channels[channel_num];
}
// Get the frequency that we should be using for a voice.
@@ -714,8 +693,7 @@
static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event)
{
opl_channel_data_t *channel;
- opl_voice_t *rover;
- opl_voice_t *prev;
+ int i;
unsigned int key;
/*
@@ -731,32 +709,17 @@
// Turn off voices being used to play this key.
// If it is a double voice instrument there will be two.
- rover = voice_alloced_list;
- prev = NULL;
-
- while (rover != NULL)
+ for (i = 0; i < voice_alloced_num; i++)
{
- if (rover->channel == channel && rover->key == key)
+ if (voice_alloced_list[i]->channel == channel
+ && voice_alloced_list[i]->key == key)
{
- VoiceKeyOff(rover);
-
// Finished with this voice now.
- ReleaseVoice(rover);
- if (prev == NULL)
- {
- rover = voice_alloced_list;
- }
- else
- {
- rover = prev->next;
- }
+ ReleaseVoice(i);
+
+ i--;
}
- else
- {
- prev = rover;
- rover = rover->next;
- }
}
}
@@ -767,8 +730,8 @@
static void ReplaceExistingVoice(void)
{
- opl_voice_t *rover;
- opl_voice_t *result;
+ int i;
+ int result;
// Check the allocated voices, if we find an instrument that is
// of a lower priority to the new instrument, discard it.
@@ -778,18 +741,18 @@
// than higher-numbered channels, eg. MIDI channel 1 is never
// discarded for MIDI channel 2.
- result = voice_alloced_list;
+ result = 0;
- for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+ for (i = 0; i < voice_alloced_num; i++)
{
- if (rover->current_instr_voice != 0
- || rover->channel >= result->channel)
+ if (voice_alloced_list[i]->current_instr_voice != 0
+ || voice_alloced_list[i]->channel
+ >= voice_alloced_list[result]->channel)
{
- result = rover;
+ result = i;
}
}
- VoiceKeyOff(result);
ReleaseVoice(result);
}
@@ -798,53 +761,43 @@
static void ReplaceExistingVoiceDoom1(void)
{
- opl_voice_t *rover;
- opl_voice_t *result;
+ int i;
+ int result;
- result = voice_alloced_list;
+ result = 0;
- for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+ for (i = 0; i < voice_alloced_num; i++)
{
- if (rover->channel > result->channel)
+ if (voice_alloced_list[i]->channel
+ > voice_alloced_list[result]->channel)
{
- result = rover;
+ result = i;
}
}
- 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 result;
int priority;
- result = voice_alloced_list;
+ result = 0;
- roverend = voice_alloced_list;
+ priority = 0x8000;
for (i = 0; i < voice_alloced_num - 3; i++)
{
- roverend = roverend->next;
- }
-
- priority = 0x8000;
-
- for (rover = voice_alloced_list; rover != roverend; rover = rover->next)
- {
- if (rover->priority < priority
- && rover->channel >= channel)
+ if (voice_alloced_list[i]->priority < priority
+ && voice_alloced_list[i]->channel >= channel)
{
- priority = rover->priority;
- result = rover;
+ priority = voice_alloced_list[i]->priority;
+ result = i;
}
}
- VoiceKeyOff(result);
ReleaseVoice(result);
}
@@ -1101,7 +1054,7 @@
break;
default:
case opl_doom_1_9:
- if (voice_free_list == NULL)
+ if (voice_free_num == 0)
{
ReplaceExistingVoice();
}
@@ -1210,35 +1163,18 @@
// Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event.
static void AllNotesOff(opl_channel_data_t *channel, unsigned int param)
{
- opl_voice_t *rover;
- opl_voice_t *prev;
+ int i;
- rover = voice_alloced_list;
- prev = NULL;
-
- while (rover!=NULL)
+ for (i = 0; i < voice_alloced_num; i++)
{
- if (rover->channel == channel)
+ if (voice_alloced_list[i]->channel == channel)
{
- VoiceKeyOff(rover);
-
// Finished with this voice now.
- ReleaseVoice(rover);
- if (prev == NULL)
- {
- rover = voice_alloced_list;
- }
- else
- {
- rover = prev->next;
- }
+ ReleaseVoice(i);
+
+ i--;
}
- else
- {
- prev = rover;
- rover = rover->next;
- }
}
}
@@ -1286,7 +1222,11 @@
static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event)
{
opl_channel_data_t *channel;
- unsigned int i;
+ int i;
+ opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2];
+ unsigned int voice_updated_num = 0;
+ opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2];
+ unsigned int voice_not_updated_num = 0;
// Update the channel bend value. Only the MSB of the pitch bend
// value is considered: this is what Doom does.
@@ -1296,13 +1236,29 @@
// Update all voices for this channel.
- for (i = 0; i < num_opl_voices; ++i)
+ for (i = 0; i < voice_alloced_num; ++i)
{
- if (voices[i].channel == channel)
+ if (voice_alloced_list[i]->channel == channel)
{
- UpdateVoiceFrequency(&voices[i]);
+ UpdateVoiceFrequency(voice_alloced_list[i]);
+ voice_updated_list[voice_updated_num++] = voice_alloced_list[i];
}
+ else
+ {
+ voice_not_updated_list[voice_not_updated_num++] =
+ voice_alloced_list[i];
+ }
}
+
+ for (i = 0; i < voice_not_updated_num; i++)
+ {
+ voice_alloced_list[i] = voice_not_updated_list[i];
+ }
+
+ for (i = 0; i < voice_updated_num; i++)
+ {
+ voice_alloced_list[i + voice_not_updated_num] = voice_updated_list[i];
+ }
}
static void MetaSetTempo(unsigned int tempo)
@@ -1400,13 +1356,13 @@
}
static void ScheduleTrack(opl_track_data_t *track);
-static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel);
+static void InitChannel(opl_channel_data_t *channel);
// Restart a song from the beginning.
static void RestartSong(void *unused)
{
- unsigned int i, j;
+ unsigned int i;
running_tracks = num_tracks;
@@ -1416,11 +1372,12 @@
{
MIDI_RestartIterator(tracks[i].iter);
ScheduleTrack(&tracks[i]);
- for (j = 0; j < MIDI_CHANNELS_PER_TRACK; ++j)
- {
- InitChannel(&tracks[i], &tracks[i].channels[j]);
- }
}
+
+ for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
+ {
+ InitChannel(&channels[i]);
+ }
}
// Callback function invoked when another event needs to be read from
@@ -1485,7 +1442,7 @@
// Initialize a channel.
-static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel)
+static void InitChannel(opl_channel_data_t *channel)
{
// TODO: Work out sensible defaults?
@@ -1505,16 +1462,10 @@
static void StartTrack(midi_file_t *file, unsigned int track_num)
{
opl_track_data_t *track;
- unsigned int i;
track = &tracks[track_num];
track->iter = MIDI_IterateTrack(file, track_num);
- for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
- {
- InitChannel(track, &track->channels[i]);
- }
-
// Schedule the first event.
ScheduleTrack(track);
@@ -1555,6 +1506,11 @@
{
StartTrack(file, i);
}
+
+ for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
+ {
+ InitChannel(&channels[i]);
+ }
}
static void I_OPL_PauseSong(void)
@@ -1610,13 +1566,9 @@
// Free all voices.
- for (i = 0; i < num_opl_voices; ++i)
+ for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
{
- if (voices[i].channel != NULL)
- {
- VoiceKeyOff(&voices[i]);
- ReleaseVoice(&voices[i]);
- }
+ AllNotesOff(&channels[i], 0);
}
// Free all track data.
@@ -1747,7 +1699,7 @@
// Release GENMIDI lump
- W_ReleaseLumpName("GENMIDI");
+ W_ReleaseLumpName(DEH_String("genmidi"));
music_initialized = false;
}
@@ -1854,7 +1806,7 @@
for (i = MIDI_CHANNELS_PER_TRACK - 1; i >= 0; --i)
{
- if (tracks[0].channels[i].instrument != &main_instrs[0])
+ if (channels[i].instrument != &main_instrs[0])
{
return i + 1;
}
@@ -1865,11 +1817,11 @@
static int ChannelInUse(opl_channel_data_t *channel)
{
- opl_voice_t *voice;
+ int i;
- for (voice = voice_alloced_list; voice != NULL; voice = voice->next)
+ for (i = 0; i < voice_alloced_num; i++)
{
- if (voice->channel == channel)
+ if (voice_alloced_list[i]->channel == channel)
{
return 1;
}
@@ -1896,17 +1848,17 @@
for (i = 0; i < NumActiveChannels(); ++i)
{
- if (tracks[0].channels[i].instrument == NULL)
+ if (channels[i].instrument == NULL)
{
continue;
}
- instr_num = tracks[0].channels[i].instrument - main_instrs;
+ instr_num = channels[i].instrument - main_instrs;
M_snprintf(tmp, sizeof(tmp),
"chan %i: %c i#%i (%s)\n",
i,
- ChannelInUse(&tracks[0].channels[i]) ? '\'' : ' ',
+ ChannelInUse(&channels[i]) ? '\'' : ' ',
instr_num + 1,
main_instr_names[instr_num]);
M_StringConcat(result, tmp, result_len);
--- a/src/w_file.h
+++ b/src/w_file.h
@@ -28,35 +28,31 @@
typedef struct
{
// Open a file for reading.
-
wad_file_t *(*OpenFile)(char *path);
// Close the specified file.
-
void (*CloseFile)(wad_file_t *file);
// Read data from the specified position in the file into the
// provided buffer. Returns the number of bytes read.
-
size_t (*Read)(wad_file_t *file, unsigned int offset,
void *buffer, size_t buffer_len);
-
} wad_file_class_t;
struct _wad_file_s
{
// Class of this file.
-
wad_file_class_t *file_class;
// If this is NULL, the file cannot be mapped into memory. If this
// is non-NULL, it is a pointer to the mapped file.
-
byte *mapped;
// Length of the file, in bytes.
-
unsigned int length;
+
+ // File's location on disk.
+ const char *path;
};
// Open the specified file. Returns a pointer to a new wad_file_t
--- a/src/w_file_posix.c
+++ b/src/w_file_posix.c
@@ -26,6 +26,7 @@
#include <sys/mman.h>
#include <string.h>
+#include "m_misc.h"
#include "w_file.h"
#include "z_zone.h"
@@ -90,6 +91,7 @@
result = Z_Malloc(sizeof(posix_wad_file_t), PU_STATIC, 0);
result->wad.file_class = &posix_wad_file;
result->wad.length = GetFileLength(handle);
+ result->wad.path = M_StringDuplicate(path);
result->handle = handle;
// Try to map the file into memory with mmap:
--- a/src/w_file_stdc.c
+++ b/src/w_file_stdc.c
@@ -48,6 +48,7 @@
result->wad.file_class = &stdc_wad_file;
result->wad.mapped = NULL;
result->wad.length = M_FileLength(fstream);
+ result->wad.path = M_StringDuplicate(path);
result->fstream = fstream;
return &result->wad;
--- a/src/w_file_win32.c
+++ b/src/w_file_win32.c
@@ -26,6 +26,7 @@
#include <windows.h>
#include "i_system.h"
+#include "m_misc.h"
#include "w_file.h"
#include "z_zone.h"
@@ -115,6 +116,7 @@
result = Z_Malloc(sizeof(win32_wad_file_t), PU_STATIC, 0);
result->wad.file_class = &win32_wad_file;
result->wad.length = GetFileLength(handle);
+ result->wad.path = M_StringDuplicate(path);
result->handle = handle;
// Try to map the file into memory with mmap: