summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore19
-rw-r--r--.lvimrc3
-rw-r--r--BUGS15
-rw-r--r--ChangeLog887
-rw-r--r--INSTALL159
-rw-r--r--Makefile.am5
-rw-r--r--NEWS66
-rw-r--r--README55
-rw-r--r--README.OPL107
-rw-r--r--TODO20
-rw-r--r--codeblocks/config.h6
-rw-r--r--codeblocks/game-res.rc10
-rw-r--r--codeblocks/setup-res.rc8
-rw-r--r--configure.in13
-rw-r--r--man/manpage.template11
-rw-r--r--msvc/config.h6
-rw-r--r--msvc/win32.rc10
-rw-r--r--opl/.gitignore7
-rw-r--r--opl/Makefile.am19
-rw-r--r--opl/dbopl.c1602
-rw-r--r--opl/dbopl.h203
-rw-r--r--opl/examples/.gitignore7
-rw-r--r--opl/examples/Makefile.am8
-rw-r--r--opl/examples/droplay.c217
-rw-r--r--opl/ioperm_sys.c361
-rw-r--r--opl/ioperm_sys.h36
-rw-r--r--opl/opl.c466
-rw-r--r--opl/opl.h137
-rw-r--r--opl/opl_internal.h64
-rw-r--r--opl/opl_linux.c110
-rw-r--r--opl/opl_obsd.c125
-rw-r--r--opl/opl_queue.c280
-rw-r--r--opl/opl_queue.h45
-rw-r--r--opl/opl_sdl.c510
-rw-r--r--opl/opl_timer.c251
-rw-r--r--opl/opl_timer.h42
-rw-r--r--opl/opl_win32.c172
-rw-r--r--pkg/config.make.in1
-rw-r--r--pkg/osx/GNUmakefile4
-rw-r--r--pkg/win32/GNUmakefile2
-rw-r--r--rpm.spec.in4
-rw-r--r--src/.gitignore4
-rw-r--r--src/Makefile.am14
-rw-r--r--src/deh_main.c10
-rw-r--r--src/deh_mapping.c140
-rw-r--r--src/deh_mapping.h22
-rw-r--r--src/deh_str.c227
-rw-r--r--src/deh_str.h9
-rw-r--r--src/doom/.gitignore4
-rw-r--r--src/doom/am_map.c6
-rw-r--r--src/doom/d_main.c196
-rw-r--r--src/doom/d_net.c10
-rw-r--r--src/doom/f_finale.c2
-rw-r--r--src/doom/g_game.c188
-rw-r--r--src/doom/hu_stuff.c14
-rw-r--r--src/doom/m_menu.c4
-rw-r--r--src/doom/p_saveg.c24
-rw-r--r--src/doom/p_saveg.h1
-rw-r--r--src/doom/p_setup.c100
-rw-r--r--src/doom/r_draw.c111
-rw-r--r--src/doom/s_sound.c9
-rw-r--r--src/doom/st_stuff.c22
-rw-r--r--src/doom/wi_stuff.c47
-rw-r--r--src/heretic/.gitignore7
-rw-r--r--src/heretic/Makefile.am13
-rw-r--r--src/heretic/am_map.c7
-rw-r--r--src/heretic/ct_chat.c13
-rw-r--r--src/heretic/d_main.c102
-rw-r--r--src/heretic/deh_ammo.c122
-rw-r--r--src/heretic/deh_frame.c344
-rw-r--r--src/heretic/deh_htext.c856
-rw-r--r--src/heretic/deh_htic.c186
-rw-r--r--src/heretic/deh_htic.h60
-rw-r--r--src/heretic/deh_sound.c118
-rw-r--r--src/heretic/deh_thing.c150
-rw-r--r--src/heretic/deh_weapon.c131
-rw-r--r--src/heretic/doomdef.h1
-rw-r--r--src/heretic/dstrings.h177
-rw-r--r--src/heretic/f_finale.c38
-rw-r--r--src/heretic/g_game.c21
-rw-r--r--src/heretic/in_lude.c73
-rw-r--r--src/heretic/info.c162
-rw-r--r--src/heretic/info.h15
-rw-r--r--src/heretic/mn_menu.c60
-rw-r--r--src/heretic/p_action.h160
-rw-r--r--src/heretic/p_doors.c7
-rw-r--r--src/heretic/p_inter.c75
-rw-r--r--src/heretic/p_pspr.c11
-rw-r--r--src/heretic/p_spec.c40
-rw-r--r--src/heretic/p_switch.c5
-rw-r--r--src/heretic/p_user.c3
-rw-r--r--src/heretic/r_data.c40
-rw-r--r--src/heretic/r_draw.c49
-rw-r--r--src/heretic/r_plane.c3
-rw-r--r--src/heretic/r_things.c14
-rw-r--r--src/heretic/s_sound.c21
-rw-r--r--src/heretic/sb_bar.c170
-rw-r--r--src/heretic/sounds.h5
-rw-r--r--src/hexen/.gitignore6
-rw-r--r--src/hexen/h2_main.c28
-rw-r--r--src/hexen/mn_menu.c47
-rw-r--r--src/hexen/p_acs.c2
-rw-r--r--src/hexen/p_spec.c2
-rw-r--r--src/hexen/r_things.c5
-rw-r--r--src/hexen/s_sound.c16
-rw-r--r--src/i_oplmusic.c1464
-rw-r--r--src/i_scale.c14
-rw-r--r--src/i_sdlmusic.c2
-rw-r--r--src/i_sdlsound.c72
-rw-r--r--src/i_sound.c10
-rw-r--r--src/i_video.c281
-rw-r--r--src/i_video.h4
-rw-r--r--src/m_argv.c2
-rw-r--r--src/m_config.c132
-rw-r--r--src/m_controls.c47
-rw-r--r--src/m_controls.h17
-rw-r--r--src/m_misc.c34
-rw-r--r--src/m_misc.h2
-rw-r--r--src/midifile.c814
-rw-r--r--src/midifile.h175
-rw-r--r--src/net_client.c3
-rw-r--r--src/setup/.gitignore3
-rw-r--r--src/setup/joystick.c2
-rw-r--r--src/setup/keyboard.c27
-rw-r--r--src/setup/keyboard.h53
-rw-r--r--src/setup/mainmenu.c60
-rw-r--r--src/setup/mouse.c9
-rw-r--r--src/setup/multiplayer.c35
-rw-r--r--src/setup/sound.c37
-rw-r--r--src/setup/txt_keyinput.c2
-rw-r--r--src/setup/txt_mouseinput.c4
-rw-r--r--src/strife/.gitignore4
-rw-r--r--src/strife/am_map.c6
-rw-r--r--src/strife/d_main.c196
-rw-r--r--src/strife/d_net.c10
-rw-r--r--src/strife/f_finale.c2
-rw-r--r--src/strife/g_game.c183
-rw-r--r--src/strife/hu_stuff.c14
-rw-r--r--src/strife/m_menu.c4
-rw-r--r--src/strife/p_saveg.c24
-rw-r--r--src/strife/p_saveg.h1
-rw-r--r--src/strife/p_setup.c100
-rw-r--r--src/strife/r_draw.c111
-rw-r--r--src/strife/st_stuff.c4
-rw-r--r--src/strife/wi_stuff.c47
-rw-r--r--src/w_main.c206
-rw-r--r--src/w_main.h32
-rw-r--r--src/z_zone.c3
-rw-r--r--textscreen/Makefile.am6
149 files changed, 13145 insertions, 1715 deletions
diff --git a/.gitignore b/.gitignore
index 57ab998c..34652d0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,24 +1,21 @@
-CMDLINE
-INSTALL
-Makefile
Makefile.in
-TAGS
-aclocal.m4
-autom4te.cache
+Makefile
+INSTALL
+CMDLINE
autotools
-bin
-config.h
+aclocal.m4
+configure
config.hin
config.log
config.status
-configure
-lib
-obj
+config.h
+autom4te.cache
rpm.spec
stamp-h
stamp-h.in
stamp-h1
tags
+TAGS
# These are the default patterns globally ignored by Subversion:
*.o
diff --git a/.lvimrc b/.lvimrc
index cc2893d9..39b0158f 100644
--- a/.lvimrc
+++ b/.lvimrc
@@ -16,8 +16,9 @@ set tags+=tags
let tagfiles = findfile("tags", ".;", -1)
" Add tag files for libraries:
-call add(tagfiles, topdir . "textscreen/tags")
+call add(tagfiles, topdir . "opl/tags")
call add(tagfiles, topdir . "pcsound/tags")
+call add(tagfiles, topdir . "textscreen/tags")
for tagfile in tagfiles
" Don't go beyond the project top level when adding parent dirs:
diff --git a/BUGS b/BUGS
index 5dd8a531..ee6bf09a 100644
--- a/BUGS
+++ b/BUGS
@@ -6,21 +6,6 @@
effects are cut off at the wrong distance. This needs further
investigation.
-* Music plays back differently.
-
- Vanilla Doom was typically played with a SoundBlaster (or compatible)
- card. It programmed the registers for the OPL music chip directly
- in order to emulate the various General MIDI instruments. However,
- Chocolate Doom uses the OS's native MIDI playback interfaces to play
- MIDI sound. As the OPL is programmed differently, the music sounds
- different to the original, even when using an original SoundBlaster
- card.
-
- This can be worked around in the future: OPL emulation code exists that
- simulates an OPL chip in software. Furthermore, depending on the OS,
- it may be possible to program the OPL directly in order to get the
- same sound.
-
* A small number of Doom bugs are almost impossible to emulate.
An example of this can be seen in Ledmeister's "Blackbug" demo which
diff --git a/ChangeLog b/ChangeLog
index a370d4bd..0120d5db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,890 @@
+2010-05-30 04:03:44 fraggle
+
+ Add INSTALL to all distribution packages, add note in README.
+
+2010-05-30 03:56:58 fraggle
+
+ Clarify/update install instructions.
+
+2010-05-14 19:42:32 fraggle
+
+ Don't grab the mouse when the demo sequence advances.
+
+2010-05-03 17:47:25 fraggle
+
+ Oops.
+
+2010-05-03 16:58:52 fraggle
+
+ Update NEWS.
+
+2010-05-01 22:47:26 fraggle
+
+ Further sanity checking on use of strcpy() with dehacked string
+ replacements.
+
+2010-05-01 22:20:30 fraggle
+
+ Silence printf(DEH_String(...)) warnings, by providing a DEH_printf
+ function that checks the format string is a valid replacement. Also
+ add DEH_fprintf and DEH_snprintf functions to use throughout the code
+ to do similar checking.
+
+2010-05-01 20:22:52 fraggle
+
+ Fix compiler warnings with savegame and response file code.
+
+2010-04-30 20:58:30 fraggle
+
+ Merge contents of OPL-TODO into TODO file.
+
+2010-04-30 20:38:24 fraggle
+
+ Add textscreen Doxyfile to dist. Add .desktop file to svn:ignore. Add
+ opl ctags file to localvimrc.
+
+2010-04-25 00:53:03 fraggle
+
+ Add -reject_pad_with_ff parameter to allow padding value to be
+ specified.
+
+2010-04-23 21:46:29 fraggle
+
+ Add REJECT buffer overflow emulation, based on code from PrBoom+
+ (thanks entryway). Fixes YDFEAR25.LMP.
+
+2010-04-22 22:38:51 fraggle
+
+ Disable OPL debugging messages.
+
+2010-03-08 18:52:59 fraggle
+
+ Add OPL-TODO to dist, set svn:ignore properties.
+
+2010-03-08 18:50:29 fraggle
+
+ Use native MIDI music by default.
+
+2010-03-08 01:14:23 fraggle
+
+ Merge opl-branch to trunk.
+
+ OPL support still isn't perfect, and it certainly isn't complete.
+ However, for now, it's good enough.
+
+2010-02-10 20:21:21 fraggle
+
+ Bump version number, update ChangeLog and NEWS.
+
+2010-01-31 18:21:50 fraggle
+
+ Change Windows resource file to use PACKAGE_COPYRIGHT and
+ PACKAGE_LICENSE macros.
+
+2010-01-30 16:14:04 fraggle
+
+ Change directory to home directory before launching the game, so that
+ recorded demos go somewhere sensible.
+
+2010-01-30 16:04:24 fraggle
+
+ Set launch button as default button, so that it is possible to launch
+ the game by pressing return.
+
+2010-01-30 00:37:17 fraggle
+
+ Rename mus2mid functions to be consistent with coding standard.
+
+2010-01-29 23:28:35 fraggle
+
+ Remove unused PACKAGE_LONGDESC.
+
+2010-01-29 19:17:56 fraggle
+
+ When doing a MUS to MID conversion, allocate MIDI channels so that the
+ lowest-numbered MIDI channels are used before higher-numbered ones.
+ Fixes ear-piercing whistle sound in the MAP05 music when playing with
+ timidity and EAWPATS (thanks entryway / HackNeyed).
+
+2010-01-29 03:55:20 fraggle
+
+ Fix indentation/style etc. in mus2mid.c.
+
+2010-01-27 19:16:26 fraggle
+
+ Add tags files to svn:ignore properties.
+
+2010-01-26 19:21:18 fraggle
+
+ Minor fix of British spelling -> American.
+
+2010-01-26 19:18:18 fraggle
+
+ Fix glass hack windows where a linedef is flagged as two sided but has
+ only one side. Fixes WADs such as OTTAWAU.WAD (thanks Never_Again).
+
+2010-01-23 23:06:45 fraggle
+
+ Add menu item to launcher to open a terminal window that can be used
+ to start the game. Add missing 'edit' menu. Set svn:ignore property
+ for osx directory.
+
+2010-01-18 19:40:50 fraggle
+
+ Fix package source URL.
+
+2010-01-18 19:29:48 fraggle
+
+ Tweak package description slightly.
+
+2010-01-18 19:14:54 fraggle
+
+ Define project short description, copyright, maintainer and URL in
+ configure.in. Use these for the Info-gnustep.plist file. Add
+ generated .spec file for building RPM packages.
+
+2010-01-17 16:58:37 fraggle
+
+ Update NEWS.
+
+2010-01-17 16:31:03 fraggle
+
+ Restore the original cursor when shutting down video code, this should
+ hopefully fix the problem with the mouse cursor disappearing when
+ exiting on Win9x (thanks Janizdreg).
+
+2010-01-16 19:20:11 fraggle
+
+ Update TODO file.
+
+2010-01-15 19:29:28 fraggle
+
+ Don't open the configuration window when the launcher is first run;
+ display an error message if the user tries to launch the game without
+ an IWAD selected.
+
+2010-01-15 19:14:02 fraggle
+
+ Fix GNUstep info panel.
+
+2010-01-15 18:51:35 fraggle
+
+ Center the launcher window and config window on startup.
+
+2010-01-15 18:40:37 fraggle
+
+ Add wadfile.png for GNUstep build.
+
+2010-01-15 18:13:05 fraggle
+
+ Extend osx makefile to allow building of a working GNUstep
+ application, for testing purposes. Add GNUstep version of Info.plist,
+ remove app-skeleton directory and move contents up.
+
+2010-01-14 18:47:03 fraggle
+
+ In Chex Quest, use the radiation suit colormap instead of the red
+ colormaps that are usually used when taking damage (or using the
+ berserk pack). This matches the Vanilla chex.exe behavior (thanks
+ Fuzztooth).
+
+2010-01-12 20:16:25 fraggle
+
+ Oops.
+
+2010-01-12 20:15:34 fraggle
+
+ Strip executables when building Windows CE packages.
+
+2010-01-12 20:14:11 fraggle
+
+ Rearrange order of Makefile generation to alphabetical order.
+
+2010-01-12 20:12:56 fraggle
+
+ Move Makefile definitions for CC and STRIP into config.make, use
+ versions from autoconf.
+
+2010-01-12 20:09:54 fraggle
+
+ Set main menu title based on package name, not fixed string.
+
+2010-01-12 20:09:01 fraggle
+
+ Place commercial level name strings consecutively in the same array,
+ so that overflowing the end of one list accesses the start of the
+ next. This trick is used by pl2.wad for its MAP33 secret level.
+
+2010-01-12 01:32:24 fraggle
+
+ Add missing connection for plutonia.wad set button.
+
+2010-01-12 01:20:48 fraggle
+
+ Add document icon file and use for file associations.
+
+2010-01-11 19:10:42 fraggle
+
+ Insert new files into the command line at the correct location,
+ allowing multiple files to be opened at once.
+
+2010-01-11 01:35:04 fraggle
+
+ When launching a file from the finder, add the new file into the
+ command line at the appropriate position in the command line string.
+
+2010-01-10 20:46:15 fraggle
+
+ Change "@executable_path@" to "@executable_path"
+
+2010-01-10 18:48:21 fraggle
+
+ Install docs with a single cp, rather than using a for loop.
+
+2010-01-10 18:42:35 fraggle
+
+ Recursively copy library dependencies into destination package.
+ Identify libraries to be installed based on the path in which they are
+ located, rather than whether there is "libSDL" in the name. Use
+ install_name_tool to change the search path so that the system looks
+ for libraries in @executable_path@ rather than their location on the
+ machine upon which the program was built.
+
+2010-01-09 21:06:31 fraggle
+
+ Clear existing arguments when adding a file.
+
+2010-01-09 20:42:30 fraggle
+
+ Add file to command line when opened; add link from AppController to
+ LauncherManager.
+
+2010-01-09 18:54:04 fraggle
+
+ Initial code to identify file type by extension and add file to
+ command line.
+
+2010-01-09 18:38:48 fraggle
+
+ Hook in AppController as delegate for application, add file
+ associations to property list file.
+
+2010-01-05 17:20:58 fraggle
+
+ Add "clean" target to package makefiles.
+
+2010-01-05 15:52:12 fraggle
+
+ Move config.make up to pkg/ directory. Use static makefiles to
+ generate all packages, rather than dynamically generated makefiles.
+ Add pkg/osx to dist. Make OS X staging directory depend on top level
+ documentation files. Generate CMDLINE as part of standard build if it
+ is not already present. Set svn:ignore properties.
+
+2010-01-04 22:53:44 fraggle
+
+ Fix single space error when listing libraries.
+
+2010-01-04 22:45:45 fraggle
+
+ Copy binaries into app dir along with libraries.
+
+2010-01-04 22:24:48 fraggle
+
+ Include documentation files in package.
+
+2010-01-04 22:19:53 fraggle
+
+ Fix GNUstep build.
+
+2010-01-04 22:11:11 fraggle
+
+ Generate Info.plist and config.make in configure and remove temporary
+ versions. Include config.h from top level.
+
+2010-01-04 22:01:32 fraggle
+
+ Import OS X launcher code to trunk.
+
+2010-01-03 03:49:11 fraggle
+
+ Add quotes around $@ in autogen script (thanks exp[x])
+
+2009-12-28 20:57:20 fraggle
+
+ When recording low resolution (non-longtics) Vanilla demos, carry
+ forward the error from angleturn caused by the reduced resolution, so
+ that consecutive errors can accumulate, possibly making turning
+ slightly smoother.
+
+2009-12-27 01:42:13 fraggle
+
+ Oops.
+
+2009-12-27 00:11:18 fraggle
+
+ Allow DOOMWADDIR/DOOMWADPATH to contain the complete path to IWAD
+ files, as well as directories in which to search for IWAD files.
+
+2009-12-18 22:11:06 fraggle
+
+ Fix poor quality application icons seen when the game is running. Add
+ back 8-bit icon files alongside files including both 8-bit and high
+ quality 32-bit versions. Use the high quality icon files for resource
+ files includes, and the low quality ones for in-game SDL.
+
+2009-12-18 21:11:32 fraggle
+
+ Update generated source files containing icon data.
+
+2009-12-18 21:10:35 fraggle
+
+ Make ExecuteCommand() under Unix return a failure when the executable
+ cannot be executed.
+
+2009-12-14 20:57:04 fraggle
+
+ Use GetModuleFileNameW to get the (Unicode) path to the Doom
+ executable. This hopefully fixes problems with Unicode directory
+ names.
+
+2009-12-14 18:54:25 fraggle
+
+ Add Chocolate Doom/setup icons with scaled versions for various
+ different icon sizes (thanks MikeRS).
+
+2009-12-12 01:20:49 fraggle
+
+ Fix textscreen black border bug.
+
+2009-12-09 02:40:39 fraggle
+
+ Fix the setup tool on Windows Vista/7 to not prompt for elevated
+ permissions and to disable the "Program Compatibility Assistant"
+ (thanks hobbs and MikeRS).
+
+2009-11-29 22:50:17 fraggle
+
+ Add other missing files to dist.
+
+2009-11-29 22:25:51 fraggle
+
+ Include .lvimrc in dist.
+
+2009-11-21 03:56:59 fraggle
+
+ Add Makefile to build Win32 packages.
+
+2009-11-21 02:05:56 fraggle
+
+ Use execvp() rather than execv(), to look up Doom binary in the PATH
+ if necessary.
+
+2009-11-21 00:40:58 fraggle
+
+ Apply configuration file invalid key setting fix to setup code.
+
+2009-11-21 00:38:16 fraggle
+
+ Don't crash if key settings are set in a configuration file that are
+ out of range (thanks entryway).
+
+2009-11-21 00:24:59 fraggle
+
+ Fix crash with chocolate-setup under Windows (thanks Janizdreg).
+
+2009-11-19 21:49:13 fraggle
+
+ Rework the OS X MIDI disabling code, as SDL_mixer 1.2.11 fixes the
+ crash. Check and disable MIDI by default if using an older version of
+ SDL on OS X.
+
+2009-11-19 21:07:31 fraggle
+
+ Make chocolate-setup use its own location in the filesystem to find
+ the location of the chocolate-doom executable. Remove INSTALL_DIR
+ define.
+
+2009-11-05 19:57:55 fraggle
+
+ Perform bounds checking on values passed to TXT_UpdateScreenArea() to
+ avoid crashes.
+
+2009-10-26 19:28:12 fraggle
+
+ Initial hacks for compiling under SDL 1.3.
+
+2009-10-17 21:13:54 fraggle
+
+ Fix error in last change.
+
+2009-10-17 20:39:37 fraggle
+
+ Use M_StrToInt() when processing values passed with -spechit, so that
+ hex values can be specified.
+
+2009-10-17 20:29:46 fraggle
+
+ Import donut overrun emulation code from PrBoom+ (Thanks entryway).
+
+2009-10-16 19:10:30 fraggle
+
+ Fix compilation under MSVC (thanks entryway).
+
+2009-10-10 23:58:25 fraggle
+
+ Rename pkg/wince/Makefile to pkg/wince/GNUmakefile (it uses GNU
+ extensions).
+
+2009-10-10 22:46:14 fraggle
+
+ Add pkg directory to make dist.
+
+2009-10-10 02:02:58 fraggle
+
+ Don't crash when using the donut special type and the joining linedef
+ is one sided (thanks Alexander Waldmann).
+
+2009-10-05 21:25:53 fraggle
+
+ Fix desync in ep1-0500.lmp on 64-bit (thanks exp(x)).
+
+2009-10-05 00:38:14 fraggle
+
+ Provide pointer to STARTUPINFO structure when calling CreateProcessW,
+ to stop crash under normal Windows (not CE) when launching Doom from
+ the setup tools (thanks Janizdreg).
+
+2009-10-01 20:08:21 fraggle
+
+ Oops.
+
+2009-10-01 02:04:00 fraggle
+
+ Oops.
+
+2009-10-01 00:07:03 fraggle
+
+ Change British English spellings to American English, for consistency.
+
+2009-09-20 16:27:40 fraggle
+
+ Use "const char" in libtextscreen where appropriate (thanks entryway).
+
+2009-09-11 22:56:47 fraggle
+
+ Add (lack of) copyright notice for SDL workaround.
+
+2009-09-07 20:43:04 fraggle
+
+ Fix compilation under MacOS X.
+
+2009-09-06 19:15:52 fraggle
+
+ Fixes for MSVC compile (thanks entryway).
+
+2009-08-28 00:27:47 fraggle
+
+ Allow PGUP/PGDN to scroll up and down in scroll panes (thanks
+ LionsPhil).
+
+2009-07-20 23:27:59 fraggle
+
+ Remove redundant variable assignment (thanks Quasar/Yagisan)
+
+2009-07-20 01:37:41 fraggle
+
+ Save and display the loading disk icon as a fixed 16x16 square, from
+ an image drawn at the bottom right corner of the screen. This seems
+ to be the same as how Vanilla behaves, and fixes chook3.wad, that uses
+ an STDISK replacement with an offset that pushes the image to the
+ left.
+
+2009-07-13 23:43:06 fraggle
+
+ Add stdio.h include to fix MSVC build (thanks Kaiser)
+
+2009-07-12 17:47:12 fraggle
+
+ Fix compile with libsamplerate.
+
+2009-07-12 15:00:50 fraggle
+
+ On Windows CE, use the Windows API to find the amount of available
+ memory, so that at least two megabytes are always left available to
+ the OS.
+
+2009-07-11 12:15:32 fraggle
+
+ Add missing item to NEWS.
+
+2009-07-07 20:46:55 fraggle
+
+ Update NEWS.
+
+2009-07-07 20:38:00 fraggle
+
+ Fix launching of the game from the setup tool in Windows CE.
+
+2009-06-21 20:33:35 fraggle
+
+ Add Makefile for building CAB files, dependency calculation.
+
+2009-06-21 20:19:43 fraggle
+
+ Use correct filename for SDL_net DLL.
+
+2009-06-21 20:03:38 fraggle
+
+ Remove temporary files after generating CAB file.
+
+2009-06-20 23:13:44 fraggle
+
+ Add script to generate Windows CE install package.
+
+2009-06-16 20:47:13 fraggle
+
+ Automatically allocate a smaller zone size if it was not possible to
+ allocate the default 16 MiB.
+
+2009-06-13 18:10:18 fraggle
+
+ Don't post zero key events.
+
+2009-06-12 20:07:55 fraggle
+
+ On Windows CE systems without a keyboard, patch the default settings
+ to use hardware keys.
+
+2009-06-12 18:58:42 fraggle
+
+ Remove debug messages.
+
+2009-06-12 18:35:39 fraggle
+
+ Set the USER environment variable based on the owner information from
+ the registry.
+
+2009-06-12 18:34:27 fraggle
+
+ Always grab input on Windows CE.
+
+2009-06-11 22:34:36 fraggle
+
+ Include libc_wince.a in chocolate-server build.
+
+2009-06-11 20:41:20 fraggle
+
+ Grab the input in setup when reading a new key binding, so that
+ Windows CE buttons are read properly. Map buttons to PC function
+ keys.
+
+2009-06-11 19:19:05 fraggle
+
+ Include libc_wince.h on Windows CE.
+
+2009-06-11 19:18:12 fraggle
+
+ Declare getenv/putenv on Windows CE for recent SDL versions that do
+ not declare it.
+
+2009-06-10 20:03:08 fraggle
+
+ Add key bindings for pause, message refresh.
+
+2009-06-08 20:26:29 fraggle
+
+ Remove debugging code.
+
+2009-06-08 19:15:57 fraggle
+
+ Use SDL's getenv/putenv implementation, and populate at startup.
+
+2009-06-08 00:41:10 fraggle
+
+ Use CreateFileW instead of OpenFile (doesn't exist on Windows CE)
+
+2009-06-07 20:08:08 fraggle
+
+ Fix header includes (thanks exp[x])
+
+2009-06-07 19:18:02 fraggle
+
+ Don't add DirectX/Windib selector on Windows CE.
+
+2009-06-07 18:53:25 fraggle
+
+ Use home dir to store configuration and savegames under Windows CE.
+
+2009-06-07 18:33:19 fraggle
+
+ Fix setup tool display configuration dialog when fullscreen is not
+ supported.
+
+2009-06-07 18:10:05 fraggle
+
+ Make auto-adjust code switch to windowed mode if no fullscreen modes
+ are available.
+
+2009-06-07 17:41:46 fraggle
+
+ Catch errors when initialising SDL. Use the small textscreen font by
+ default on Windows CE if no fullscreen modes are available.
+
+2009-06-07 17:39:08 fraggle
+
+ Add missing SDL_thread include.
+
+2009-06-07 17:35:43 fraggle
+
+ Don't try to use the SDL DirectX driver under Windows CE.
+
+2009-06-07 16:21:41 fraggle
+
+ Fix setup tool compile on Windows CE.
+
+2009-06-07 16:15:40 fraggle
+
+ Remove call to setbuf.
+
+2009-06-07 15:35:27 fraggle
+
+ Add IWAD search dirs for Windows CE.
+
+2009-06-07 15:20:46 fraggle
+
+ Exit with an error on failure to allocate zone memory.
+
+2009-06-07 03:10:21 fraggle
+
+ Use MessageBoxW instead of MessageBox (doesn't exist on Windows CE)
+
+2009-06-07 02:59:49 fraggle
+
+ Add README file for Windows CE library.
+
+2009-06-07 02:56:21 fraggle
+
+ Detect Windows CE target and build/include libc_wince files as
+ necessary.
+
+2009-06-07 02:50:47 rtc_marine
+
+ - Update textscreen codeblocks project to include txt_scrollpane.* and
+ txt_smallfont.h
+
+2009-06-07 02:33:58 fraggle
+
+ Include libc_wince.h when on Windows CE.
+
+2009-06-07 02:32:15 fraggle
+
+ Add CPU affinity function for Windows CE.
+
+2009-06-07 02:27:58 fraggle
+
+ Add libc_wince.h header, and EISDIR error value.
+
+2009-06-07 02:27:30 fraggle
+
+ Use GetUserNameExW, not GetUserName (doesn't exist on WinCE)
+
+2009-06-07 02:26:45 fraggle
+
+ Fix compile with FEATURE_MULTIPLAYER disabled.
+
+2009-06-07 02:24:40 fraggle
+
+ Fix compile with FEATURE_SOUND disabled.
+
+2009-06-07 01:56:23 fraggle
+
+ Add Windows CE implementations of some ANSI C functions that are
+ missing.
+
+2009-06-06 22:13:44 fraggle
+
+ Don't check for Steam/CD installer versions on Windows CE.
+
+2009-06-05 17:58:48 fraggle
+
+ Add key binding variables for automap and weapon keys.
+
+2009-06-04 00:37:02 fraggle
+
+ Increase height of menu bindings dialog.
+
+2009-06-04 00:35:05 fraggle
+
+ Use newer keyboard bindings dialog layout from raven-branch.
+
+2009-06-04 00:20:37 fraggle
+
+ Add unique key groups for menu navigation and shortcuts.
+
+2009-06-04 00:20:06 fraggle
+
+ Use key for confirming menu messages, not typed char.
+
+2009-06-03 21:45:54 fraggle
+
+ Add dialog to setup tool for editing menu shortcuts.
+
+2009-06-03 21:18:04 fraggle
+
+ Add config file variables to increase/decrease screen size.
+
+2009-06-03 20:59:26 fraggle
+
+ Fix shortcut keys for menu items.
+
+2009-06-03 20:55:50 fraggle
+
+ Add configuration file entries for menu key bindings.
+
+2009-06-03 20:37:19 fraggle
+
+ Add key_ variables for the keys used to control the menu.
+
+2009-05-26 23:14:24 fraggle
+
+ Fix tags for functions using TXT_UNCAST_ARG.
+
+2009-05-26 22:13:18 fraggle
+
+ Set appropriate vim 'tags' variable for ctags files.
+
+2009-05-21 20:18:38 fraggle
+
+ Set display settings window position based on screen dimensions,
+ rather than hard coding position.
+
+2009-05-19 18:07:49 fraggle
+
+ Fix manpage documentation for DOOMWADPATH (thanks MikeRS)
+
+2009-05-18 19:30:49 fraggle
+
+ Fix A_BossDeath behavior in v1.9 emulation mode (thanks entryway)
+
+2009-05-17 14:54:19 fraggle
+
+ Always use an SDL buffer size that is a power of two. Reduce buffer
+ size to 70ms.
+
+2009-05-12 19:03:20 fraggle
+
+ Add option to "join game" dialog in setup tool to autojoin a LAN game.
+
+2009-05-12 19:01:27 fraggle
+
+ Make txt_inputboxes emit a "changed" signal when their value is
+ changed.
+
+2009-05-07 22:59:38 fraggle
+
+ Calculate SDL buffer size automatically based on sample rate.
+
+2009-05-05 01:00:53 fraggle
+
+ Better ASCII chart.
+
+2009-05-05 00:46:27 fraggle
+
+ Minor smallfont fixups.
+
+2009-05-01 22:05:57 fraggle
+
+ Add copyright headers to textscreen examples.
+
+2009-04-26 17:59:08 fraggle
+
+ More smallfont fixups.
+
+2009-04-23 20:58:11 fraggle
+
+ Fix up some extended ASCII characters.
+
+2009-04-23 19:19:52 fraggle
+
+ Oops.
+
+2009-04-23 19:18:43 fraggle
+
+ Add small textscreen font for low resolution displays, based on the
+ Atari-Small font by Tom Fine.
+
+2009-03-15 14:44:23 fraggle
+
+ Fix clipped sounds when using libsamplerate (thanks David Flater)
+
+2009-03-14 15:28:41 fraggle
+
+ Add check to allow sched_setaffinity code to work on older versions of
+ libc.
+
+2009-03-12 18:55:27 fraggle
+
+ Define INVALID_SET_FILE_POINTER if it is not defined, to fix
+ compilation under MSVC6 (thanks Quasar)
+
+2009-03-08 22:51:25 fraggle
+
+ Add "make doc" target to run Doxygen, and add a Doxyfile. Add @file
+ tags to start of header files so that Doxygen will process them.
+
+2009-03-07 00:35:08 fraggle
+
+ Add documentation for high-level txt_desktop.h functions.
+
+2009-03-07 00:24:45 fraggle
+
+ Add documentation for high-level textscreen functions.
+
+2009-03-06 20:01:32 fraggle
+
+ Fix signed/unsigned conversion warning.
+
+2009-03-03 19:26:20 fraggle
+
+ Look up SetProcessAffinityMask function at runtime, so that the
+ program should work under Win9x again.
+
+2009-01-30 23:53:47 fraggle
+
+ Fix layout of widgets within scroll panes. Scroll scroll panes in
+ response to keyboard events.
+
+2009-01-29 23:26:03 fraggle
+
+ Shrink text box slightly.
+
+2009-01-29 23:00:14 fraggle
+
+ Allow clicking within scroll bars to set position.
+
+2009-01-29 22:54:13 fraggle
+
+ Add scrollable pane widget to textscreen library.
+
+2009-01-17 14:05:31 fraggle
+
+ Fix '-mmap' command line parameter.
+
+2009-01-07 22:05:13 fraggle
+
+ Create the ~/.chocolate-doom/savegames directory on startup if it does
+ not exist.
+
+2009-01-07 21:51:37 fraggle
+
+ Replace -nommap with -mmap; do not use mmap()ed file access by
+ default. Fixes Plutonia 2, and several other minor things.
+
+2008-12-10 20:25:05 fraggle
+
+ Bump version to 1.2.1, update NEWS and ChangeLog.
+
2008-12-10 20:20:10 fraggle
Fix crash when playing Doom 1 levels.
diff --git a/INSTALL b/INSTALL
index 80df9361..7ad85b7f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -2,13 +2,18 @@
Chocolate Doom installation
===========================
-These are instructions for how to install Chocolate Doom on Unix-like
-Operating Systems.
+These are instructions for how to install and set up Chocolate Doom
+for play.
-Dependencies
-------------
+Building Chocolate Doom
+-----------------------
-Chocolate Doom requires the following to be installed:
+Before you can play Chocolate Doom, you need to compile a binary that
+you can run. If you are using Windows or Mac OS X, precompiled
+binaries are available on the website for download, and you can skip
+this section.
+
+For compilation, Chocolate Doom requires the following to be installed:
* A C compiler (gcc is recommended)
* make (GNU make is recommended)
@@ -17,84 +22,108 @@ Chocolate Doom requires the following to be installed:
* SDL_net (see http://www.libsdl.org/projects/SDL_net/)
* Python (optional)
-Building Chocolate Doom
------------------------
-
-On a Unix system, follow the standard instructions for installing an
-autotools-based package:
+Follow the standard instructions for installing an autotools-based
+package:
1. Run './configure' to initialize the package.
2. Run 'make' to compile the package.
3. Run 'make install' to install the package.
-Advanced topics such as cross-compilation are beyond the scope of this
+Advanced topics such as cross-compilation are beyond the scope of this
document. Please see the GNU autoconf / automake documentation for more
information.
-Installing an IWAD file
------------------------
+Obtaining an IWAD file
+----------------------
-To play Doom, an IWAD file is needed. This contains the Doom game data. The
-file usually has one of the following filenames:
+To play Doom, you need an IWAD file. This file contains the game data
+that is used in gameplay (graphics, sounds, etc). The full versions of
+the Doom games are proprietary and need to be bought. The IWAD file
+has one of the following names:
doom1.wad (Shareware Doom)
doom.wad (Registered / Ultimate Doom)
doom2.wad (Doom 2)
tnt.wad (Final Doom: TNT: Evilution)
plutonia.wad (Final Doom: Plutonia Experiment)
+ chex.wad (Chex Quest)
-When you have this file (see the next section, "Obtaining an IWAD file", for
-how to get this file), install it through one of the following methods:
+If you don't have a copy of the commercial version, you can download
+the shareware version (extract the file named doom1.wad):
- * Put the file into the /usr/share/games/doom or
- /usr/local/share/games/doom directories.
- * Install it into a directory and set the environment variable DOOMWADDIR to
- be the path to that directory.
- * Install multiple IWADs into separate directories and set the environment
- variable DOOMWADPATH to be a colon-separated list of directories to search
- (similar to the Unix PATH environment variable).
- * Run Chocolate Doom with the '-iwad' command line parameter to specify the
- IWAD file to use, eg.
+ * http://www.doomworld.com/idgames/index.php?id=7053
+ (idstuff/doom/win95/doom95.zip in your nearest /idgames mirror)
- chocolate-doom -iwad /root/doom2.wad
+If you have a commercial version, obtaining the IWAD file may slightly
+complicated. The method depends on how you obtained your copy of the
+game:
-Obtaining an IWAD file
-----------------------
+ * There have been several CD-based versions of Doom. Generally, the
+ IWAD files can be found on the CD and copied off directly.
-Obtaining the IWAD file may be a complicated process under Unix. The method
-depends on how you obtained your copy of the game:
+ * The IWAD files might not be directly available on the CD. Look for
+ a program named "deice.exe". In the same directory, there should
+ be a single large file with a numbered extension (eg.
+ "resource.1"); to extract this, follow the same instructions as for
+ the floppy disk version (see below).
- * There have been several CD-based versions of Doom. Generally, the IWAD
- files can be found on the CD and copied off directly.
+ * If you have the floppy disk version of Doom, first copy the
+ contents of all the floppy disks into a directory together. You
+ will have several large files with numbered extensions.
+ Concatenate these into a single file, eg.
- * The IWAD files may not be directly available on the CD. Look for a program
- named "deice.exe". In the same directory, there should be a single large
- file with a numbered extension (eg. "resource.1"); to extract this, follow
- the same instructions as for the floppy disk version (see below).
+ (Unix instructions)
+ cat doom_se.1 doom_se.2 doom_se.3 doom_se.4 doom_se.5 > doom_se.exe
- * If you have the floppy disk version of Doom, first copy the contents of all
- the floppy disks into a directory together. You will have several large
- files with numbered extensions. Concatenate these into a single file, eg.
+ (Windows/DOS instructions)
+ copy doom_se.1+doom_se.2+doom_se.3+doom_se.4+doom_se+5 doom_se.exe
- cat doom_se.1 doom_se.2 doom_se.3 doom_se.4 doom_se.5 > doom_se.exe
-
- The resulting file is self-extracting LHA file. If you have a DOS emulator
- (such as DOSbox), you can run it to extract the files; alternatively, you
- can use the Unix LHA tool to extract the archive.
+ The resulting file is self-extracting LHA file. If you have a DOS
+ emulator (such as DOSbox), you can run it to extract the files;
+ alternatively, you can use the Unix LHA tool to extract the
+ archive.
* The Doom games are also available for download on Steam
- (http://www.steampowered.com/). To find the IWAD files, look in your Steam
- directory, under the "steamapps/common" path.
+ (http://www.steampowered.com/). To find the IWAD files, look in
+ your Steam directory, under the "steamapps/common" path.
+
+Running the game
+----------------
+
+When you have an IWAD file, install it through one of the following
+methods:
+
+ * Under Mac OS X, you can specify the locations of the IWAD files
+ through the graphical launcher program. Click the "Configure..."
+ button, and then click "Set..." for each IWAD location to choose
+ its location.
+
+ * Under Unix, put the file into the /usr/share/games/doom or
+ /usr/local/share/games/doom directories.
+
+ * Place it in a directory and set the environment variable DOOMWADDIR
+ to be the path to that directory.
+
+ * Install multiple IWADs into separate directories and set the
+ environment variable DOOMWADPATH to be a colon-separated list of
+ directories to search (similar to the Unix PATH environment
+ variable).
+
+ * Run Chocolate Doom with the '-iwad' command line parameter to
+ specify the IWAD file to use, eg.
+
+ chocolate-doom -iwad /root/doom2.wad
Playing with Chex Quest
-----------------------
-Chex Quest is a game based on Doom with some minor modifications that was
-distributed with boxes of Chex cereal in 1997. It is possible to play
-Chex Quest using Chocolate Doom. To do this, the following files are
-needed:
+Chex Quest is a game based on Doom with some minor modifications that
+was distributed with boxes of Chex cereal in 1997. It is possible to
+play Chex Quest using Chocolate Doom. To do this, the following files
+are needed:
* The IWAD file 'chex.wad', from the Chex Quest CD.
+
* The dehacked patch 'chex.deh', which can be found in the /idgames
repository in utils/exe_edit/patches/chexdeh.zip.
@@ -106,30 +135,30 @@ line parameter to specify the Chex Quest IWAD file:
Installing upgrades
-------------------
-Chocolate Doom requires a Doom 1.9 IWAD file. Generally, if you install a
-recent version of Doom you should automatically have a 1.9 IWAD. However, if
-you are installing from a very old CD version or from floppy disks, you might
-find you have an older version.
+Chocolate Doom requires a Doom 1.9 IWAD file. Generally, if you
+install a recent version of Doom you should automatically have a 1.9
+IWAD. However, if you are installing from a very old CD version or
+from floppy disks, you might find you have an older version.
-The most obvious symptom of an out of date IWAD file is that the game will
-exit at the title screen before the demo starts, with the message "Demo is
-from a different game version!". If this happens, your IWAD file is out of
-date and you need to upgrade.
+The most obvious symptom of an out of date IWAD file is that the game
+will exit at the title screen before the demo starts, with the message
+"Demo is from a different game version!". If this happens, your IWAD
+file is out of date and you need to upgrade.
-Id Software released upgrade patches that will update your game to 1.9. The
-following sites have the patches:
+Id Software released upgrade patches that will update your game to
+1.9. The following sites have the patches:
- http://www.doomworld.com/files/patches.shtml
+ http://www.doomworld.com/files/patches.shtml
http://www.doom2.net/doom2/utils.html
ftp://ftp.idsoftware.com/idstuff/doom2
-As the patches are binary patches that run as DOS executables, you will
-need a DOS emulator (such as DOSBox) to install them.
+As the patches are binary patches that run as DOS executables, you
+will need a DOS emulator (such as DOSBox) to install them.
Music support
-------------
-Support for Doom's MIDI music is available through timidity:
+Support for Doom's MIDI music is available through Timidity:
http://timidity.sourceforge.net/
diff --git a/Makefile.am b/Makefile.am
index e331d204..97a18507 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -50,6 +50,7 @@ EXTRA_DIST= \
config.h \
CMDLINE \
HACKING \
+ README.OPL \
TODO \
BUGS \
rpm.spec
@@ -57,7 +58,9 @@ EXTRA_DIST= \
MAINTAINERCLEANFILES = $(AUX_DIST_GEN)
docdir=$(prefix)/share/doc/@PACKAGE@
-SUBDIRS=wince textscreen pcsound src man
+
+SUBDIRS=wince textscreen opl pcsound src man
+
DIST_SUBDIRS=pkg $(SUBDIRS)
if HAVE_PYTHON
diff --git a/NEWS b/NEWS
index ac12bded..8fc37b5f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,66 @@
-...
+1.5.0 (2010-??-??):
+
+ Big changes in this version:
+ * The DOSbox OPL emulator (DBOPL) has been imported to replace
+ the older FMOPL code. The quality of OPL emulation is now
+ therefore much better.
+ * When running in windowed mode, it is now possible to
+ dynamically resize the window by dragging the window borders.
+ * There are now keyboard, mouse and joystick bindings to cycle
+ through available weapons, making play with joypads or mobile
+ devices (ie. without a proper keyboard) much more practical.
+ * There is now a key binding to change the multiplayer spy key
+ (usually F12).
+ * There is now a configuration file parameter to set the OPL I/O
+ port, for cards that don't use port 0x388.
+ * Up to 8 mouse buttons are now supported (including the
+ mousewheel).
+
+ Bugs fixed:
+ * It is now possible to use OPL emulation at 11025Hz sound
+ sampling rate (thanks to the new OPL emulator).
+ * The span renderer function (used for drawing floors and
+ ceilings) now behaves the same as Vanilla Doom, so screenshots
+ are pixel-perfect identical to Vanilla Doom (thanks Porsche
+ Monty).
+ * The zone memory system now aligns allocated memory to 8-byte
+ boundaries on 64-bit systems, which may fix crashes on systems
+ such as sparc64.
+ * The configure script now checks for libm, fixing compile
+ problems on Fedora Linux.
+
+1.4.0 (2010-07-10):
+
+ The biggest change in this version is the addition of OPL
+ emulation. This emulates Vanilla Doom's MIDI playback when
+ using a Yamaha OPL synthesizer chip, as was found on
+ SoundBlaster compatible cards.
+
+ A software OPL emulator is included as most modern computers do
+ not have a hardware OPL chip any more. If you do have one, you
+ can configure Chocolate Doom to use it; see README.OPL.
+
+ The OPL playback feature is not yet perfect or 100% complete,
+ but is judged to be good enough for general use. If you find
+ music that does not play back properly, please report it as a
+ bug.
+
+ Other changes:
+ * The REJECT overflow emulation code from PrBoom+ has been
+ imported. This fixes demo desync on some demos, although
+ others will still desync.
+ * Warnings are now generated for invalid dehacked replacements of
+ printf format strings. Some potential buffer overflows are
+ also checked.
+ * The installation instructions (INSTALL file) have been
+ clarified and made more platform-agnostic.
+ * The mouse is no longer warped to the center of the screen when
+ the demo sequence advances.
+ * Key bindings can now be changed for the demo recording quit key
+ (normally 'q') and the multiplayer messaging keys (normally
+ 't', 'g', 'i', 'b' and 'r').
+
+1.3.0 (2010-02-10):
* Chocolate Doom now runs on Windows Mobile/Windows CE!
* It is possible to rebind most/all of the keys that control the
@@ -35,6 +97,8 @@
* When recording shorttics demos, errors caused by the reduced
turning resolution are carried forward, possibly making turning
smoother.
+ * The source tarball can now be used to build an RPM package:
+ rpmbuild -tb chocolate-doom-VER.tar.gz
Compatibility:
* The A_BossDeath behavior in v1.9 emulation mode was fixed
diff --git a/README b/README
index c0cf24df..0b9eaf7c 100644
--- a/README
+++ b/README
@@ -13,33 +13,37 @@ Chocolate Doom aims to:
* As far as possible, provide all the same features that are available
using the DOS version.
+== Setting up gameplay ==
+
+For instructions on how to set up Chocolate Doom for play, see the
+INSTALL file.
+
== Configuration File ==
Chocolate Doom is compatible with the DOS Doom configuration file
-(normally named 'default.cfg'). Existing configuration files for
-DOS Doom should therefore simply work out of the box. However,
-Chocolate Doom also provides some extra settings. These are stored
-in a separate file named 'chocolate-doom.cfg'.
+(normally named 'default.cfg'). Existing configuration files for DOS
+Doom should therefore simply work out of the box. However, Chocolate
+Doom also provides some extra settings. These are stored in a
+separate file named 'chocolate-doom.cfg'.
The configuration can be edited using the chocolate-setup tool.
== Command-line options ==
-For a complete list of command-line options, see the CMDLINE
-file.
+For a complete list of command-line options, see the CMDLINE file.
== Playing TCs ==
-With Vanilla Doom there is no way to include sprites in PWAD files.
-Chocolate Doom's '-file' command line option behaves exactly the
-same as Vanilla Doom, and trying to play TCs by adding the WAD files
-using '-file' will not work.
+With Vanilla Doom there is no way to include sprites in PWAD files.
+Chocolate Doom's '-file' command line option behaves exactly the same
+as Vanilla Doom, and trying to play TCs by adding the WAD files using
+'-file' will not work.
Many Total Conversions (TCs) are distributed as a PWAD file which must
be merged into the main IWAD. Typically a copy of DEUSF.EXE is
included which performs this merge. Chocolate Doom includes a new
-option, '-merge', which will simulate this merge. Essentially, the
-WAD directory is merged in memory, removing the need to modify the
+option, '-merge', which will simulate this merge. Essentially, the
+WAD directory is merged in memory, removing the need to modify the
IWAD on disk.
To play TCs using Chocolate Doom, run like this:
@@ -53,26 +57,27 @@ Here are some examples:
== Other information ==
- * More information, including information about how to play various classic
- TCs, is available on the Chocolate Doom website:
+ * More information, including information about how to play various
+ classic TCs, is available on the Chocolate Doom website:
http://www.chocolate-doom.org/
- You are encouraged to sign up and contribute any useful information you may
- have regarding the port!
+ You are encouraged to sign up and contribute any useful information
+ you may have regarding the port!
- * Chocolate Doom is not perfect. See the BUGS file for a list of known
- issues. New bug reports can be submitted to the Chocolate Doom bug
- tracker on Sourceforge. See:
+ * Chocolate Doom is not perfect. See the BUGS file for a list of
+ known issues. New bug reports can be submitted to the Chocolate
+ Doom bug tracker on Sourceforge. See:
http://sourceforge.net/projects/chocolate-doom
- * Source code patches are welcome, but please follow the style guidelines -
- see the file named HACKING included with the source distribution.
+ * Source code patches are welcome, but please follow the style
+ guidelines - see the file named HACKING included with the source
+ distribution.
- * Chocolate Doom is distributed under the GNU GPL. See the COPYING file
- for more information.
+ * Chocolate Doom is distributed under the GNU GPL. See the COPYING
+ file for more information.
- * Please send any feedback, questions or suggestions to fraggle@gmail.com.
- Thanks!
+ * Please send any feedback, questions or suggestions to
+ fraggle@gmail.com. Thanks!
diff --git a/README.OPL b/README.OPL
new file mode 100644
index 00000000..07699232
--- /dev/null
+++ b/README.OPL
@@ -0,0 +1,107 @@
+== Chocolate Doom hardware OPL support notes ==
+
+Chocolate Doom is able to play MIDI music as it sounds in Vanilla Doom
+with an OPL chip (as found in the Yamaha Adlib card, the Sound Blaster
+and its clones). Most modern computers do not include an OPL chip any
+more, as CPUs are fast enough to do decent software MIDI synthesis.
+For this reason, a software OPL emulator is included as a substitute.
+
+However, no software emulator sounds exactly like a real (hardware)
+OPL chip, so if you do have a sound card with hardware OPL, here's how
+to configure Chocolate Doom to use it.
+
+=== Sound cards with OPL chips ===
+
+If you have an ISA sound card, it almost certainly includes an OPL
+chip. Modern computers don't have slots for ISA cards though, so you
+must be running a pretty old machine.
+
+If you have a PCI sound card, you probably don't have an OPL chip.
+However, there are some exceptions to this. The following cards are
+known to include "legacy" OPL support:
+
+ * C-Media CMI8738 (*)
+ * Forte Media FM801
+ * Cards based on the Yamaha YMF724 (*)
+
+Other cards that apparently have OPL support but have not been tested:
+
+ * S3 SonicVibes
+ * AZTech PCI 168 (AZT 3328 chipset)
+ * ESS Solo-1 sound cards (ES1938, ES1946, ES1969 chipset)
+ * Conexant Riptide Audio/Modem combo cards
+ * Cards based on the Crystal Semiconductors CS4281
+ * Cards based on the Avance Logic ALS300
+ * Cards based on the Avance Logic ALS4000
+
+If you desperately want hardware OPL music, you may be able to find
+one of these cards for sale cheap on eBay.
+
+For the cards listed above with (*) next to them, OPL support is
+disabled by default and must be explictly enabled in software.
+
+If your machine is not a PC, you don't have an OPL chip, and you will
+have to use the software OPL.
+
+=== Operating System support ===
+
+If you're certain that you have a sound card with hardware OPL, you
+may need to take extra steps to configure your operating system to
+allow access to it. To do hardware OPL, Chocolate Doom must access
+the chip directly, which is usually not possible in modern operating
+systems unless you are running as the superuser (root/Administrator).
+
+=== Windows 9x ===
+
+If you're running Windows 95, 98 or Me, there is no need to configure
+anything. Windows allows direct access to the OPL chip. You can
+confirm that hardware OPL is working by checking for this message in
+stdout.txt:
+
+ OPL_Init: Using driver 'Win32'.
+
+=== Windows NT (including 2000, XP and later) ===
+
+If you're running an NT-based system, it is not possible to directly
+access the OPL chip, even when running as Administrator. Fortunately,
+it is possible to use the "ioperm.sys" driver developed for Cygwin:
+
+ http://openwince.sourceforge.net/ioperm/
+
+It is not necessary to have Cygwin installed to use this. Copy the
+ioperm.sys file into the same directory as the Chocolate Doom
+executable and it should be automatically loaded.
+
+You can confirm that hardware OPL is working by checking for this
+message in stdout.txt:
+
+ OPL_Init: Using driver 'Win32'.
+
+=== Linux ===
+
+If you are using a system based on the Linux kernel, you can access
+the OPL chip directly, but you must be running as root. You can
+confirm that hardware OPL is working, by checking for this message on
+startup:
+
+ OPL_Init: Using driver 'Linux'.
+
+If you are using one of the PCI cards in the list above with a (*)
+next to it, you may need to manually enable FM legacy support. Add
+the following to your /etc/modprobe.conf file to do this:
+
+ options snd-ymfpci fm_port=0x388
+ options snd-cmipci fm_port=0x388
+
+=== OpenBSD/NetBSD ===
+
+You must be running as root to access the hardware OPL directly. You
+can confirm that hadware OPL is working by checking for this message
+on startup:
+
+ OPL_Init: Using driver 'OpenBSD'.
+
+=== FreeBSD ===
+
+There is no native OPL backend for FreeBSD yet. Sorry!
+
diff --git a/TODO b/TODO
index b0acb8d3..18d7dc67 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,5 @@
Currently in progress:
-* OPL MIDI playback (see: opl-branch)
* Heretic/Hexen support (see: raven-branch)
* Strife support (see: strife-branch)
@@ -36,3 +35,22 @@ Crazy pie in the sky ideas:
* DWANGO-like interface for finding players and setting up games.
* Video capture mode?
+== OPL TODO list ==
+
+Needs research:
+
+ * Strategy when no more voices are available is still wrong
+ * Scale levels don't exactly match Vanilla (off-by-one?)
+
+Bad MIDIs:
+
+ * doom2.wad MAP01
+ * gothicdm MAP05
+ * tnt.wad MAP30
+ * Alien Vendetta (title screen, MAP01, etc)
+
+Other tasks:
+
+ * Get a better software OPL emulator
+ * DMXOPTIONS opl3/phase option support.
+
diff --git a/codeblocks/config.h b/codeblocks/config.h
index fe1bde8f..d092bf73 100644
--- a/codeblocks/config.h
+++ b/codeblocks/config.h
@@ -9,19 +9,19 @@
#define PACKAGE_NAME "Chocolate Doom"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 1.2.1"
+#define PACKAGE_STRING "Chocolate Doom 1.4.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 "1.2.1"
+#define PACKAGE_VERSION "1.4.0"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
-#define VERSION "1.2.1"
+#define VERSION "1.4.0"
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
diff --git a/codeblocks/game-res.rc b/codeblocks/game-res.rc
index 4a9e289b..eef24852 100644
--- a/codeblocks/game-res.rc
+++ b/codeblocks/game-res.rc
@@ -1,21 +1,21 @@
1 ICON "../data/doom.ico"
1 VERSIONINFO
-PRODUCTVERSION 1,2,1,0
-FILEVERSION 1,2,1,0
+PRODUCTVERSION 1,4,0,0
+FILEVERSION 1,4,0,0
FILETYPE 1
{
BLOCK "StringFileInfo"
{
BLOCK "040904E4"
{
- VALUE "FileVersion", "1.2.1"
- VALUE "FileDescription", "1.2.1"
+ VALUE "FileVersion", "1.4.0"
+ VALUE "FileDescription", "1.4.0"
VALUE "InternalName", "Chocolate-Doom"
VALUE "CompanyName", "Chocolate-Doom"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate-Doom"
- VALUE "ProductVersion", "1.2.1"
+ VALUE "ProductVersion", "1.4.0"
}
}
}
diff --git a/codeblocks/setup-res.rc b/codeblocks/setup-res.rc
index fe791088..f1602adb 100644
--- a/codeblocks/setup-res.rc
+++ b/codeblocks/setup-res.rc
@@ -3,21 +3,21 @@
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "setup-manifest.xml"
1 VERSIONINFO
-PRODUCTVERSION 1,2,1,0
-FILEVERSION 1,2,1,0
+PRODUCTVERSION 1,4,0,0
+FILEVERSION 1,4,0,0
FILETYPE 1
{
BLOCK "StringFileInfo"
{
BLOCK "040904E4"
{
- VALUE "FileVersion", "1.2.1"
+ VALUE "FileVersion", "1.4.0"
VALUE "FileDescription", "Chocolate-Doom Setup"
VALUE "InternalName", "chocolate-setup"
VALUE "CompanyName", "fraggle@gmail.com"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate-Doom Setup"
- VALUE "ProductVersion", "1.2.1"
+ VALUE "ProductVersion", "1.4.0"
}
}
}
diff --git a/configure.in b/configure.in
index de537f7c..4bea1201 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-AC_INIT(Chocolate Doom, 1.2.1, fraggle@gmail.com, chocolate-doom)
+AC_INIT(Chocolate Doom, 1.4.0, fraggle@gmail.com, chocolate-doom)
PACKAGE_SHORTDESC="Conservative Doom source port"
PACKAGE_COPYRIGHT="Copyright (C) 1993-2010"
@@ -74,9 +74,16 @@ AC_SDL_MAIN_WORKAROUND([
# Check for libsamplerate.
AC_CHECK_LIB(samplerate, src_new)
+ AC_CHECK_LIB(m, log)
AC_CHECK_HEADERS([linux/kd.h dev/isa/spkrio.h dev/speaker/speaker.h])
- AC_CHECK_FUNCS(mmap sched_setaffinity)
+ AC_CHECK_FUNCS(mmap sched_setaffinity ioperm)
+
+ # OpenBSD I/O i386 library for I/O port access.
+ # (64 bit has the same thing with a different name!)
+
+ AC_CHECK_LIB(i386, i386_iopl)
+ AC_CHECK_LIB(amd64, amd64_iopl)
])
AC_CHECK_TOOL(WINDRES, windres, )
@@ -138,6 +145,8 @@ AC_DEFUN([AC_DATAROOTDIR_CHECKED])
AC_OUTPUT([
Makefile
man/Makefile
+opl/Makefile
+opl/examples/Makefile
pcsound/Makefile
pkg/Makefile
pkg/config.make
diff --git a/man/manpage.template b/man/manpage.template
index 052ccb63..41f06dc0 100644
--- a/man/manpage.template
+++ b/man/manpage.template
@@ -28,6 +28,17 @@ specifies a PC speaker driver to use for sound effect playback. Valid
options are "Linux" for the Linux console mode driver, "BSD" for the
NetBSD/OpenBSD PC speaker driver, and "SDL" for SDL-based emulated PC speaker
playback (using the digital output).
+.TP
+\fBOPL_DRIVER\fR
+When using OPL MIDI playback, this environment variable specifies an
+OPL backend driver to use. Valid options are "SDL" for an SDL-based
+software emulated OPL chip, "Linux" for the Linux hardware OPL driver,
+and "OpenBSD" for the OpenBSD/NetBSD hardware OPL driver.
+
+Generally speaking, a real hardware OPL chip sounds better than software
+emulation; however, modern machines do not often include one. If
+present, it may still require extra work to set up and elevated
+security privileges to access.
.SH FILES
.TP
\fB$HOME/.chocolate-doom/default.cfg\fR
diff --git a/msvc/config.h b/msvc/config.h
index 7880c3c1..006d7c74 100644
--- a/msvc/config.h
+++ b/msvc/config.h
@@ -11,16 +11,16 @@
#define PACKAGE_NAME "Chocolate Doom"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 1.2.1"
+#define PACKAGE_STRING "Chocolate Doom 1.4.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 "1.2.1"
+#define PACKAGE_VERSION "1.4.0"
/* Version number of package */
-#define VERSION "1.2.1"
+#define VERSION "1.4.0"
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
diff --git a/msvc/win32.rc b/msvc/win32.rc
index b9bc06a2..94a35372 100644
--- a/msvc/win32.rc
+++ b/msvc/win32.rc
@@ -32,21 +32,21 @@
#endif
1 VERSIONINFO
-PRODUCTVERSION 1,2,1,0
-FILEVERSION 1,2,1,0
+PRODUCTVERSION 1,4,0,0
+FILEVERSION 1,4,0,0
FILETYPE 1
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
- VALUE "FileVersion", "1.2.1"
- VALUE "FileDescription", "Chocolate Doom 1.2.1"
+ VALUE "FileVersion", "1.4.0"
+ VALUE "FileDescription", "Chocolate Doom 1.4.0"
VALUE "InternalName", "chocolate-doom"
VALUE "CompanyName", "fraggle@gmail.com"
VALUE "LegalCopyright", "GNU General Public License"
VALUE "ProductName", "Chocolate Doom"
- VALUE "ProductVersion", "1.2.1"
+ VALUE "ProductVersion", "1.4.0"
END
END
END
diff --git a/opl/.gitignore b/opl/.gitignore
new file mode 100644
index 00000000..8aac4e77
--- /dev/null
+++ b/opl/.gitignore
@@ -0,0 +1,7 @@
+Makefile.in
+Makefile
+.deps
+libopl.a
+*.rc
+tags
+TAGS
diff --git a/opl/Makefile.am b/opl/Makefile.am
new file mode 100644
index 00000000..be1619d8
--- /dev/null
+++ b/opl/Makefile.am
@@ -0,0 +1,19 @@
+
+AM_CFLAGS=@SDLMIXER_CFLAGS@
+
+SUBDIRS = . examples
+
+noinst_LIBRARIES=libopl.a
+
+libopl_a_SOURCES = \
+ opl_internal.h \
+ opl.c opl.h \
+ opl_linux.c \
+ opl_obsd.c \
+ opl_queue.c opl_queue.h \
+ opl_sdl.c \
+ opl_timer.c opl_timer.h \
+ opl_win32.c \
+ ioperm_sys.c ioperm_sys.h \
+ dbopl.c dbopl.h
+
diff --git a/opl/dbopl.c b/opl/dbopl.c
new file mode 100644
index 00000000..159cae45
--- /dev/null
+++ b/opl/dbopl.c
@@ -0,0 +1,1602 @@
+/*
+ * Copyright (C) 2002-2010 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+//
+// Chocolate Doom-related discussion:
+//
+// This is the DosBox OPL emulator code (src/hardware/dbopl.cpp) r3635,
+// converted to C. The bulk of the work was done using the minus-minus
+// script in the Chocolate Doom SVN repository, then the result tweaked
+// by hand until working.
+//
+
+
+/*
+ DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator.
+ Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2
+ Except for the table generation it's all integer math
+ Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms
+ The generation was based on the MAME implementation but tried to have it use less memory and be faster in general
+ MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times
+
+ //TODO Don't delay first operator 1 sample in opl3 mode
+ //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter
+ //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though?
+ //TODO Check if having the same accuracy in all frequency multipliers sounds better or not
+
+ //DUNNO Keyon in 4op, switch to 2op without keyoff.
+*/
+
+/* $Id: dbopl.cpp,v 1.10 2009-06-10 19:54:51 harekiet Exp $ */
+
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+//#include "dosbox.h"
+#include "dbopl.h"
+
+
+#define GCC_UNLIKELY(x) x
+
+#define TRUE 1
+#define FALSE 0
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+#define OPLRATE ((double)(14318180.0 / 288.0))
+#define TREMOLO_TABLE 52
+
+//Try to use most precision for frequencies
+//Else try to keep different waves in synch
+//#define WAVE_PRECISION 1
+#ifndef WAVE_PRECISION
+//Wave bits available in the top of the 32bit range
+//Original adlib uses 10.10, we use 10.22
+#define WAVE_BITS 10
+#else
+//Need some extra bits at the top to have room for octaves and frequency multiplier
+//We support to 8 times lower rate
+//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits
+#define WAVE_BITS 14
+#endif
+#define WAVE_SH ( 32 - WAVE_BITS )
+#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 )
+
+//Use the same accuracy as the waves
+#define LFO_SH ( WAVE_SH - 10 )
+//LFO is controlled by our tremolo 256 sample limit
+#define LFO_MAX ( 256 << ( LFO_SH ) )
+
+
+//Maximum amount of attenuation bits
+//Envelope goes to 511, 9 bits
+#if (DBOPL_WAVE == WAVE_TABLEMUL )
+//Uses the value directly
+#define ENV_BITS ( 9 )
+#else
+//Add 3 bits here for more accuracy and would have to be shifted up either way
+#define ENV_BITS ( 9 )
+#endif
+//Limits of the envelope with those bits and when the envelope goes silent
+#define ENV_MIN 0
+#define ENV_EXTRA ( ENV_BITS - 9 )
+#define ENV_MAX ( 511 << ENV_EXTRA )
+#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) )
+#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT )
+
+//Attack/decay/release rate counter shift
+#define RATE_SH 24
+#define RATE_MASK ( ( 1 << RATE_SH ) - 1 )
+//Has to fit within 16bit lookuptable
+#define MUL_SH 16
+
+//Check some ranges
+#if ENV_EXTRA > 3
+#error Too many envelope bits
+#endif
+
+static inline void Operator__SetState(Operator *self, Bit8u s );
+static inline Bit32u Chip__ForwardNoise(Chip *self);
+
+// C++'s template<> sure is useful sometimes.
+
+static Channel* Channel__BlockTemplate(Channel *self, Chip* chip,
+ Bit32u samples, Bit32s* output,
+ SynthMode mode );
+#define BLOCK_TEMPLATE(mode) \
+ static Channel* Channel__BlockTemplate_ ## mode(Channel *self, Chip* chip, \
+ Bit32u samples, Bit32s* output) \
+ { \
+ return Channel__BlockTemplate(self, chip, samples, output, mode); \
+ }
+
+BLOCK_TEMPLATE(sm2AM)
+BLOCK_TEMPLATE(sm2FM)
+BLOCK_TEMPLATE(sm3AM)
+BLOCK_TEMPLATE(sm3FM)
+BLOCK_TEMPLATE(sm3FMFM)
+BLOCK_TEMPLATE(sm3AMFM)
+BLOCK_TEMPLATE(sm3FMAM)
+BLOCK_TEMPLATE(sm3AMAM)
+BLOCK_TEMPLATE(sm2Percussion)
+BLOCK_TEMPLATE(sm3Percussion)
+
+//How much to substract from the base value for the final attenuation
+static const Bit8u KslCreateTable[16] = {
+ //0 will always be be lower than 7 * 8
+ 64, 32, 24, 19,
+ 16, 12, 11, 10,
+ 8, 6, 5, 4,
+ 3, 2, 1, 0,
+};
+
+#define M(_X_) ((Bit8u)( (_X_) * 2))
+static const Bit8u FreqCreateTable[16] = {
+ M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ),
+ M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15)
+};
+#undef M
+
+//We're not including the highest attack rate, that gets a special value
+static const Bit8u AttackSamplesTable[13] = {
+ 69, 55, 46, 40,
+ 35, 29, 23, 20,
+ 19, 15, 11, 10,
+ 9
+};
+//On a real opl these values take 8 samples to reach and are based upon larger tables
+static const Bit8u EnvelopeIncreaseTable[13] = {
+ 4, 5, 6, 7,
+ 8, 10, 12, 14,
+ 16, 20, 24, 28,
+ 32,
+};
+
+#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
+static Bit16u ExpTable[ 256 ];
+#endif
+
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+//PI table used by WAVEHANDLER
+static Bit16u SinTable[ 512 ];
+#endif
+
+#if ( DBOPL_WAVE > WAVE_HANDLER )
+//Layout of the waveform table in 512 entry intervals
+//With overlapping waves we reduce the table to half it's size
+
+// | |//\\|____|WAV7|//__|/\ |____|/\/\|
+// |\\//| | |WAV7| | \/| | |
+// |06 |0126|17 |7 |3 |4 |4 5 |5 |
+
+//6 is just 0 shifted and masked
+
+static Bit16s WaveTable[ 8 * 512 ];
+//Distance into WaveTable the wave starts
+static const Bit16u WaveBaseTable[8] = {
+ 0x000, 0x200, 0x200, 0x800,
+ 0xa00, 0xc00, 0x100, 0x400,
+
+};
+//Mask the counter with this
+static const Bit16u WaveMaskTable[8] = {
+ 1023, 1023, 511, 511,
+ 1023, 1023, 512, 1023,
+};
+
+//Where to start the counter on at keyon
+static const Bit16u WaveStartTable[8] = {
+ 512, 0, 0, 0,
+ 0, 512, 512, 256,
+};
+#endif
+
+#if ( DBOPL_WAVE == WAVE_TABLEMUL )
+static Bit16u MulTable[ 384 ];
+#endif
+
+static Bit8u KslTable[ 8 * 16 ];
+static Bit8u TremoloTable[ TREMOLO_TABLE ];
+//Start of a channel behind the chip struct start
+static Bit16u ChanOffsetTable[32];
+//Start of an operator behind the chip struct start
+static Bit16u OpOffsetTable[64];
+
+//The lower bits are the shift of the operator vibrato value
+//The highest bit is right shifted to generate -1 or 0 for negation
+//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0
+static const Bit8s VibratoTable[ 8 ] = {
+ 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00,
+ 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80
+};
+
+//Shift strength for the ksl value determined by ksl strength
+static const Bit8u KslShiftTable[4] = {
+ 31,1,2,0
+};
+
+//Generate a table index and table shift value using input value from a selected rate
+static void EnvelopeSelect( Bit8u val, Bit8u *index, Bit8u *shift ) {
+ if ( val < 13 * 4 ) { //Rate 0 - 12
+ *shift = 12 - ( val >> 2 );
+ *index = val & 3;
+ } else if ( val < 15 * 4 ) { //rate 13 - 14
+ *shift = 0;
+ *index = val - 12 * 4;
+ } else { //rate 15 and up
+ *shift = 0;
+ *index = 12;
+ }
+}
+
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+/*
+ Generate the different waveforms out of the sine/exponetial table using handlers
+*/
+static inline Bits MakeVolume( Bitu wave, Bitu volume ) {
+ Bitu total = wave + volume;
+ Bitu index = total & 0xff;
+ Bitu sig = ExpTable[ index ];
+ Bitu exp = total >> 8;
+#if 0
+ //Check if we overflow the 31 shift limit
+ if ( exp >= 32 ) {
+ LOG_MSG( "WTF %d %d", total, exp );
+ }
+#endif
+ return (sig >> exp);
+};
+
+static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) {
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) {
+ Bit32u wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) {
+ Bitu wave = SinTable[i & 511];
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) {
+ Bitu wave = SinTable[i & 255];
+ wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) {
+ //Twice as fast
+ i <<= 1;
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) {
+ //Twice as fast
+ i <<= 1;
+ Bitu wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) {
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ return (MakeVolume( 0, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) {
+ //Negative is reversed here
+ Bits neg = (( i >> 9) & 1) - 1;
+ Bitu wave = (i << 3);
+ //When negative the volume also runs backwards
+ wave = ((wave ^ neg) - neg) & 4095;
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+
+static const WaveHandler WaveHandlerTable[8] = {
+ WaveForm0, WaveForm1, WaveForm2, WaveForm3,
+ WaveForm4, WaveForm5, WaveForm6, WaveForm7
+};
+
+#endif
+
+/*
+ Operator
+*/
+
+//We zero out when rate == 0
+static inline void Operator__UpdateAttack(Operator *self, const Chip* chip ) {
+ Bit8u rate = self->reg60 >> 4;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + self->ksr;
+ self->attackAdd = chip->attackRates[ val ];
+ self->rateZero &= ~(1 << ATTACK);
+ } else {
+ self->attackAdd = 0;
+ self->rateZero |= (1 << ATTACK);
+ }
+}
+static inline void Operator__UpdateDecay(Operator *self, const Chip* chip ) {
+ Bit8u rate = self->reg60 & 0xf;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + self->ksr;
+ self->decayAdd = chip->linearRates[ val ];
+ self->rateZero &= ~(1 << DECAY);
+ } else {
+ self->decayAdd = 0;
+ self->rateZero |= (1 << DECAY);
+ }
+}
+static inline void Operator__UpdateRelease(Operator *self, const Chip* chip ) {
+ Bit8u rate = self->reg80 & 0xf;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + self->ksr;
+ self->releaseAdd = chip->linearRates[ val ];
+ self->rateZero &= ~(1 << RELEASE);
+ if ( !(self->reg20 & MASK_SUSTAIN ) ) {
+ self->rateZero &= ~( 1 << SUSTAIN );
+ }
+ } else {
+ self->rateZero |= (1 << RELEASE);
+ self->releaseAdd = 0;
+ if ( !(self->reg20 & MASK_SUSTAIN ) ) {
+ self->rateZero |= ( 1 << SUSTAIN );
+ }
+ }
+}
+
+static inline void Operator__UpdateAttenuation(Operator *self) {
+ Bit8u kslBase = (Bit8u)((self->chanData >> SHIFT_KSLBASE) & 0xff);
+ Bit32u tl = self->reg40 & 0x3f;
+ Bit8u kslShift = KslShiftTable[ self->reg40 >> 6 ];
+ //Make sure the attenuation goes to the right bits
+ self->totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max
+ self->totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift;
+}
+
+static void Operator__UpdateFrequency(Operator *self) {
+ Bit32u freq = self->chanData & (( 1 << 10 ) - 1);
+ Bit32u block = (self->chanData >> 10) & 0xff;
+#ifdef WAVE_PRECISION
+ block = 7 - block;
+ self->waveAdd = ( freq * self->freqMul ) >> block;
+#else
+ self->waveAdd = ( freq << block ) * self->freqMul;
+#endif
+ if ( self->reg20 & MASK_VIBRATO ) {
+ self->vibStrength = (Bit8u)(freq >> 7);
+
+#ifdef WAVE_PRECISION
+ self->vibrato = ( self->vibStrength * self->freqMul ) >> block;
+#else
+ self->vibrato = ( self->vibStrength << block ) * self->freqMul;
+#endif
+ } else {
+ self->vibStrength = 0;
+ self->vibrato = 0;
+ }
+}
+
+static void Operator__UpdateRates(Operator *self, const Chip* chip ) {
+ //Mame seems to reverse this where enabling ksr actually lowers
+ //the rate, but pdf manuals says otherwise?
+ Bit8u newKsr = (Bit8u)((self->chanData >> SHIFT_KEYCODE) & 0xff);
+ if ( !( self->reg20 & MASK_KSR ) ) {
+ newKsr >>= 2;
+ }
+ if ( self->ksr == newKsr )
+ return;
+ self->ksr = newKsr;
+ Operator__UpdateAttack( self, chip );
+ Operator__UpdateDecay( self, chip );
+ Operator__UpdateRelease( self, chip );
+}
+
+static inline Bit32s Operator__RateForward(Operator *self, Bit32u add ) {
+ self->rateIndex += add;
+ Bit32s ret = self->rateIndex >> RATE_SH;
+ self->rateIndex = self->rateIndex & RATE_MASK;
+ return ret;
+}
+
+static Bits Operator__TemplateVolume(Operator *self, OperatorState yes) {
+ Bit32s vol = self->volume;
+ Bit32s change;
+ switch ( yes ) {
+ case OFF:
+ return ENV_MAX;
+ case ATTACK:
+ change = Operator__RateForward( self, self->attackAdd );
+ if ( !change )
+ return vol;
+ vol += ( (~vol) * change ) >> 3;
+ if ( vol < ENV_MIN ) {
+ self->volume = ENV_MIN;
+ self->rateIndex = 0;
+ Operator__SetState( self, DECAY );
+ return ENV_MIN;
+ }
+ break;
+ case DECAY:
+ vol += Operator__RateForward( self, self->decayAdd );
+ if ( GCC_UNLIKELY(vol >= self->sustainLevel) ) {
+ //Check if we didn't overshoot max attenuation, then just go off
+ if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
+ self->volume = ENV_MAX;
+ Operator__SetState( self, OFF );
+ return ENV_MAX;
+ }
+ //Continue as sustain
+ self->rateIndex = 0;
+ Operator__SetState( self, SUSTAIN );
+ }
+ break;
+ case SUSTAIN:
+ if ( self->reg20 & MASK_SUSTAIN ) {
+ return vol;
+ }
+ //In sustain phase, but not sustaining, do regular release
+ case RELEASE:
+ vol += Operator__RateForward( self, self->releaseAdd );;
+ if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
+ self->volume = ENV_MAX;
+ Operator__SetState( self, OFF );
+ return ENV_MAX;
+ }
+ break;
+ }
+ self->volume = vol;
+ return vol;
+}
+
+#define TEMPLATE_VOLUME(mode) \
+ static Bits Operator__TemplateVolume ## mode(Operator *self) \
+ { \
+ return Operator__TemplateVolume(self, mode); \
+ }
+
+TEMPLATE_VOLUME(OFF)
+TEMPLATE_VOLUME(RELEASE)
+TEMPLATE_VOLUME(SUSTAIN)
+TEMPLATE_VOLUME(ATTACK)
+TEMPLATE_VOLUME(DECAY)
+
+static const VolumeHandler VolumeHandlerTable[5] = {
+ &Operator__TemplateVolumeOFF,
+ &Operator__TemplateVolumeRELEASE,
+ &Operator__TemplateVolumeSUSTAIN,
+ &Operator__TemplateVolumeDECAY,
+ &Operator__TemplateVolumeATTACK,
+};
+
+static inline Bitu Operator__ForwardVolume(Operator *self) {
+ return self->currentLevel + (self->volHandler)(self);
+}
+
+
+static inline Bitu Operator__ForwardWave(Operator *self) {
+ self->waveIndex += self->waveCurrent;
+ return self->waveIndex >> WAVE_SH;
+}
+
+static void Operator__Write20(Operator *self, const Chip* chip, Bit8u val ) {
+ Bit8u change = (self->reg20 ^ val );
+ if ( !change )
+ return;
+ self->reg20 = val;
+ //Shift the tremolo bit over the entire register, saved a branch, YES!
+ self->tremoloMask = (Bit8s)(val) >> 7;
+ self->tremoloMask &= ~(( 1 << ENV_EXTRA ) -1);
+ //Update specific features based on changes
+ if ( change & MASK_KSR ) {
+ Operator__UpdateRates( self, chip );
+ }
+ //With sustain enable the volume doesn't change
+ if ( self->reg20 & MASK_SUSTAIN || ( !self->releaseAdd ) ) {
+ self->rateZero |= ( 1 << SUSTAIN );
+ } else {
+ self->rateZero &= ~( 1 << SUSTAIN );
+ }
+ //Frequency multiplier or vibrato changed
+ if ( change & (0xf | MASK_VIBRATO) ) {
+ self->freqMul = chip->freqMul[ val & 0xf ];
+ Operator__UpdateFrequency(self);
+ }
+}
+
+static void Operator__Write40(Operator *self, const Chip *chip, Bit8u val ) {
+ if (!(self->reg40 ^ val ))
+ return;
+ self->reg40 = val;
+ Operator__UpdateAttenuation( self );
+}
+
+static void Operator__Write60(Operator *self, const Chip* chip, Bit8u val ) {
+ Bit8u change = self->reg60 ^ val;
+ self->reg60 = val;
+ if ( change & 0x0f ) {
+ Operator__UpdateDecay( self, chip );
+ }
+ if ( change & 0xf0 ) {
+ Operator__UpdateAttack( self, chip );
+ }
+}
+
+static void Operator__Write80(Operator *self, const Chip* chip, Bit8u val ) {
+ Bit8u change = (self->reg80 ^ val );
+ if ( !change )
+ return;
+ self->reg80 = val;
+ Bit8u sustain = val >> 4;
+ //Turn 0xf into 0x1f
+ sustain |= ( sustain + 1) & 0x10;
+ self->sustainLevel = sustain << ( ENV_BITS - 5 );
+ if ( change & 0x0f ) {
+ Operator__UpdateRelease( self, chip );
+ }
+}
+
+static void Operator__WriteE0(Operator *self, const Chip* chip, Bit8u val ) {
+ if ( !(self->regE0 ^ val) )
+ return;
+ //in opl3 mode you can always selet 7 waveforms regardless of waveformselect
+ Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) );
+ self->regE0 = val;
+#if( DBOPL_WAVE == WAVE_HANDLER )
+ self->waveHandler = WaveHandlerTable[ waveForm ];
+#else
+ self->waveBase = WaveTable + WaveBaseTable[ waveForm ];
+ self->waveStart = WaveStartTable[ waveForm ] << WAVE_SH;
+ self->waveMask = WaveMaskTable[ waveForm ];
+#endif
+}
+
+static inline void Operator__SetState(Operator *self, Bit8u s ) {
+ self->state = s;
+ self->volHandler = VolumeHandlerTable[ s ];
+}
+
+static inline int Operator__Silent(Operator *self) {
+ if ( !ENV_SILENT( self->totalLevel + self->volume ) )
+ return FALSE;
+ if ( !(self->rateZero & ( 1 << self->state ) ) )
+ return FALSE;
+ return TRUE;
+}
+
+static inline void Operator__Prepare(Operator *self, const Chip* chip ) {
+ self->currentLevel = self->totalLevel + (chip->tremoloValue & self->tremoloMask);
+ self->waveCurrent = self->waveAdd;
+ if ( self->vibStrength >> chip->vibratoShift ) {
+ Bit32s add = self->vibrato >> chip->vibratoShift;
+ //Sign extend over the shift value
+ Bit32s neg = chip->vibratoSign;
+ //Negate the add with -1 or 0
+ add = ( add ^ neg ) - neg;
+ self->waveCurrent += add;
+ }
+}
+
+static void Operator__KeyOn(Operator *self, Bit8u mask ) {
+ if ( !self->keyOn ) {
+ //Restart the frequency generator
+#if( DBOPL_WAVE > WAVE_HANDLER )
+ self->waveIndex = self->waveStart;
+#else
+ self->waveIndex = 0;
+#endif
+ self->rateIndex = 0;
+ Operator__SetState( self, ATTACK );
+ }
+ self->keyOn |= mask;
+}
+
+static void Operator__KeyOff(Operator *self, Bit8u mask ) {
+ self->keyOn &= ~mask;
+ if ( !self->keyOn ) {
+ if ( self->state != OFF ) {
+ Operator__SetState( self, RELEASE );
+ }
+ }
+}
+
+static inline Bits Operator__GetWave(Operator *self, Bitu index, Bitu vol ) {
+#if( DBOPL_WAVE == WAVE_HANDLER )
+ return self->waveHandler( index, vol << ( 3 - ENV_EXTRA ) );
+#elif( DBOPL_WAVE == WAVE_TABLEMUL )
+ return(self->waveBase[ index & self->waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH;
+#elif( DBOPL_WAVE == WAVE_TABLELOG )
+ Bit32s wave = self->waveBase[ index & self->waveMask ];
+ Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA );
+ Bit32s sig = ExpTable[ total & 0xff ];
+ Bit32u exp = total >> 8;
+ Bit32s neg = wave >> 16;
+ return((sig ^ neg) - neg) >> exp;
+#else
+#error "No valid wave routine"
+#endif
+}
+
+static inline Bits Operator__GetSample(Operator *self, Bits modulation ) {
+ Bitu vol = Operator__ForwardVolume(self);
+ if ( ENV_SILENT( vol ) ) {
+ //Simply forward the wave
+ self->waveIndex += self->waveCurrent;
+ return 0;
+ } else {
+ Bitu index = Operator__ForwardWave(self);
+ index += modulation;
+ return Operator__GetWave( self, index, vol );
+ }
+}
+
+static void Operator__Operator(Operator *self) {
+ self->chanData = 0;
+ self->freqMul = 0;
+ self->waveIndex = 0;
+ self->waveAdd = 0;
+ self->waveCurrent = 0;
+ self->keyOn = 0;
+ self->ksr = 0;
+ self->reg20 = 0;
+ self->reg40 = 0;
+ self->reg60 = 0;
+ self->reg80 = 0;
+ self->regE0 = 0;
+ Operator__SetState( self, OFF );
+ self->rateZero = (1 << OFF);
+ self->sustainLevel = ENV_MAX;
+ self->currentLevel = ENV_MAX;
+ self->totalLevel = ENV_MAX;
+ self->volume = ENV_MAX;
+ self->releaseAdd = 0;
+}
+
+/*
+ Channel
+*/
+
+static void Channel__Channel(Channel *self) {
+ Operator__Operator(&self->op[0]);
+ Operator__Operator(&self->op[1]);
+ self->old[0] = self->old[1] = 0;
+ self->chanData = 0;
+ self->regB0 = 0;
+ self->regC0 = 0;
+ self->maskLeft = -1;
+ self->maskRight = -1;
+ self->feedback = 31;
+ self->fourMask = 0;
+ self->synthHandler = Channel__BlockTemplate_sm2FM;
+};
+
+static inline Operator* Channel__Op( Channel *self, Bitu index ) {
+ return &( ( self + (index >> 1) )->op[ index & 1 ]);
+}
+
+static void Channel__SetChanData(Channel *self, const Chip* chip, Bit32u data ) {
+ Bit32u change = self->chanData ^ data;
+ self->chanData = data;
+ Channel__Op( self, 0 )->chanData = data;
+ Channel__Op( self, 1 )->chanData = data;
+ //Since a frequency update triggered this, always update frequency
+ Operator__UpdateFrequency(Channel__Op( self, 0 ));
+ Operator__UpdateFrequency(Channel__Op( self, 1 ));
+ if ( change & ( 0xff << SHIFT_KSLBASE ) ) {
+ Operator__UpdateAttenuation(Channel__Op( self, 0 ));
+ Operator__UpdateAttenuation(Channel__Op( self, 1 ));
+ }
+ if ( change & ( 0xff << SHIFT_KEYCODE ) ) {
+ Operator__UpdateRates(Channel__Op( self, 0 ), chip);
+ Operator__UpdateRates(Channel__Op( self, 1 ), chip);
+ }
+}
+
+static void Channel__UpdateFrequency(Channel *self, const Chip* chip, Bit8u fourOp ) {
+ //Extrace the frequency bits
+ Bit32u data = self->chanData & 0xffff;
+ Bit32u kslBase = KslTable[ data >> 6 ];
+ Bit32u keyCode = ( data & 0x1c00) >> 9;
+ if ( chip->reg08 & 0x40 ) {
+ keyCode |= ( data & 0x100)>>8; /* notesel == 1 */
+ } else {
+ keyCode |= ( data & 0x200)>>9; /* notesel == 0 */
+ }
+ //Add the keycode and ksl into the highest bits of chanData
+ data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE );
+ Channel__SetChanData( self + 0, chip, data );
+ if ( fourOp & 0x3f ) {
+ Channel__SetChanData( self + 1, chip, data );
+ }
+}
+
+static void Channel__WriteA0(Channel *self, const Chip* chip, Bit8u val ) {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & self->fourMask;
+ //Don't handle writes to silent fourop channels
+ if ( fourOp > 0x80 )
+ return;
+ Bit32u change = (self->chanData ^ val ) & 0xff;
+ if ( change ) {
+ self->chanData ^= change;
+ Channel__UpdateFrequency( self, chip, fourOp );
+ }
+}
+
+static void Channel__WriteB0(Channel *self, const Chip* chip, Bit8u val ) {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & self->fourMask;
+ //Don't handle writes to silent fourop channels
+ if ( fourOp > 0x80 )
+ return;
+ Bitu change = (self->chanData ^ ( val << 8 ) ) & 0x1f00;
+ if ( change ) {
+ self->chanData ^= change;
+ Channel__UpdateFrequency( self, chip, fourOp );
+ }
+ //Check for a change in the keyon/off state
+ if ( !(( val ^ self->regB0) & 0x20))
+ return;
+ self->regB0 = val;
+ if ( val & 0x20 ) {
+ Operator__KeyOn( Channel__Op(self, 0), 0x1 );
+ Operator__KeyOn( Channel__Op(self, 1), 0x1 );
+ if ( fourOp & 0x3f ) {
+ Operator__KeyOn( Channel__Op(self + 1, 0), 1 );
+ Operator__KeyOn( Channel__Op(self + 1, 1), 1 );
+ }
+ } else {
+ Operator__KeyOff( Channel__Op(self, 0), 0x1 );
+ Operator__KeyOff( Channel__Op(self, 1), 0x1 );
+ if ( fourOp & 0x3f ) {
+ Operator__KeyOff( Channel__Op(self + 1, 0), 1 );
+ Operator__KeyOff( Channel__Op(self + 1, 1), 1 );
+ }
+ }
+}
+
+static void Channel__WriteC0(Channel *self, const Chip* chip, Bit8u val ) {
+ Bit8u change = val ^ self->regC0;
+ if ( !change )
+ return;
+ self->regC0 = val;
+ self->feedback = ( val >> 1 ) & 7;
+ if ( self->feedback ) {
+ //We shift the input to the right 10 bit wave index value
+ self->feedback = 9 - self->feedback;
+ } else {
+ self->feedback = 31;
+ }
+ //Select the new synth mode
+ if ( chip->opl3Active ) {
+ //4-op mode enabled for this channel
+ if ( (chip->reg104 & self->fourMask) & 0x3f ) {
+ Channel* chan0, *chan1;
+ //Check if it's the 2nd channel in a 4-op
+ if ( !(self->fourMask & 0x80 ) ) {
+ chan0 = self;
+ chan1 = self + 1;
+ } else {
+ chan0 = self - 1;
+ chan1 = self;
+ }
+
+ Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 );
+ switch ( synth ) {
+ case 0:
+ chan0->synthHandler = Channel__BlockTemplate_sm3FMFM;
+ break;
+ case 1:
+ chan0->synthHandler = Channel__BlockTemplate_sm3AMFM;
+ break;
+ case 2:
+ chan0->synthHandler = Channel__BlockTemplate_sm3FMAM ;
+ break;
+ case 3:
+ chan0->synthHandler = Channel__BlockTemplate_sm3AMAM ;
+ break;
+ }
+ //Disable updating percussion channels
+ } else if ((self->fourMask & 0x40) && ( chip->regBD & 0x20) ) {
+
+ //Regular dual op, am or fm
+ } else if ( val & 1 ) {
+ self->synthHandler = Channel__BlockTemplate_sm3AM;
+ } else {
+ self->synthHandler = Channel__BlockTemplate_sm3FM;
+ }
+ self->maskLeft = ( val & 0x10 ) ? -1 : 0;
+ self->maskRight = ( val & 0x20 ) ? -1 : 0;
+ //opl2 active
+ } else {
+ //Disable updating percussion channels
+ if ( (self->fourMask & 0x40) && ( chip->regBD & 0x20 ) ) {
+
+ //Regular dual op, am or fm
+ } else if ( val & 1 ) {
+ self->synthHandler = Channel__BlockTemplate_sm2AM;
+ } else {
+ self->synthHandler = Channel__BlockTemplate_sm2FM;
+ }
+ }
+}
+
+static void Channel__ResetC0(Channel *self, const Chip* chip ) {
+ Bit8u val = self->regC0;
+ self->regC0 ^= 0xff;
+ Channel__WriteC0( self, chip, val );
+};
+
+static inline void Channel__GeneratePercussion(Channel *self, Chip* chip,
+ Bit32s* output, int opl3Mode ) {
+ Channel* chan = self;
+
+ //BassDrum
+ Bit32s mod = (Bit32u)((self->old[0] + self->old[1])) >> self->feedback;
+ self->old[0] = self->old[1];
+ self->old[1] = Operator__GetSample( Channel__Op(self, 0), mod );
+
+ //When bassdrum is in AM mode first operator is ignoed
+ if ( chan->regC0 & 1 ) {
+ mod = 0;
+ } else {
+ mod = self->old[0];
+ }
+ Bit32s sample = Operator__GetSample( Channel__Op(self, 1), mod );
+
+ //Precalculate stuff used by other outputs
+ Bit32u noiseBit = Chip__ForwardNoise(chip) & 0x1;
+ Bit32u c2 = Operator__ForwardWave(Channel__Op(self, 2));
+ Bit32u c5 = Operator__ForwardWave(Channel__Op(self, 5));
+ Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00;
+
+ //Hi-Hat
+ Bit32u hhVol = Operator__ForwardVolume(Channel__Op(self, 2));
+ if ( !ENV_SILENT( hhVol ) ) {
+ Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 )));
+ sample += Operator__GetWave( Channel__Op(self, 2), hhIndex, hhVol );
+ }
+ //Snare Drum
+ Bit32u sdVol = Operator__ForwardVolume( Channel__Op(self, 3) );
+ if ( !ENV_SILENT( sdVol ) ) {
+ Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 );
+ sample += Operator__GetWave( Channel__Op(self, 3), sdIndex, sdVol );
+ }
+ //Tom-tom
+ sample += Operator__GetSample( Channel__Op(self, 4), 0 );
+
+ //Top-Cymbal
+ Bit32u tcVol = Operator__ForwardVolume(Channel__Op(self, 5));
+ if ( !ENV_SILENT( tcVol ) ) {
+ Bit32u tcIndex = (1 + phaseBit) << 8;
+ sample += Operator__GetWave( Channel__Op(self, 5), tcIndex, tcVol );
+ }
+ sample <<= 1;
+ if ( opl3Mode ) {
+ output[0] += sample;
+ output[1] += sample;
+ } else {
+ output[0] += sample;
+ }
+}
+
+Channel* Channel__BlockTemplate(Channel *self, Chip* chip,
+ Bit32u samples, Bit32s* output,
+ SynthMode mode ) {
+ Bitu i;
+
+ switch( mode ) {
+ case sm2AM:
+ case sm3AM:
+ if ( Operator__Silent(Channel__Op(self, 0))
+ && Operator__Silent(Channel__Op(self, 1))) {
+ self->old[0] = self->old[1] = 0;
+ return(self + 1);
+ }
+ break;
+ case sm2FM:
+ case sm3FM:
+ if ( Operator__Silent(Channel__Op(self, 1))) {
+ self->old[0] = self->old[1] = 0;
+ return (self + 1);
+ }
+ break;
+ case sm3FMFM:
+ if ( Operator__Silent(Channel__Op(self, 3))) {
+ self->old[0] = self->old[1] = 0;
+ return (self + 2);
+ }
+ break;
+ case sm3AMFM:
+ if ( Operator__Silent( Channel__Op(self, 0) )
+ && Operator__Silent( Channel__Op(self, 3) )) {
+ self->old[0] = self->old[1] = 0;
+ return (self + 2);
+ }
+ break;
+ case sm3FMAM:
+ if ( Operator__Silent( Channel__Op(self, 1))
+ && Operator__Silent( Channel__Op(self, 3))) {
+ self->old[0] = self->old[1] = 0;
+ return (self + 2);
+ }
+ break;
+ case sm3AMAM:
+ if ( Operator__Silent( Channel__Op(self, 0) )
+ && Operator__Silent( Channel__Op(self, 2) )
+ && Operator__Silent( Channel__Op(self, 3) )) {
+ self->old[0] = self->old[1] = 0;
+ return (self + 2);
+ }
+ break;
+
+ default:
+ abort();
+ }
+ //Init the operators with the the current vibrato and tremolo values
+ Operator__Prepare( Channel__Op( self, 0 ), chip );
+ Operator__Prepare( Channel__Op( self, 1 ), chip );
+ if ( mode > sm4Start ) {
+ Operator__Prepare( Channel__Op( self, 2 ), chip );
+ Operator__Prepare( Channel__Op( self, 3 ), chip );
+ }
+ if ( mode > sm6Start ) {
+ Operator__Prepare( Channel__Op( self, 4 ), chip );
+ Operator__Prepare( Channel__Op( self, 5 ), chip );
+ }
+ for ( i = 0; i < samples; i++ ) {
+ //Early out for percussion handlers
+ if ( mode == sm2Percussion ) {
+ Channel__GeneratePercussion( self, chip, output + i, FALSE );
+ continue; //Prevent some unitialized value bitching
+ } else if ( mode == sm3Percussion ) {
+ Channel__GeneratePercussion( self, chip, output + i * 2, TRUE );
+ continue; //Prevent some unitialized value bitching
+ }
+
+ //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise
+ Bit32s mod = (Bit32u)((self->old[0] + self->old[1])) >> self->feedback;
+ self->old[0] = self->old[1];
+ self->old[1] = Operator__GetSample( Channel__Op(self, 0), mod );
+ Bit32s sample = 0;
+ Bit32s out0 = self->old[0];
+ if ( mode == sm2AM || mode == sm3AM ) {
+ sample = out0 + Operator__GetSample( Channel__Op(self, 1), 0 );
+ } else if ( mode == sm2FM || mode == sm3FM ) {
+ sample = Operator__GetSample( Channel__Op(self, 1), out0 );
+ } else if ( mode == sm3FMFM ) {
+ Bits next = Operator__GetSample( Channel__Op(self, 1), out0 );
+ next = Operator__GetSample( Channel__Op(self, 2), next );
+ sample = Operator__GetSample( Channel__Op(self, 3), next );
+ } else if ( mode == sm3AMFM ) {
+ sample = out0;
+ Bits next = Operator__GetSample( Channel__Op(self, 1), 0 );
+ next = Operator__GetSample( Channel__Op(self, 2), next );
+ sample += Operator__GetSample( Channel__Op(self, 3), next );
+ } else if ( mode == sm3FMAM ) {
+ sample = Operator__GetSample( Channel__Op(self, 1), out0 );
+ Bits next = Operator__GetSample( Channel__Op(self, 2), 0 );
+ sample += Operator__GetSample( Channel__Op(self, 3), next );
+ } else if ( mode == sm3AMAM ) {
+ sample = out0;
+ Bits next = Operator__GetSample( Channel__Op(self, 1), 0 );
+ sample += Operator__GetSample( Channel__Op(self, 2), next );
+ sample += Operator__GetSample( Channel__Op(self, 3), 0 );
+ }
+ switch( mode ) {
+ case sm2AM:
+ case sm2FM:
+ output[ i ] += sample;
+ break;
+ case sm3AM:
+ case sm3FM:
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ output[ i * 2 + 0 ] += sample & self->maskLeft;
+ output[ i * 2 + 1 ] += sample & self->maskRight;
+ break;
+ default:
+ abort();
+ }
+ }
+ switch( mode ) {
+ case sm2AM:
+ case sm2FM:
+ case sm3AM:
+ case sm3FM:
+ return ( self + 1 );
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ return ( self + 2 );
+ case sm2Percussion:
+ case sm3Percussion:
+ return( self + 3 );
+ default:
+ abort();
+ }
+ return 0;
+}
+
+/*
+ Chip
+*/
+
+void Chip__Chip(Chip *self) {
+ int i;
+
+ for (i=0; i<18; ++i) {
+ Channel__Channel(&self->chan[i]);
+ }
+
+ self->reg08 = 0;
+ self->reg04 = 0;
+ self->regBD = 0;
+ self->reg104 = 0;
+ self->opl3Active = 0;
+}
+
+static inline Bit32u Chip__ForwardNoise(Chip *self) {
+ self->noiseCounter += self->noiseAdd;
+ Bitu count = self->noiseCounter >> LFO_SH;
+ self->noiseCounter &= WAVE_MASK;
+ for ( ; count > 0; --count ) {
+ //Noise calculation from mame
+ self->noiseValue ^= ( 0x800302 ) & ( 0 - (self->noiseValue & 1 ) );
+ self->noiseValue >>= 1;
+ }
+ return self->noiseValue;
+}
+
+static inline Bit32u Chip__ForwardLFO(Chip *self, Bit32u samples ) {
+ //Current vibrato value, runs 4x slower than tremolo
+ self->vibratoSign = ( VibratoTable[ self->vibratoIndex >> 2] ) >> 7;
+ self->vibratoShift = ( VibratoTable[ self->vibratoIndex >> 2] & 7) + self->vibratoStrength;
+ self->tremoloValue = TremoloTable[ self->tremoloIndex ] >> self->tremoloStrength;
+
+ //Check hom many samples there can be done before the value changes
+ Bit32u todo = LFO_MAX - self->lfoCounter;
+ Bit32u count = (todo + self->lfoAdd - 1) / self->lfoAdd;
+ if ( count > samples ) {
+ count = samples;
+ self->lfoCounter += count * self->lfoAdd;
+ } else {
+ self->lfoCounter += count * self->lfoAdd;
+ self->lfoCounter &= (LFO_MAX - 1);
+ //Maximum of 7 vibrato value * 4
+ self->vibratoIndex = ( self->vibratoIndex + 1 ) & 31;
+ //Clip tremolo to the the table size
+ if ( self->tremoloIndex + 1 < TREMOLO_TABLE )
+ ++self->tremoloIndex;
+ else
+ self->tremoloIndex = 0;
+ }
+ return count;
+}
+
+
+static void Chip__WriteBD(Chip *self, Bit8u val ) {
+ Bit8u change = self->regBD ^ val;
+ if ( !change )
+ return;
+ self->regBD = val;
+ //TODO could do this with shift and xor?
+ self->vibratoStrength = (val & 0x40) ? 0x00 : 0x01;
+ self->tremoloStrength = (val & 0x80) ? 0x00 : 0x02;
+ if ( val & 0x20 ) {
+ //Drum was just enabled, make sure channel 6 has the right synth
+ if ( change & 0x20 ) {
+ if ( self->opl3Active ) {
+ self->chan[6].synthHandler
+ = Channel__BlockTemplate_sm3Percussion;
+ } else {
+ self->chan[6].synthHandler
+ = Channel__BlockTemplate_sm2Percussion;
+ }
+ }
+ //Bass Drum
+ if ( val & 0x10 ) {
+ Operator__KeyOn( &self->chan[6].op[0], 0x2 );
+ Operator__KeyOn( &self->chan[6].op[1], 0x2 );
+ } else {
+ Operator__KeyOff( &self->chan[6].op[0], 0x2 );
+ Operator__KeyOff( &self->chan[6].op[1], 0x2 );
+ }
+ //Hi-Hat
+ if ( val & 0x1 ) {
+ Operator__KeyOn( &self->chan[7].op[0], 0x2 );
+ } else {
+ Operator__KeyOff( &self->chan[7].op[0], 0x2 );
+ }
+ //Snare
+ if ( val & 0x8 ) {
+ Operator__KeyOn( &self->chan[7].op[1], 0x2 );
+ } else {
+ Operator__KeyOff( &self->chan[7].op[1], 0x2 );
+ }
+ //Tom-Tom
+ if ( val & 0x4 ) {
+ Operator__KeyOn( &self->chan[8].op[0], 0x2 );
+ } else {
+ Operator__KeyOff( &self->chan[8].op[0], 0x2 );
+ }
+ //Top Cymbal
+ if ( val & 0x2 ) {
+ Operator__KeyOn( &self->chan[8].op[1], 0x2 );
+ } else {
+ Operator__KeyOff( &self->chan[8].op[1], 0x2 );
+ }
+ //Toggle keyoffs when we turn off the percussion
+ } else if ( change & 0x20 ) {
+ //Trigger a reset to setup the original synth handler
+ Channel__ResetC0( &self->chan[6], self );
+ Operator__KeyOff( &self->chan[6].op[0], 0x2 );
+ Operator__KeyOff( &self->chan[6].op[1], 0x2 );
+ Operator__KeyOff( &self->chan[7].op[0], 0x2 );
+ Operator__KeyOff( &self->chan[7].op[1], 0x2 );
+ Operator__KeyOff( &self->chan[8].op[0], 0x2 );
+ Operator__KeyOff( &self->chan[8].op[1], 0x2 );
+ }
+}
+
+
+#define REGOP( _FUNC_ ) \
+ index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \
+ if ( OpOffsetTable[ index ] ) { \
+ Operator* regOp = (Operator*)( ((char *)self ) + OpOffsetTable[ index ] ); \
+ Operator__ ## _FUNC_ (regOp, self, val); \
+ }
+
+#define REGCHAN( _FUNC_ ) \
+ index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \
+ if ( ChanOffsetTable[ index ] ) { \
+ Channel* regChan = (Channel*)( ((char *)self ) + ChanOffsetTable[ index ] ); \
+ Channel__ ## _FUNC_ (regChan, self, val); \
+ }
+
+void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val ) {
+ Bitu index;
+ switch ( (reg & 0xf0) >> 4 ) {
+ case 0x00 >> 4:
+ if ( reg == 0x01 ) {
+ self->waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0;
+ } else if ( reg == 0x104 ) {
+ //Only detect changes in lowest 6 bits
+ if ( !((self->reg104 ^ val) & 0x3f) )
+ return;
+ //Always keep the highest bit enabled, for checking > 0x80
+ self->reg104 = 0x80 | ( val & 0x3f );
+ } else if ( reg == 0x105 ) {
+ int i;
+
+ //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register
+ if ( !((self->opl3Active ^ val) & 1 ) )
+ return;
+ self->opl3Active = ( val & 1 ) ? 0xff : 0;
+ //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers
+ for ( i = 0; i < 18;i++ ) {
+ Channel__ResetC0( &self->chan[i], self );
+ }
+ } else if ( reg == 0x08 ) {
+ self->reg08 = val;
+ }
+ case 0x10 >> 4:
+ break;
+ case 0x20 >> 4:
+ case 0x30 >> 4:
+ REGOP( Write20 );
+ break;
+ case 0x40 >> 4:
+ case 0x50 >> 4:
+ REGOP( Write40 );
+ break;
+ case 0x60 >> 4:
+ case 0x70 >> 4:
+ REGOP( Write60 );
+ break;
+ case 0x80 >> 4:
+ case 0x90 >> 4:
+ REGOP( Write80 );
+ break;
+ case 0xa0 >> 4:
+ REGCHAN( WriteA0 );
+ break;
+ case 0xb0 >> 4:
+ if ( reg == 0xbd ) {
+ Chip__WriteBD( self, val );
+ } else {
+ REGCHAN( WriteB0 );
+ }
+ break;
+ case 0xc0 >> 4:
+ REGCHAN( WriteC0 );
+ case 0xd0 >> 4:
+ break;
+ case 0xe0 >> 4:
+ case 0xf0 >> 4:
+ REGOP( WriteE0 );
+ break;
+ }
+}
+
+Bit32u Chip__WriteAddr(Chip *self, Bit32u port, Bit8u val ) {
+ switch ( port & 3 ) {
+ case 0:
+ return val;
+ case 2:
+ if ( self->opl3Active || (val == 0x05) )
+ return 0x100 | val;
+ else
+ return val;
+ }
+ return 0;
+}
+
+void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output ) {
+ while ( total > 0 ) {
+ Channel *ch;
+ int count;
+
+ Bit32u samples = Chip__ForwardLFO( self, total );
+ memset(output, 0, sizeof(Bit32s) * samples);
+ count = 0;
+ for ( ch = self->chan; ch < self->chan + 9; ) {
+ count++;
+ ch = (ch->synthHandler)( ch, self, samples, output );
+ }
+ total -= samples;
+ output += samples;
+ }
+}
+
+void Chip__GenerateBlock3(Chip *self, Bitu total, Bit32s* output ) {
+ while ( total > 0 ) {
+ int count;
+ Channel *ch;
+
+ Bit32u samples = Chip__ForwardLFO( self, total );
+ memset(output, 0, sizeof(Bit32s) * samples *2);
+ count = 0;
+ for ( ch = self->chan; ch < self->chan + 18; ) {
+ count++;
+ ch = (ch->synthHandler)( ch, self, samples, output );
+ }
+ total -= samples;
+ output += samples * 2;
+ }
+}
+
+void Chip__Setup(Chip *self, Bit32u rate ) {
+ double original = OPLRATE;
+ Bit32u i;
+// double original = rate;
+ double scale = original / (double)rate;
+
+ //Noise counter is run at the same precision as general waves
+ self->noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
+ self->noiseCounter = 0;
+ self->noiseValue = 1; //Make sure it triggers the noise xor the first time
+ //The low frequency oscillation counter
+ //Every time his overflows vibrato and tremoloindex are increased
+ self->lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
+ self->lfoCounter = 0;
+ self->vibratoIndex = 0;
+ self->tremoloIndex = 0;
+
+ //With higher octave this gets shifted up
+ //-1 since the freqCreateTable = *2
+#ifdef WAVE_PRECISION
+ double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10));
+ for ( i = 0; i < 16; i++ ) {
+ self->freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] );
+ }
+#else
+ Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10)));
+ for ( i = 0; i < 16; i++ ) {
+ self->freqMul[i] = freqScale * FreqCreateTable[ i ];
+ }
+#endif
+
+ //-3 since the real envelope takes 8 steps to reach the single value we supply
+ for ( i = 0; i < 76; i++ ) {
+ Bit8u index, shift;
+ EnvelopeSelect( i, &index, &shift );
+ self->linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 )));
+ }
+ //Generate the best matching attack rate
+ for ( i = 0; i < 62; i++ ) {
+ Bit8u index, shift;
+ EnvelopeSelect( i, &index, &shift );
+ //Original amount of samples the attack would take
+ Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale);
+
+ Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 )));
+ Bit32s bestAdd = guessAdd;
+ Bit32u bestDiff = 1 << 30;
+ Bit32u passes;
+
+ for ( passes = 0; passes < 16; passes ++ ) {
+ Bit32s volume = ENV_MAX;
+ Bit32s samples = 0;
+ Bit32u count = 0;
+ while ( volume > 0 && samples < original * 2 ) {
+ count += guessAdd;
+ Bit32s change = count >> RATE_SH;
+ count &= RATE_MASK;
+ if ( GCC_UNLIKELY(change) ) { // less than 1 %
+ volume += ( ~volume * change ) >> 3;
+ }
+ samples++;
+
+ }
+ Bit32s diff = original - samples;
+ Bit32u lDiff = labs( diff );
+ //Init last on first pass
+ if ( lDiff < bestDiff ) {
+ bestDiff = lDiff;
+ bestAdd = guessAdd;
+ if ( !bestDiff )
+ break;
+ }
+ //Below our target
+ if ( diff < 0 ) {
+ //Better than the last time
+ Bit32s mul = ((original - diff) << 12) / original;
+ guessAdd = ((guessAdd * mul) >> 12);
+ guessAdd++;
+ } else if ( diff > 0 ) {
+ Bit32s mul = ((original - diff) << 12) / original;
+ guessAdd = (guessAdd * mul) >> 12;
+ guessAdd--;
+ }
+ }
+ self->attackRates[i] = bestAdd;
+ }
+ for ( i = 62; i < 76; i++ ) {
+ //This should provide instant volume maximizing
+ self->attackRates[i] = 8 << RATE_SH;
+ }
+ //Setup the channels with the correct four op flags
+ //Channels are accessed through a table so they appear linear here
+ self->chan[ 0].fourMask = 0x00 | ( 1 << 0 );
+ self->chan[ 1].fourMask = 0x80 | ( 1 << 0 );
+ self->chan[ 2].fourMask = 0x00 | ( 1 << 1 );
+ self->chan[ 3].fourMask = 0x80 | ( 1 << 1 );
+ self->chan[ 4].fourMask = 0x00 | ( 1 << 2 );
+ self->chan[ 5].fourMask = 0x80 | ( 1 << 2 );
+
+ self->chan[ 9].fourMask = 0x00 | ( 1 << 3 );
+ self->chan[10].fourMask = 0x80 | ( 1 << 3 );
+ self->chan[11].fourMask = 0x00 | ( 1 << 4 );
+ self->chan[12].fourMask = 0x80 | ( 1 << 4 );
+ self->chan[13].fourMask = 0x00 | ( 1 << 5 );
+ self->chan[14].fourMask = 0x80 | ( 1 << 5 );
+
+ //mark the percussion channels
+ self->chan[ 6].fourMask = 0x40;
+ self->chan[ 7].fourMask = 0x40;
+ self->chan[ 8].fourMask = 0x40;
+
+ //Clear Everything in opl3 mode
+ Chip__WriteReg( self, 0x105, 0x1 );
+ for ( i = 0; i < 512; i++ ) {
+ if ( i == 0x105 )
+ continue;
+ Chip__WriteReg( self, i, 0xff );
+ Chip__WriteReg( self, i, 0x0 );
+ }
+ Chip__WriteReg( self, 0x105, 0x0 );
+ //Clear everything in opl2 mode
+ for ( i = 0; i < 255; i++ ) {
+ Chip__WriteReg( self, i, 0xff );
+ Chip__WriteReg( self, i, 0x0 );
+ }
+}
+
+static int doneTables = FALSE;
+void DBOPL_InitTables( void ) {
+ int i, oct;
+
+ if ( doneTables )
+ return;
+ doneTables = TRUE;
+#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
+ //Exponential volume table, same as the real adlib
+ for ( i = 0; i < 256; i++ ) {
+ //Save them in reverse
+ ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 );
+ ExpTable[i] += 1024; //or remove the -1 oh well :)
+ //Preshift to the left once so the final volume can shift to the right
+ ExpTable[i] *= 2;
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ //Add 0.5 for the trunc rounding of the integer cast
+ //Do a PI sinetable instead of the original 0.5 PI
+ for ( i = 0; i < 512; i++ ) {
+ SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_TABLEMUL )
+ //Multiplication based tables
+ for ( i = 0; i < 384; i++ ) {
+ int s = i * 8;
+ //TODO maybe keep some of the precision errors of the original table?
+ double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH ));
+ MulTable[i] = (Bit16u)(val);
+ }
+
+ //Sine Wave Base
+ for ( i = 0; i < 512; i++ ) {
+ WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084);
+ WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ];
+ }
+ //Exponential wave
+ for ( i = 0; i < 256; i++ ) {
+ WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 );
+ WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ];
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_TABLELOG )
+ //Sine Wave Base
+ for ( i = 0; i < 512; i++ ) {
+ WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
+ WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i];
+ }
+ //Exponential wave
+ for ( i = 0; i < 256; i++ ) {
+ WaveTable[ 0x700 + i ] = i * 8;
+ WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8;
+ }
+#endif
+
+ // | |//\\|____|WAV7|//__|/\ |____|/\/\|
+ // |\\//| | |WAV7| | \/| | |
+ // |06 |0126|27 |7 |3 |4 |4 5 |5 |
+
+#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL ))
+ for ( i = 0; i < 256; i++ ) {
+ //Fill silence gaps
+ WaveTable[ 0x400 + i ] = WaveTable[0];
+ WaveTable[ 0x500 + i ] = WaveTable[0];
+ WaveTable[ 0x900 + i ] = WaveTable[0];
+ WaveTable[ 0xc00 + i ] = WaveTable[0];
+ WaveTable[ 0xd00 + i ] = WaveTable[0];
+ //Replicate sines in other pieces
+ WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ];
+ //double speed sines
+ WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ];
+ WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ }
+#endif
+
+ //Create the ksl table
+ for ( oct = 0; oct < 8; oct++ ) {
+ int base = oct * 8;
+ for ( i = 0; i < 16; i++ ) {
+ int val = base - KslCreateTable[i];
+ if ( val < 0 )
+ val = 0;
+ //*4 for the final range to match attenuation range
+ KslTable[ oct * 16 + i ] = val * 4;
+ }
+ }
+ //Create the Tremolo table, just increase and decrease a triangle wave
+ for ( i = 0; i < TREMOLO_TABLE / 2; i++ ) {
+ Bit8u val = i << ENV_EXTRA;
+ TremoloTable[i] = val;
+ TremoloTable[TREMOLO_TABLE - 1 - i] = val;
+ }
+ //Create a table with offsets of the channels from the start of the chip
+ Chip *chip = NULL;
+ for ( i = 0; i < 32; i++ ) {
+ Bitu index = i & 0xf;
+ if ( index >= 9 ) {
+ ChanOffsetTable[i] = 0;
+ continue;
+ }
+ //Make sure the four op channels follow eachother
+ if ( index < 6 ) {
+ index = (index % 3) * 2 + ( index / 3 );
+ }
+ //Add back the bits for highest ones
+ if ( i >= 16 )
+ index += 9;
+ Bitu blah = (Bitu) ( &(chip->chan[ index ]) );
+ ChanOffsetTable[i] = blah;
+ }
+ //Same for operators
+ for ( i = 0; i < 64; i++ ) {
+ if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) {
+ OpOffsetTable[i] = 0;
+ continue;
+ }
+ Bitu chNum = (i / 8) * 3 + (i % 8) % 3;
+ //Make sure we use 16 and up for the 2nd range to match the chanoffset gap
+ if ( chNum >= 12 )
+ chNum += 16 - 12;
+ Bitu opNum = ( i % 8 ) / 3;
+ Channel* chan = NULL;
+ Bitu blah = (Bitu) ( &(chan->op[opNum]) );
+ OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah;
+ }
+#if 0
+ //Stupid checks if table's are correct
+ for ( Bitu i = 0; i < 18; i++ ) {
+ Bit32u find = (Bit16u)( &(chip->chan[ i ]) );
+ for ( Bitu c = 0; c < 32; c++ ) {
+ if ( ChanOffsetTable[c] == find ) {
+ find = 0;
+ break;
+ }
+ }
+ if ( find ) {
+ find = find;
+ }
+ }
+ for ( Bitu i = 0; i < 36; i++ ) {
+ Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) );
+ for ( Bitu c = 0; c < 64; c++ ) {
+ if ( OpOffsetTable[c] == find ) {
+ find = 0;
+ break;
+ }
+ }
+ if ( find ) {
+ find = find;
+ }
+ }
+#endif
+}
+
+/*
+
+Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) {
+ return chip.WriteAddr( port, val );
+
+}
+void Handler::WriteReg( Bit32u addr, Bit8u val ) {
+ chip.WriteReg( addr, val );
+}
+
+void Handler::Generate( MixerChannel* chan, Bitu samples ) {
+ Bit32s buffer[ 512 * 2 ];
+ if ( GCC_UNLIKELY(samples > 512) )
+ samples = 512;
+ if ( !chip.opl3Active ) {
+ chip.GenerateBlock2( samples, buffer );
+ chan->AddSamples_m32( samples, buffer );
+ } else {
+ chip.GenerateBlock3( samples, buffer );
+ chan->AddSamples_s32( samples, buffer );
+ }
+}
+
+void Handler::Init( Bitu rate ) {
+ InitTables();
+ chip.Setup( rate );
+}
+*/
+
diff --git a/opl/dbopl.h b/opl/dbopl.h
new file mode 100644
index 00000000..a5c10bfd
--- /dev/null
+++ b/opl/dbopl.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2002-2010 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <inttypes.h>
+
+//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
+#define WAVE_HANDLER 10
+//Use a logarithmic wavetable with an exponential table for volume
+#define WAVE_TABLELOG 11
+//Use a linear wavetable with a multiply table for volume
+#define WAVE_TABLEMUL 12
+
+//Select the type of wave generator routine
+#define DBOPL_WAVE WAVE_TABLEMUL
+
+typedef struct _Chip Chip;
+typedef struct _Operator Operator;
+typedef struct _Channel Channel;
+
+typedef uintptr_t Bitu;
+typedef intptr_t Bits;
+typedef uint32_t Bit32u;
+typedef int32_t Bit32s;
+typedef uint16_t Bit16u;
+typedef int16_t Bit16s;
+typedef uint8_t Bit8u;
+typedef int8_t Bit8s;
+
+#if (DBOPL_WAVE == WAVE_HANDLER)
+typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume );
+#endif
+
+#define DB_FASTCALL
+
+typedef Bits (*VolumeHandler)(Operator *self);
+typedef Channel* (*SynthHandler)(Channel *self, Chip* chip, Bit32u samples, Bit32s* output );
+
+//Different synth modes that can generate blocks of data
+typedef enum {
+ sm2AM,
+ sm2FM,
+ sm3AM,
+ sm3FM,
+ sm4Start,
+ sm3FMFM,
+ sm3AMFM,
+ sm3FMAM,
+ sm3AMAM,
+ sm6Start,
+ sm2Percussion,
+ sm3Percussion,
+} SynthMode;
+
+//Shifts for the values contained in chandata variable
+enum {
+ SHIFT_KSLBASE = 16,
+ SHIFT_KEYCODE = 24,
+};
+
+//Masks for operator 20 values
+enum {
+ MASK_KSR = 0x10,
+ MASK_SUSTAIN = 0x20,
+ MASK_VIBRATO = 0x40,
+ MASK_TREMOLO = 0x80,
+};
+
+typedef enum {
+ OFF,
+ RELEASE,
+ SUSTAIN,
+ DECAY,
+ ATTACK,
+} OperatorState;
+
+struct _Operator {
+ VolumeHandler volHandler;
+
+#if (DBOPL_WAVE == WAVE_HANDLER)
+ WaveHandler waveHandler; //Routine that generate a wave
+#else
+ Bit16s* waveBase;
+ Bit32u waveMask;
+ Bit32u waveStart;
+#endif
+ Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index
+ Bit32u waveAdd; //The base frequency without vibrato
+ Bit32u waveCurrent; //waveAdd + vibratao
+
+ Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this
+ Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove?
+ Bit32u vibrato; //Scaled up vibrato strength
+ Bit32s sustainLevel; //When stopping at sustain level stop here
+ Bit32s totalLevel; //totalLevel is added to every generated volume
+ Bit32u currentLevel; //totalLevel + tremolo
+ Bit32s volume; //The currently active volume
+
+ Bit32u attackAdd; //Timers for the different states of the envelope
+ Bit32u decayAdd;
+ Bit32u releaseAdd;
+ Bit32u rateIndex; //Current position of the evenlope
+
+ Bit8u rateZero; //Bits for the different states of the envelope having no changes
+ Bit8u keyOn; //Bitmask of different values that can generate keyon
+ //Registers, also used to check for changes
+ Bit8u reg20, reg40, reg60, reg80, regE0;
+ //Active part of the envelope we're in
+ Bit8u state;
+ //0xff when tremolo is enabled
+ Bit8u tremoloMask;
+ //Strength of the vibrato
+ Bit8u vibStrength;
+ //Keep track of the calculated KSR so we can check for changes
+ Bit8u ksr;
+};
+
+struct _Channel {
+ Operator op[2];
+ SynthHandler synthHandler;
+ Bit32u chanData; //Frequency/octave and derived values
+ Bit32s old[2]; //Old data for feedback
+
+ Bit8u feedback; //Feedback shift
+ Bit8u regB0; //Register values to check for changes
+ Bit8u regC0;
+ //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
+ Bit8u fourMask;
+ Bit8s maskLeft; //Sign extended values for both channel's panning
+ Bit8s maskRight;
+
+};
+
+struct _Chip {
+ //This is used as the base counter for vibrato and tremolo
+ Bit32u lfoCounter;
+ Bit32u lfoAdd;
+
+
+ Bit32u noiseCounter;
+ Bit32u noiseAdd;
+ Bit32u noiseValue;
+
+ //Frequency scales for the different multiplications
+ Bit32u freqMul[16];
+ //Rates for decay and release for rate of this chip
+ Bit32u linearRates[76];
+ //Best match attack rates for the rate of this chip
+ Bit32u attackRates[76];
+
+ //18 channels with 2 operators each
+ Channel chan[18];
+
+ Bit8u reg104;
+ Bit8u reg08;
+ Bit8u reg04;
+ Bit8u regBD;
+ Bit8u vibratoIndex;
+ Bit8u tremoloIndex;
+ Bit8s vibratoSign;
+ Bit8u vibratoShift;
+ Bit8u tremoloValue;
+ Bit8u vibratoStrength;
+ Bit8u tremoloStrength;
+ //Mask for allowed wave forms
+ Bit8u waveFormMask;
+ //0 or -1 when enabled
+ Bit8s opl3Active;
+
+};
+
+/*
+struct Handler : public Adlib::Handler {
+ DBOPL::Chip chip;
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val );
+ virtual void WriteReg( Bit32u addr, Bit8u val );
+ virtual void Generate( MixerChannel* chan, Bitu samples );
+ virtual void Init( Bitu rate );
+};
+*/
+
+
+void Chip__Setup(Chip *self, Bit32u rate );
+void DBOPL_InitTables( void );
+void Chip__Chip(Chip *self);
+void Chip__WriteReg(Chip *self, Bit32u reg, Bit8u val );
+void Chip__GenerateBlock2(Chip *self, Bitu total, Bit32s* output );
+
+
diff --git a/opl/examples/.gitignore b/opl/examples/.gitignore
new file mode 100644
index 00000000..4d589c73
--- /dev/null
+++ b/opl/examples/.gitignore
@@ -0,0 +1,7 @@
+Makefile.in
+Makefile
+.deps
+droplay
+*.exe
+tags
+TAGS
diff --git a/opl/examples/Makefile.am b/opl/examples/Makefile.am
new file mode 100644
index 00000000..7c2c7c8a
--- /dev/null
+++ b/opl/examples/Makefile.am
@@ -0,0 +1,8 @@
+
+AM_CFLAGS = -I..
+
+noinst_PROGRAMS=droplay
+
+droplay_LDADD = ../libopl.a @LDFLAGS@ @SDL_LIBS@ @SDLMIXER_LIBS@
+droplay_SOURCES = droplay.c
+
diff --git a/opl/examples/droplay.c b/opl/examples/droplay.c
new file mode 100644
index 00000000..36f5c3c0
--- /dev/null
+++ b/opl/examples/droplay.c
@@ -0,0 +1,217 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Demonstration program for OPL library to play back DRO
+// format files.
+//
+//-----------------------------------------------------------------------------
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "SDL.h"
+
+#include "opl.h"
+
+#define HEADER_STRING "DBRAWOPL"
+#define ADLIB_PORT 0x388
+
+void WriteReg(unsigned int reg, unsigned int val)
+{
+ int i;
+
+ // This was recorded from an OPL2, but we are probably playing
+ // back on an OPL3, so we need to enable the original OPL2
+ // channels. Doom does this already, but other games don't.
+
+ if ((reg & 0xf0) == OPL_REGS_FEEDBACK)
+ {
+ val |= 0x30;
+ }
+
+ OPL_WritePort(OPL_REGISTER_PORT, reg);
+
+ for (i=0; i<6; ++i)
+ {
+ OPL_ReadPort(OPL_REGISTER_PORT);
+ }
+
+ OPL_WritePort(OPL_DATA_PORT, val);
+
+ for (i=0; i<35; ++i)
+ {
+ OPL_ReadPort(OPL_REGISTER_PORT);
+ }
+}
+
+void ClearAllRegs(void)
+{
+ int i;
+
+ for (i=0; i<=0xff; ++i)
+ {
+ WriteReg(i, 0x00);
+ }
+}
+
+void Init(void)
+{
+ if (SDL_Init(SDL_INIT_TIMER) < 0)
+ {
+ fprintf(stderr, "Unable to initialise SDL timer\n");
+ exit(-1);
+ }
+
+ if (!OPL_Init(ADLIB_PORT))
+ {
+ fprintf(stderr, "Unable to initialise OPL layer\n");
+ exit(-1);
+ }
+}
+
+void Shutdown(void)
+{
+ OPL_Shutdown();
+}
+
+struct timer_data
+{
+ int running;
+ FILE *fstream;
+};
+
+void TimerCallback(void *data)
+{
+ struct timer_data *timer_data = data;
+ int delay;
+
+ if (!timer_data->running)
+ {
+ return;
+ }
+
+ // Read data until we must make a delay.
+
+ for (;;)
+ {
+ int reg, val;
+
+ // End of file?
+
+ if (feof(timer_data->fstream))
+ {
+ timer_data->running = 0;
+ return;
+ }
+
+ reg = fgetc(timer_data->fstream);
+ val = fgetc(timer_data->fstream);
+
+ // Register value of 0 or 1 indicates a delay.
+
+ if (reg == 0x00)
+ {
+ delay = val;
+ break;
+ }
+ else if (reg == 0x01)
+ {
+ val |= (fgetc(timer_data->fstream) << 8);
+ delay = val;
+ break;
+ }
+ else
+ {
+ WriteReg(reg, val);
+ }
+ }
+
+ // Schedule the next timer callback.
+
+ OPL_SetCallback(delay, TimerCallback, timer_data);
+}
+
+void PlayFile(char *filename)
+{
+ struct timer_data timer_data;
+ int running;
+ char buf[8];
+
+ timer_data.fstream = fopen(filename, "rb");
+
+ if (timer_data.fstream == NULL)
+ {
+ fprintf(stderr, "Failed to open %s\n", filename);
+ exit(-1);
+ }
+
+ if (fread(buf, 1, 8, timer_data.fstream) < 8)
+ {
+ fprintf(stderr, "failed to read raw OPL header\n");
+ exit(-1);
+ }
+
+ if (strncmp(buf, HEADER_STRING, 8) != 0)
+ {
+ fprintf(stderr, "Raw OPL header not found\n");
+ exit(-1);
+ }
+
+ fseek(timer_data.fstream, 28, SEEK_SET);
+ timer_data.running = 1;
+
+ // Start callback loop sequence.
+
+ OPL_SetCallback(0, TimerCallback, &timer_data);
+
+ // Sleep until the playback finishes.
+
+ do
+ {
+ OPL_Lock();
+ running = timer_data.running;
+ OPL_Unlock();
+
+ SDL_Delay(100);
+ } while (running);
+
+ fclose(timer_data.fstream);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2)
+ {
+ printf("Usage: %s <filename>\n", argv[0]);
+ exit(-1);
+ }
+
+ Init();
+
+ PlayFile(argv[1]);
+
+ ClearAllRegs();
+ Shutdown();
+
+ return 0;
+}
+
diff --git a/opl/ioperm_sys.c b/opl/ioperm_sys.c
new file mode 100644
index 00000000..8f50bcd3
--- /dev/null
+++ b/opl/ioperm_sys.c
@@ -0,0 +1,361 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2002, 2003 Marcel Telka
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Interface to the ioperm.sys driver, based on code from the
+// Cygwin ioperm library.
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+
+#include <errno.h>
+
+#include "ioperm_sys.h"
+
+#define IOPERM_FILE L"\\\\.\\ioperm"
+
+#define IOCTL_IOPERM \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+struct ioperm_data
+{
+ unsigned long from;
+ unsigned long num;
+ int turn_on;
+};
+
+// Function pointers for advapi32.dll. This DLL does not exist on
+// Windows 9x, so they are dynamically loaded from the DLL at runtime.
+
+static SC_HANDLE WINAPI (*MyOpenSCManagerW)(wchar_t *lpMachineName,
+ wchar_t *lpDatabaseName,
+ DWORD dwDesiredAccess) = NULL;
+static SC_HANDLE WINAPI (*MyCreateServiceW)(SC_HANDLE hSCManager,
+ wchar_t *lpServiceName,
+ wchar_t *lpDisplayName,
+ DWORD dwDesiredAccess,
+ DWORD dwServiceType,
+ DWORD dwStartType,
+ DWORD dwErrorControl,
+ wchar_t *lpBinaryPathName,
+ wchar_t *lpLoadOrderGroup,
+ LPDWORD lpdwTagId,
+ wchar_t *lpDependencies,
+ wchar_t *lpServiceStartName,
+ wchar_t *lpPassword);
+static SC_HANDLE WINAPI (*MyOpenServiceW)(SC_HANDLE hSCManager,
+ wchar_t *lpServiceName,
+ DWORD dwDesiredAccess);
+static BOOL WINAPI (*MyStartServiceW)(SC_HANDLE hService,
+ DWORD dwNumServiceArgs,
+ wchar_t **lpServiceArgVectors);
+static BOOL WINAPI (*MyControlService)(SC_HANDLE hService,
+ DWORD dwControl,
+ LPSERVICE_STATUS lpServiceStatus);
+static BOOL WINAPI (*MyCloseServiceHandle)(SC_HANDLE hSCObject);
+static BOOL WINAPI (*MyDeleteService)(SC_HANDLE hService);
+
+static struct
+{
+ char *name;
+ void **fn;
+} dll_functions[] = {
+ { "OpenSCManagerW", (void **) &MyOpenSCManagerW },
+ { "CreateServiceW", (void **) &MyCreateServiceW },
+ { "OpenServiceW", (void **) &MyOpenServiceW },
+ { "StartServiceW", (void **) &MyStartServiceW },
+ { "ControlService", (void **) &MyControlService },
+ { "CloseServiceHandle", (void **) &MyCloseServiceHandle },
+ { "DeleteService", (void **) &MyDeleteService },
+};
+
+// Globals
+
+static SC_HANDLE scm = NULL;
+static SC_HANDLE svc = NULL;
+static int service_was_created = 0;
+static int service_was_started = 0;
+
+static int LoadLibraryPointers(void)
+{
+ HMODULE dll;
+ int i;
+
+ // Already loaded?
+
+ if (MyOpenSCManagerW != NULL)
+ {
+ return 1;
+ }
+
+ dll = LoadLibraryW(L"advapi32.dll");
+
+ if (dll == NULL)
+ {
+ fprintf(stderr, "LoadLibraryPointers: Failed to open advapi32.dll\n");
+ return 0;
+ }
+
+ for (i = 0; i < sizeof(dll_functions) / sizeof(*dll_functions); ++i)
+ {
+ *dll_functions[i].fn = GetProcAddress(dll, dll_functions[i].name);
+
+ if (*dll_functions[i].fn == NULL)
+ {
+ fprintf(stderr, "LoadLibraryPointers: Failed to get address "
+ "for '%s'\n", dll_functions[i].name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on)
+{
+ HANDLE h;
+ struct ioperm_data ioperm_data;
+ DWORD BytesReturned;
+ BOOL r;
+
+ h = CreateFileW(IOPERM_FILE, GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ ioperm_data.from = from;
+ ioperm_data.num = num;
+ ioperm_data.turn_on = turn_on;
+
+ r = DeviceIoControl(h, IOCTL_IOPERM,
+ &ioperm_data, sizeof ioperm_data,
+ NULL, 0,
+ &BytesReturned, NULL);
+
+ if (!r)
+ {
+ errno = EPERM;
+ }
+
+ CloseHandle(h);
+
+ return r != 0;
+}
+
+// Load ioperm.sys driver.
+// Returns 1 for success, 0 for failure.
+// Remember to call IOperm_UninstallDriver to uninstall the driver later.
+
+int IOperm_InstallDriver(void)
+{
+ wchar_t driver_path[MAX_PATH];
+ int error;
+ int result = 1;
+
+ if (!LoadLibraryPointers())
+ {
+ return 0;
+ }
+
+ scm = MyOpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+
+ if (scm == NULL)
+ {
+ error = GetLastError();
+ fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i).\n",
+ error);
+ return 0;
+ }
+
+ // Get the full path to the driver file.
+
+ GetFullPathNameW(L"ioperm.sys", MAX_PATH, driver_path, NULL);
+
+ // Create the service.
+
+ svc = MyCreateServiceW(scm,
+ L"ioperm",
+ L"ioperm support for Cygwin driver",
+ SERVICE_ALL_ACCESS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ driver_path,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (svc == NULL)
+ {
+ error = GetLastError();
+
+ if (error != ERROR_SERVICE_EXISTS)
+ {
+ fprintf(stderr,
+ "IOperm_InstallDriver: Failed to create service (%i).\n",
+ error);
+ }
+ else
+ {
+ svc = MyOpenServiceW(scm, L"ioperm", SERVICE_ALL_ACCESS);
+
+ if (svc == NULL)
+ {
+ error = GetLastError();
+
+ fprintf(stderr,
+ "IOperm_InstallDriver: Failed to open service (%i).\n",
+ error);
+ }
+ }
+
+ if (svc == NULL)
+ {
+ MyCloseServiceHandle(scm);
+ return 0;
+ }
+ }
+ else
+ {
+ service_was_created = 1;
+ }
+
+ // Start the service. If the service already existed, it might have
+ // already been running as well.
+
+ if (!MyStartServiceW(svc, 0, NULL))
+ {
+ error = GetLastError();
+
+ if (error != ERROR_SERVICE_ALREADY_RUNNING)
+ {
+ fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i).\n",
+ error);
+
+ result = 0;
+ }
+ else
+ {
+ printf("IOperm_InstallDriver: ioperm driver already running.\n");
+ }
+ }
+ else
+ {
+ printf("IOperm_InstallDriver: ioperm driver installed.\n");
+ service_was_started = 1;
+ }
+
+ // If we failed to start the driver running, we need to clean up
+ // before finishing.
+
+ if (result == 0)
+ {
+ IOperm_UninstallDriver();
+ }
+
+ return result;
+}
+
+int IOperm_UninstallDriver(void)
+{
+ SERVICE_STATUS stat;
+ int result = 1;
+ int error;
+
+ // If we started the service, stop it.
+
+ if (service_was_started)
+ {
+ if (!MyControlService(svc, SERVICE_CONTROL_STOP, &stat))
+ {
+ error = GetLastError();
+
+ if (error == ERROR_SERVICE_NOT_ACTIVE)
+ {
+ fprintf(stderr,
+ "IOperm_UninstallDriver: Service not active? (%i)\n",
+ error);
+ }
+ else
+ {
+ fprintf(stderr,
+ "IOperm_UninstallDriver: Failed to stop service (%i).\n",
+ error);
+ result = 0;
+ }
+ }
+ }
+
+ // If we created the service, delete it.
+
+ if (service_was_created)
+ {
+ if (!MyDeleteService(svc))
+ {
+ error = GetLastError();
+
+ fprintf(stderr,
+ "IOperm_UninstallDriver: DeleteService failed (%i).\n",
+ error);
+
+ result = 0;
+ }
+ else if (service_was_started)
+ {
+ printf("IOperm_UnInstallDriver: ioperm driver uninstalled.\n");
+ }
+ }
+
+ // Close handles.
+
+ if (svc != NULL)
+ {
+ MyCloseServiceHandle(svc);
+ svc = NULL;
+ }
+
+ if (scm != NULL)
+ {
+ MyCloseServiceHandle(scm);
+ scm = NULL;
+ }
+
+ service_was_created = 0;
+ service_was_started = 0;
+
+ return result;
+}
+
+#endif /* #ifndef _WIN32 */
+
diff --git a/opl/ioperm_sys.h b/opl/ioperm_sys.h
new file mode 100644
index 00000000..faf17bf3
--- /dev/null
+++ b/opl/ioperm_sys.h
@@ -0,0 +1,36 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2002, 2003 Marcel Telka
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Interface to the ioperm.sys driver, based on code from the
+// Cygwin ioperm library.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef IOPERM_SYS_H
+#define IOPERM_SYS_H
+
+int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on);
+int IOperm_InstallDriver(void);
+int IOperm_UninstallDriver(void);
+
+#endif /* #ifndef IOPERM_SYS_H */
+
diff --git a/opl/opl.c b/opl/opl.c
new file mode 100644
index 00000000..6d0e16db
--- /dev/null
+++ b/opl/opl.c
@@ -0,0 +1,466 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL interface.
+//
+//-----------------------------------------------------------------------------
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32_WCE
+#include "libc_wince.h"
+#endif
+
+#include "SDL.h"
+
+#include "opl.h"
+#include "opl_internal.h"
+
+//#define OPL_DEBUG_TRACE
+
+#ifdef HAVE_IOPERM
+extern opl_driver_t opl_linux_driver;
+#endif
+#if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64)
+extern opl_driver_t opl_openbsd_driver;
+#endif
+#ifdef _WIN32
+extern opl_driver_t opl_win32_driver;
+#endif
+extern opl_driver_t opl_sdl_driver;
+
+static opl_driver_t *drivers[] =
+{
+#ifdef HAVE_IOPERM
+ &opl_linux_driver,
+#endif
+#if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64)
+ &opl_openbsd_driver,
+#endif
+#ifdef _WIN32
+ &opl_win32_driver,
+#endif
+ &opl_sdl_driver,
+ NULL
+};
+
+static opl_driver_t *driver = NULL;
+static int init_stage_reg_writes = 1;
+
+unsigned int opl_sample_rate = 22050;
+
+//
+// Init/shutdown code.
+//
+
+// 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)
+{
+ // Initialize the driver.
+
+ if (!_driver->init_func(port_base))
+ {
+ return 0;
+ }
+
+ // The driver was initialized okay, so we now have somewhere
+ // to write to. It doesn't mean there's an OPL chip there,
+ // though. Perform the detection sequence to make sure.
+ // (it's done twice, like how Doom does it).
+
+ driver = _driver;
+ init_stage_reg_writes = 1;
+
+ if (!OPL_Detect() || !OPL_Detect())
+ {
+ printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
+ _driver->shutdown_func();
+ driver = NULL;
+ return 0;
+ }
+
+ // Initialize all registers.
+
+ OPL_InitRegisters();
+
+ init_stage_reg_writes = 0;
+
+ printf("OPL_Init: Using driver '%s'.\n", driver->name);
+
+ return 1;
+}
+
+// Find a driver automatically by trying each in the list.
+
+static int AutoSelectDriver(unsigned int port_base)
+{
+ int i;
+
+ for (i=0; drivers[i] != NULL; ++i)
+ {
+ if (InitDriver(drivers[i], port_base))
+ {
+ return 1;
+ }
+ }
+
+ printf("OPL_Init: Failed to find a working driver.\n");
+
+ return 0;
+}
+
+// Initialize the OPL library. Returns true if initialized
+// successfully.
+
+int OPL_Init(unsigned int port_base)
+{
+ char *driver_name;
+ int i;
+
+ driver_name = getenv("OPL_DRIVER");
+
+ if (driver_name != NULL)
+ {
+ // Search the list until we find the driver with this name.
+
+ for (i=0; drivers[i] != NULL; ++i)
+ {
+ if (!strcmp(driver_name, drivers[i]->name))
+ {
+ if (InitDriver(drivers[i], port_base))
+ {
+ return 1;
+ }
+ else
+ {
+ printf("OPL_Init: Failed to initialize "
+ "driver: '%s'.\n", driver_name);
+ return 0;
+ }
+ }
+ }
+
+ printf("OPL_Init: unknown driver: '%s'.\n", driver_name);
+
+ return 0;
+ }
+ else
+ {
+ return AutoSelectDriver(port_base);
+ }
+}
+
+// Shut down the OPL library.
+
+void OPL_Shutdown(void)
+{
+ if (driver != NULL)
+ {
+ driver->shutdown_func();
+ driver = NULL;
+ }
+}
+
+// Set the sample rate used for software OPL emulation.
+
+void OPL_SetSampleRate(unsigned int rate)
+{
+ opl_sample_rate = rate;
+}
+
+void OPL_WritePort(opl_port_t port, unsigned int value)
+{
+ if (driver != NULL)
+ {
+#ifdef OPL_DEBUG_TRACE
+ printf("OPL_write: %i, %x\n", port, value);
+ fflush(stdout);
+#endif
+ driver->write_port_func(port, value);
+ }
+}
+
+unsigned int OPL_ReadPort(opl_port_t port)
+{
+ if (driver != NULL)
+ {
+ unsigned int result;
+
+#ifdef OPL_DEBUG_TRACE
+ printf("OPL_read: %i...\n", port);
+ fflush(stdout);
+#endif
+
+ result = driver->read_port_func(port);
+
+#ifdef OPL_DEBUG_TRACE
+ printf("OPL_read: %i -> %x\n", port, result);
+ fflush(stdout);
+#endif
+
+ return result;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//
+// Higher-level functions, based on the lower-level functions above
+// (register write, etc).
+//
+
+unsigned int OPL_ReadStatus(void)
+{
+ return OPL_ReadPort(OPL_REGISTER_PORT);
+}
+
+// Write an OPL register value
+
+void OPL_WriteRegister(int reg, int value)
+{
+ int i;
+
+ OPL_WritePort(OPL_REGISTER_PORT, reg);
+
+ // For timing, read the register port six times after writing the
+ // register number to cause the appropriate delay
+
+ for (i=0; i<6; ++i)
+ {
+ // An oddity of the Doom OPL code: at startup initialization,
+ // the spacing here is performed by reading from the register
+ // port; after initialization, the data port is read, instead.
+
+ if (init_stage_reg_writes)
+ {
+ OPL_ReadPort(OPL_REGISTER_PORT);
+ }
+ else
+ {
+ OPL_ReadPort(OPL_DATA_PORT);
+ }
+ }
+
+ OPL_WritePort(OPL_DATA_PORT, value);
+
+ // Read the register port 24 times after writing the value to
+ // cause the appropriate delay
+
+ for (i=0; i<24; ++i)
+ {
+ OPL_ReadStatus();
+ }
+}
+
+// Detect the presence of an OPL chip
+
+int OPL_Detect(void)
+{
+ int result1, result2;
+ int i;
+
+ // Reset both timers:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+
+ // Enable interrupts:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ // Read status
+ result1 = OPL_ReadStatus();
+
+ // Set timer:
+ OPL_WriteRegister(OPL_REG_TIMER1, 0xff);
+
+ // Start timer 1:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21);
+
+ // Wait for 80 microseconds
+ // This is how Doom does it:
+
+ for (i=0; i<200; ++i)
+ {
+ OPL_ReadStatus();
+ }
+
+ OPL_Delay(1);
+
+ // Read status
+ result2 = OPL_ReadStatus();
+
+ // Reset both timers:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+
+ // Enable interrupts:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ return (result1 & 0xe0) == 0x00
+ && (result2 & 0xe0) == 0xc0;
+}
+
+// Initialize registers on startup
+
+void OPL_InitRegisters(void)
+{
+ int r;
+
+ // Initialize level registers
+
+ for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r, 0x3f);
+ }
+
+ // Initialize other registers
+ // These two loops write to registers that actually don't exist,
+ // but this is what Doom does ...
+ // Similarly, the <= is also intenational.
+
+ for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r, 0x00);
+ }
+
+ // More registers ...
+
+ for (r=1; r < OPL_REGS_LEVEL; ++r)
+ {
+ OPL_WriteRegister(r, 0x00);
+ }
+
+ // Re-initialize the low registers:
+
+ // Reset both timers and enable interrupts:
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+ OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ // "Allow FM chips to control the waveform of each operator":
+ OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
+
+ // Keyboard split point on (?)
+ OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
+}
+
+//
+// Timer functions.
+//
+
+void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data)
+{
+ if (driver != NULL)
+ {
+ driver->set_callback_func(ms, callback, data);
+ }
+}
+
+void OPL_ClearCallbacks(void)
+{
+ if (driver != NULL)
+ {
+ driver->clear_callbacks_func();
+ }
+}
+
+void OPL_Lock(void)
+{
+ if (driver != NULL)
+ {
+ driver->lock_func();
+ }
+}
+
+void OPL_Unlock(void)
+{
+ if (driver != NULL)
+ {
+ driver->unlock_func();
+ }
+}
+
+typedef struct
+{
+ int finished;
+
+ SDL_mutex *mutex;
+ SDL_cond *cond;
+} delay_data_t;
+
+static void DelayCallback(void *_delay_data)
+{
+ delay_data_t *delay_data = _delay_data;
+
+ SDL_LockMutex(delay_data->mutex);
+ delay_data->finished = 1;
+
+ SDL_CondSignal(delay_data->cond);
+
+ SDL_UnlockMutex(delay_data->mutex);
+}
+
+void OPL_Delay(unsigned int ms)
+{
+ delay_data_t delay_data;
+
+ if (driver == NULL)
+ {
+ return;
+ }
+
+ // Create a callback that will signal this thread after the
+ // specified time.
+
+ delay_data.finished = 0;
+ delay_data.mutex = SDL_CreateMutex();
+ delay_data.cond = SDL_CreateCond();
+
+ OPL_SetCallback(ms, DelayCallback, &delay_data);
+
+ // Wait until the callback is invoked.
+
+ SDL_LockMutex(delay_data.mutex);
+
+ while (!delay_data.finished)
+ {
+ SDL_CondWait(delay_data.cond, delay_data.mutex);
+ }
+
+ SDL_UnlockMutex(delay_data.mutex);
+
+ // Clean up.
+
+ SDL_DestroyMutex(delay_data.mutex);
+ SDL_DestroyCond(delay_data.cond);
+}
+
+void OPL_SetPaused(int paused)
+{
+ if (driver != NULL)
+ {
+ driver->set_paused_func(paused);
+ }
+}
+
diff --git a/opl/opl.h b/opl/opl.h
new file mode 100644
index 00000000..04d3cf27
--- /dev/null
+++ b/opl/opl.h
@@ -0,0 +1,137 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL interface.
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef OPL_OPL_H
+#define OPL_OPL_H
+
+typedef void (*opl_callback_t)(void *data);
+
+typedef enum
+{
+ OPL_REGISTER_PORT = 0,
+ OPL_DATA_PORT = 1
+} opl_port_t;
+
+#define OPL_NUM_OPERATORS 21
+#define OPL_NUM_VOICES 9
+
+#define OPL_REG_WAVEFORM_ENABLE 0x01
+#define OPL_REG_TIMER1 0x02
+#define OPL_REG_TIMER2 0x03
+#define OPL_REG_TIMER_CTRL 0x04
+#define OPL_REG_FM_MODE 0x08
+
+// Operator registers (21 of each):
+
+#define OPL_REGS_TREMOLO 0x20
+#define OPL_REGS_LEVEL 0x40
+#define OPL_REGS_ATTACK 0x60
+#define OPL_REGS_SUSTAIN 0x80
+#define OPL_REGS_WAVEFORM 0xE0
+
+// Voice registers (9 of each):
+
+#define OPL_REGS_FREQ_1 0xA0
+#define OPL_REGS_FREQ_2 0xB0
+#define OPL_REGS_FEEDBACK 0xC0
+
+//
+// Low-level functions.
+//
+
+// Initialize the OPL subsystem.
+
+int OPL_Init(unsigned int port_base);
+
+// Shut down the OPL subsystem.
+
+void OPL_Shutdown(void);
+
+// Set the sample rate used for software emulation.
+
+void OPL_SetSampleRate(unsigned int rate);
+
+// Write to one of the OPL I/O ports:
+
+void OPL_WritePort(opl_port_t port, unsigned int value);
+
+// Read from one of the OPL I/O ports:
+
+unsigned int OPL_ReadPort(opl_port_t port);
+
+//
+// Higher-level functions.
+//
+
+// Read the cuurrent status byte of the OPL chip.
+
+unsigned int OPL_ReadStatus(void);
+
+// Write to an OPL register.
+
+void OPL_WriteRegister(int reg, int value);
+
+// Perform a detection sequence to determine that an
+// OPL chip is present.
+
+int OPL_Detect(void);
+
+// Initialize all registers, performed on startup.
+
+void OPL_InitRegisters(void);
+
+//
+// Timer callback functions.
+//
+
+// Set a timer callback. After the specified number of milliseconds
+// have elapsed, the callback will be invoked.
+
+void OPL_SetCallback(unsigned int ms, opl_callback_t callback, void *data);
+
+// Clear all OPL callbacks that have been set.
+
+void OPL_ClearCallbacks(void);
+
+// Begin critical section, during which, OPL callbacks will not be
+// invoked.
+
+void OPL_Lock(void);
+
+// End critical section.
+
+void OPL_Unlock(void);
+
+// Block until the specified number of milliseconds have elapsed.
+
+void OPL_Delay(unsigned int ms);
+
+// Pause the OPL callbacks.
+
+void OPL_SetPaused(int paused);
+
+#endif
+
diff --git a/opl/opl_internal.h b/opl/opl_internal.h
new file mode 100644
index 00000000..4a46b060
--- /dev/null
+++ b/opl/opl_internal.h
@@ -0,0 +1,64 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL internal interface.
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef OPL_INTERNAL_H
+#define OPL_INTERNAL_H
+
+#include "opl.h"
+
+typedef int (*opl_init_func)(unsigned int port_base);
+typedef void (*opl_shutdown_func)(void);
+typedef unsigned int (*opl_read_port_func)(opl_port_t port);
+typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value);
+typedef void (*opl_set_callback_func)(unsigned int ms,
+ opl_callback_t callback,
+ void *data);
+typedef void (*opl_clear_callbacks_func)(void);
+typedef void (*opl_lock_func)(void);
+typedef void (*opl_unlock_func)(void);
+typedef void (*opl_set_paused_func)(int paused);
+
+typedef struct
+{
+ char *name;
+
+ opl_init_func init_func;
+ opl_shutdown_func shutdown_func;
+ opl_read_port_func read_port_func;
+ opl_write_port_func write_port_func;
+ opl_set_callback_func set_callback_func;
+ opl_clear_callbacks_func clear_callbacks_func;
+ opl_lock_func lock_func;
+ opl_unlock_func unlock_func;
+ opl_set_paused_func set_paused_func;
+} opl_driver_t;
+
+// Sample rate to use when doing software emulation.
+
+extern unsigned int opl_sample_rate;
+
+#endif /* #ifndef OPL_INTERNAL_H */
+
diff --git a/opl/opl_linux.c b/opl/opl_linux.c
new file mode 100644
index 00000000..319686b8
--- /dev/null
+++ b/opl/opl_linux.c
@@ -0,0 +1,110 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL Linux interface.
+//
+//-----------------------------------------------------------------------------
+
+#include "config.h"
+
+#ifdef HAVE_IOPERM
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/io.h>
+
+#include "opl.h"
+#include "opl_internal.h"
+#include "opl_timer.h"
+
+static unsigned int opl_port_base;
+
+static int OPL_Linux_Init(unsigned int port_base)
+{
+ // Try to get permissions:
+
+ if (ioperm(port_base, 2, 1) < 0)
+ {
+ fprintf(stderr, "Failed to get I/O port permissions for 0x%x: %s\n",
+ port_base, strerror(errno));
+
+ if (errno == EPERM)
+ {
+ fprintf(stderr,
+ "\tYou may need to run the program as root in order\n"
+ "\tto acquire I/O port permissions for OPL MIDI playback.\n");
+ }
+
+ return 0;
+ }
+
+ opl_port_base = port_base;
+
+ // Start callback thread
+
+ if (!OPL_Timer_StartThread())
+ {
+ ioperm(port_base, 2, 0);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void OPL_Linux_Shutdown(void)
+{
+ // Stop callback thread
+
+ OPL_Timer_StopThread();
+
+ // Release permissions
+
+ ioperm(opl_port_base, 2, 0);
+}
+
+static unsigned int OPL_Linux_PortRead(opl_port_t port)
+{
+ return inb(opl_port_base + port);
+}
+
+static void OPL_Linux_PortWrite(opl_port_t port, unsigned int value)
+{
+ outb(value, opl_port_base + port);
+}
+
+opl_driver_t opl_linux_driver =
+{
+ "Linux",
+ OPL_Linux_Init,
+ OPL_Linux_Shutdown,
+ OPL_Linux_PortRead,
+ OPL_Linux_PortWrite,
+ OPL_Timer_SetCallback,
+ OPL_Timer_ClearCallbacks,
+ OPL_Timer_Lock,
+ OPL_Timer_Unlock,
+ OPL_Timer_SetPaused
+};
+
+#endif /* #ifdef HAVE_IOPERM */
+
diff --git a/opl/opl_obsd.c b/opl/opl_obsd.c
new file mode 100644
index 00000000..b07a0421
--- /dev/null
+++ b/opl/opl_obsd.c
@@ -0,0 +1,125 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL OpenBSD interface (also NetBSD)
+//
+//-----------------------------------------------------------------------------
+
+#include "config.h"
+
+// OpenBSD has a i386_iopl on i386 and amd64_iopl on x86_64,
+// even though they do the same thing. Take care of this
+// here, and map set_iopl to point to the appropriate name.
+
+#if defined(HAVE_LIBI386)
+
+#include <sys/types.h>
+#include <machine/sysarch.h>
+#include <i386/pio.h>
+#define set_iopl i386_iopl
+
+#elif defined(HAVE_LIBAMD64)
+
+#include <sys/types.h>
+#include <machine/sysarch.h>
+#include <amd64/pio.h>
+#define set_iopl amd64_iopl
+
+#else
+#define NO_OBSD_DRIVER
+#endif
+
+// If the above succeeded, proceed with the rest.
+
+#ifndef NO_OBSD_DRIVER
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "opl.h"
+#include "opl_internal.h"
+#include "opl_timer.h"
+
+static unsigned int opl_port_base;
+
+static int OPL_OpenBSD_Init(unsigned int port_base)
+{
+ // Try to get permissions:
+
+ if (set_iopl(3) < 0)
+ {
+ fprintf(stderr, "Failed to get raise I/O privilege level: "
+ "check that you are running as root.\n");
+ return 0;
+ }
+
+ opl_port_base = port_base;
+
+ // Start callback thread
+
+ if (!OPL_Timer_StartThread())
+ {
+ set_iopl(0);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void OPL_OpenBSD_Shutdown(void)
+{
+ // Stop callback thread
+
+ OPL_Timer_StopThread();
+
+ // Release I/O port permissions:
+
+ set_iopl(0);
+}
+
+static unsigned int OPL_OpenBSD_PortRead(opl_port_t port)
+{
+ return inb(opl_port_base + port);
+}
+
+static void OPL_OpenBSD_PortWrite(opl_port_t port, unsigned int value)
+{
+ outb(opl_port_base + port, value);
+}
+
+opl_driver_t opl_openbsd_driver =
+{
+ "OpenBSD",
+ OPL_OpenBSD_Init,
+ OPL_OpenBSD_Shutdown,
+ OPL_OpenBSD_PortRead,
+ OPL_OpenBSD_PortWrite,
+ OPL_Timer_SetCallback,
+ OPL_Timer_ClearCallbacks,
+ OPL_Timer_Lock,
+ OPL_Timer_Unlock,
+ OPL_Timer_SetPaused
+};
+
+#endif /* #ifndef NO_OBSD_DRIVER */
+
diff --git a/opl/opl_queue.c b/opl/opl_queue.c
new file mode 100644
index 00000000..f9d4c377
--- /dev/null
+++ b/opl/opl_queue.c
@@ -0,0 +1,280 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Queue of waiting callbacks, stored in a binary min heap, so that we
+// can always get the first callback.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "opl_queue.h"
+
+#define MAX_OPL_QUEUE 64
+
+typedef struct
+{
+ opl_callback_t callback;
+ void *data;
+ unsigned int time;
+} opl_queue_entry_t;
+
+struct opl_callback_queue_s
+{
+ opl_queue_entry_t entries[MAX_OPL_QUEUE];
+ unsigned int num_entries;
+};
+
+opl_callback_queue_t *OPL_Queue_Create(void)
+{
+ opl_callback_queue_t *queue;
+
+ queue = malloc(sizeof(opl_callback_queue_t));
+ queue->num_entries = 0;
+
+ return queue;
+}
+
+void OPL_Queue_Destroy(opl_callback_queue_t *queue)
+{
+ free(queue);
+}
+
+int OPL_Queue_IsEmpty(opl_callback_queue_t *queue)
+{
+ return queue->num_entries == 0;
+}
+
+void OPL_Queue_Clear(opl_callback_queue_t *queue)
+{
+ queue->num_entries = 0;
+}
+
+void OPL_Queue_Push(opl_callback_queue_t *queue,
+ opl_callback_t callback, void *data,
+ unsigned int time)
+{
+ int entry_id;
+ int parent_id;
+
+ if (queue->num_entries >= MAX_OPL_QUEUE)
+ {
+ fprintf(stderr, "OPL_Queue_Push: Exceeded maximum callbacks\n");
+ return;
+ }
+
+ // Add to last queue entry.
+
+ entry_id = queue->num_entries;
+ ++queue->num_entries;
+
+ // Shift existing entries down in the heap.
+
+ while (entry_id > 0)
+ {
+ parent_id = (entry_id - 1) / 2;
+
+ // Is the heap condition satisfied?
+
+ if (time >= queue->entries[parent_id].time)
+ {
+ break;
+ }
+
+ // Move the existing entry down in the heap.
+
+ memcpy(&queue->entries[entry_id],
+ &queue->entries[parent_id],
+ sizeof(opl_queue_entry_t));
+
+ // Advance to the parent.
+
+ entry_id = parent_id;
+ }
+
+ // Insert new callback data.
+
+ queue->entries[entry_id].callback = callback;
+ queue->entries[entry_id].data = data;
+ queue->entries[entry_id].time = time;
+}
+
+int OPL_Queue_Pop(opl_callback_queue_t *queue,
+ opl_callback_t *callback, void **data)
+{
+ opl_queue_entry_t *entry;
+ int child1, child2;
+ int i, next_i;
+
+ // Empty?
+
+ if (queue->num_entries <= 0)
+ {
+ return 0;
+ }
+
+ // Store the result:
+
+ *callback = queue->entries[0].callback;
+ *data = queue->entries[0].data;
+
+ // Decrease the heap size, and keep pointer to the last entry in
+ // the heap, which must now be percolated down from the top.
+
+ --queue->num_entries;
+ entry = &queue->entries[queue->num_entries];
+
+ // Percolate down.
+
+ i = 0;
+
+ for (;;)
+ {
+ child1 = i * 2 + 1;
+ child2 = i * 2 + 2;
+
+ if (child1 < queue->num_entries
+ && queue->entries[child1].time < entry->time)
+ {
+ // Left child is less than entry.
+ // Use the minimum of left and right children.
+
+ if (child2 < queue->num_entries
+ && queue->entries[child2].time < queue->entries[child1].time)
+ {
+ next_i = child2;
+ }
+ else
+ {
+ next_i = child1;
+ }
+ }
+ else if (child2 < queue->num_entries
+ && queue->entries[child2].time < entry->time)
+ {
+ // Right child is less than entry. Go down the right side.
+
+ next_i = child2;
+ }
+ else
+ {
+ // Finished percolating.
+ break;
+ }
+
+ // Percolate the next value up and advance.
+
+ memcpy(&queue->entries[i],
+ &queue->entries[next_i],
+ sizeof(opl_queue_entry_t));
+ i = next_i;
+ }
+
+ // Store the old last-entry at its new position.
+
+ memcpy(&queue->entries[i], entry, sizeof(opl_queue_entry_t));
+
+ return 1;
+}
+
+unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue)
+{
+ if (queue->num_entries > 0)
+ {
+ return queue->entries[0].time;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#ifdef TEST
+
+#include <assert.h>
+
+static void PrintQueueNode(opl_callback_queue_t *queue, int node, int depth)
+{
+ int i;
+
+ if (node >= queue->num_entries)
+ {
+ return;
+ }
+
+ for (i=0; i<depth * 3; ++i)
+ {
+ printf(" ");
+ }
+
+ printf("%i\n", queue->entries[node].time);
+
+ PrintQueueNode(queue, node * 2 + 1, depth + 1);
+ PrintQueueNode(queue, node * 2 + 2, depth + 1);
+}
+
+static void PrintQueue(opl_callback_queue_t *queue)
+{
+ PrintQueueNode(queue, 0, 0);
+}
+
+int main()
+{
+ opl_callback_queue_t *queue;
+ int iteration;
+
+ queue = OPL_Queue_Create();
+
+ for (iteration=0; iteration<5000; ++iteration)
+ {
+ opl_callback_t callback;
+ void *data;
+ unsigned int time;
+ unsigned int newtime;
+ int i;
+
+ for (i=0; i<MAX_OPL_QUEUE; ++i)
+ {
+ time = rand() % 0x10000;
+ OPL_Queue_Push(queue, NULL, NULL, time);
+ }
+
+ time = 0;
+
+ for (i=0; i<MAX_OPL_QUEUE; ++i)
+ {
+ assert(!OPL_Queue_IsEmpty(queue));
+ newtime = OPL_Queue_Peek(queue);
+ assert(OPL_Queue_Pop(queue, &callback, &data));
+
+ assert(newtime >= time);
+ time = newtime;
+ }
+
+ assert(OPL_Queue_IsEmpty(queue));
+ assert(!OPL_Queue_Pop(queue, &callback, &data));
+ }
+}
+
+#endif
+
diff --git a/opl/opl_queue.h b/opl/opl_queue.h
new file mode 100644
index 00000000..2447702b
--- /dev/null
+++ b/opl/opl_queue.h
@@ -0,0 +1,45 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL callback queue.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef OPL_QUEUE_H
+#define OPL_QUEUE_H
+
+#include "opl.h"
+
+typedef struct opl_callback_queue_s opl_callback_queue_t;
+
+opl_callback_queue_t *OPL_Queue_Create(void);
+int OPL_Queue_IsEmpty(opl_callback_queue_t *queue);
+void OPL_Queue_Clear(opl_callback_queue_t *queue);
+void OPL_Queue_Destroy(opl_callback_queue_t *queue);
+void OPL_Queue_Push(opl_callback_queue_t *queue,
+ opl_callback_t callback, void *data,
+ unsigned int time);
+int OPL_Queue_Pop(opl_callback_queue_t *queue,
+ opl_callback_t *callback, void **data);
+unsigned int OPL_Queue_Peek(opl_callback_queue_t *queue);
+
+#endif /* #ifndef OPL_QUEUE_H */
+
diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c
new file mode 100644
index 00000000..f6a3b229
--- /dev/null
+++ b/opl/opl_sdl.c
@@ -0,0 +1,510 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL SDL interface.
+//
+//-----------------------------------------------------------------------------
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "SDL.h"
+#include "SDL_mixer.h"
+
+#include "dbopl.h"
+
+#include "opl.h"
+#include "opl_internal.h"
+
+#include "opl_queue.h"
+
+#define MAX_SOUND_SLICE_TIME 100 /* ms */
+
+typedef struct
+{
+ unsigned int rate; // Number of times the timer is advanced per sec.
+ unsigned int enabled; // Non-zero if timer is enabled.
+ unsigned int value; // Last value that was set.
+ unsigned int expire_time; // Calculated time that timer will expire.
+} opl_timer_t;
+
+// When the callback mutex is locked using OPL_Lock, callback functions
+// are not invoked.
+
+static SDL_mutex *callback_mutex = NULL;
+
+// Queue of callbacks waiting to be invoked.
+
+static opl_callback_queue_t *callback_queue;
+
+// Mutex used to control access to the callback queue.
+
+static SDL_mutex *callback_queue_mutex = NULL;
+
+// Current time, in number of samples since startup:
+
+static int current_time;
+
+// If non-zero, playback is currently paused.
+
+static int opl_sdl_paused;
+
+// Time offset (in samples) due to the fact that callbacks
+// were previously paused.
+
+static unsigned int pause_offset;
+
+// OPL software emulator structure.
+
+static Chip opl_chip;
+
+// Temporary mixing buffer used by the mixing callback.
+
+static int32_t *mix_buffer = NULL;
+
+// Register number that was written.
+
+static int register_num = 0;
+
+// Timers; DBOPL does not do timer stuff itself.
+
+static opl_timer_t timer1 = { 12500, 0, 0, 0 };
+static opl_timer_t timer2 = { 3125, 0, 0, 0 };
+
+// SDL parameters.
+
+static int sdl_was_initialized = 0;
+static int mixing_freq, mixing_channels;
+static Uint16 mixing_format;
+
+static int SDLIsInitialized(void)
+{
+ int freq, channels;
+ Uint16 format;
+
+ return Mix_QuerySpec(&freq, &format, &channels);
+}
+
+// Advance time by the specified number of samples, invoking any
+// callback functions as appropriate.
+
+static void AdvanceTime(unsigned int nsamples)
+{
+ opl_callback_t callback;
+ void *callback_data;
+
+ SDL_LockMutex(callback_queue_mutex);
+
+ // Advance time.
+
+ current_time += nsamples;
+
+ if (opl_sdl_paused)
+ {
+ pause_offset += nsamples;
+ }
+
+ // Are there callbacks to invoke now? Keep invoking them
+ // until there are none more left.
+
+ while (!OPL_Queue_IsEmpty(callback_queue)
+ && current_time >= OPL_Queue_Peek(callback_queue) + pause_offset)
+ {
+ // Pop the callback from the queue to invoke it.
+
+ if (!OPL_Queue_Pop(callback_queue, &callback, &callback_data))
+ {
+ break;
+ }
+
+ // The mutex stuff here is a bit complicated. We must
+ // hold callback_mutex when we invoke the callback (so that
+ // the control thread can use OPL_Lock() to prevent callbacks
+ // from being invoked), but we must not be holding
+ // callback_queue_mutex, as the callback must be able to
+ // call OPL_SetCallback to schedule new callbacks.
+
+ SDL_UnlockMutex(callback_queue_mutex);
+
+ SDL_LockMutex(callback_mutex);
+ callback(callback_data);
+ SDL_UnlockMutex(callback_mutex);
+
+ SDL_LockMutex(callback_queue_mutex);
+ }
+
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
+// Call the OPL emulator code to fill the specified buffer.
+
+static void FillBuffer(int16_t *buffer, unsigned int nsamples)
+{
+ unsigned int i;
+
+ // This seems like a reasonable assumption. mix_buffer is
+ // 1 second long, which should always be much longer than the
+ // SDL mix buffer.
+
+ assert(nsamples < mixing_freq);
+
+ Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer);
+
+ // Mix into the destination buffer, doubling up into stereo.
+
+ for (i=0; i<nsamples; ++i)
+ {
+ buffer[i * 2] = (int16_t) (mix_buffer[i] * 2);
+ buffer[i * 2 + 1] = (int16_t) (mix_buffer[i] * 2);
+ }
+}
+
+// Callback function to fill a new sound buffer:
+
+static void OPL_Mix_Callback(void *udata,
+ Uint8 *byte_buffer,
+ int buffer_bytes)
+{
+ int16_t *buffer;
+ unsigned int buffer_len;
+ unsigned int filled = 0;
+
+ // Buffer length in samples (quadrupled, because of 16-bit and stereo)
+
+ buffer = (int16_t *) byte_buffer;
+ buffer_len = buffer_bytes / 4;
+
+ // Repeatedly call the OPL emulator update function until the buffer is
+ // full.
+
+ while (filled < buffer_len)
+ {
+ unsigned int next_callback_time;
+ unsigned int nsamples;
+
+ SDL_LockMutex(callback_queue_mutex);
+
+ // Work out the time until the next callback waiting in
+ // the callback queue must be invoked. We can then fill the
+ // buffer with this many samples.
+
+ if (opl_sdl_paused || OPL_Queue_IsEmpty(callback_queue))
+ {
+ nsamples = buffer_len - filled;
+ }
+ else
+ {
+ next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset;
+
+ nsamples = next_callback_time - current_time;
+
+ if (nsamples > buffer_len - filled)
+ {
+ nsamples = buffer_len - filled;
+ }
+ }
+
+ SDL_UnlockMutex(callback_queue_mutex);
+
+ // Add emulator output to buffer.
+
+ FillBuffer(buffer + filled * 2, nsamples);
+ filled += nsamples;
+
+ // Invoke callbacks for this point in time.
+
+ AdvanceTime(nsamples);
+ }
+}
+
+static void OPL_SDL_Shutdown(void)
+{
+ Mix_HookMusic(NULL, NULL);
+
+ if (sdl_was_initialized)
+ {
+ Mix_CloseAudio();
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ OPL_Queue_Destroy(callback_queue);
+ free(mix_buffer);
+ sdl_was_initialized = 0;
+ }
+
+/*
+ if (opl_chip != NULL)
+ {
+ OPLDestroy(opl_chip);
+ opl_chip = NULL;
+ }
+ */
+
+ if (callback_mutex != NULL)
+ {
+ SDL_DestroyMutex(callback_mutex);
+ callback_mutex = NULL;
+ }
+
+ if (callback_queue_mutex != NULL)
+ {
+ SDL_DestroyMutex(callback_queue_mutex);
+ callback_queue_mutex = NULL;
+ }
+}
+
+static unsigned int GetSliceSize(void)
+{
+ int limit;
+ int n;
+
+ limit = (opl_sample_rate * MAX_SOUND_SLICE_TIME) / 1000;
+
+ // Try all powers of two, not exceeding the limit.
+
+ for (n=0;; ++n)
+ {
+ // 2^n <= limit < 2^n+1 ?
+
+ if ((1 << (n + 1)) > limit)
+ {
+ return (1 << n);
+ }
+ }
+
+ // Should never happen?
+
+ return 1024;
+}
+
+static int OPL_SDL_Init(unsigned int port_base)
+{
+ // Check if SDL_mixer has been opened already
+ // If not, we must initialize it now
+
+ if (!SDLIsInitialized())
+ {
+ if (SDL_Init(SDL_INIT_AUDIO) < 0)
+ {
+ fprintf(stderr, "Unable to set up sound.\n");
+ return 0;
+ }
+
+ if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, GetSliceSize()) < 0)
+ {
+ fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
+
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ return 0;
+ }
+
+ SDL_PauseAudio(0);
+
+ // When this module shuts down, it has the responsibility to
+ // shut down SDL.
+
+ sdl_was_initialized = 1;
+ }
+ else
+ {
+ sdl_was_initialized = 0;
+ }
+
+ opl_sdl_paused = 0;
+ pause_offset = 0;
+
+ // Queue structure of callbacks to invoke.
+
+ callback_queue = OPL_Queue_Create();
+ current_time = 0;
+
+ // Get the mixer frequency, format and number of channels.
+
+ Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels);
+
+ // Only supports AUDIO_S16SYS
+
+ if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
+ {
+ fprintf(stderr,
+ "OPL_SDL only supports native signed 16-bit LSB, "
+ "stereo format!\n");
+
+ OPL_SDL_Shutdown();
+ return 0;
+ }
+
+ // Mix buffer:
+
+ mix_buffer = malloc(mixing_freq * sizeof(uint32_t));
+
+ // Create the emulator structure:
+
+ DBOPL_InitTables();
+ Chip__Chip(&opl_chip);
+ Chip__Setup(&opl_chip, mixing_freq);
+
+ callback_mutex = SDL_CreateMutex();
+ callback_queue_mutex = SDL_CreateMutex();
+
+ // TODO: This should be music callback? or-?
+ Mix_HookMusic(OPL_Mix_Callback, NULL);
+
+ return 1;
+}
+
+static unsigned int OPL_SDL_PortRead(opl_port_t port)
+{
+ unsigned int result = 0;
+
+ if (timer1.enabled && current_time > timer1.expire_time)
+ {
+ result |= 0x80; // Either have expired
+ result |= 0x40; // Timer 1 has expired
+ }
+
+ if (timer2.enabled && current_time > timer2.expire_time)
+ {
+ result |= 0x80; // Either have expired
+ result |= 0x20; // Timer 2 has expired
+ }
+
+ return result;
+}
+
+static void OPLTimer_CalculateEndTime(opl_timer_t *timer)
+{
+ int tics;
+
+ // If the timer is enabled, calculate the time when the timer
+ // will expire.
+
+ if (timer->enabled)
+ {
+ tics = 0x100 - timer->value;
+ timer->expire_time = current_time
+ + (tics * opl_sample_rate) / timer->rate;
+ }
+}
+
+static void WriteRegister(unsigned int reg_num, unsigned int value)
+{
+ switch (reg_num)
+ {
+ case OPL_REG_TIMER1:
+ timer1.value = value;
+ OPLTimer_CalculateEndTime(&timer1);
+ break;
+
+ case OPL_REG_TIMER2:
+ timer2.value = value;
+ OPLTimer_CalculateEndTime(&timer2);
+ break;
+
+ case OPL_REG_TIMER_CTRL:
+ if (value & 0x80)
+ {
+ timer1.enabled = 0;
+ timer2.enabled = 0;
+ }
+ else
+ {
+ if ((value & 0x40) == 0)
+ {
+ timer1.enabled = (value & 0x01) != 0;
+ OPLTimer_CalculateEndTime(&timer1);
+ }
+
+ if ((value & 0x20) == 0)
+ {
+ timer1.enabled = (value & 0x02) != 0;
+ OPLTimer_CalculateEndTime(&timer2);
+ }
+ }
+
+ break;
+
+ default:
+ Chip__WriteReg(&opl_chip, reg_num, value);
+ break;
+ }
+}
+
+static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value)
+{
+ if (port == OPL_REGISTER_PORT)
+ {
+ register_num = value;
+ }
+ else if (port == OPL_DATA_PORT)
+ {
+ WriteRegister(register_num, value);
+ }
+}
+
+static void OPL_SDL_SetCallback(unsigned int ms,
+ opl_callback_t callback,
+ void *data)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ OPL_Queue_Push(callback_queue, callback, data,
+ current_time - pause_offset + (ms * mixing_freq) / 1000);
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
+static void OPL_SDL_ClearCallbacks(void)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ OPL_Queue_Clear(callback_queue);
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
+static void OPL_SDL_Lock(void)
+{
+ SDL_LockMutex(callback_mutex);
+}
+
+static void OPL_SDL_Unlock(void)
+{
+ SDL_UnlockMutex(callback_mutex);
+}
+
+static void OPL_SDL_SetPaused(int paused)
+{
+ opl_sdl_paused = paused;
+}
+
+opl_driver_t opl_sdl_driver =
+{
+ "SDL",
+ OPL_SDL_Init,
+ OPL_SDL_Shutdown,
+ OPL_SDL_PortRead,
+ OPL_SDL_PortWrite,
+ OPL_SDL_SetCallback,
+ OPL_SDL_ClearCallbacks,
+ OPL_SDL_Lock,
+ OPL_SDL_Unlock,
+ OPL_SDL_SetPaused
+};
+
diff --git a/opl/opl_timer.c b/opl/opl_timer.c
new file mode 100644
index 00000000..35b2092f
--- /dev/null
+++ b/opl/opl_timer.c
@@ -0,0 +1,251 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL timer thread.
+// Once started using OPL_Timer_StartThread, the thread sleeps,
+// waking up to invoke callbacks set using OPL_Timer_SetCallback.
+//
+//-----------------------------------------------------------------------------
+
+#include "SDL.h"
+
+#include "opl_timer.h"
+#include "opl_queue.h"
+
+typedef enum
+{
+ THREAD_STATE_STOPPED,
+ THREAD_STATE_RUNNING,
+ THREAD_STATE_STOPPING,
+} thread_state_t;
+
+static SDL_Thread *timer_thread = NULL;
+static thread_state_t timer_thread_state;
+static int current_time;
+
+// If non-zero, callbacks are currently paused.
+
+static int opl_timer_paused;
+
+// Offset in milliseconds to adjust time due to the fact that playback
+// was paused.
+
+static unsigned int pause_offset = 0;
+
+// Queue of callbacks waiting to be invoked.
+// The callback queue mutex is held while the callback queue structure
+// or current_time is being accessed.
+
+static opl_callback_queue_t *callback_queue;
+static SDL_mutex *callback_queue_mutex;
+
+// The timer mutex is held while timer callback functions are being
+// invoked, so that the calling code can prevent clashes.
+
+static SDL_mutex *timer_mutex;
+
+// Returns true if there is a callback at the head of the queue ready
+// to be invoked. Otherwise, next_time is set to the time when the
+// timer thread must wake up again to check.
+
+static int CallbackWaiting(unsigned int *next_time)
+{
+ // If paused, just wait in 50ms increments until unpaused.
+ // Update pause_offset so after we unpause, the callback
+ // times will be right.
+
+ if (opl_timer_paused)
+ {
+ *next_time = current_time + 50;
+ pause_offset += 50;
+ return 0;
+ }
+
+ // If there are no queued callbacks, sleep for 50ms at a time
+ // until a callback is added.
+
+ if (OPL_Queue_IsEmpty(callback_queue))
+ {
+ *next_time = current_time + 50;
+ return 0;
+ }
+
+ // Read the time of the first callback in the queue.
+ // If the time for the callback has not yet arrived,
+ // we must sleep until the callback time.
+
+ *next_time = OPL_Queue_Peek(callback_queue) + pause_offset;
+
+ return *next_time <= current_time;
+}
+
+static unsigned int GetNextTime(void)
+{
+ opl_callback_t callback;
+ void *callback_data;
+ unsigned int next_time;
+ int have_callback;
+
+ // Keep running through callbacks until there are none ready to
+ // run. When we run out of callbacks, next_time will be set.
+
+ do
+ {
+ SDL_LockMutex(callback_queue_mutex);
+
+ // Check if the callback at the head of the list is ready to
+ // be invoked. If so, pop from the head of the queue.
+
+ have_callback = CallbackWaiting(&next_time);
+
+ if (have_callback)
+ {
+ OPL_Queue_Pop(callback_queue, &callback, &callback_data);
+ }
+
+ SDL_UnlockMutex(callback_queue_mutex);
+
+ // Now invoke the callback, if we have one.
+ // The timer mutex is held while the callback is invoked.
+
+ if (have_callback)
+ {
+ SDL_LockMutex(timer_mutex);
+ callback(callback_data);
+ SDL_UnlockMutex(timer_mutex);
+ }
+ } while (have_callback);
+
+ return next_time;
+}
+
+static int ThreadFunction(void *unused)
+{
+ unsigned int next_time;
+ unsigned int now;
+
+ // Keep running until OPL_Timer_StopThread is called.
+
+ while (timer_thread_state == THREAD_STATE_RUNNING)
+ {
+ // Get the next time that we must sleep until, and
+ // wait until that time.
+
+ next_time = GetNextTime();
+ now = SDL_GetTicks();
+
+ if (next_time > now)
+ {
+ SDL_Delay(next_time - now);
+ }
+
+ // Update the current time.
+
+ SDL_LockMutex(callback_queue_mutex);
+ current_time = next_time;
+ SDL_UnlockMutex(callback_queue_mutex);
+ }
+
+ timer_thread_state = THREAD_STATE_STOPPED;
+
+ return 0;
+}
+
+static void InitResources(void)
+{
+ callback_queue = OPL_Queue_Create();
+ timer_mutex = SDL_CreateMutex();
+ callback_queue_mutex = SDL_CreateMutex();
+}
+
+static void FreeResources(void)
+{
+ OPL_Queue_Destroy(callback_queue);
+ SDL_DestroyMutex(callback_queue_mutex);
+ SDL_DestroyMutex(timer_mutex);
+}
+
+int OPL_Timer_StartThread(void)
+{
+ InitResources();
+
+ timer_thread_state = THREAD_STATE_RUNNING;
+ current_time = SDL_GetTicks();
+ opl_timer_paused = 0;
+ pause_offset = 0;
+
+ timer_thread = SDL_CreateThread(ThreadFunction, NULL);
+
+ if (timer_thread == NULL)
+ {
+ timer_thread_state = THREAD_STATE_STOPPED;
+ FreeResources();
+
+ return 0;
+ }
+
+ return 1;
+}
+
+void OPL_Timer_StopThread(void)
+{
+ timer_thread_state = THREAD_STATE_STOPPING;
+
+ while (timer_thread_state != THREAD_STATE_STOPPED)
+ {
+ SDL_Delay(1);
+ }
+
+ FreeResources();
+}
+
+void OPL_Timer_SetCallback(unsigned int ms, opl_callback_t callback, void *data)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ OPL_Queue_Push(callback_queue, callback, data,
+ current_time + ms - pause_offset);
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
+void OPL_Timer_ClearCallbacks(void)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ OPL_Queue_Clear(callback_queue);
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
+void OPL_Timer_Lock(void)
+{
+ SDL_LockMutex(timer_mutex);
+}
+
+void OPL_Timer_Unlock(void)
+{
+ SDL_UnlockMutex(timer_mutex);
+}
+
+void OPL_Timer_SetPaused(int paused)
+{
+ SDL_LockMutex(callback_queue_mutex);
+ opl_timer_paused = paused;
+ SDL_UnlockMutex(callback_queue_mutex);
+}
+
diff --git a/opl/opl_timer.h b/opl/opl_timer.h
new file mode 100644
index 00000000..f03fc499
--- /dev/null
+++ b/opl/opl_timer.h
@@ -0,0 +1,42 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL timer thread.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef OPL_TIMER_H
+#define OPL_TIMER_H
+
+#include "opl.h"
+
+int OPL_Timer_StartThread(void);
+void OPL_Timer_StopThread(void);
+void OPL_Timer_SetCallback(unsigned int ms,
+ opl_callback_t callback,
+ void *data);
+void OPL_Timer_ClearCallbacks(void);
+void OPL_Timer_Lock(void);
+void OPL_Timer_Unlock(void);
+void OPL_Timer_SetPaused(int paused);
+
+#endif /* #ifndef OPL_TIMER_H */
+
diff --git a/opl/opl_win32.c b/opl/opl_win32.c
new file mode 100644
index 00000000..29df3643
--- /dev/null
+++ b/opl/opl_win32.c
@@ -0,0 +1,172 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// OPL Win32 native interface.
+//
+//-----------------------------------------------------------------------------
+
+#include "config.h"
+
+#ifdef _WIN32
+
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "opl.h"
+#include "opl_internal.h"
+#include "opl_timer.h"
+
+#include "ioperm_sys.h"
+
+static unsigned int opl_port_base;
+
+// MingW?
+
+#if defined(__GNUC__) && defined(__i386__)
+
+static unsigned int OPL_Win32_PortRead(opl_port_t port)
+{
+ unsigned char result;
+
+ __asm__ volatile (
+ "movl %1, %%edx\n"
+ "inb %%dx, %%al\n"
+ "movb %%al, %0"
+ : "=m" (result)
+ : "r" (opl_port_base + port)
+ : "edx", "al", "memory"
+ );
+
+ return result;
+}
+
+static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
+{
+ __asm__ volatile (
+ "movl %0, %%edx\n"
+ "movb %1, %%al\n"
+ "outb %%al, %%dx"
+ :
+ : "r" (opl_port_base + port), "r" ((unsigned char) value)
+ : "edx", "al"
+ );
+}
+
+// TODO: MSVC version
+// #elif defined(_MSC_VER) && defined(_M_IX6) ...
+
+#else
+
+// Not x86, or don't know how to do port R/W on this compiler.
+
+#define NO_PORT_RW
+
+static unsigned int OPL_Win32_PortRead(opl_port_t port)
+{
+ return 0;
+}
+
+static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value)
+{
+}
+
+#endif
+
+static int OPL_Win32_Init(unsigned int port_base)
+{
+#ifndef NO_PORT_RW
+
+ OSVERSIONINFO version_info;
+
+ opl_port_base = port_base;
+
+ // Check the OS version.
+
+ memset(&version_info, 0, sizeof(version_info));
+ version_info.dwOSVersionInfoSize = sizeof(version_info);
+
+ GetVersionEx(&version_info);
+
+ // On NT-based systems, we must acquire I/O port permissions
+ // using the ioperm.sys driver.
+
+ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ // Install driver.
+
+ if (!IOperm_InstallDriver())
+ {
+ return 0;
+ }
+
+ // Open port range.
+
+ if (!IOperm_EnablePortRange(opl_port_base, 2, 1))
+ {
+ IOperm_UninstallDriver();
+ return 0;
+ }
+ }
+
+ // Start callback thread
+
+ if (!OPL_Timer_StartThread())
+ {
+ IOperm_UninstallDriver();
+ return 0;
+ }
+
+ return 1;
+
+#endif
+
+ return 0;
+}
+
+static void OPL_Win32_Shutdown(void)
+{
+ // Stop callback thread
+
+ OPL_Timer_StopThread();
+
+ // Unload IOperm library.
+
+ IOperm_UninstallDriver();
+}
+
+opl_driver_t opl_win32_driver =
+{
+ "Win32",
+ OPL_Win32_Init,
+ OPL_Win32_Shutdown,
+ OPL_Win32_PortRead,
+ OPL_Win32_PortWrite,
+ OPL_Timer_SetCallback,
+ OPL_Timer_ClearCallbacks,
+ OPL_Timer_Lock,
+ OPL_Timer_Unlock,
+ OPL_Timer_SetPaused
+};
+
+#endif /* #ifdef _WIN32 */
+
diff --git a/pkg/config.make.in b/pkg/config.make.in
index b8a1dbe5..dc2d2888 100644
--- a/pkg/config.make.in
+++ b/pkg/config.make.in
@@ -22,6 +22,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@
DOC_FILES = README \
COPYING \
ChangeLog \
+ INSTALL \
NEWS \
BUGS \
CMDLINE \
diff --git a/pkg/osx/GNUmakefile b/pkg/osx/GNUmakefile
index a0f6151f..aa31daf4 100644
--- a/pkg/osx/GNUmakefile
+++ b/pkg/osx/GNUmakefile
@@ -6,6 +6,10 @@
include ../config.make
+# Build so that the package will work on older versions.
+
+export MACOSX_DEPLOYMENT_TARGET=10.4
+
STAGING_DIR=staging
DMG=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).dmg
diff --git a/pkg/win32/GNUmakefile b/pkg/win32/GNUmakefile
index 147e2890..bfe4f7ef 100644
--- a/pkg/win32/GNUmakefile
+++ b/pkg/win32/GNUmakefile
@@ -13,6 +13,8 @@ DLL_FILES=$(TOPLEVEL)/src/SDL.dll \
$(TOPLEVEL)/src/SDL_mixer.dll \
$(TOPLEVEL)/src/SDL_net.dll
+DOC_FILES += README.OPL
+
ZIP=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-win32.zip
$(ZIP) : staging
diff --git a/rpm.spec.in b/rpm.spec.in
index 1b7e90c7..b37a2876 100644
--- a/rpm.spec.in
+++ b/rpm.spec.in
@@ -49,9 +49,11 @@ rm -rf $RPM_BUILD_ROOT
%files
%doc %{_mandir}/man5/*
%doc %{_mandir}/man6/*
+%doc README
+%doc README.OPL
+%doc INSTALL
%doc NEWS
%doc AUTHORS
-%doc README
%doc COPYING
%doc CMDLINE
%doc BUGS
diff --git a/src/.gitignore b/src/.gitignore
index 973a0073..aa8a4c05 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -3,7 +3,11 @@ Makefile.in
.deps
*.rc
chocolate-doom
+chocolate-heretic
+chocolate-hexen
chocolate-server
+chocolate-setup
*.exe
+*.desktop
tags
TAGS
diff --git a/src/Makefile.am b/src/Makefile.am
index 51baa567..2ba6dbef 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,6 +11,7 @@ games_PROGRAMS = @PROGRAM_PREFIX@doom \
@PROGRAM_PREFIX@setup
AM_CFLAGS = -I$(top_builddir)/textscreen \
+ -I$(top_builddir)/opl \
-I$(top_builddir)/pcsound \
@SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@
@@ -72,6 +73,7 @@ tables.c tables.h \
v_video.c v_video.h \
v_patch.h \
w_checksum.c w_checksum.h \
+w_main.c w_main.h \
w_wad.c w_wad.h \
w_file.c w_file.h \
w_file_stdc.c \
@@ -115,6 +117,8 @@ FEATURE_SOUND_SOURCE_FILES = \
i_pcsound.c \
i_sdlsound.c \
i_sdlmusic.c \
+i_oplmusic.c \
+midifile.c midifile.h \
mus2mid.c mus2mid.h
# Some games support dehacked patches, some don't:
@@ -131,10 +135,11 @@ EXTRA_LIBS = \
$(top_builddir)/wince/libc_wince.a \
$(top_builddir)/textscreen/libtextscreen.a \
$(top_builddir)/pcsound/libpcsound.a \
+ $(top_builddir)/opl/libopl.a \
@LDFLAGS@ \
@SDL_LIBS@ \
@SDLMIXER_LIBS@ \
- @SDLNET_LIBS@
+ @SDLNET_LIBS@
if HAVE_WINDRES
@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
@@ -145,9 +150,9 @@ endif
@PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS)
if HAVE_WINDRES
-@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES) resource.rc
+@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
else
-@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES)
+@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH)
endif
@PROGRAM_PREFIX@heretic_LDADD = heretic/libheretic.a $(EXTRA_LIBS)
@@ -204,3 +209,6 @@ icon.c : $(top_builddir)/data/doom8.ico
endif
+midiread : midifile.c
+ $(CC) -DTEST $(CFLAGS) @LDFLAGS@ $^ -o $@
+
diff --git a/src/deh_main.c b/src/deh_main.c
index 3f0a6f29..75934087 100644
--- a/src/deh_main.c
+++ b/src/deh_main.c
@@ -39,6 +39,8 @@
extern deh_section_t *deh_section_types[];
extern char *deh_signatures[];
+static boolean deh_initialized = false;
+
// If true, we can do long string replacements.
boolean deh_allow_long_strings = false;
@@ -322,6 +324,12 @@ int DEH_LoadFile(char *filename)
{
deh_context_t *context;
+ if (!deh_initialized)
+ {
+ InitializeSections();
+ deh_initialized = true;
+ }
+
printf(" loading %s\n", filename);
context = DEH_OpenFile(filename);
@@ -346,8 +354,6 @@ void DEH_Init(void)
char *filename;
int p;
- InitializeSections();
-
//!
// @category mod
//
diff --git a/src/deh_mapping.c b/src/deh_mapping.c
index b215b128..f061c298 100644
--- a/src/deh_mapping.c
+++ b/src/deh_mapping.c
@@ -34,12 +34,9 @@
#include "i_system.h"
#include "deh_mapping.h"
-//
-// Set the value of a particular field in a structure by name
-//
-
-boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
- void *structptr, char *name, int value)
+static deh_mapping_entry_t *GetMappingEntryByName(deh_context_t *context,
+ deh_mapping_t *mapping,
+ char *name)
{
int i;
@@ -49,44 +46,121 @@ boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
if (!strcasecmp(entry->name, name))
{
- void *location;
-
if (entry->location == NULL)
{
DEH_Warning(context, "Field '%s' is unsupported", name);
- return false;
+ return NULL;
}
- location = (uint8_t *)structptr + ((uint8_t *)entry->location - (uint8_t *)mapping->base);
-
- // printf("Setting %p::%s to %i (%i bytes)\n",
- // structptr, name, value, entry->size);
-
- switch (entry->size)
- {
- case 1:
- * ((uint8_t *) location) = value;
- break;
- case 2:
- * ((uint16_t *) location) = value;
- break;
- case 4:
- * ((uint32_t *) location) = value;
- break;
- default:
- DEH_Error(context, "Unknown field type for '%s' (BUG)", name);
- return false;
- }
-
- return true;
+ return entry;
}
}
- // field with this name not found
+ // Not found.
DEH_Warning(context, "Field named '%s' not found", name);
- return false;
+ return NULL;
+}
+
+//
+// Get the location of the specified field in the specified structure.
+//
+
+static void *GetStructField(void *structptr,
+ deh_mapping_t *mapping,
+ deh_mapping_entry_t *entry)
+{
+ unsigned int offset;
+
+ offset = (uint8_t *)entry->location - (uint8_t *)mapping->base;
+
+ return (uint8_t *)structptr + offset;
+}
+
+//
+// Set the value of a particular field in a structure by name
+//
+
+boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
+ void *structptr, char *name, int value)
+{
+ deh_mapping_entry_t *entry;
+ void *location;
+
+ entry = GetMappingEntryByName(context, mapping, name);
+
+ if (entry == NULL)
+ {
+ return false;
+ }
+
+ // Sanity check:
+
+ if (entry->is_string)
+ {
+ DEH_Error(context, "Tried to set '%s' as integer (BUG)", name);
+ return false;
+ }
+
+ location = GetStructField(structptr, mapping, entry);
+
+ // printf("Setting %p::%s to %i (%i bytes)\n",
+ // structptr, name, value, entry->size);
+
+ // Set field content based on its type:
+
+ switch (entry->size)
+ {
+ case 1:
+ * ((uint8_t *) location) = value;
+ break;
+ case 2:
+ * ((uint16_t *) location) = value;
+ break;
+ case 4:
+ * ((uint32_t *) location) = value;
+ break;
+ default:
+ DEH_Error(context, "Unknown field type for '%s' (BUG)", name);
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Set the value of a string field in a structure by name
+//
+
+boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping,
+ void *structptr, char *name, char *value)
+{
+ deh_mapping_entry_t *entry;
+ void *location;
+
+ entry = GetMappingEntryByName(context, mapping, name);
+
+ if (entry == NULL)
+ {
+ return false;
+ }
+
+ // Sanity check:
+
+ if (!entry->is_string)
+ {
+ DEH_Error(context, "Tried to set '%s' as string (BUG)", name);
+ return false;
+ }
+
+ location = GetStructField(structptr, mapping, entry);
+
+ // Copy value into field:
+
+ strncpy(location, value, entry->size);
+
+ return true;
}
void DEH_StructMD5Sum(md5_context_t *context, deh_mapping_t *mapping,
diff --git a/src/deh_mapping.h b/src/deh_mapping.h
index 4862dec9..129ddcfd 100644
--- a/src/deh_mapping.h
+++ b/src/deh_mapping.h
@@ -42,17 +42,23 @@
#define DEH_MAPPING(deh_name, fieldname) \
{deh_name, &deh_mapping_base.fieldname, \
- sizeof(deh_mapping_base.fieldname)},
+ sizeof(deh_mapping_base.fieldname), \
+ false},
+
+#define DEH_MAPPING_STRING(deh_name, fieldname) \
+ {deh_name, &deh_mapping_base.fieldname, \
+ sizeof(deh_mapping_base.fieldname), \
+ true},
#define DEH_UNSUPPORTED_MAPPING(deh_name) \
- {deh_name, NULL, -1},
-
+ {deh_name, NULL, -1, false},
+
#define DEH_END_MAPPING \
{NULL, NULL, -1} \
} \
};
-
+
#define MAX_MAPPING_ENTRIES 32
@@ -73,6 +79,10 @@ struct deh_mapping_entry_s
// field size
int size;
+
+ // if true, this is a string value.
+
+ boolean is_string;
};
struct deh_mapping_s
@@ -81,8 +91,10 @@ struct deh_mapping_s
deh_mapping_entry_t entries[MAX_MAPPING_ENTRIES];
};
-boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
+boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping,
void *structptr, char *name, int value);
+boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping,
+ void *structptr, char *name, char *value);
void DEH_StructMD5Sum(md5_context_t *context, deh_mapping_t *mapping,
void *structptr);
diff --git a/src/deh_str.c b/src/deh_str.c
index 0baaa7e8..9bd429b6 100644
--- a/src/deh_str.c
+++ b/src/deh_str.c
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include "doomtype.h"
#include "deh_str.h"
@@ -179,3 +180,229 @@ void DEH_AddStringReplacement(char *from_text, char *to_text)
DEH_AddToHashtable(sub);
}
+typedef enum
+{
+ FORMAT_ARG_INVALID,
+ FORMAT_ARG_INT,
+ FORMAT_ARG_FLOAT,
+ FORMAT_ARG_CHAR,
+ FORMAT_ARG_STRING,
+ FORMAT_ARG_PTR,
+ FORMAT_ARG_SAVE_POS
+} format_arg_t;
+
+// Get the type of a format argument.
+// We can mix-and-match different format arguments as long as they
+// are for the same data type.
+
+static format_arg_t FormatArgumentType(char c)
+{
+ switch (c)
+ {
+ case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
+ return FORMAT_ARG_INT;
+
+ case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+ case 'a': case 'A':
+ return FORMAT_ARG_FLOAT;
+
+ case 'c': case 'C':
+ return FORMAT_ARG_CHAR;
+
+ case 's': case 'S':
+ return FORMAT_ARG_STRING;
+
+ case 'p':
+ return FORMAT_ARG_PTR;
+
+ case 'n':
+ return FORMAT_ARG_SAVE_POS;
+
+ default:
+ return FORMAT_ARG_INVALID;
+ }
+}
+
+// Given the specified string, get the type of the first format
+// string encountered.
+
+static format_arg_t NextFormatArgument(char **str)
+{
+ format_arg_t argtype;
+
+ // Search for the '%' starting the next string.
+
+ while (**str != '\0')
+ {
+ if (**str == '%')
+ {
+ ++*str;
+
+ // Don't stop for double-%s.
+
+ if (**str != '%')
+ {
+ break;
+ }
+ }
+
+ ++*str;
+ }
+
+ // Find the type of the format string.
+
+ while (**str != '\0')
+ {
+ argtype = FormatArgumentType(**str);
+
+ if (argtype != FORMAT_ARG_INVALID)
+ {
+ ++*str;
+
+ return argtype;
+ }
+
+ ++*str;
+ }
+
+ // Stop searching, we have reached the end.
+
+ *str = NULL;
+
+ return FORMAT_ARG_INVALID;
+}
+
+// Check if the specified argument type is a valid replacement for
+// the original.
+
+static boolean ValidArgumentReplacement(format_arg_t original,
+ format_arg_t replacement)
+{
+ // In general, the original and replacement types should be
+ // identical. However, there are some cases where the replacement
+ // is valid and the types don't match.
+
+ // Characters can be represented as ints.
+
+ if (original == FORMAT_ARG_CHAR && replacement == FORMAT_ARG_INT)
+ {
+ return true;
+ }
+
+ // Strings are pointers.
+
+ if (original == FORMAT_ARG_STRING && replacement == FORMAT_ARG_PTR)
+ {
+ return true;
+ }
+
+ return original == replacement;
+}
+
+// Return true if the specified string contains no format arguments.
+
+static boolean ValidFormatReplacement(char *original, char *replacement)
+{
+ char *rover1;
+ char *rover2;
+ int argtype1, argtype2;
+
+ // Check each argument in turn and compare types.
+
+ rover1 = original; rover2 = replacement;
+
+ for (;;)
+ {
+ argtype1 = NextFormatArgument(&rover1);
+ argtype2 = NextFormatArgument(&rover2);
+
+ if (argtype2 == FORMAT_ARG_INVALID)
+ {
+ // No more arguments left to read from the replacement string.
+
+ break;
+ }
+ else if (argtype1 == FORMAT_ARG_INVALID)
+ {
+ // Replacement string has more arguments than the original.
+
+ return false;
+ }
+ else if (!ValidArgumentReplacement(argtype1, argtype2))
+ {
+ // Not a valid replacement argument.
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Get replacement format string, checking arguments.
+
+static char *FormatStringReplacement(char *s)
+{
+ char *repl;
+
+ repl = DEH_String(s);
+
+ if (!ValidFormatReplacement(s, repl))
+ {
+ printf("WARNING: Unsafe dehacked replacement provided for "
+ "printf format string: %s\n", s);
+
+ return s;
+ }
+
+ return repl;
+}
+
+// printf(), performing a replacement on the format string.
+
+void DEH_printf(char *fmt, ...)
+{
+ va_list args;
+ char *repl;
+
+ repl = FormatStringReplacement(fmt);
+
+ va_start(args, fmt);
+
+ vprintf(repl, args);
+
+ va_end(args);
+}
+
+// fprintf(), performing a replacement on the format string.
+
+void DEH_fprintf(FILE *fstream, char *fmt, ...)
+{
+ va_list args;
+ char *repl;
+
+ repl = FormatStringReplacement(fmt);
+
+ va_start(args, fmt);
+
+ vfprintf(fstream, repl, args);
+
+ va_end(args);
+}
+
+// snprintf(), performing a replacement on the format string.
+
+void DEH_snprintf(char *buffer, size_t len, char *fmt, ...)
+{
+ va_list args;
+ char *repl;
+
+ repl = FormatStringReplacement(fmt);
+
+ va_start(args, fmt);
+
+ vsnprintf(buffer, len, repl, args);
+
+ va_end(args);
+}
+
diff --git a/src/deh_str.h b/src/deh_str.h
index 986536de..06bcb420 100644
--- a/src/deh_str.h
+++ b/src/deh_str.h
@@ -27,6 +27,8 @@
#ifndef DEH_STR_H
#define DEH_STR_H
+#include <stdio.h>
+
#include "doomfeatures.h"
// Used to do dehacked text substitutions throughout the program
@@ -34,11 +36,18 @@
#ifdef FEATURE_DEHACKED
char *DEH_String(char *s);
+void DEH_printf(char *fmt, ...);
+void DEH_fprintf(FILE *fstream, char *fmt, ...);
+void DEH_snprintf(char *buffer, size_t len, char *fmt, ...);
void DEH_AddStringReplacement(char *from_text, char *to_text);
+
#else
#define DEH_String(x) (x)
+#define DEH_printf printf
+#define DEH_fprintf fprintf
+#define DEH_snprintf snprintf
#endif
diff --git a/src/doom/.gitignore b/src/doom/.gitignore
index 973a0073..d4e88e5a 100644
--- a/src/doom/.gitignore
+++ b/src/doom/.gitignore
@@ -1,9 +1,5 @@
Makefile
Makefile.in
.deps
-*.rc
-chocolate-doom
-chocolate-server
-*.exe
tags
TAGS
diff --git a/src/doom/am_map.c b/src/doom/am_map.c
index 89a5dffc..d957ea67 100644
--- a/src/doom/am_map.c
+++ b/src/doom/am_map.c
@@ -500,7 +500,7 @@ void AM_loadPics(void)
for (i=0;i<10;i++)
{
- sprintf(namebuf, DEH_String("AMMNUM%d"), i);
+ DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
}
@@ -513,7 +513,7 @@ void AM_unloadPics(void)
for (i=0;i<10;i++)
{
- sprintf(namebuf, DEH_String("AMMNUM%d"), i);
+ DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
W_ReleaseLumpName(namebuf);
}
}
@@ -1020,7 +1020,7 @@ AM_drawFline
|| fl->b.x < 0 || fl->b.x >= f_w
|| fl->b.y < 0 || fl->b.y >= f_h)
{
- fprintf(stderr, DEH_String("fuck %d \r"), fuck++);
+ DEH_fprintf(stderr, "fuck %d \r", fuck++);
return;
}
diff --git a/src/doom/d_main.c b/src/doom/d_main.c
index 4e7812cc..f3a4b037 100644
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -45,8 +45,8 @@
#include "d_iwad.h"
#include "z_zone.h"
+#include "w_main.h"
#include "w_wad.h"
-#include "w_merge.h"
#include "s_sound.h"
#include "v_video.h"
@@ -358,6 +358,12 @@ void D_BindVariables(void)
M_BindWeaponControls();
M_BindMapControls();
M_BindMenuControls();
+ M_BindChatControls(MAXPLAYERS);
+
+ key_multi_msgplayer[0] = HUSTR_KEYGREEN;
+ key_multi_msgplayer[1] = HUSTR_KEYINDIGO;
+ key_multi_msgplayer[2] = HUSTR_KEYBROWN;
+ key_multi_msgplayer[3] = HUSTR_KEYRED;
#ifdef FEATURE_MULTIPLAYER
NET_BindVariables();
@@ -827,7 +833,6 @@ static boolean CheckChex(char *iwadname)
// print title for every printed line
char title[128];
-
static boolean D_AddFile(char *filename)
{
wad_file_t *handle;
@@ -1069,7 +1074,7 @@ void D_DoomMain (void)
I_PrintBanner(PACKAGE_STRING);
- printf (DEH_String("Z_Init: Init zone memory allocation daemon. \n"));
+ DEH_printf("Z_Init: Init zone memory allocation daemon. \n");
Z_Init ();
#ifdef FEATURE_MULTIPLAYER
@@ -1188,7 +1193,7 @@ void D_DoomMain (void)
deathmatch = 2;
if (devparm)
- printf(DEH_String(D_DEVSTR));
+ DEH_printf(D_DEVSTR);
// find which dir to use for config files
@@ -1236,7 +1241,7 @@ void D_DoomMain (void)
scale = 10;
if (scale > 400)
scale = 400;
- printf (DEH_String("turbo scale: %i%%\n"),scale);
+ DEH_printf("turbo scale: %i%%\n", scale);
forwardmove[0] = forwardmove[0]*scale/100;
forwardmove[1] = forwardmove[1]*scale/100;
sidemove[0] = sidemove[0]*scale/100;
@@ -1244,11 +1249,11 @@ void D_DoomMain (void)
}
// init subsystems
- printf(DEH_String("V_Init: allocate screens.\n"));
- V_Init();
+ DEH_printf("V_Init: allocate screens.\n");
+ V_Init ();
// Load configuration files before initialising other subsystems.
- printf(DEH_String("M_LoadDefaults: Load system defaults.\n"));
+ DEH_printf("M_LoadDefaults: Load system defaults.\n");
M_SetConfigFilenames("default.cfg", PROGRAM_PREFIX "doom.cfg");
D_BindVariables();
M_LoadDefaults();
@@ -1256,160 +1261,9 @@ void D_DoomMain (void)
// Save configuration at exit.
I_AtExit(M_SaveDefaults, false);
- printf (DEH_String("W_Init: Init WADfiles.\n"));
+ DEH_printf("W_Init: Init WADfiles.\n");
D_AddFile(iwadfile);
-
-#ifdef FEATURE_WAD_MERGE
-
- // Merged PWADs are loaded first, because they are supposed to be
- // modified IWADs.
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of deutex's -merge option, merging a PWAD
- // into the main IWAD. Multiple files may be specified.
- //
-
- p = M_CheckParm("-merge");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging %s\n", filename);
- W_MergeFile(filename);
- }
- }
-
- // NWT-style merging:
-
- // NWT's -merge option:
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of NWT's -merge option. Multiple files
- // may be specified.
-
- p = M_CheckParm("-nwtmerge");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" performing NWT-style merge of %s\n", filename);
- W_NWTDashMerge(filename);
- }
- }
-
- // Add flats
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of NWT's -af option, merging flats into
- // the main IWAD directory. Multiple files may be specified.
- //
-
- p = M_CheckParm("-af");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging flats from %s\n", filename);
- W_NWTMergeFile(filename, W_NWT_MERGE_FLATS);
- }
- }
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of NWT's -as option, merging sprites
- // into the main IWAD directory. Multiple files may be specified.
- //
-
- p = M_CheckParm("-as");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging sprites from %s\n", filename);
- W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES);
- }
- }
-
- //!
- // @arg <files>
- // @category mod
- //
- // Equivalent to "-af <files> -as <files>".
- //
-
- p = M_CheckParm("-aa");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging sprites and flats from %s\n", filename);
- W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS);
- }
- }
-
-#endif
-
- //!
- // @arg <files>
- // @vanilla
- //
- // Load the specified PWAD files.
- //
-
- p = M_CheckParm ("-file");
- if (p)
- {
- // the parms after p are wadfile/lump names,
- // until end of parms or another - preceded parm
- modifiedgame = true; // homebrew levels
- while (++p != myargc && myargv[p][0] != '-')
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- D_AddFile(filename);
- }
- }
-
- // Debug:
-// W_PrintDirectory();
+ modifiedgame = W_ParseCommandLine();
// add any files specified on the command line with -file wadfile
// to the wad list
@@ -1603,8 +1457,8 @@ void D_DoomMain (void)
if (p && p < myargc-1 && deathmatch)
{
- printf(DEH_String("Austin Virtual Gaming: Levels will end "
- "after 20 minutes\n"));
+ DEH_printf("Austin Virtual Gaming: Levels will end "
+ "after 20 minutes\n");
timelimit = 20;
}
@@ -1686,16 +1540,16 @@ void D_DoomMain (void)
I_PrintStartupBanner(gamedescription);
PrintDehackedBanners();
- printf (DEH_String("M_Init: Init miscellaneous info.\n"));
+ DEH_printf("M_Init: Init miscellaneous info.\n");
M_Init ();
- printf (DEH_String("R_Init: Init DOOM refresh daemon - "));
+ DEH_printf("R_Init: Init DOOM refresh daemon - ");
R_Init ();
- printf (DEH_String("\nP_Init: Init Playloop state.\n"));
+ DEH_printf("\nP_Init: Init Playloop state.\n");
P_Init ();
- printf (DEH_String("I_Init: Setting up machine state.\n"));
+ DEH_printf("I_Init: Setting up machine state.\n");
I_CheckIsScreensaver();
I_InitTimer();
I_InitJoystick();
@@ -1705,18 +1559,18 @@ void D_DoomMain (void)
NET_Init ();
#endif
- printf (DEH_String("S_Init: Setting up sound.\n"));
+ DEH_printf("S_Init: Setting up sound.\n");
S_Init (sfxVolume * 8, musicVolume * 8);
- printf (DEH_String("D_CheckNetGame: Checking network game status.\n"));
+ DEH_printf("D_CheckNetGame: Checking network game status.\n");
D_CheckNetGame ();
PrintGameVersion();
- printf (DEH_String("HU_Init: Setting up heads up display.\n"));
+ DEH_printf("HU_Init: Setting up heads up display.\n");
HU_Init ();
- printf (DEH_String("ST_Init: Init status bar.\n"));
+ DEH_printf("ST_Init: Init status bar.\n");
ST_Init ();
// If Doom II without a MAP01 lump, this is a store demo.
diff --git a/src/doom/d_net.c b/src/doom/d_net.c
index e30ead83..464922d4 100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -374,17 +374,17 @@ void D_CheckNetGame (void)
++num_players;
}
- printf (DEH_String("startskill %i deathmatch: %i startmap: %i startepisode: %i\n"),
- startskill, deathmatch, startmap, startepisode);
+ DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
+ startskill, deathmatch, startmap, startepisode);
- printf(DEH_String("player %i of %i (%i nodes)\n"),
- consoleplayer+1, num_players, num_players);
+ DEH_printf("player %i of %i (%i nodes)\n",
+ consoleplayer+1, num_players, num_players);
// Show players here; the server might have specified a time limit
if (timelimit > 0)
{
- printf(DEH_String("Levels will end after %d minute"),timelimit);
+ DEH_printf("Levels will end after %d minute", timelimit);
if (timelimit > 1)
printf("s");
printf(".\n");
diff --git a/src/doom/f_finale.c b/src/doom/f_finale.c
index ece82b43..dfbeafbe 100644
--- a/src/doom/f_finale.c
+++ b/src/doom/f_finale.c
@@ -663,7 +663,7 @@ void F_BunnyScroll (void)
laststage = stage;
}
- sprintf (name, DEH_String("END%i"), stage);
+ DEH_snprintf(name, 10, "END%i", stage);
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
(SCREENHEIGHT - 8 * 8) / 2,
W_CacheLumpName (name,PU_CACHE));
diff --git a/src/doom/g_game.c b/src/doom/g_game.c
index 8d0e4503..ad7155a0 100644
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -176,14 +176,37 @@ static int *weapon_keys[] = {
&key_weapon8
};
+// Set to -1 or +1 to switch to the previous or next weapon.
+
+static int next_weapon = 0;
+
+// Used for prev/next weapon keys.
+
+static const struct
+{
+ weapontype_t weapon;
+ weapontype_t weapon_num;
+} weapon_order_table[] = {
+ { wp_fist, wp_fist },
+ { wp_chainsaw, wp_fist },
+ { wp_pistol, wp_pistol },
+ { wp_shotgun, wp_shotgun },
+ { wp_supershotgun, wp_shotgun },
+ { wp_chaingun, wp_chaingun },
+ { wp_missile, wp_missile },
+ { wp_plasma, wp_plasma },
+ { wp_bfg, wp_bfg }
+};
+
#define SLOWTURNTICS 6
#define NUMKEYS 256
+#define MAX_JOY_BUTTONS 20
static boolean gamekeydown[NUMKEYS];
static int turnheld; // for accelerative turning
-static boolean mousearray[4];
+static boolean mousearray[MAX_MOUSE_BUTTONS + 1];
static boolean *mousebuttons = &mousearray[1]; // allow [-1]
// mouse values are used once
@@ -197,8 +220,6 @@ static int dclicktime2;
static boolean dclickstate2;
static int dclicks2;
-#define MAX_JOY_BUTTONS 20
-
// joystick values are repeated
static int joyxmove;
static int joyymove;
@@ -337,7 +358,63 @@ int G_CmdChecksum (ticcmd_t* cmd)
return sum;
}
-
+
+static boolean WeaponSelectable(weapontype_t weapon)
+{
+ // Can't select a weapon if we don't own it.
+
+ if (!players[consoleplayer].weaponowned[weapon])
+ {
+ return false;
+ }
+
+ // Can't select the fist if we have the chainsaw, unless
+ // we also have the berserk pack.
+
+ if (weapon == wp_fist
+ && players[consoleplayer].weaponowned[wp_chainsaw]
+ && !players[consoleplayer].powers[pw_strength])
+ {
+ return false;
+ }
+
+ return true;
+}
+
+static int G_NextWeapon(int direction)
+{
+ weapontype_t weapon;
+ int i;
+
+ // Find index in the table.
+
+ if (players[consoleplayer].pendingweapon == wp_nochange)
+ {
+ weapon = players[consoleplayer].readyweapon;
+ }
+ else
+ {
+ weapon = players[consoleplayer].pendingweapon;
+ }
+
+ for (i=0; i<arrlen(weapon_order_table); ++i)
+ {
+ if (weapon_order_table[i].weapon == weapon)
+ {
+ break;
+ }
+ }
+
+ // Switch weapon.
+
+ do
+ {
+ i += direction;
+ i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table);
+ } while (!WeaponSelectable(weapon_order_table[i].weapon));
+
+ return weapon_order_table[i].weapon_num;
+}
//
// G_BuildTiccmd
@@ -465,20 +542,34 @@ void G_BuildTiccmd (ticcmd_t* cmd)
dclicks = 0;
}
- // chainsaw overrides
+ // If the previous or next weapon button is pressed, the
+ // next_weapon variable is set to change weapons when
+ // we generate a ticcmd. Choose a new weapon.
- for (i=0; i<arrlen(weapon_keys); ++i)
+ if (next_weapon != 0)
+ {
+ i = G_NextWeapon(next_weapon);
+ cmd->buttons |= BT_CHANGE;
+ cmd->buttons |= i << BT_WEAPONSHIFT;
+ next_weapon = 0;
+ }
+ else
{
- int key = *weapon_keys[i];
+ // Check weapon keys.
- if (gamekeydown[key])
+ for (i=0; i<arrlen(weapon_keys); ++i)
{
- cmd->buttons |= BT_CHANGE;
- cmd->buttons |= i<<BT_WEAPONSHIFT;
- break;
+ int key = *weapon_keys[i];
+
+ if (gamekeydown[key])
+ {
+ cmd->buttons |= BT_CHANGE;
+ cmd->buttons |= i<<BT_WEAPONSHIFT;
+ break;
+ }
}
}
-
+
// mouse
if (mousebuttons[mousebforward])
{
@@ -657,7 +748,6 @@ void G_DoLoadLevel (void)
players[consoleplayer].message = "Press escape to quit.";
}
}
-
static void SetJoyButtons(unsigned int buttons_mask)
{
@@ -665,10 +755,54 @@ static void SetJoyButtons(unsigned int buttons_mask)
for (i=0; i<MAX_JOY_BUTTONS; ++i)
{
- joybuttons[i] = (buttons_mask & (1 << i)) != 0;
+ int button_on = (buttons_mask & (1 << i)) != 0;
+
+ // Detect button press:
+
+ if (!joybuttons[i] && button_on)
+ {
+ // Weapon cycling:
+
+ if (i == joybprevweapon)
+ {
+ next_weapon = -1;
+ }
+ else if (i == joybnextweapon)
+ {
+ next_weapon = 1;
+ }
+ }
+
+ joybuttons[i] = button_on;
}
}
-
+
+static void SetMouseButtons(unsigned int buttons_mask)
+{
+ int i;
+
+ for (i=0; i<MAX_MOUSE_BUTTONS; ++i)
+ {
+ unsigned int button_on = (buttons_mask & (1 << i)) != 0;
+
+ // Detect button press:
+
+ if (!mousebuttons[i] && button_on)
+ {
+ if (i == mousebprevweapon)
+ {
+ next_weapon = -1;
+ }
+ else if (i == mousebnextweapon)
+ {
+ next_weapon = 1;
+ }
+ }
+
+ mousebuttons[i] = button_on;
+ }
+}
+
//
// G_Responder
// Get info needed to make ticcmd_ts for the players.
@@ -677,7 +811,7 @@ boolean G_Responder (event_t* ev)
{
// allow spy mode changes even during the demo
if (gamestate == GS_LEVEL && ev->type == ev_keydown
- && ev->data1 == KEY_F12 && (singledemo || !deathmatch) )
+ && ev->data1 == key_spy && (singledemo || !deathmatch) )
{
// spy mode
do
@@ -737,6 +871,18 @@ boolean G_Responder (event_t* ev)
testcontrols_mousespeed = abs(ev->data2);
}
+ // If the next/previous weapon keys are pressed, set the next_weapon
+ // variable to change weapons when the next ticcmd is generated.
+
+ if (ev->type == ev_keydown && ev->data1 == key_prevweapon)
+ {
+ next_weapon = -1;
+ }
+ else if (ev->type == ev_keydown && ev->data1 == key_nextweapon)
+ {
+ next_weapon = 1;
+ }
+
switch (ev->type)
{
case ev_keydown:
@@ -757,9 +903,7 @@ boolean G_Responder (event_t* ev)
return false; // always let key up events filter down
case ev_mouse:
- mousebuttons[0] = ev->data1 & 1;
- mousebuttons[1] = ev->data1 & 2;
- mousebuttons[2] = ev->data1 & 4;
+ SetMouseButtons(ev->data1);
mousex = ev->data2*(mouseSensitivity+5)/10;
mousey = ev->data3*(mouseSensitivity+5)/10;
return true; // eat events
@@ -1428,6 +1572,8 @@ void G_DoLoadGame (void)
return;
}
+ savegame_error = false;
+
if (!P_ReadSaveGameHeader())
{
fclose(save_stream);
@@ -1495,6 +1641,8 @@ void G_DoSaveGame (void)
return;
}
+ savegame_error = false;
+
P_WriteSaveGameHeader(savedescription);
P_ArchivePlayers ();
@@ -1780,7 +1928,7 @@ void G_WriteDemoTiccmd (ticcmd_t* cmd)
{
byte *demo_start;
- if (gamekeydown['q']) // press q to end demo recording
+ if (gamekeydown[key_demo_quit]) // press q to end demo recording
G_CheckDemoStatus ();
demo_start = demo_p;
diff --git a/src/doom/hu_stuff.c b/src/doom/hu_stuff.c
index ca74ce92..f9271b3d 100644
--- a/src/doom/hu_stuff.c
+++ b/src/doom/hu_stuff.c
@@ -302,7 +302,7 @@ void HU_Init(void)
j = HU_FONTSTART;
for (i=0;i<HU_FONTSIZE;i++)
{
- sprintf(buffer, DEH_String("STCFN%.3d"), j++);
+ DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
}
@@ -529,14 +529,6 @@ boolean HU_Responder(event_t *ev)
int i;
int numplayers;
- static char destination_keys[MAXPLAYERS] =
- {
- HUSTR_KEYGREEN,
- HUSTR_KEYINDIGO,
- HUSTR_KEYBROWN,
- HUSTR_KEYRED
- };
-
static int num_nobrainers = 0;
numplayers = 0;
@@ -565,7 +557,7 @@ boolean HU_Responder(event_t *ev)
message_counter = HU_MSGTIMEOUT;
eatkey = true;
}
- else if (netgame && ev->data2 == HU_INPUTTOGGLE)
+ else if (netgame && ev->data2 == key_multi_msg)
{
eatkey = chat_on = true;
HUlib_resetIText(&w_chat);
@@ -575,7 +567,7 @@ boolean HU_Responder(event_t *ev)
{
for (i=0; i<MAXPLAYERS ; i++)
{
- if (ev->data2 == destination_keys[i])
+ if (ev->data2 == key_multi_msgplayer[i])
{
if (playeringame[i] && i!=consoleplayer)
{
diff --git a/src/doom/m_menu.c b/src/doom/m_menu.c
index a0d66dd4..a6f7bbfb 100644
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -707,7 +707,7 @@ void M_QuickSave(void)
quickSaveSlot = -2; // means to pick a slot now
return;
}
- sprintf(tempstring,DEH_String(QSPROMPT),savegamestrings[quickSaveSlot]);
+ DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]);
M_StartMessage(tempstring,M_QuickSaveResponse,true);
}
@@ -739,7 +739,7 @@ void M_QuickLoad(void)
M_StartMessage(DEH_String(QSAVESPOT),NULL,false);
return;
}
- sprintf(tempstring,DEH_String(QLPROMPT),savegamestrings[quickSaveSlot]);
+ DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]);
M_StartMessage(tempstring,M_QuickLoadResponse,true);
}
diff --git a/src/doom/p_saveg.c b/src/doom/p_saveg.c
index e557e494..968120c0 100644
--- a/src/doom/p_saveg.c
+++ b/src/doom/p_saveg.c
@@ -44,6 +44,7 @@
FILE *save_stream;
int savegamelength;
+boolean savegame_error;
// Get the filename of a temporary file to write the savegame to. After
// the file has been successfully saved, it will be renamed to the
@@ -75,7 +76,7 @@ char *P_SaveGameFile(int slot)
filename = malloc(strlen(savegamedir) + 32);
}
- sprintf(basename, DEH_String(SAVEGAMENAME "%d.dsg"), slot);
+ DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot);
sprintf(filename, "%s%s", savegamedir, basename);
@@ -88,14 +89,31 @@ static byte saveg_read8(void)
{
byte result;
- fread(&result, 1, 1, save_stream);
+ if (fread(&result, 1, 1, save_stream) < 1)
+ {
+ if (!savegame_error)
+ {
+ fprintf(stderr, "saveg_read8: Unexpected end of file while "
+ "reading save game\n");
+
+ savegame_error = true;
+ }
+ }
return result;
}
static void saveg_write8(byte value)
{
- fwrite(&value, 1, 1, save_stream);
+ if (fwrite(&value, 1, 1, save_stream) < 1)
+ {
+ if (!savegame_error)
+ {
+ fprintf(stderr, "saveg_write8: Error while writing save game\n");
+
+ savegame_error = true;
+ }
+ }
}
static short saveg_read16(void)
diff --git a/src/doom/p_saveg.h b/src/doom/p_saveg.h
index 3a96cc3e..5488289c 100644
--- a/src/doom/p_saveg.h
+++ b/src/doom/p_saveg.h
@@ -64,6 +64,7 @@ void P_ArchiveSpecials (void);
void P_UnArchiveSpecials (void);
extern FILE *save_stream;
+extern boolean savegame_error;
#endif
diff --git a/src/doom/p_setup.c b/src/doom/p_setup.c
index 2a3a8f85..7d9d4318 100644
--- a/src/doom/p_setup.c
+++ b/src/doom/p_setup.c
@@ -33,6 +33,7 @@
#include "deh_main.h"
#include "i_swap.h"
+#include "m_argv.h"
#include "m_bbox.h"
#include "g_game.h"
@@ -76,6 +77,7 @@ line_t* lines;
int numsides;
side_t* sides;
+static int totallines;
// BLOCKMAP
// Created from axis aligned bounding box
@@ -534,7 +536,6 @@ void P_GroupLines (void)
line_t** linebuffer;
int i;
int j;
- int total;
line_t* li;
sector_t* sector;
subsector_t* ss;
@@ -552,21 +553,21 @@ void P_GroupLines (void)
// count number of lines in each sector
li = lines;
- total = 0;
+ totallines = 0;
for (i=0 ; i<numlines ; i++, li++)
{
- total++;
+ totallines++;
li->frontsector->linecount++;
if (li->backsector && li->backsector != li->frontsector)
{
li->backsector->linecount++;
- total++;
+ totallines++;
}
}
// build line tables for each sector
- linebuffer = Z_Malloc (total*sizeof(line_t *), PU_LEVEL, 0);
+ linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0);
for (i=0; i<numsectors; ++i)
{
@@ -643,6 +644,87 @@ void P_GroupLines (void)
}
+// Pad the REJECT lump with extra data when the lump is too small,
+// to simulate a REJECT buffer overflow in Vanilla Doom.
+
+static void PadRejectArray(byte *array, unsigned int len)
+{
+ unsigned int i;
+ unsigned int byte_num;
+ byte *dest;
+ unsigned int padvalue;
+
+ // Values to pad the REJECT array with:
+
+ unsigned int rejectpad[4] =
+ {
+ ((totallines * 4 + 3) & ~3) + 24, // Size
+ 0, // Part of z_zone block header
+ 50, // PU_LEVEL
+ 0x1d4a11 // DOOM_CONST_ZONEID
+ };
+
+ // Copy values from rejectpad into the destination array.
+
+ dest = array;
+
+ for (i=0; i<len && i<sizeof(rejectpad); ++i)
+ {
+ byte_num = i % 4;
+ *dest = (rejectpad[i / 4] >> (byte_num * 8)) & 0xff;
+ ++dest;
+ }
+
+ // We only have a limited pad size. Print a warning if the
+ // REJECT lump is too small.
+
+ if (len > sizeof(rejectpad))
+ {
+ fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%i > %i)\n",
+ len, sizeof(rejectpad));
+
+ // Pad remaining space with 0 (or 0xff, if specified on command line).
+
+ if (M_CheckParm("-reject_pad_with_ff"))
+ {
+ padvalue = 0xff;
+ }
+ else
+ {
+ padvalue = 0xf00;
+ }
+
+ memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad));
+ }
+}
+
+static void P_LoadReject(int lumpnum)
+{
+ int minlength;
+ int lumplen;
+
+ // Calculate the size that the REJECT lump *should* be.
+
+ minlength = (numsectors * numsectors + 7) / 8;
+
+ // If the lump meets the minimum length, it can be loaded directly.
+ // Otherwise, we need to allocate a buffer of the correct size
+ // and pad it with appropriate data.
+
+ lumplen = W_LumpLength(lumpnum);
+
+ if (lumplen >= minlength)
+ {
+ rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL);
+ }
+ else
+ {
+ rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix);
+ W_ReadLump(lumpnum, rejectmatrix);
+
+ PadRejectArray(rejectmatrix + lumplen, minlength - lumplen);
+ }
+}
//
// P_SetupLevel
@@ -692,9 +774,9 @@ P_SetupLevel
if ( gamemode == commercial)
{
if (map<10)
- sprintf (lumpname, DEH_String("map0%i"), map);
+ DEH_snprintf(lumpname, 9, "map0%i", map);
else
- sprintf (lumpname, DEH_String("map%i"), map);
+ DEH_snprintf(lumpname, 9, "map%i", map);
}
else
{
@@ -719,9 +801,9 @@ P_SetupLevel
P_LoadSubsectors (lumpnum+ML_SSECTORS);
P_LoadNodes (lumpnum+ML_NODES);
P_LoadSegs (lumpnum+ML_SEGS);
-
- rejectmatrix = W_CacheLumpNum (lumpnum+ML_REJECT,PU_LEVEL);
+
P_GroupLines ();
+ P_LoadReject (lumpnum+ML_REJECT);
bodyqueslot = 0;
deathmatch_p = deathmatchstarts;
diff --git a/src/doom/r_draw.c b/src/doom/r_draw.c
index 1b6420d9..80362208 100644
--- a/src/doom/r_draw.c
+++ b/src/doom/r_draw.c
@@ -597,48 +597,54 @@ int dscount;
// Draws the actual span.
void R_DrawSpan (void)
{
- fixed_t xfrac;
- fixed_t yfrac;
- byte* dest;
- int count;
- int spot;
-
-#ifdef RANGECHECK
+ unsigned int position, step;
+ byte *dest;
+ int count;
+ int spot;
+ unsigned int xtemp, ytemp;
+
+#ifdef RANGECHECK
if (ds_x2 < ds_x1
|| ds_x1<0
- || ds_x2>=SCREENWIDTH
+ || ds_x2>=SCREENWIDTH
|| (unsigned)ds_y>SCREENHEIGHT)
{
I_Error( "R_DrawSpan: %i to %i at %i",
ds_x1,ds_x2,ds_y);
}
-// dscount++;
-#endif
+// dscount++;
+#endif
+
+ // Pack position and step variables into a single 32-bit integer,
+ // with x in the top 16 bits and y in the bottom 16 bits. For
+ // each 16-bit part, the top 6 bits are the integer part and the
+ // bottom 10 bits are the fractional part of the pixel position.
+
+ position = ((ds_xfrac << 10) & 0xffff0000)
+ | ((ds_yfrac >> 6) & 0x0000ffff);
+ step = ((ds_xstep << 10) & 0xffff0000)
+ | ((ds_ystep >> 6) & 0x0000ffff);
-
- xfrac = ds_xfrac;
- yfrac = ds_yfrac;
-
dest = ylookup[ds_y] + columnofs[ds_x1];
// We do not check for zero spans here?
- count = ds_x2 - ds_x1;
+ count = ds_x2 - ds_x1;
- do
+ do
{
- // Current texture index in u,v.
- spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
+ // Calculate current texture index in u,v.
+ ytemp = (position >> 4) & 0x0fc0;
+ xtemp = (position >> 26);
+ spot = xtemp | ytemp;
// Lookup pixel from flat texture tile,
// re-index using light/colormap.
*dest++ = ds_colormap[ds_source[spot]];
- // Next step in u,v.
- xfrac += ds_xstep;
- yfrac += ds_ystep;
-
- } while (count--);
-}
+ position += step;
+
+ } while (count--);
+}
@@ -718,49 +724,54 @@ void R_DrawSpan (void)
//
// Again..
//
-void R_DrawSpanLow (void)
-{
- fixed_t xfrac;
- fixed_t yfrac;
- byte* dest;
- int count;
- int spot;
-
-#ifdef RANGECHECK
+void R_DrawSpanLow (void)
+{
+ unsigned int position, step;
+ unsigned int xtemp, ytemp;
+ byte *dest;
+ int count;
+ int spot;
+
+#ifdef RANGECHECK
if (ds_x2 < ds_x1
|| ds_x1<0
- || ds_x2>=SCREENWIDTH
+ || ds_x2>=SCREENWIDTH
|| (unsigned)ds_y>SCREENHEIGHT)
{
I_Error( "R_DrawSpan: %i to %i at %i",
ds_x1,ds_x2,ds_y);
}
// dscount++;
-#endif
-
- xfrac = ds_xfrac;
- yfrac = ds_yfrac;
-
- count = (ds_x2 - ds_x1);
+#endif
+
+ position = ((ds_xfrac << 10) & 0xffff0000)
+ | ((ds_yfrac >> 6) & 0x0000ffff);
+ step = ((ds_xstep << 10) & 0xffff0000)
+ | ((ds_ystep >> 6) & 0x0000ffff);
+
+ count = (ds_x2 - ds_x1);
// Blocky mode, need to multiply by 2.
ds_x1 <<= 1;
ds_x2 <<= 1;
-
+
dest = ylookup[ds_y] + columnofs[ds_x1];
-
- do
- {
- spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
+
+ do
+ {
+ // Calculate current texture index in u,v.
+ ytemp = (position >> 4) & 0x0fc0;
+ xtemp = (position >> 26);
+ spot = xtemp | ytemp;
+
// Lowres/blocky mode does it twice,
// while scale is adjusted appropriately.
- *dest++ = ds_colormap[ds_source[spot]];
*dest++ = ds_colormap[ds_source[spot]];
-
- xfrac += ds_xstep;
- yfrac += ds_ystep;
+ *dest++ = ds_colormap[ds_source[spot]];
- } while (count--);
+ position += step;
+
+ } while (count--);
}
//
diff --git a/src/doom/s_sound.c b/src/doom/s_sound.c
index f829956c..7f4411dd 100644
--- a/src/doom/s_sound.c
+++ b/src/doom/s_sound.c
@@ -618,6 +618,15 @@ void S_ChangeMusic(int musicnum, int looping)
char namebuf[9];
void *handle;
+ // The Doom IWAD file has two versions of the intro music: d_intro
+ // and d_introa. The latter is used for OPL playback.
+
+ if (musicnum == mus_intro && (snd_musicdevice == SNDDEVICE_ADLIB
+ || snd_musicdevice == SNDDEVICE_SB))
+ {
+ musicnum = mus_introa;
+ }
+
if (musicnum <= mus_None || musicnum >= NUMMUSIC)
{
I_Error("Bad music number %d", musicnum);
diff --git a/src/doom/st_stuff.c b/src/doom/st_stuff.c
index 160244ce..7e5e225c 100644
--- a/src/doom/st_stuff.c
+++ b/src/doom/st_stuff.c
@@ -1085,10 +1085,10 @@ static void ST_loadUnloadGraphics(load_callback_t callback)
// Load the numbers, tall and short
for (i=0;i<10;i++)
{
- sprintf(namebuf, DEH_String("STTNUM%d"), i);
+ DEH_snprintf(namebuf, 9, "STTNUM%d", i);
callback(namebuf, &tallnum[i]);
- sprintf(namebuf, DEH_String("STYSNUM%d"), i);
+ DEH_snprintf(namebuf, 9, "STYSNUM%d", i);
callback(namebuf, &shortnum[i]);
}
@@ -1100,7 +1100,7 @@ static void ST_loadUnloadGraphics(load_callback_t callback)
// key cards
for (i=0;i<NUMCARDS;i++)
{
- sprintf(namebuf, DEH_String("STKEYS%d"), i);
+ DEH_snprintf(namebuf, 9, "STKEYS%d", i);
callback(namebuf, &keys[i]);
}
@@ -1110,7 +1110,7 @@ static void ST_loadUnloadGraphics(load_callback_t callback)
// arms ownership widgets
for (i=0; i<6; i++)
{
- sprintf(namebuf, DEH_String("STGNUM%d"), i+2);
+ DEH_snprintf(namebuf, 9, "STGNUM%d", i+2);
// gray #
callback(namebuf, &arms[i][0]);
@@ -1120,7 +1120,7 @@ static void ST_loadUnloadGraphics(load_callback_t callback)
}
// face backgrounds for different color players
- sprintf(namebuf, DEH_String("STFB%d"), consoleplayer);
+ DEH_snprintf(namebuf, 9, "STFB%d", consoleplayer);
callback(namebuf, &faceback);
// status bar background bits
@@ -1132,23 +1132,23 @@ static void ST_loadUnloadGraphics(load_callback_t callback)
{
for (j=0; j<ST_NUMSTRAIGHTFACES; j++)
{
- sprintf(namebuf, DEH_String("STFST%d%d"), i, j);
+ DEH_snprintf(namebuf, 9, "STFST%d%d", i, j);
callback(namebuf, &faces[facenum]);
++facenum;
}
- sprintf(namebuf, DEH_String("STFTR%d0"), i); // turn right
+ DEH_snprintf(namebuf, 9, "STFTR%d0", i); // turn right
callback(namebuf, &faces[facenum]);
++facenum;
- sprintf(namebuf, DEH_String("STFTL%d0"), i); // turn left
+ DEH_snprintf(namebuf, 9, "STFTL%d0", i); // turn left
callback(namebuf, &faces[facenum]);
++facenum;
- sprintf(namebuf, DEH_String("STFOUCH%d"), i); // ouch!
+ DEH_snprintf(namebuf, 9, "STFOUCH%d", i); // ouch!
callback(namebuf, &faces[facenum]);
++facenum;
- sprintf(namebuf, DEH_String("STFEVL%d"), i); // evil grin ;)
+ DEH_snprintf(namebuf, 9, "STFEVL%d", i); // evil grin ;)
callback(namebuf, &faces[facenum]);
++facenum;
- sprintf(namebuf, DEH_String("STFKILL%d"), i); // pissed off
+ DEH_snprintf(namebuf, 9, "STFKILL%d", i); // pissed off
callback(namebuf, &faces[facenum]);
++facenum;
}
diff --git a/src/doom/wi_stuff.c b/src/doom/wi_stuff.c
index 83f5052f..45c09343 100644
--- a/src/doom/wi_stuff.c
+++ b/src/doom/wi_stuff.c
@@ -1571,16 +1571,16 @@ static void WI_loadUnloadData(load_callback_t callback)
if (gamemode == commercial)
{
for (i=0 ; i<NUMCMAPS ; i++)
- {
- sprintf(name, DEH_String("CWILV%2.2d"), i);
+ {
+ DEH_snprintf(name, 9, "CWILV%2.2d", i);
callback(name, &lnames[i]);
- }
+ }
}
else
{
for (i=0 ; i<NUMMAPS ; i++)
{
- sprintf(name, DEH_String("WILV%d%d"), wbs->epsd, i);
+ DEH_snprintf(name, 9, "WILV%d%d", wbs->epsd, i);
callback(name, &lnames[i]);
}
@@ -1592,7 +1592,7 @@ static void WI_loadUnloadData(load_callback_t callback)
// splat
callback(DEH_String("WISPLAT"), &splat[0]);
-
+
if (wbs->epsd < 3)
{
for (j=0;j<NUMANIMS[wbs->epsd];j++)
@@ -1601,17 +1601,16 @@ static void WI_loadUnloadData(load_callback_t callback)
for (i=0;i<a->nanims;i++)
{
// MONDO HACK!
- if (wbs->epsd != 1 || j != 8)
+ if (wbs->epsd != 1 || j != 8)
{
// animations
- sprintf(name, DEH_String("WIA%d%.2d%.2d"),
- wbs->epsd, j, i);
+ DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i);
callback(name, &a->p[i]);
}
else
{
// HACK ALERT!
- a->p[i] = anims[1][4].p[i];
+ a->p[i] = anims[1][4].p[i];
}
}
}
@@ -1624,7 +1623,7 @@ static void WI_loadUnloadData(load_callback_t callback)
for (i=0;i<10;i++)
{
// numbers 0-9
- sprintf(name, DEH_String("WINUM%d"), i);
+ DEH_snprintf(name, 9, "WINUM%d", i);
callback(name, &num[i]);
}
@@ -1665,13 +1664,13 @@ static void WI_loadUnloadData(load_callback_t callback)
callback(DEH_String("WICOLON"), &colon);
// "time"
- callback(DEH_String("WITIME"), &timepatch);
+ callback(DEH_String("WITIME"), &timepatch);
// "sucks"
- callback(DEH_String("WISUCKS"), &sucks);
+ callback(DEH_String("WISUCKS"), &sucks);
// "par"
- callback(DEH_String("WIPAR"), &par);
+ callback(DEH_String("WIPAR"), &par);
// "killers" (vertical)
callback(DEH_String("WIKILRS"), &killers);
@@ -1680,16 +1679,16 @@ static void WI_loadUnloadData(load_callback_t callback)
callback(DEH_String("WIVCTMS"), &victims);
// "total"
- callback(DEH_String("WIMSTT"), &total);
+ callback(DEH_String("WIMSTT"), &total);
for (i=0 ; i<MAXPLAYERS ; i++)
{
// "1,2,3,4"
- sprintf(name, DEH_String("STPB%d"), i);
+ DEH_snprintf(name, 9, "STPB%d", i);
callback(name, &p[i]);
// "1,2,3,4"
- sprintf(name, DEH_String("WIBP%d"), i+1);
+ DEH_snprintf(name, 9, "WIBP%d", i+1);
callback(name, &bp[i]);
}
@@ -1697,19 +1696,21 @@ static void WI_loadUnloadData(load_callback_t callback)
if (gamemode == commercial)
{
- strcpy(name, DEH_String("INTERPIC"));
+ strncpy(name, DEH_String("INTERPIC"), 9);
+ name[8] = '\0';
}
else if (gamemode == retail && wbs->epsd == 3)
{
- strcpy(name, DEH_String("INTERPIC"));
+ strncpy(name, DEH_String("INTERPIC"), 9);
+ name[8] = '\0';
}
- else
+ else
{
- sprintf(name, DEH_String("WIMAP%d"), wbs->epsd);
+ DEH_snprintf(name, 9, "WIMAP%d", wbs->epsd);
}
-
+
// Draw backdrop and save to a temporary buffer
-
+
callback(name, &background);
}
@@ -1722,7 +1723,7 @@ void WI_loadData(void)
{
if (gamemode == commercial)
{
- NUMCMAPS = 32;
+ NUMCMAPS = 32;
lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS,
PU_STATIC, NULL);
}
diff --git a/src/heretic/.gitignore b/src/heretic/.gitignore
index d7e732ad..76092240 100644
--- a/src/heretic/.gitignore
+++ b/src/heretic/.gitignore
@@ -1,7 +1,6 @@
Makefile
Makefile.in
.deps
-*.rc
-chocolate-doom
-chocolate-server
-*.exe
+tags
+TAGS
+
diff --git a/src/heretic/Makefile.am b/src/heretic/Makefile.am
index 2a2f8245..e56ee806 100644
--- a/src/heretic/Makefile.am
+++ b/src/heretic/Makefile.am
@@ -20,6 +20,7 @@ info.c info.h \
in_lude.c \
m_random.c m_random.h \
mn_menu.c \
+ p_action.h \
p_ceilng.c \
p_doors.c \
p_enemy.c \
@@ -55,5 +56,15 @@ EXTRA_DIST= \
i_sound.c \
i_ibm.c
-libheretic_a_SOURCES=$(SOURCE_FILES)
+FEATURE_DEHACKED_SOURCE_FILES = \
+deh_ammo.c \
+deh_frame.c \
+deh_htext.c \
+deh_htic.c \
+deh_sound.c \
+deh_thing.c \
+deh_weapon.c
+
+libheretic_a_SOURCES=$(SOURCE_FILES) \
+ $(FEATURE_DEHACKED_SOURCE_FILES)
diff --git a/src/heretic/am_map.c b/src/heretic/am_map.c
index 99a8e206..aae30cc9 100644
--- a/src/heretic/am_map.c
+++ b/src/heretic/am_map.c
@@ -27,6 +27,7 @@
#include <stdio.h>
#include "doomdef.h"
+#include "deh_str.h"
#include "i_video.h"
#include "m_controls.h"
#include "p_local.h"
@@ -408,7 +409,7 @@ void AM_loadPics(void)
sprintf(namebuf, "AMMNUM%d", i);
marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
}*/
- maplump = W_CacheLumpName("AUTOPAGE", PU_STATIC);
+ maplump = W_CacheLumpName(DEH_String("AUTOPAGE"), PU_STATIC);
}
/*void AM_unloadPics(void)
@@ -1473,6 +1474,7 @@ void AM_drawCrosshair(int color)
void AM_Drawer(void)
{
+ char *level_name;
int numepisodes;
if (!automapactive)
@@ -1505,7 +1507,8 @@ void AM_Drawer(void)
if (gameepisode <= numepisodes && gamemap < 10)
{
- MN_DrTextA(LevelNames[(gameepisode - 1) * 9 + gamemap - 1], 20, 145);
+ level_name = LevelNames[(gameepisode - 1) * 9 + gamemap - 1];
+ MN_DrTextA(DEH_String(level_name), 20, 145);
}
// I_Update();
// V_MarkRect(f_x, f_y, f_w, f_h);
diff --git a/src/heretic/ct_chat.c b/src/heretic/ct_chat.c
index 75fa6035..8059b74d 100644
--- a/src/heretic/ct_chat.c
+++ b/src/heretic/ct_chat.c
@@ -29,6 +29,7 @@
#include <ctype.h>
#include "doomdef.h"
#include "doomkeys.h"
+#include "deh_str.h"
#include "p_local.h"
#include "s_sound.h"
#include "v_video.h"
@@ -115,7 +116,7 @@ void CT_Init(void)
memset(plr_lastmsg[i], 0, MESSAGESIZE);
memset(chat_msg[i], 0, MESSAGESIZE);
}
- FontABaseLump = W_GetNumForName("FONTA_S") + 1;
+ FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
return;
}
@@ -300,7 +301,9 @@ void CT_Ticker(void)
CT_AddChar(i, 0); // set the end of message character
if (numplayers > 2)
{
- strcpy(plr_lastmsg[i], CT_FromPlrText[i]);
+ strncpy(plr_lastmsg[i], DEH_String(CT_FromPlrText[i]),
+ MESSAGESIZE + 9);
+ plr_lastmsg[i][MESSAGESIZE + 8] = '\0';
strcat(plr_lastmsg[i], chat_msg[i]);
}
else
@@ -320,13 +323,13 @@ void CT_Ticker(void)
if (numplayers > 1)
{
P_SetMessage(&players[consoleplayer],
- "-MESSAGE SENT-", true);
+ DEH_String("-MESSAGE SENT-"), true);
S_StartSound(NULL, sfx_chat);
}
else
{
P_SetMessage(&players[consoleplayer],
- "THERE ARE NO OTHER PLAYERS IN THE GAME!",
+ DEH_String("THERE ARE NO OTHER PLAYERS IN THE GAME!"),
true);
S_StartSound(NULL, sfx_chat);
}
@@ -376,7 +379,7 @@ void CT_Drawer(void)
x += patch->width;
}
}
- V_DrawPatch(x, 10, W_CacheLumpName("FONTA59", PU_CACHE));
+ V_DrawPatch(x, 10, W_CacheLumpName(DEH_String("FONTA59"), PU_CACHE));
BorderTopRefresh = true;
UpdateState |= I_MESSAGES;
}
diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c
index 10f5fd3e..169a86ea 100644
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -33,6 +33,7 @@
#include "config.h"
#include "ct_chat.h"
#include "doomdef.h"
+#include "deh_main.h"
#include "d_iwad.h"
#include "i_endoom.h"
#include "i_joystick.h"
@@ -45,6 +46,7 @@
#include "m_controls.h"
#include "p_local.h"
#include "s_sound.h"
+#include "w_main.h"
#include "v_video.h"
#define STARTUP_WINDOW_X 17
@@ -184,12 +186,12 @@ void D_Display(void)
{
if (!netgame)
{
- V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName("PAUSED",
+ V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName(DEH_String("PAUSED"),
PU_CACHE));
}
else
{
- V_DrawPatch(160, 70, W_CacheLumpName("PAUSED", PU_CACHE));
+ V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE));
}
}
// Handle player messages
@@ -315,7 +317,7 @@ void D_PageDrawer(void)
V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
if (demosequence == 1)
{
- V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE));
+ V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE));
}
UpdateState |= I_FULLSCRN;
}
@@ -347,45 +349,45 @@ void D_DoAdvanceDemo(void)
case 0:
pagetic = 210;
gamestate = GS_DEMOSCREEN;
- pagename = "TITLE";
+ pagename = DEH_String("TITLE");
S_StartSong(mus_titl, false);
break;
case 1:
pagetic = 140;
gamestate = GS_DEMOSCREEN;
- pagename = "TITLE";
+ pagename = DEH_String("TITLE");
break;
case 2:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
- G_DeferedPlayDemo("demo1");
+ G_DeferedPlayDemo(DEH_String("demo1"));
break;
case 3:
pagetic = 200;
gamestate = GS_DEMOSCREEN;
- pagename = "CREDIT";
+ pagename = DEH_String("CREDIT");
break;
case 4:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
- G_DeferedPlayDemo("demo2");
+ G_DeferedPlayDemo(DEH_String("demo2"));
break;
case 5:
pagetic = 200;
gamestate = GS_DEMOSCREEN;
if (gamemode == shareware)
{
- pagename = "ORDER";
+ pagename = DEH_String("ORDER");
}
else
{
- pagename = "CREDIT";
+ pagename = DEH_String("CREDIT");
}
break;
case 6:
BorderNeedRefresh = true;
UpdateState |= I_FULLSCRN;
- G_DeferedPlayDemo("demo3");
+ G_DeferedPlayDemo(DEH_String("demo3"));
break;
}
}
@@ -638,7 +640,7 @@ void initStartup(void)
// Blit main screen
textScreen = TXT_GetScreenData();
- loading = W_CacheLumpName("LOADING", PU_CACHE);
+ loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE);
memcpy(textScreen, loading, 4000);
// Print version string
@@ -698,7 +700,7 @@ void tprintf(char *msg, int initflag)
// haleyjd: moved up, removed WATCOMC code
void CleanExit(void)
{
- printf("Exited from HERETIC.\n");
+ DEH_printf("Exited from HERETIC.\n");
exit(1);
}
@@ -746,6 +748,7 @@ void D_BindVariables(void)
M_BindBaseControls();
M_BindHereticControls();
M_BindWeaponControls();
+ M_BindChatControls(MAXPLAYERS);
M_BindMenuControls();
M_BindMapControls();
@@ -782,7 +785,7 @@ static void D_Endoom(void)
return;
}
- endoom_data = W_CacheLumpName("ENDTEXT", PU_STATIC);
+ endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC);
I_Endoom(endoom_data);
}
@@ -847,7 +850,7 @@ void D_DoomMain(void)
//
// init subsystems
//
- printf("V_Init: allocate screens.\n");
+ DEH_printf("V_Init: allocate screens.\n");
V_Init();
// Check for -CDROM
@@ -872,7 +875,7 @@ void D_DoomMain(void)
if (cdrom)
{
- M_SetConfigDir("c:\\heretic.cd\\");
+ M_SetConfigDir(DEH_String("c:\\heretic.cd"));
}
else
{
@@ -880,17 +883,22 @@ void D_DoomMain(void)
}
// Load defaults before initing other systems
- printf("M_LoadDefaults: Load system defaults.\n");
+ DEH_printf("M_LoadDefaults: Load system defaults.\n");
D_BindVariables();
M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg");
M_LoadDefaults();
I_AtExit(M_SaveDefaults, false);
- printf("Z_Init: Init zone memory allocation daemon.\n");
+ DEH_printf("Z_Init: Init zone memory allocation daemon.\n");
Z_Init();
- printf("W_Init: Init WADfiles.\n");
+#ifdef FEATURE_DEHACKED
+ printf("DEH_Init: Init Dehacked support.\n");
+ DEH_Init();
+#endif
+
+ DEH_printf("W_Init: Init WADfiles.\n");
iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission);
@@ -901,24 +909,7 @@ void D_DoomMain(void)
}
D_AddFile(iwadfile);
-
- // -FILE [filename] [filename] ...
- // Add files to the wad list.
- p = M_CheckParm("-file");
-
- if (p)
- {
- char *filename;
-
- // the parms after p are wadfile/lump names, until end of parms
- // or another - preceded parm
-
- while (++p != myargc && myargv[p][0] != '-')
- {
- filename = D_FindWADByName(myargv[p]);
- D_AddFile(filename);
- }
- }
+ W_ParseCommandLine();
p = M_CheckParm("-playdemo");
if (!p)
@@ -927,12 +918,12 @@ void D_DoomMain(void)
}
if (p && p < myargc - 1)
{
- sprintf(file, "%s.lmp", myargv[p + 1]);
+ DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]);
D_AddFile(file);
- printf("Playing demo %s.lmp.\n", myargv[p + 1]);
+ DEH_printf("Playing demo %s.lmp.\n", myargv[p + 1]);
}
- if (W_CheckNumForName("E2M1") == -1)
+ if (W_CheckNumForName(DEH_String("E2M1")) == -1)
{
gamemode = shareware;
gamedescription = "Heretic (shareware)";
@@ -960,54 +951,55 @@ void D_DoomMain(void)
//
smsg[0] = 0;
if (deathmatch)
- status("DeathMatch...");
+ status(DEH_String("DeathMatch..."));
if (nomonsters)
- status("No Monsters...");
+ status(DEH_String("No Monsters..."));
if (respawnparm)
- status("Respawning...");
+ status(DEH_String("Respawning..."));
if (autostart)
{
char temp[64];
- sprintf(temp, "Warp to Episode %d, Map %d, Skill %d ",
- startepisode, startmap, startskill + 1);
+ DEH_snprintf(temp, sizeof(temp),
+ "Warp to Episode %d, Map %d, Skill %d ",
+ startepisode, startmap, startskill + 1);
status(temp);
}
wadprintf(); // print the added wadfiles
- tprintf("MN_Init: Init menu system.\n", 1);
+ tprintf(DEH_String("MN_Init: Init menu system.\n"), 1);
MN_Init();
CT_Init();
- tprintf("R_Init: Init Heretic refresh daemon.", 1);
- hprintf("Loading graphics");
+ tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1);
+ hprintf(DEH_String("Loading graphics"));
R_Init();
tprintf("\n", 0);
- tprintf("P_Init: Init Playloop state.\n", 1);
- hprintf("Init game engine.");
+ tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1);
+ hprintf(DEH_String("Init game engine."));
P_Init();
IncThermo();
- tprintf("I_Init: Setting up machine state.\n", 1);
+ tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1);
I_CheckIsScreensaver();
I_InitTimer();
I_InitJoystick();
IncThermo();
- tprintf("S_Init: Setting up sound.\n", 1);
+ tprintf(DEH_String("S_Init: Setting up sound.\n"), 1);
S_Init();
//IO_StartupTimer();
S_Start();
- tprintf("D_CheckNetGame: Checking network game status.\n", 1);
- hprintf("Checking network game status.");
+ tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1);
+ hprintf(DEH_String("Checking network game status."));
D_CheckNetGame();
IncThermo();
// haleyjd: removed WATCOMC
- tprintf("SB_Init: Loading patches.\n", 1);
+ tprintf(DEH_String("SB_Init: Loading patches.\n"), 1);
SB_Init();
IncThermo();
diff --git a/src/heretic/deh_ammo.c b/src/heretic/deh_ammo.c
new file mode 100644
index 00000000..fe86c757
--- /dev/null
+++ b/src/heretic/deh_ammo.c
@@ -0,0 +1,122 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Ammo" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomdef.h"
+#include "doomtype.h"
+#include "deh_defs.h"
+#include "deh_io.h"
+#include "deh_main.h"
+#include "p_local.h"
+
+static void *DEH_AmmoStart(deh_context_t *context, char *line)
+{
+ int ammo_number = 0;
+
+ if (sscanf(line, "Ammo %i", &ammo_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ if (ammo_number < 0 || ammo_number >= NUMAMMO)
+ {
+ DEH_Warning(context, "Invalid ammo number: %i", ammo_number);
+ return NULL;
+ }
+
+ return &maxammo[ammo_number];
+}
+
+static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag)
+{
+ char *variable_name, *value;
+ int ivalue;
+ int ammo_number;
+
+ if (tag == NULL)
+ return;
+
+ ammo_number = ((int *) tag) - maxammo;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ ivalue = atoi(value);
+
+ if (!strcasecmp(variable_name, "Per ammo"))
+ {
+ // Heretic doesn't have a "per clip" ammo array, instead
+ // it is per weapon. However, the weapon number lines
+ // up with the ammo number if we add one.
+
+ GetWeaponAmmo[ammo_number + 1] = ivalue;
+ }
+ else if (!strcasecmp(variable_name, "Max ammo"))
+ {
+ maxammo[ammo_number] = ivalue;
+ }
+ else
+ {
+ DEH_Warning(context, "Field named '%s' not found", variable_name);
+ }
+}
+
+static void DEH_AmmoMD5Hash(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMAMMO; ++i)
+ {
+ MD5_UpdateInt32(context, maxammo[i]);
+ }
+
+ for (i=0; i<NUMWEAPONS; ++i)
+ {
+ MD5_UpdateInt32(context, GetWeaponAmmo[i]);
+ }
+}
+
+deh_section_t deh_section_ammo =
+{
+ "Ammo",
+ NULL,
+ DEH_AmmoStart,
+ DEH_AmmoParseLine,
+ NULL,
+ DEH_AmmoMD5Hash,
+};
+
diff --git a/src/heretic/deh_frame.c b/src/heretic/deh_frame.c
new file mode 100644
index 00000000..8623ab0c
--- /dev/null
+++ b/src/heretic/deh_frame.c
@@ -0,0 +1,344 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Frame" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "doomtype.h"
+#include "info.h"
+
+#include "deh_defs.h"
+#include "deh_io.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+#include "deh_htic.h"
+
+#include "p_action.h"
+
+typedef struct
+{
+ int offsets[deh_hhe_num_versions];
+ void (*func)();
+} hhe_action_pointer_t;
+
+// Offsets of action pointers within the Heretic executables.
+// Different versions have different offsets.
+// (Seriously Greg, was this really necessary? What was wrong with the
+// "copying action pointer from another frame" technique used in dehacked?)
+
+// Offset Action function
+// v1.0 v1.2 v1.3
+
+static const hhe_action_pointer_t action_pointers[] =
+{
+ { { 77680, 80144, 80208 }, A_AccTeleGlitter },
+ { { 78608, 81104, 81168 }, A_AddPlayerCorpse },
+ { { 115808, 118000, 118240 }, A_AddPlayerRain },
+ { { 112272, 114480, 114720 }, A_BeakAttackPL1 },
+ { { 112448, 114656, 114896 }, A_BeakAttackPL2 },
+ { { 111856, 114176, 114416 }, A_BeakRaise },
+ { { 111568, 113888, 114128 }, A_BeakReady },
+ { { 74640, 77120, 77184 }, A_BeastAttack },
+ { { 70480, 72992, 73056 }, A_BeastPuff },
+ { { 73120, 75600, 75664 }, A_BlueSpark },
+ { { 115456, 117648, 117888 }, A_BoltSpark },
+ { { 77344, 79808, 79872 }, A_BossDeath },
+ { { 69328, 71856, 71920 }, A_Chase },
+ { { 0, 80976, 81040 }, A_CheckBurnGone },
+ { { 78480, 80944, 81008 }, A_CheckSkullDone },
+ { { 78448, 80912, 80976 }, A_CheckSkullFloor },
+ { { 71376, 73888, 73952 }, A_ChicAttack },
+ { { 71488, 74000, 74064 }, A_ChicChase },
+ { { 71456, 73968, 74032 }, A_ChicLook },
+ { { 71520, 74032, 74096 }, A_ChicPain },
+ { { 75792, 78208, 78272 }, A_ClinkAttack },
+ { { 108432, 110816, 111056 }, A_ContMobjSound },
+ { { 114752, 116944, 117184 }, A_DeathBallImpact },
+ { { 70016, 72528, 72592 }, A_DripBlood },
+ { { 77472, 79936, 80000 }, A_ESound },
+ { { 76784, 79248, 79312 }, A_Explode },
+ { { 69872, 72400, 72464 }, A_FaceTarget },
+ { { 71568, 74080, 74144 }, A_Feathers },
+ { { 112928, 115136, 115376 }, A_FireBlasterPL1 },
+ { { 113072, 115280, 115520 }, A_FireBlasterPL2 },
+ { { 115232, 117424, 117664 }, A_FireCrossbowPL1 },
+ { { 115312, 117504, 117744 }, A_FireCrossbowPL2 },
+ { { 113152, 115360, 115600 }, A_FireGoldWandPL1 },
+ { { 113296, 115504, 115744 }, A_FireGoldWandPL2 },
+ { { 113760, 115968, 116208 }, A_FireMacePL1 },
+ { { 114624, 116816, 117056 }, A_FireMacePL2 },
+ { { 116368, 118544, 118784 }, A_FirePhoenixPL1 },
+ { { 116736, 118896, 119136 }, A_FirePhoenixPL2 },
+ { { 115568, 117760, 118000 }, A_FireSkullRodPL1 },
+ { { 115648, 117840, 118080 }, A_FireSkullRodPL2 },
+ { { 117120, 119280, 119520 }, A_FlameEnd },
+ { { 78704, 81200, 81264 }, A_FlameSnd },
+ { { 117152, 119312, 119552 }, A_FloatPuff },
+ { { 78512, 81008, 81072 }, A_FreeTargMobj },
+ { { 117184, 119344, 119584 }, A_GauntletAttack },
+ { { 73232, 75712, 75776 }, A_GenWizard },
+ { { 75872, 78304, 78368 }, A_GhostOff },
+ { { 74752, 77232, 77296 }, A_HeadAttack },
+ { { 75488, 77984, 78048 }, A_HeadFireGrow },
+ { { 75328, 77824, 77888 }, A_HeadIceImpact },
+ { { 116336, 118512, 118752 }, A_HideInCeiling },
+ { { 78736, 81232, 81296 }, A_HideThing },
+ { { 70976, 73488, 73552 }, A_ImpDeath },
+ { { 70304, 72816, 72880 }, A_ImpExplode },
+ { { 70592, 73104, 73168 }, A_ImpMeAttack },
+ { { 70672, 73184, 73248 }, A_ImpMsAttack },
+ { { 70880, 73392, 73456 }, A_ImpMsAttack2 },
+ { { 71024, 73536, 73600 }, A_ImpXDeath1 },
+ { { 71072, 73584, 73648 }, A_ImpXDeath2 },
+ { { 77728, 80192, 80256 }, A_InitKeyGizmo },
+ { { 116720, 118880, 119120 }, A_InitPhoenixPL2 },
+ { { 70160, 72672, 72736 }, A_KnightAttack },
+ { { 117648, 119824, 120064 }, A_Light0 },
+ { { 69200, 71728, 71792 }, A_Look },
+ { { 111760, 114080, 114320 }, A_Lower },
+ { { 114032, 116224, 116464 }, A_MaceBallImpact },
+ { { 114192, 116384, 116624 }, A_MaceBallImpact2 },
+ { { 113904, 116112, 116352 }, A_MacePL1Check },
+ { { 77104, 79568, 79632 }, A_MakePod },
+ { { 73648, 76128, 76192 }, A_MinotaurAtk1 },
+ { { 74112, 76592, 76656 }, A_MinotaurAtk2 },
+ { { 74352, 76832, 76896 }, A_MinotaurAtk3 },
+ { { 74032, 76512, 76576 }, A_MinotaurCharge },
+ { { 73760, 76240, 76304 }, A_MinotaurDecide },
+ { { 74528, 77008, 77072 }, A_MntrFloorFire },
+ { { 71808, 74288, 74352 }, A_MummyAttack },
+ { { 71920, 74400, 74464 }, A_MummyAttack2 },
+ { { 72016, 74496, 74560 }, A_MummyFX1Seek },
+ { { 72048, 74528, 74592 }, A_MummySoul },
+ { { 76400, 78832, 78896 }, A_NoBlocking },
+ { { 69984, 72496, 72560 }, A_Pain },
+ { { 116496, 118656, 118896 }, A_PhoenixPuff },
+ { { 76896, 79360, 79424 }, A_PodPain },
+ { { 116272, 118448, 118688 }, A_RainImpact },
+ { { 111920, 114240, 114480 }, A_Raise },
+ { { 111696, 114016, 114256 }, A_ReFire },
+ { { 77056, 79520, 79584 }, A_RemovePod },
+ { { 116480, 0, 0 }, A_RemovedPhoenixFunc },
+ { { 81952, 84464, 84528 }, A_RestoreArtifact },
+ { { 82048, 84544, 84608 }, A_RestoreSpecialThing1 },
+ { { 82128, 84592, 84656 }, A_RestoreSpecialThing2 },
+ { { 76144, 78576, 78640 }, A_Scream },
+ { { 117104, 119264, 119504 }, A_ShutdownPhoenixPL2 },
+ { { 78288, 80752, 80816 }, A_SkullPop },
+ { { 115776, 117968, 118208 }, A_SkullRodPL2Seek },
+ { { 115984, 118176, 118416 }, A_SkullRodStorm },
+ { { 75632, 78048, 78112 }, A_SnakeAttack },
+ { { 75712, 78128, 78192 }, A_SnakeAttack2 },
+ { { 72144, 74624, 74688 }, A_Sor1Chase },
+ { { 72096, 74576, 74640 }, A_Sor1Pain },
+ { { 73392, 75872, 75936 }, A_Sor2DthInit },
+ { { 73424, 75904, 75968 }, A_Sor2DthLoop },
+ { { 73584, 76064, 76128 }, A_SorDBon },
+ { { 73552, 76032, 76096 }, A_SorDExp },
+ { { 73520, 76000, 76064 }, A_SorDSph },
+ { { 73488, 75968, 76032 }, A_SorRise },
+ { { 73616, 76096, 76160 }, A_SorSightSnd },
+ { { 73456, 75936, 76000 }, A_SorZap },
+ { { 72480, 74960, 75024 }, A_SorcererRise },
+ { { 115088, 117280, 117520 }, A_SpawnRippers },
+ { { 77520, 79984, 80048 }, A_SpawnTeleGlitter },
+ { { 77600, 80064, 80128 }, A_SpawnTeleGlitter2 },
+ { { 72192, 74672, 74736 }, A_Srcr1Attack },
+ { { 72896, 75376, 75440 }, A_Srcr2Attack },
+ { { 72816, 75296, 75360 }, A_Srcr2Decide },
+ { { 112640, 114848, 115088 }, A_StaffAttackPL1 },
+ { { 112784, 114992, 115232 }, A_StaffAttackPL2 },
+ { { 78752, 81248, 81312 }, A_UnHideThing },
+ { { 78080, 80544, 80608 }, A_VolcBallImpact },
+ { { 77856, 80320, 80384 }, A_VolcanoBlast },
+ { { 77824, 80288, 80352 }, A_VolcanoSet },
+ { { 111168, 113488, 113728 }, A_WeaponReady },
+ { { 75168, 77664, 77728 }, A_WhirlwindSeek },
+ { { 75888, 78320, 78384 }, A_WizAtk1 },
+ { { 75920, 78352, 78416 }, A_WizAtk2 },
+ { { 75952, 78384, 78448 }, A_WizAtk3 },
+};
+
+DEH_BEGIN_MAPPING(state_mapping, state_t)
+ DEH_MAPPING("Sprite number", sprite)
+ DEH_MAPPING("Sprite subnumber", frame)
+ DEH_MAPPING("Duration", tics)
+ DEH_MAPPING("Next frame", nextstate)
+ DEH_MAPPING("Unknown 1", misc1)
+ DEH_MAPPING("Unknown 2", misc2)
+DEH_END_MAPPING
+
+static void DEH_FrameInit(void)
+{
+ // Bit of a hack here:
+ DEH_HereticInit();
+}
+
+static void *DEH_FrameStart(deh_context_t *context, char *line)
+{
+ int frame_number = 0;
+ int mapped_frame_number;
+ state_t *state;
+
+ if (sscanf(line, "Frame %i", &frame_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ // Map the HHE frame number (which assumes a Heretic 1.0 state table)
+ // to the internal frame number (which is is the Heretic 1.3 state table):
+
+ mapped_frame_number = DEH_MapHereticFrameNumber(frame_number);
+
+ if (mapped_frame_number < 0 || mapped_frame_number >= DEH_HERETIC_NUMSTATES)
+ {
+ DEH_Warning(context, "Invalid frame number: %i", frame_number);
+ return NULL;
+ }
+
+ state = &states[mapped_frame_number];
+
+ return state;
+}
+
+static boolean GetActionPointerForOffset(int offset, void **result)
+{
+ int i;
+
+ // Special case.
+
+ if (offset == 0)
+ {
+ *result = NULL;
+ return true;
+ }
+
+ for (i=0; i<arrlen(action_pointers); ++i)
+ {
+ if (action_pointers[i].offsets[deh_hhe_version] == offset)
+ {
+ *result = action_pointers[i].func;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// If an invalid action pointer is specified, the patch may be for a
+// different version from the version we are currently set to. Try to
+// suggest a different version to use.
+
+static void SuggestOtherVersions(unsigned int offset)
+{
+ unsigned int i, v;
+
+ for (i=0; i<arrlen(action_pointers); ++i)
+ {
+ for (v=0; v<deh_hhe_num_versions; ++v)
+ {
+ if (action_pointers[i].offsets[v] == offset)
+ {
+ DEH_SuggestHereticVersion(v);
+ }
+ }
+ }
+}
+
+static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag)
+{
+ state_t *state;
+ char *variable_name, *value;
+ int ivalue;
+
+ if (tag == NULL)
+ return;
+
+ state = (state_t *) tag;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ // all values are integers
+
+ ivalue = atoi(value);
+
+ // Action pointer field is a special case:
+
+ if (!strcasecmp(variable_name, "Action pointer"))
+ {
+ void *func;
+
+ if (!GetActionPointerForOffset(ivalue, &func))
+ {
+ SuggestOtherVersions(ivalue);
+ DEH_Error(context, "Unknown action pointer: %i", ivalue);
+ return;
+ }
+
+ state->action = func;
+ }
+ else
+ {
+ // "Next frame" numbers need to undergo mapping.
+
+ if (!strcasecmp(variable_name, "Next frame"))
+ {
+ ivalue = DEH_MapHereticFrameNumber(ivalue);
+ }
+
+ DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue);
+ }
+}
+
+static void DEH_FrameMD5Sum(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMSTATES; ++i)
+ {
+ DEH_StructMD5Sum(context, &state_mapping, &states[i]);
+ }
+}
+
+deh_section_t deh_section_frame =
+{
+ "Frame",
+ DEH_FrameInit,
+ DEH_FrameStart,
+ DEH_FrameParseLine,
+ NULL,
+ DEH_FrameMD5Sum,
+};
+
diff --git a/src/heretic/deh_htext.c b/src/heretic/deh_htext.c
new file mode 100644
index 00000000..5dfddac3
--- /dev/null
+++ b/src/heretic/deh_htext.c
@@ -0,0 +1,856 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2010 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses Text substitution sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+
+#include "doomtype.h"
+#include "dstrings.h"
+
+#include "z_zone.h"
+
+#include "deh_defs.h"
+#include "deh_io.h"
+#include "deh_htic.h"
+#include "deh_main.h"
+
+//
+// Ok, Greg, the action pointers thing was bad enough, but this really
+// takes the biscuit. Why does HHE's text replacement address strings
+// by offset??!! The dehacked way was much nicer, why change it?
+//
+
+typedef struct
+{
+ unsigned int offsets[deh_hhe_num_versions];
+ char *string;
+} hhe_string_t;
+
+// Offsets String
+// v1.0 v1.2 v1.3
+
+static const hhe_string_t strings[] =
+{
+ { { 228, 228, 228 }, "PLAYPAL" },
+ { { 1240, 1252, 1252 }, "E1M1: THE DOCKS" },
+ { { 1260, 1272, 1272 }, "E1M2: THE DUNGEONS" },
+ { { 1280, 1292, 1292 }, "E1M3: THE GATEHOUSE" },
+ { { 1304, 1316, 1316 }, "E1M4: THE GUARD TOWER" },
+ { { 1328, 1340, 1340 }, "E1M5: THE CITADEL" },
+ { { 1348, 1360, 1360 }, "E1M6: THE CATHEDRAL" },
+ { { 1372, 1384, 1384 }, "E1M7: THE CRYPTS" },
+ { { 1392, 1404, 1404 }, "E1M8: HELL'S MAW" },
+ { { 1412, 1424, 1424 }, "E1M9: THE GRAVEYARD" },
+ { { 1436, 1448, 1448 }, "E2M1: THE CRATER" },
+ { { 1456, 1468, 1468 }, "E2M2: THE LAVA PITS" },
+ { { 1480, 1492, 1492 }, "E2M3: THE RIVER OF FIRE" },
+ { { 1508, 1520, 1520 }, "E2M4: THE ICE GROTTO" },
+ { { 1532, 1544, 1544 }, "E2M5: THE CATACOMBS" },
+ { { 1556, 1568, 1568 }, "E2M6: THE LABYRINTH" },
+ { { 1580, 1592, 1592 }, "E2M7: THE GREAT HALL" },
+ { { 1604, 1616, 1616 }, "E2M8: THE PORTALS OF CHAOS" },
+ { { 1632, 1644, 1644 }, "E2M9: THE GLACIER" },
+ { { 1652, 1664, 1664 }, "E3M1: THE STOREHOUSE" },
+ { { 1676, 1688, 1688 }, "E3M2: THE CESSPOOL" },
+ { { 1696, 1708, 1708 }, "E3M3: THE CONFLUENCE" },
+ { { 1720, 1732, 1732 }, "E3M4: THE AZURE FORTRESS" },
+ { { 1748, 1760, 1760 }, "E3M5: THE OPHIDIAN LAIR" },
+ { { 1776, 1788, 1788 }, "E3M6: THE HALLS OF FEAR" },
+ { { 1804, 1816, 1816 }, "E3M7: THE CHASM" },
+ { { 1824, 1836, 1836 }, "E3M8: D'SPARIL'S KEEP" },
+ { { 1848, 1860, 1860 }, "E3M9: THE AQUIFER" },
+ { { 0, 1880, 1880 }, "E4M1: CATAFALQUE" },
+ { { 0, 1900, 1900 }, "E4M2: BLOCKHOUSE" },
+ { { 0, 1920, 1920 }, "E4M3: AMBULATORY" },
+ { { 0, 1940, 1940 }, "E4M4: SEPULCHER" },
+ { { 0, 1960, 1960 }, "E4M5: GREAT STAIR" },
+ { { 0, 1980, 1980 }, "E4M6: HALLS OF THE APOSTATE" },
+ { { 0, 2012, 2012 }, "E4M7: RAMPARTS OF PERDITION" },
+ { { 0, 2044, 2044 }, "E4M8: SHATTERED BRIDGE" },
+ { { 0, 2068, 2068 }, "E4M9: MAUSOLEUM" },
+ { { 0, 2088, 2088 }, "E5M1: OCHRE CLIFFS" },
+ { { 0, 2108, 2108 }, "E5M2: RAPIDS" },
+ { { 0, 2124, 2124 }, "E5M3: QUAY" },
+ { { 0, 2136, 2136 }, "E5M4: COURTYARD" },
+ { { 0, 2156, 2156 }, "E5M5: HYDRATYR" },
+ { { 0, 2172, 2172 }, "E5M6: COLONNADE" },
+ { { 0, 2192, 2192 }, "E5M7: FOETID MANSE" },
+ { { 0, 2212, 2212 }, "E5M8: FIELD OF JUDGEMENT" },
+ { { 0, 2240, 2240 }, "E5M9: SKEIN OF D'SPARIL" },
+ { { 1868, 2268, 2268 }, "AUTOPAGE" },
+ { { 1880, 2280, 2280 }, "FOLLOW MODE ON" },
+ { { 1896, 2296, 2296 }, "FOLLOW MODE OFF" },
+ { { 1924, 2324, 2324 }, "GREEN: " },
+ { { 1936, 2336, 2336 }, "YELLOW: " },
+ { { 1948, 2348, 2348 }, "RED: " },
+ { { 1956, 2356, 2356 }, "BLUE: " },
+ { { 1964, 2364, 2364 }, "FONTA_S" },
+ { { 1972, 2372, 2372 }, "-MESSAGE SENT-" },
+ { { 1988, 2388, 2388 }, "THERE ARE NO OTHER PLAYERS IN THE GAME!" },
+ { { 2028, 2428, 2428 }, "FONTA59" },
+ { { 2036, 2504, 2504 }, "PAUSED" },
+ { { 2072, 2540, 2540 }, "ADVISOR" },
+ { { 2080, 2548, 2548 }, "TITLE" },
+ { { 2088, 2556, 2556 }, "demo1" },
+ { { 2096, 2564, 2564 }, "CREDIT" },
+ { { 2104, 2572, 2572 }, "demo2" },
+ { { 2112, 2580, 2580 }, "ORDER" },
+ { { 2120, 2588, 2588 }, "demo3" },
+ { { 2304, 2696, 2696 }, "Exited from HERETIC.\n" },
+ { { 2412, 2800, 2800 }, "c:\\heretic.cd" },
+ { { 2528, 2916, 2916 }, "Playing demo %s.lmp.\n" },
+ { { 2592, 2980, 2980 }, "V_Init: allocate screens.\n" },
+ { { 2620, 3008, 3008 }, "M_LoadDefaults: Load system defaults.\n" },
+ { { 2660, 3048, 3048 }, "Z_Init: Init zone memory allocation daemon.\n" },
+ { { 2708, 3096, 3096 }, "W_Init: Init WADfiles.\n" },
+ { { 2732, 3120, 3120 }, "E2M1" },
+ { { 0, 3128, 3128 }, "EXTENDED" },
+ { { 2740, 3140, 3140 }, "LOADING" },
+ { { 2748, 3148, 3148 }, "DeathMatch..." },
+ { { 2764, 3164, 3164 }, "No Monsters..." },
+ { { 2780, 3180, 3180 }, "Respawning..." },
+ { { 2796, 3196, 3196 }, "Warp to Episode %d, Map %d, Skill %d " },
+ { { 2836, 3236, 3236 }, "MN_Init: Init menu system.\n" },
+ { { 2864, 3264, 3264 }, "R_Init: Init Heretic refresh daemon." },
+ { { 2904, 3304, 3304 }, "Loading graphics" },
+ { { 2924, 3324, 3324 }, "P_Init: Init Playloop state." },
+ { { 2956, 3356, 3356 }, "Init game engine." },
+ { { 2976, 3376, 3376 }, "I_Init: Setting up machine state.\n" },
+ { { 3012, 3412, 3412 }, "D_CheckNetGame: Checking network game status.\n" },
+ { { 3060, 3460, 3460 }, "Checking network game status." },
+ { { 3092, 3492, 3492 }, "SB_Init: Loading patches.\n" },
+ { { 0, 3752, 3752 }, "PLAYER 1 LEFT THE GAME" },
+ { { 3508, 3932, 3932 }, "Network game synchronization aborted." },
+ { { 0, 3972, 3972 }, "Different DOOM versions cannot play a net game!" },
+ { { 3908, 4132, 4132 }, "SKY1" },
+ { { 3916, 4140, 4140 }, "SKY2" },
+ { { 3924, 4148, 4148 }, "SKY3" },
+ { { 3736, 4196, 4196 }, "NET GAME" },
+ { { 3748, 4208, 4208 }, "SAVE GAME" },
+ { { 3760, 4220, 4220 }, "Only %i deathmatch spots, 4 required" },
+ { { 3800, 4260, 4260 }, "version %i" },
+ { { 3828, 4372, 4372 }, "c:\\heretic.cd\\hticsav%d.hsg" },
+ { { 3856, 4400, 4400 }, "hticsav%d.hsg" },
+ { { 3896, 4416, 4416 }, "GAME SAVED" },
+ { { 4016, 4456, 4456 }, E1TEXT },
+ { { 4536, 4976, 4976 }, E2TEXT },
+ { { 5068, 5508, 5508 }, E3TEXT },
+ { { 0, 6072, 6072 }, E4TEXT },
+ { { 0, 6780, 6780 }, E5TEXT },
+ { { 5632, 7468, 7468 }, "FLOOR25" },
+ { { 5640, 7476, 7476 }, "FLATHUH1" },
+ { { 5652, 7488, 7488 }, "FLTWAWA2" },
+ { { 0, 7500, 7500 }, "FLOOR28" },
+ { { 0, 7508, 7508 }, "FLOOR08" },
+ { { 5664, 7516, 7516 }, "FONTA_S" },
+ { { 5704, 7524, 7524 }, "PLAYPAL" },
+ { { 5672, 7532, 7532 }, "FINAL1" },
+ { { 5680, 7540, 7540 }, "FINAL2" },
+ { { 5688, 7548, 7548 }, "E2PAL" },
+ { { 5696, 7556, 7556 }, "E2END" },
+ { { 7884, 7564, 7564 }, "TITLE" },
+ { { 5712, 7572, 7572 }, "ORDER" },
+ { { 0, 7580, 7580 }, "CREDIT" },
+ { { 5720, 7588, 7588 }, "IMPX" },
+ { { 5728, 7596, 7596 }, "ACLO" },
+ { { 5736, 7604, 7604 }, "PTN1" },
+ { { 5744, 7612, 7612 }, "SHLD" },
+ { { 5752, 7620, 7620 }, "SHD2" },
+ { { 5760, 7628, 7628 }, "BAGH" },
+ { { 5768, 7636, 7636 }, "SPMP" },
+ { { 5776, 7644, 7644 }, "INVS" },
+ { { 5784, 7652, 7652 }, "PTN2" },
+ { { 5792, 7660, 7660 }, "SOAR" },
+ { { 5800, 7668, 7668 }, "INVU" },
+ { { 5808, 7676, 7676 }, "PWBK" },
+ { { 5816, 7684, 7684 }, "EGGC" },
+ { { 5824, 7692, 7692 }, "EGGM" },
+ { { 5832, 7700, 7700 }, "FX01" },
+ { { 5840, 7708, 7708 }, "SPHL" },
+ { { 5848, 7716, 7716 }, "TRCH" },
+ { { 5856, 7724, 7724 }, "FBMB" },
+ { { 5864, 7732, 7732 }, "XPL1" },
+ { { 5872, 7740, 7740 }, "ATLP" },
+ { { 5880, 7748, 7748 }, "PPOD" },
+ { { 5888, 7756, 7756 }, "AMG1" },
+ { { 5896, 7764, 7764 }, "SPSH" },
+ { { 5904, 7772, 7772 }, "LVAS" },
+ { { 5912, 7780, 7780 }, "SLDG" },
+ { { 5920, 7788, 7788 }, "SKH1" },
+ { { 5928, 7796, 7796 }, "SKH2" },
+ { { 5936, 7804, 7804 }, "SKH3" },
+ { { 5944, 7812, 7812 }, "SKH4" },
+ { { 5952, 7820, 7820 }, "CHDL" },
+ { { 5960, 7828, 7828 }, "SRTC" },
+ { { 5968, 7836, 7836 }, "SMPL" },
+ { { 5976, 7844, 7844 }, "STGS" },
+ { { 5984, 7852, 7852 }, "STGL" },
+ { { 5992, 7860, 7860 }, "STCS" },
+ { { 6000, 7868, 7868 }, "STCL" },
+ { { 6008, 7876, 7876 }, "KFR1" },
+ { { 6016, 7884, 7884 }, "BARL" },
+ { { 6024, 7892, 7892 }, "BRPL" },
+ { { 6032, 7900, 7900 }, "MOS1" },
+ { { 6040, 7908, 7908 }, "MOS2" },
+ { { 6048, 7916, 7916 }, "WTRH" },
+ { { 6056, 7924, 7924 }, "HCOR" },
+ { { 6064, 7932, 7932 }, "KGZ1" },
+ { { 6072, 7940, 7940 }, "KGZB" },
+ { { 6080, 7948, 7948 }, "KGZG" },
+ { { 6088, 7956, 7956 }, "KGZY" },
+ { { 6096, 7964, 7964 }, "VLCO" },
+ { { 6104, 7972, 7972 }, "VFBL" },
+ { { 6112, 7980, 7980 }, "VTFB" },
+ { { 6120, 7988, 7988 }, "SFFI" },
+ { { 6128, 7996, 7996 }, "TGLT" },
+ { { 6136, 8004, 8004 }, "TELE" },
+ { { 6144, 8012, 8012 }, "STFF" },
+ { { 6152, 8020, 8020 }, "PUF3" },
+ { { 6160, 8028, 8028 }, "PUF4" },
+ { { 6168, 8036, 8036 }, "BEAK" },
+ { { 6176, 8044, 8044 }, "WGNT" },
+ { { 6184, 8052, 8052 }, "GAUN" },
+ { { 6192, 8060, 8060 }, "PUF1" },
+ { { 6200, 8068, 8068 }, "WBLS" },
+ { { 6208, 8076, 8076 }, "BLSR" },
+ { { 6216, 8084, 8084 }, "FX18" },
+ { { 6224, 8092, 8092 }, "FX17" },
+ { { 6232, 8100, 8100 }, "WMCE" },
+ { { 6240, 8108, 8108 }, "MACE" },
+ { { 6248, 8116, 8116 }, "FX02" },
+ { { 6256, 8124, 8124 }, "WSKL" },
+ { { 6264, 8132, 8132 }, "HROD" },
+ { { 6272, 8140, 8140 }, "FX00" },
+ { { 6280, 8148, 8148 }, "FX20" },
+ { { 6288, 8156, 8156 }, "FX21" },
+ { { 6296, 8164, 8164 }, "FX22" },
+ { { 6304, 8172, 8172 }, "FX23" },
+ { { 6312, 8180, 8180 }, "GWND" },
+ { { 6320, 8188, 8188 }, "PUF2" },
+ { { 6328, 8196, 8196 }, "WPHX" },
+ { { 6336, 8204, 8204 }, "PHNX" },
+ { { 6344, 8212, 8212 }, "FX04" },
+ { { 6352, 8220, 8220 }, "FX08" },
+ { { 6360, 8228, 8228 }, "FX09" },
+ { { 6368, 8236, 8236 }, "WBOW" },
+ { { 6376, 8244, 8244 }, "CRBW" },
+ { { 6384, 8252, 8252 }, "FX03" },
+ { { 6392, 8260, 8260 }, "BLOD" },
+ { { 6400, 8268, 8268 }, "PLAY" },
+ { { 6408, 8276, 8276 }, "FDTH" },
+ { { 6416, 8284, 8284 }, "BSKL" },
+ { { 6424, 8292, 8292 }, "CHKN" },
+ { { 6432, 8300, 8300 }, "MUMM" },
+ { { 6440, 8308, 8308 }, "FX15" },
+ { { 6448, 8316, 8316 }, "BEAS" },
+ { { 6456, 8324, 8324 }, "FRB1" },
+ { { 6464, 8332, 8332 }, "SNKE" },
+ { { 6472, 8340, 8340 }, "SNFX" },
+ { { 6480, 8348, 8348 }, "HEAD" },
+ { { 6488, 8356, 8356 }, "FX05" },
+ { { 6496, 8364, 8364 }, "FX06" },
+ { { 6504, 8372, 8372 }, "FX07" },
+ { { 6512, 8380, 8380 }, "CLNK" },
+ { { 6520, 8388, 8388 }, "WZRD" },
+ { { 6528, 8396, 8396 }, "FX11" },
+ { { 6536, 8404, 8404 }, "FX10" },
+ { { 6544, 8412, 8412 }, "KNIG" },
+ { { 6552, 8420, 8420 }, "SPAX" },
+ { { 6560, 8428, 8428 }, "RAXE" },
+ { { 6568, 8436, 8436 }, "SRCR" },
+ { { 6576, 8444, 8444 }, "FX14" },
+ { { 6584, 8452, 8452 }, "SOR2" },
+ { { 6592, 8460, 8460 }, "SDTH" },
+ { { 6600, 8468, 8468 }, "FX16" },
+ { { 6608, 8476, 8476 }, "MNTR" },
+ { { 6616, 8484, 8484 }, "FX12" },
+ { { 6624, 8492, 8492 }, "FX13" },
+ { { 6632, 8500, 8500 }, "AKYY" },
+ { { 6640, 8508, 8508 }, "BKYY" },
+ { { 6648, 8516, 8516 }, "CKYY" },
+ { { 6656, 8524, 8524 }, "AMG2" },
+ { { 6664, 8532, 8532 }, "AMM1" },
+ { { 6672, 8540, 8540 }, "AMM2" },
+ { { 6680, 8548, 8548 }, "AMC1" },
+ { { 6688, 8556, 8556 }, "AMC2" },
+ { { 6696, 8564, 8564 }, "AMS1" },
+ { { 6704, 8572, 8572 }, "AMS2" },
+ { { 6712, 8580, 8580 }, "AMP1" },
+ { { 6720, 8588, 8588 }, "AMP2" },
+ { { 6728, 8596, 8596 }, "AMB1" },
+ { { 6736, 8604, 8604 }, "AMB2" },
+ { { 6744, 8612, 8612 }, "K" },
+ { { 6748, 8616, 8616 }, "I" },
+ { { 6752, 8620, 8620 }, "L" },
+ { { 6756, 8624, 8624 }, "E" },
+ { { 6760, 8628, 8628 }, "R" },
+ { { 6764, 8632, 8632 }, "S" },
+ { { 6768, 8636, 8636 }, "PLAYPAL" },
+ { { 6776, 8644, 8644 }, "MAPE1" },
+ { { 6784, 8652, 8652 }, "MAPE2" },
+ { { 6792, 8660, 8660 }, "MAPE3" },
+ { { 6800, 8668, 8668 }, "IN_X" },
+ { { 6808, 8676, 8676 }, "IN_YAH" },
+ { { 6816, 8684, 8684 }, "FONTB16" },
+ { { 6824, 8692, 8692 }, "FONTB_S" },
+ { { 6832, 8700, 8700 }, "FONTB13" },
+ { { 6840, 8708, 8708 }, "FONTB15" },
+ { { 6848, 8716, 8716 }, "FONTB05" },
+ { { 6856, 8724, 8724 }, "FACEA0" },
+ { { 6864, 8732, 8732 }, "FACEB0" },
+ { { 6940, 8808, 8808 }, "FLOOR16" },
+ { { 6948, 8816, 8816 }, "FINISHED" },
+ { { 6960, 8828, 8828 }, "NOW ENTERING:" },
+ { { 6976, 8844, 8844 }, "KILLS" },
+ { { 6984, 8852, 8852 }, "ITEMS" },
+ { { 6992, 8860, 8860 }, "SECRETS" },
+ { { 7000, 8868, 8868 }, "TIME" },
+ { { 7008, 8876, 8876 }, "BONUS" },
+ { { 7016, 8884, 8884 }, "SECRET" },
+ { { 7024, 8892, 8892 }, "TOTAL" },
+ { { 7032, 8900, 8900 }, "VICTIMS" },
+ { { 7040, 8908, 8908 }, ":" },
+ { { 7044, 8912, 8912 }, "NEW GAME" },
+ { { 7056, 8924, 8924 }, "OPTIONS" },
+ { { 7064, 8932, 8932 }, "GAME FILES" },
+ { { 7076, 8944, 8944 }, "INFO" },
+ { { 7084, 8952, 8952 }, "QUIT GAME" },
+ { { 7096, 8964, 8964 }, "CITY OF THE DAMNED" },
+ { { 7116, 8984, 8984 }, "HELL'S MAW" },
+ { { 7128, 8996, 8996 }, "THE DOME OF D'SPARIL" },
+ { { 0, 9020, 9020 }, "THE OSSUARY" },
+ { { 0, 9032, 9032 }, "THE STAGNANT DEMESNE" },
+ { { 7152, 9056, 9056 }, "LOAD GAME" },
+ { { 7164, 9068, 9068 }, "SAVE GAME" },
+ { { 7176, 9080, 9080 }, "THOU NEEDETH A WET-NURSE" },
+ { { 7204, 9108, 9108 }, "YELLOWBELLIES-R-US" },
+ { { 7224, 9128, 9128 }, "BRINGEST THEM ONETH" },
+ { { 7244, 9148, 9148 }, "THOU ART A SMITE-MEISTER" },
+ { { 7272, 9176, 9176 }, "BLACK PLAGUE POSSESSES THEE" },
+ { { 7300, 9204, 9204 }, "END GAME" },
+ { { 7312, 9216, 9216 }, "MESSAGES : " },
+ { { 7324, 9228, 9228 }, "MOUSE SENSITIVITY" },
+ { { 7344, 9248, 9248 }, "MORE..." },
+ { { 7352, 9256, 9256 }, "SCREEN SIZE" },
+ { { 7364, 9268, 9268 }, "SFX VOLUME" },
+ { { 7376, 9280, 9280 }, "MUSIC VOLUME" },
+ { { 7416, 9296, 9296 }, "ARE YOU SURE YOU WANT TO QUIT?" },
+ { { 7448, 9328, 9328 }, "ARE YOU SURE YOU WANT TO END THE GAME?" },
+ { { 7488, 9368, 9368 }, "DO YOU WANT TO QUICKSAVE THE GAME NAMED" },
+ { { 7528, 9408, 9408 }, "DO YOU WANT TO QUICKLOAD THE GAME NAMED" },
+ { { 7392, 9448, 9448 }, "M_SKL00" },
+ { { 7400, 9456, 9456 }, "FONTA_S" },
+ { { 7408, 9464, 9464 }, "FONTB_S" },
+ { { 7568, 9472, 9472 }, "?" },
+ { { 7572, 9476, 9476 }, "M_SLCTR1" },
+ { { 7584, 9488, 9488 }, "M_SLCTR2" },
+ { { 7596, 9500, 9500 }, "M_HTIC" },
+ { { 7604, 9508, 9508 }, "c:\\heretic.cd\\hticsav%d.hsg" },
+ { { 7632, 9536, 9536 }, "hticsav%d.hsg" },
+ { { 7652, 9556, 9556 }, "M_FSLOT" },
+ { { 7660, 9564, 9564 }, "ON" },
+ { { 7664, 9568, 9568 }, "OFF" },
+ { { 0, 9572, 9572 }, "YOU CAN'T START A NEW GAME IN NETPLAY!" },
+ { { 0, 9612, 9612 }, "YOU CAN'T LOAD A GAME IN NETPLAY!" },
+ { { 7668, 9648, 9648 }, "MESSAGES ON" },
+ { { 7680, 9660, 9660 }, "MESSAGES OFF" },
+ { { 7748, 9676, 9676 }, "ONLY AVAILABLE IN THE REGISTERED VERSION" },
+ { { 7792, 9720, 9720 }, "PLAYPAL" },
+ { { 7800, 9728, 9728 }, "QUICKSAVING...." },
+ { { 7816, 9744, 9744 }, "QUICKLOADING...." },
+ { { 7836, 9764, 9764 }, "CHOOSE A QUICKSAVE SLOT" },
+ { { 7860, 9788, 9788 }, "CHOOSE A QUICKLOAD SLOT" },
+ { { 0, 9812, 9812 }, "TITLE" },
+ { { 7892, 9820, 9820 }, "M_SLDLT" },
+ { { 7900, 9828, 9828 }, "M_SLDMD1" },
+ { { 7912, 9840, 9840 }, "M_SLDMD2" },
+ { { 7924, 9852, 9852 }, "M_SLDRT" },
+ { { 7932, 9860, 9860 }, "M_SLDKB" },
+ { { 9016, 10944, 10944 }, "SCREEN SHOT" },
+ { { 9028, 10956, 10956 }, "YOU NEED A BLUE KEY TO OPEN THIS DOOR" },
+ { { 9068, 10996, 10996 }, "YOU NEED A YELLOW KEY TO OPEN THIS DOOR" },
+ { { 9108, 11036, 11036 }, "YOU NEED A GREEN KEY TO OPEN THIS DOOR" },
+ { { 9244, 11172, 11172 }, "CRYSTAL VIAL" },
+ { { 9260, 11188, 11188 }, "SILVER SHIELD" },
+ { { 9276, 11204, 11204 }, "ENCHANTED SHIELD" },
+ { { 9296, 11224, 11224 }, "BAG OF HOLDING" },
+ { { 9312, 11240, 11240 }, "MAP SCROLL" },
+ { { 9324, 11252, 11252 }, "BLUE KEY" },
+ { { 9336, 11264, 11264 }, "YELLOW KEY" },
+ { { 9348, 11276, 11276 }, "GREEN KEY" },
+ { { 9360, 11288, 11288 }, "QUARTZ FLASK" },
+ { { 9376, 11304, 11304 }, "WINGS OF WRATH" },
+ { { 9392, 11320, 11320 }, "RING OF INVINCIBILITY" },
+ { { 9416, 11344, 11344 }, "TOME OF POWER" },
+ { { 9432, 11360, 11360 }, "SHADOWSPHERE" },
+ { { 9448, 11376, 11376 }, "MORPH OVUM" },
+ { { 9460, 11388, 11388 }, "MYSTIC URN" },
+ { { 9472, 11400, 11400 }, "TORCH" },
+ { { 9480, 11408, 11408 }, "TIME BOMB OF THE ANCIENTS" },
+ { { 9508, 11436, 11436 }, "CHAOS DEVICE" },
+ { { 9524, 11452, 11452 }, "WAND CRYSTAL" },
+ { { 9540, 11468, 11468 }, "CRYSTAL GEODE" },
+ { { 9556, 11484, 11484 }, "MACE SPHERES" },
+ { { 9572, 11500, 11500 }, "PILE OF MACE SPHERES" },
+ { { 9596, 11524, 11524 }, "ETHEREAL ARROWS" },
+ { { 9612, 11540, 11540 }, "QUIVER OF ETHEREAL ARROWS" },
+ { { 9640, 11568, 11568 }, "CLAW ORB" },
+ { { 9652, 11580, 11580 }, "ENERGY ORB" },
+ { { 9664, 11592, 11592 }, "LESSER RUNES" },
+ { { 9680, 11608, 11608 }, "GREATER RUNES" },
+ { { 9696, 11624, 11624 }, "FLAME ORB" },
+ { { 9708, 11636, 11636 }, "INFERNO ORB" },
+ { { 9720, 11648, 11648 }, "FIREMACE" },
+ { { 9732, 11660, 11660 }, "ETHEREAL CROSSBOW" },
+ { { 9752, 11680, 11680 }, "DRAGON CLAW" },
+ { { 9764, 11692, 11692 }, "HELLSTAFF" },
+ { { 9776, 11704, 11704 }, "PHOENIX ROD" },
+ { { 9788, 11716, 11716 }, "GAUNTLETS OF THE NECROMANCER" },
+ { { 10088, 12016, 12016 }, "FLTWAWA1" },
+ { { 10100, 12028, 12028 }, "FLTFLWW1" },
+ { { 10112, 12040, 12040 }, "FLTLAVA1" },
+ { { 10124, 12052, 12052 }, "FLATHUH1" },
+ { { 10136, 12064, 12064 }, "FLTSLUD1" },
+ { { 10148, 12076, 12076 }, "END" },
+ { { 10236, 12164, 12164 }, "texture2" },
+ { { 10444, 12372, 12372 }, "PLAYPAL" },
+ { { 10596, 12488, 12488 }, "PNAMES" },
+ { { 10604, 12496, 12496 }, "TEXTURE1" },
+ { { 10616, 12508, 12508 }, "TEXTURE2" },
+ { { 10628, 12520, 12520 }, "S_END" },
+ { { 10636, 12528, 12528 }, "S_START" },
+ { { 10728, 12620, 12620 }, "F_START" },
+ { { 10736, 12628, 12628 }, "F_END" },
+ { { 10744, 12636, 12636 }, "COLORMAP" },
+ { { 10756, 12648, 12648 }, "\nR_InitTextures " },
+ { { 10776, 12668, 12668 }, "R_InitFlats\n" },
+ { { 10792, 12684, 12684 }, "R_InitSpriteLumps " },
+ { { 10948, 12772, 12772 }, "TINTTAB" },
+ { { 10984, 12780, 12780 }, "FLOOR04" },
+ { { 10992, 12788, 12788 }, "FLAT513" },
+ { { 11000, 12796, 12796 }, "bordt" },
+ { { 11008, 12804, 12804 }, "bordb" },
+ { { 11016, 12812, 12812 }, "bordl" },
+ { { 11024, 12820, 12820 }, "bordr" },
+ { { 11032, 12828, 12828 }, "bordtl" },
+ { { 11040, 12836, 12836 }, "bordtr" },
+ { { 11048, 12844, 12844 }, "bordbr" },
+ { { 11056, 12852, 12852 }, "bordbl" },
+ { { 11064, 12860, 12860 }, "R_InitData " },
+ { { 11076, 12872, 12872 }, "R_InitPointToAngle\n" },
+ { { 11096, 12892, 12892 }, "R_InitTables " },
+ { { 11112, 12908, 12908 }, "R_InitPlanes\n" },
+ { { 11128, 12924, 12924 }, "R_InitLightTables " },
+ { { 11148, 12944, 12944 }, "R_InitSkyMap\n" },
+ { { 11164, 12960, 12960 }, "F_SKY1" },
+ { { 12120, 13484, 13484 }, "LTFACE" },
+ { { 12128, 13492, 13492 }, "RTFACE" },
+ { { 12136, 13500, 13500 }, "BARBACK" },
+ { { 12144, 13508, 13508 }, "INVBAR" },
+ { { 12152, 13516, 13516 }, "CHAIN" },
+ { { 12160, 13524, 13524 }, "STATBAR" },
+ { { 12168, 13532, 13532 }, "LIFEBAR" },
+ { { 12176, 13540, 13540 }, "LIFEGEM2" },
+ { { 12188, 13552, 13552 }, "LIFEGEM0" },
+ { { 12200, 13564, 13564 }, "LTFCTOP" },
+ { { 12208, 13572, 13572 }, "RTFCTOP" },
+ { { 12224, 13580, 13580 }, "SELECTBOX" },
+ { { 12236, 13592, 13592 }, "INVGEML1" },
+ { { 12248, 13604, 13604 }, "INVGEML2" },
+ { { 12260, 13616, 13616 }, "INVGEMR1" },
+ { { 12272, 13628, 13628 }, "INVGEMR2" },
+ { { 12284, 13640, 13640 }, "BLACKSQ" },
+ { { 12292, 13648, 13648 }, "ARMCLEAR" },
+ { { 12304, 13660, 13660 }, "CHAINBACK" },
+ { { 12316, 13672, 13672 }, "IN0" },
+ { { 12320, 13676, 13676 }, "NEGNUM" },
+ { { 12328, 13684, 13684 }, "FONTB16" },
+ { { 12336, 13692, 13692 }, "SMALLIN0" },
+ { { 12348, 13704, 13704 }, "PLAYPAL" },
+ { { 12356, 13712, 13712 }, "SPINBK0" },
+ { { 12364, 13720, 13720 }, "SPFLY0" },
+ { { 12372, 13728, 13728 }, "LAME" },
+ { { 12380, 13736, 13736 }, "*** SOUND DEBUG INFO ***" },
+ { { 12408, 13764, 13764 }, "NAME" },
+ { { 12416, 13772, 13772 }, "MO.T" },
+ { { 12424, 13780, 13780 }, "MO.X" },
+ { { 12432, 13788, 13788 }, "MO.Y" },
+ { { 12440, 13796, 13796 }, "ID" },
+ { { 12444, 13800, 13800 }, "PRI" },
+ { { 12448, 13804, 13804 }, "DIST" },
+ { { 12456, 13812, 13812 }, "------" },
+ { { 12464, 13820, 13820 }, "%s" },
+ { { 12468, 13824, 13824 }, "%d" },
+ { { 12472, 13828, 13828 }, "GOD1" },
+ { { 12480, 13836, 13836 }, "GOD2" },
+ { { 12488, 13844, 13844 }, "useartia" },
+ { { 12500, 13856, 13856 }, "ykeyicon" },
+ { { 12512, 13868, 13868 }, "gkeyicon" },
+ { { 12524, 13880, 13880 }, "bkeyicon" },
+ { { 12216, 13892, 13892 }, "ARTIBOX" },
+ { { 12536, 13900, 13900 }, "GOD MODE ON" },
+ { { 12548, 13912, 13912 }, "GOD MODE OFF" },
+ { { 12564, 13928, 13928 }, "NO CLIPPING ON" },
+ { { 12580, 13944, 13944 }, "NO CLIPPING OFF" },
+ { { 12596, 13960, 13960 }, "ALL WEAPONS" },
+ { { 12608, 13972, 13972 }, "POWER OFF" },
+ { { 12620, 13984, 13984 }, "POWER ON" },
+ { { 12632, 13996, 13996 }, "FULL HEALTH" },
+ { { 12644, 14008, 14008 }, "ALL KEYS" },
+ { { 12656, 14020, 14020 }, "SOUND DEBUG ON" },
+ { { 12672, 14036, 14036 }, "SOUND DEBUG OFF" },
+ { { 12688, 14052, 14052 }, "TICKER ON" },
+ { { 12700, 14064, 14064 }, "TICKER OFF" },
+ { { 12712, 14076, 14076 }, "CHOOSE AN ARTIFACT ( A - J )" },
+ { { 12744, 14108, 14108 }, "HOW MANY ( 1 - 9 )" },
+ { { 12764, 14128, 14128 }, "YOU GOT IT" },
+ { { 12776, 14140, 14140 }, "BAD INPUT" },
+ { { 12788, 14152, 14152 }, "LEVEL WARP" },
+ { { 12800, 14164, 14164 }, "CHICKEN OFF" },
+ { { 12812, 14176, 14176 }, "CHICKEN ON" },
+ { { 12824, 14188, 14188 }, "MASSACRE" },
+ { { 12836, 14200, 14200 }, "CHEATER - YOU DON'T DESERVE WEAPONS" },
+ { { 12872, 14236, 14236 }, "TRYING TO CHEAT, EH? NOW YOU DIE!" },
+};
+
+// String offsets that are valid but we don't support.
+
+static const int unsupported_strings_1_0[] =
+{
+ 0, 4, 64, 104, 160, 200, 220, 236,
+ 244, 252, 272, 288, 296, 316, 332, 372,
+ 436, 500, 504, 536, 544, 560, 576, 584,
+ 592, 612, 640, 664, 708, 712, 744, 764,
+ 808, 820, 828, 840, 876, 884, 908, 952,
+ 992, 1028, 1036, 1048, 1088, 1128, 1160, 1192,
+ 1212, 1912, 2044, 2056, 2068, 2128, 2140, 2168,
+ 2184, 2196, 2212, 2228, 2240, 2252, 2260, 2264,
+ 2284, 2292, 2296, 2300, 2328, 2340, 2352, 2364,
+ 2372, 2384, 2388, 2404, 2428, 2436, 2444, 2464,
+ 2496, 2508, 2520, 2552, 2564, 2572, 2584, 3120,
+ 3128, 3140, 3184, 3220, 3248, 3252, 3256, 3280,
+ 3304, 3320, 3352, 3380, 3400, 3432, 3464, 3548,
+ 3600, 3624, 3664, 3696, 3812, 3872, 3932, 3940,
+ 3976, 3996, 6872, 6896, 7648, 7696, 7940, 7964,
+ 7968, 7992, 8020, 8028, 8052, 8056, 8076, 8088,
+ 8104, 8116, 8128, 8136, 8148, 8164, 8180, 8192,
+ 8204, 8220, 8232, 8248, 8264, 8276, 8292, 8308,
+ 8320, 8328, 8340, 8352, 8364, 8376, 8392, 8408,
+ 8424, 8436, 8448, 8460, 8472, 8488, 8504, 8520,
+ 8536, 8548, 8560, 8572, 8584, 8596, 8608, 8612,
+ 8624, 8648, 8660, 8668, 8680, 8708, 8720, 8728,
+ 8740, 8752, 8764, 8788, 8800, 8812, 8824, 8848,
+ 8860, 8864, 8868, 8876, 8888, 8896, 8916, 8944,
+ 8948, 8960, 8964, 8968, 8980, 9148, 9172, 9212,
+ 9216, 9220, 9820, 9860, 9892, 9940, 9972, 10012,
+ 10036, 10040, 10052, 10080, 10152, 10192, 10248, 10284,
+ 10320, 10360, 10392, 10452, 10488, 10508, 10556, 10644,
+ 10684, 10812, 10844, 10880, 10912, 10956, 11172, 11200,
+ 11232, 11272, 11312, 11348, 11380, 11404, 11436, 11492,
+ 11548, 11616, 11684, 11748, 11792, 11840, 11896, 11936,
+ 11980, 12028, 12072, 12908, 12924, 12956, 12960, 12968,
+ 12976, 13020, 13048, 13076, 13104, 13136, 13168, 13196,
+ 13240, 13272, 13292, 13296, 13308, 13312, 13320, 13324,
+ 13364, 13408, 13460, 13492, 13516, 13560, 13612, 13664,
+ 13700, 13744, 13796, 13848, 13884, 13940, 13996, 14040,
+ 14084, 14140, 14148, 14156, 14164, 14184, 14192, 14204,
+ 14208, 14212, 14256, 14272, 14284, 14296, 14300, 14312,
+ 14320, 14324, 14348, 14356, 14360, 14372, 14380, 14392,
+ 14432, 14440, 14444, 14472, 14496, 14516, 14536, 14548,
+ 14560, 14572, 14580, 14588, 14596, 14604, 14612, 14620,
+ 14636, 14660, 14704, 14740, 14748, 14756, 14760, 14768,
+ -1,
+};
+
+static const int unsupported_strings_1_2[] =
+{
+ 0, 4, 64, 104, 160, 200, 220, 236,
+ 244, 252, 272, 288, 296, 316, 332, 372,
+ 436, 500, 504, 536, 544, 560, 576, 584,
+ 592, 612, 640, 664, 708, 712, 744, 756,
+ 776, 820, 832, 840, 852, 888, 896, 920,
+ 964, 1004, 1040, 1048, 1060, 1100, 1140, 1172,
+ 1204, 1224, 2312, 2436, 2448, 2464, 2480, 2492,
+ 2512, 2524, 2536, 2596, 2608, 2636, 2652, 2656,
+ 2676, 2684, 2688, 2720, 2732, 2744, 2752, 2764,
+ 2772, 2776, 2792, 2816, 2824, 2832, 2852, 2884,
+ 2896, 2908, 2940, 2952, 2960, 2972, 3520, 3528,
+ 3540, 3584, 3620, 3648, 3652, 3656, 3680, 3704,
+ 3720, 3776, 3804, 3824, 3856, 3888, 4020, 4044,
+ 4084, 4116, 4156, 4272, 4288, 4296, 4332, 4352,
+ 4428, 4432, 8740, 8764, 9552, 9868, 9888, 9900,
+ 9916, 9928, 9940, 9948, 9960, 9976, 9992, 10004,
+ 10016, 10032, 10044, 10060, 10076, 10088, 10104, 10120,
+ 10132, 10140, 10152, 10164, 10176, 10188, 10204, 10220,
+ 10236, 10248, 10260, 10272, 10284, 10300, 10316, 10332,
+ 10348, 10360, 10372, 10384, 10396, 10408, 10420, 10424,
+ 10436, 10460, 10472, 10480, 10492, 10520, 10532, 10540,
+ 10552, 10564, 10576, 10600, 10612, 10624, 10636, 10660,
+ 10672, 10676, 10700, 10704, 10728, 10756, 10764, 10788,
+ 10792, 10796, 10804, 10816, 10824, 10844, 10872, 10876,
+ 10888, 10892, 10896, 10908, 11076, 11100, 11140, 11144,
+ 11148, 11748, 11788, 11820, 11868, 11900, 11940, 11964,
+ 11968, 11980, 12008, 12080, 12120, 12176, 12212, 12248,
+ 12288, 12320, 12380, 12400, 12448, 12536, 12576, 12704,
+ 12736, 12968, 13000, 13024, 13080, 13136, 13204, 13272,
+ 13336, 13380, 13428, 14272, 14288, 14320, 14324, 14332,
+ 14340, 14384, 14412, 14440, 14468, 14500, 14532, 14560,
+ 14604, 14636, 14656, 14696, 14740, 14792, 14824, 14848,
+ 14892, 14944, 14996, 15032, 15076, 15128, 15180, 15216,
+ 15272, 15328, 15372, 15416, 15472, 15480, 15488, 15496,
+ 15516, 15524, 15536, 15540, 15544, 15588, 15604, 15616,
+ 15628, 15632, 15644, 15652, 15656, 15680, 15688, 15692,
+ 15704, 15712, 15724, 15764, 15772, 15776, 15804, 15828,
+ 15848, 15868, 15880, 15892, 15904, 15912, 15920, 15928,
+ 15936, -1,
+};
+
+static const int unsupported_strings_1_3[] =
+{
+ 0, 4, 64, 104, 160, 200, 220, 236,
+ 244, 252, 272, 288, 296, 316, 332, 372,
+ 436, 500, 504, 536, 544, 560, 576, 584,
+ 592, 612, 640, 664, 708, 712, 744, 756,
+ 776, 820, 832, 840, 852, 888, 896, 920,
+ 964, 1004, 1040, 1048, 1060, 1100, 1140, 1172,
+ 1204, 1224, 2312, 2436, 2448, 2464, 2480, 2492,
+ 2512, 2524, 2536, 2596, 2608, 2636, 2652, 2656,
+ 2676, 2684, 2688, 2720, 2732, 2744, 2752, 2764,
+ 2772, 2776, 2792, 2816, 2824, 2832, 2852, 2884,
+ 2896, 2908, 2940, 2952, 2960, 2972, 3520, 3528,
+ 3540, 3584, 3620, 3648, 3652, 3656, 3680, 3704,
+ 3720, 3776, 3804, 3824, 3856, 3888, 4020, 4044,
+ 4084, 4116, 4156, 4272, 4288, 4296, 4332, 4352,
+ 4428, 4432, 8740, 8764, 9552, 9868, 9888, 9900,
+ 9916, 9928, 9940, 9948, 9960, 9976, 9992, 10004,
+ 10016, 10032, 10044, 10060, 10076, 10088, 10104, 10120,
+ 10132, 10140, 10152, 10164, 10176, 10188, 10204, 10220,
+ 10236, 10248, 10260, 10272, 10284, 10300, 10316, 10332,
+ 10348, 10360, 10372, 10384, 10396, 10408, 10420, 10424,
+ 10436, 10460, 10472, 10480, 10492, 10520, 10532, 10540,
+ 10552, 10564, 10576, 10600, 10612, 10624, 10636, 10660,
+ 10672, 10676, 10700, 10704, 10728, 10756, 10764, 10788,
+ 10792, 10796, 10804, 10816, 10824, 10844, 10872, 10876,
+ 10888, 10892, 10896, 10908, 11076, 11100, 11140, 11144,
+ 11148, 11748, 11788, 11820, 11868, 11900, 11940, 11964,
+ 11968, 11980, 12008, 12080, 12120, 12176, 12212, 12248,
+ 12288, 12320, 12380, 12400, 12448, 12536, 12576, 12704,
+ 12736, 12968, 13000, 13024, 13080, 13136, 13204, 13272,
+ 13336, 13380, 13428, 14272, 14288, 14320, 14324, 14332,
+ 14340, 14384, 14412, 14440, 14468, 14500, 14532, 14560,
+ 14604, 14636, 14656, 14696, 14740, 14792, 14824, 14848,
+ 14892, 14944, 14996, 15032, 15076, 15128, 15180, 15216,
+ 15272, 15328, 15372, 15416, 15472, 15480, 15488, 15496,
+ 15516, 15524, 15536, 15540, 15544, 15588, 15604, 15616,
+ 15628, 15632, 15644, 15652, 15656, 15680, 15688, 15692,
+ 15704, 15712, 15724, 15764, 15772, 15776, 15804, 15828,
+ 15848, 15868, 15880, 15892, 15904, 15912, 15920, 15928,
+ 15936, -1,
+};
+
+static const int *unsupported_strings[] =
+{
+ unsupported_strings_1_0,
+ unsupported_strings_1_2,
+ unsupported_strings_1_3,
+};
+
+static boolean StringIsUnsupported(unsigned int offset)
+{
+ const int *string_list;
+ int i;
+
+ string_list = unsupported_strings[deh_hhe_version];
+
+ for (i=0; string_list[i] >= 0; ++i)
+ {
+ if ((unsigned int) string_list[i] == offset)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static boolean GetStringByOffset(unsigned int offset, char **result)
+{
+ int i;
+
+ for (i=0; i<arrlen(strings); ++i)
+ {
+ if (strings[i].offsets[deh_hhe_version] == offset)
+ {
+ *result = strings[i].string;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Given a string length, find the maximum length of a
+// string that can replace it.
+
+static int MaxStringLength(int len)
+{
+ // Enough bytes for the string and the NUL terminator
+
+ len += 1;
+
+ // All strings in doom.exe are on 4-byte boundaries, so we may be able
+ // to support a slightly longer string.
+ // Extend up to the next 4-byte boundary
+
+ len += (4 - (len % 4)) % 4;
+
+ // Less one for the NUL terminator.
+
+ return len - 1;
+}
+
+// If a string offset does not match any string, it may be because
+// we are running in the wrong version mode, and the patch was generated
+// for a different Heretic version. Search the lookup tables to find
+// versiosn that match.
+
+static void SuggestOtherVersions(unsigned int offset)
+{
+ const int *string_list;
+ unsigned int i;
+ unsigned int v;
+
+ // Check main string table.
+
+ for (i=0; i<arrlen(strings); ++i)
+ {
+ for (v=0; v<deh_hhe_num_versions; ++v)
+ {
+ if (strings[i].offsets[v] == offset)
+ {
+ DEH_SuggestHereticVersion(v);
+ }
+ }
+ }
+
+ // Check unsupported string tables.
+
+ for (v=0; v<deh_hhe_num_versions; ++v)
+ {
+ string_list = unsupported_strings[v];
+
+ for (i=0; string_list[i] >= 0; ++i)
+ {
+ if (string_list[i] == offset)
+ {
+ DEH_SuggestHereticVersion(v);
+ }
+ }
+ }
+}
+
+static void *DEH_TextStart(deh_context_t *context, char *line)
+{
+ char *repl_text;
+ char *orig_text;
+ int orig_offset, repl_len;
+ int i;
+
+ if (sscanf(line, "Text %i %i", &orig_offset, &repl_len) != 2)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ repl_text = Z_Malloc(repl_len + 1, PU_STATIC, NULL);
+
+ // read in the "to" text
+
+ for (i=0; i<repl_len; ++i)
+ {
+ int c;
+
+ c = DEH_GetChar(context);
+
+ repl_text[i] = c;
+ }
+ repl_text[repl_len] = '\0';
+
+ // We don't support all strings, but at least recognise them:
+
+ if (StringIsUnsupported(orig_offset))
+ {
+ DEH_Warning(context, "Unsupported string replacement: %i", orig_offset);
+ }
+
+ // Find the string to replace:
+
+ else if (!GetStringByOffset(orig_offset, &orig_text))
+ {
+ SuggestOtherVersions(orig_offset);
+ DEH_Error(context, "Unknown string offset: %i", orig_offset);
+ }
+
+ // Only allow string replacements that are possible in Vanilla Doom.
+ // Chocolate Doom is unforgiving!
+
+ else if (!deh_allow_long_strings
+ && repl_len > MaxStringLength(strlen(orig_text)))
+ {
+ DEH_Error(context, "Replacement string is longer than the maximum "
+ "possible in heretic.exe");
+ }
+ else
+ {
+ // Success.
+
+ DEH_AddStringReplacement(orig_text, repl_text);
+
+ return NULL;
+ }
+
+ // Failure.
+
+ Z_Free(repl_text);
+
+ return NULL;
+}
+
+static void DEH_TextParseLine(deh_context_t *context, char *line, void *tag)
+{
+ // not used
+}
+
+deh_section_t deh_section_heretic_text =
+{
+ "Text",
+ NULL,
+ DEH_TextStart,
+ DEH_TextParseLine,
+ NULL,
+ NULL,
+};
+
diff --git a/src/heretic/deh_htic.c b/src/heretic/deh_htic.c
new file mode 100644
index 00000000..440fde96
--- /dev/null
+++ b/src/heretic/deh_htic.c
@@ -0,0 +1,186 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Top-level dehacked definitions for Heretic dehacked (HHE).
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_htic.h"
+#include "info.h"
+#include "m_argv.h"
+
+char *deh_signatures[] =
+{
+ "Patch File for HHE v1.0",
+ "Patch File for HHE v1.1",
+ NULL
+};
+
+static char *hhe_versions[] =
+{
+ "1.0", "1.2", "1.3"
+};
+
+// Version number for patches.
+
+deh_hhe_version_t deh_hhe_version = deh_hhe_1_0;
+
+// deh_ammo.c:
+extern deh_section_t deh_section_ammo;
+// deh_frame.c:
+extern deh_section_t deh_section_frame;
+// deh_ptr.c:
+extern deh_section_t deh_section_pointer;
+// deh_sound.c
+extern deh_section_t deh_section_sound;
+// deh_htext.c:
+extern deh_section_t deh_section_heretic_text;
+// deh_thing.c:
+extern deh_section_t deh_section_thing;
+// deh_weapon.c:
+extern deh_section_t deh_section_weapon;
+
+//
+// List of section types:
+//
+
+deh_section_t *deh_section_types[] =
+{
+ &deh_section_ammo,
+ &deh_section_frame,
+// &deh_section_pointer, TODO
+ &deh_section_sound,
+ &deh_section_heretic_text,
+ &deh_section_thing,
+ &deh_section_weapon,
+ NULL
+};
+
+static void SetHHEVersionByName(char *name)
+{
+ int i;
+
+ for (i=0; i<arrlen(hhe_versions); ++i)
+ {
+ if (!strcmp(hhe_versions[i], name))
+ {
+ deh_hhe_version = i;
+ return;
+ }
+ }
+
+ fprintf(stderr, "Unknown Heretic version: %s\n", name);
+ fprintf(stderr, "Valid versions:\n");
+
+ for (i=0; i<arrlen(hhe_versions); ++i)
+ {
+ fprintf(stderr, "\t%s\n", hhe_versions[i]);
+ }
+}
+
+// Initialize Heretic(HHE)-specific dehacked bits.
+
+void DEH_HereticInit(void)
+{
+ int i;
+
+ //!
+ // @arg <version>
+ //
+ // Select the Heretic version number that was used to generate the
+ // HHE patch to be loaded. Patches for each of the Vanilla
+ // Heretic versions (1.0, 1.2, 1.3) can be loaded, but the correct
+ // version number must be specified.
+
+ i = M_CheckParm("-hhever");
+
+ if (i > 0)
+ {
+ SetHHEVersionByName(myargv[i + 1]);
+ }
+
+ // For v1.0 patches, we must apply a slight change to the states[]
+ // table. The table was changed between 1.0 and 1.3 to add two extra
+ // frames to the player "burning death" animation.
+ //
+ // If we are using a v1.0 patch, we must change the table to cut
+ // these out again.
+
+ if (deh_hhe_version < deh_hhe_1_2)
+ {
+ states[S_PLAY_FDTH18].nextstate = S_NULL;
+ }
+}
+
+int DEH_MapHereticFrameNumber(int frame)
+{
+ if (deh_hhe_version < deh_hhe_1_2)
+ {
+ // Between Heretic 1.0 and 1.2, two new frames
+ // were added to the "states" table, to extend the "flame death"
+ // animation displayed when the player is killed by fire. Therefore,
+ // we must map Heretic 1.0 frame numbers to corresponding indexes
+ // for our state table.
+
+ if (frame >= S_PLAY_FDTH19)
+ {
+ frame = (frame - S_PLAY_FDTH19) + S_BLOODYSKULL1;
+ }
+ }
+ else
+ {
+ // After Heretic 1.2, three unused frames were removed from the
+ // states table, unused phoenix rod frames. Our state table includes
+ // these missing states for backwards compatibility. We must therefore
+ // adjust frame numbers for v1.2/v1.3 to corresponding indexes for
+ // our state table.
+
+ if (frame >= S_PHOENIXFXIX_1)
+ {
+ frame = (frame - S_PHOENIXFXIX_1) + S_PHOENIXPUFF1;
+ }
+ }
+
+ return frame;
+}
+
+void DEH_SuggestHereticVersion(deh_hhe_version_t version)
+{
+ fprintf(stderr,
+ "\n"
+ "This patch may be for version %s. You are currently running in\n"
+ "Heretic %s mode. For %s mode, this mode, add this to your command line:\n"
+ "\n"
+ "\t-hhever %s\n"
+ "\n",
+ hhe_versions[version],
+ hhe_versions[deh_hhe_version],
+ hhe_versions[version],
+ hhe_versions[version]);
+}
+
diff --git a/src/heretic/deh_htic.h b/src/heretic/deh_htic.h
new file mode 100644
index 00000000..7855da8c
--- /dev/null
+++ b/src/heretic/deh_htic.h
@@ -0,0 +1,60 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2010 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Common header for Heretic dehacked (HHE) support.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef DEH_HTIC_H
+#define DEH_HTIC_H
+
+#include "info.h"
+
+// HHE executable version. Loading HHE patches is (unfortunately)
+// dependent on the version of the Heretic executable used to make them.
+
+typedef enum
+{
+ deh_hhe_1_0,
+ deh_hhe_1_2,
+ deh_hhe_1_3,
+ deh_hhe_num_versions
+} deh_hhe_version_t;
+
+// HHE doesn't know about the last two states in the state table, so
+// these are considered invalid.
+
+#define DEH_HERETIC_NUMSTATES (NUMSTATES - 2)
+
+// It also doesn't know about the last two things in the mobjinfo table
+// (which correspond to the states above)
+
+#define DEH_HERETIC_NUMMOBJTYPES (NUMMOBJTYPES - 2)
+
+void DEH_HereticInit(void);
+int DEH_MapHereticFrameNumber(int frame);
+void DEH_SuggestHereticVersion(deh_hhe_version_t version);
+
+extern deh_hhe_version_t deh_hhe_version;
+
+#endif /* #ifndef DEH_HTIC_H */
+
diff --git a/src/heretic/deh_sound.c b/src/heretic/deh_sound.c
new file mode 100644
index 00000000..d1f266dd
--- /dev/null
+++ b/src/heretic/deh_sound.c
@@ -0,0 +1,118 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Sound" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "doomfeatures.h"
+#include "doomtype.h"
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+
+#include "doomdef.h"
+#include "i_sound.h"
+
+#include "sounds.h"
+
+DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t)
+ DEH_MAPPING_STRING("Name", name)
+ DEH_UNSUPPORTED_MAPPING("Special")
+ DEH_MAPPING("Value", priority)
+ DEH_MAPPING("Unknown 1", usefulness)
+ DEH_UNSUPPORTED_MAPPING("Unknown 2")
+ DEH_UNSUPPORTED_MAPPING("Unknown 3")
+ DEH_MAPPING("One/Two", numchannels)
+DEH_END_MAPPING
+
+static void *DEH_SoundStart(deh_context_t *context, char *line)
+{
+ int sound_number = 0;
+
+ if (sscanf(line, "Sound %i", &sound_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ if (sound_number < 0 || sound_number >= NUMSFX)
+ {
+ DEH_Warning(context, "Invalid sound number: %i", sound_number);
+ return NULL;
+ }
+
+ if (sound_number >= DEH_VANILLA_NUMSFX)
+ {
+ DEH_Warning(context, "Attempt to modify SFX %i. This will cause "
+ "problems in Vanilla dehacked.", sound_number);
+ }
+
+ return &S_sfx[sound_number];
+}
+
+static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag)
+{
+ sfxinfo_t *sfx;
+ char *variable_name, *value;
+
+ if (tag == NULL)
+ return;
+
+ sfx = (sfxinfo_t *) tag;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ // Set the field value:
+
+ if (!strcasecmp(variable_name, "Name"))
+ {
+ DEH_SetStringMapping(context, &sound_mapping, sfx,
+ variable_name, value);
+ }
+ else
+ {
+ DEH_SetMapping(context, &sound_mapping, sfx,
+ variable_name, atoi(value));
+ }
+}
+
+deh_section_t deh_section_sound =
+{
+ "Sound",
+ NULL,
+ DEH_SoundStart,
+ DEH_SoundParseLine,
+ NULL,
+ NULL,
+};
+
diff --git a/src/heretic/deh_thing.c b/src/heretic/deh_thing.c
new file mode 100644
index 00000000..ffededf2
--- /dev/null
+++ b/src/heretic/deh_thing.c
@@ -0,0 +1,150 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Thing" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "doomtype.h"
+#include "m_misc.h"
+
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+#include "deh_htic.h"
+
+#include "info.h"
+
+DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
+ DEH_MAPPING("ID #", doomednum)
+ DEH_MAPPING("Initial frame", spawnstate)
+ DEH_MAPPING("Hit points", spawnhealth)
+ DEH_MAPPING("First moving frame", seestate)
+ DEH_MAPPING("Alert sound", seesound)
+ DEH_MAPPING("Reaction time", reactiontime)
+ DEH_MAPPING("Attack sound", attacksound)
+ DEH_MAPPING("Injury frame", painstate)
+ DEH_MAPPING("Pain chance", painchance)
+ DEH_MAPPING("Pain sound", painsound)
+ DEH_MAPPING("Close attack frame", meleestate)
+ DEH_MAPPING("Far attack frame", missilestate)
+ DEH_MAPPING("Burning frame", crashstate)
+ DEH_MAPPING("Death frame", deathstate)
+ DEH_MAPPING("Exploding frame", xdeathstate)
+ DEH_MAPPING("Death sound", deathsound)
+ DEH_MAPPING("Speed", speed)
+ DEH_MAPPING("Width", radius)
+ DEH_MAPPING("Height", height)
+ DEH_MAPPING("Mass", mass)
+ DEH_MAPPING("Missile damage", damage)
+ DEH_MAPPING("Action sound", activesound)
+ DEH_MAPPING("Bits 1", flags)
+ DEH_MAPPING("Bits 2", flags2)
+DEH_END_MAPPING
+
+static void *DEH_ThingStart(deh_context_t *context, char *line)
+{
+ int thing_number = 0;
+ mobjinfo_t *mobj;
+
+ if (sscanf(line, "Thing %i", &thing_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ // HHE thing numbers are indexed from 1
+ --thing_number;
+
+ if (thing_number < 0 || thing_number >= DEH_HERETIC_NUMMOBJTYPES)
+ {
+ DEH_Warning(context, "Invalid thing number: %i", thing_number);
+ return NULL;
+ }
+
+ mobj = &mobjinfo[thing_number];
+
+ return mobj;
+}
+
+static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag)
+{
+ mobjinfo_t *mobj;
+ char *variable_name, *value;
+ int ivalue;
+
+ if (tag == NULL)
+ return;
+
+ mobj = (mobjinfo_t *) tag;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ // all values are integers
+
+ ivalue = atoi(value);
+
+ // If the value to be set is a frame, the frame number must
+ // undergo transformation from a Heretic 1.0 index to a
+ // Heretic 1.3 index.
+
+ if (M_StrCaseStr(variable_name, "frame") != NULL)
+ {
+ ivalue = DEH_MapHereticFrameNumber(ivalue);
+ }
+
+ // Set the field value
+
+ DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue);
+}
+
+static void DEH_ThingMD5Sum(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMMOBJTYPES; ++i)
+ {
+ DEH_StructMD5Sum(context, &thing_mapping, &mobjinfo[i]);
+ }
+}
+
+deh_section_t deh_section_thing =
+{
+ "Thing",
+ NULL,
+ DEH_ThingStart,
+ DEH_ThingParseLine,
+ NULL,
+ DEH_ThingMD5Sum,
+};
+
diff --git a/src/heretic/deh_weapon.c b/src/heretic/deh_weapon.c
new file mode 100644
index 00000000..28a90c68
--- /dev/null
+++ b/src/heretic/deh_weapon.c
@@ -0,0 +1,131 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Weapon" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomtype.h"
+#include "m_misc.h"
+
+#include "doomdef.h"
+
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+#include "deh_htic.h"
+
+DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
+ DEH_MAPPING("Ammo type", ammo)
+ DEH_MAPPING("Deselect frame", upstate)
+ DEH_MAPPING("Select frame", downstate)
+ DEH_MAPPING("Bobbing frame", readystate)
+ DEH_MAPPING("Shooting frame", atkstate)
+ DEH_MAPPING("Firing frame", holdatkstate)
+ DEH_MAPPING("Unknown frame", flashstate)
+DEH_END_MAPPING
+
+static void *DEH_WeaponStart(deh_context_t *context, char *line)
+{
+ int weapon_number = 0;
+
+ if (sscanf(line, "Weapon %i", &weapon_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ if (weapon_number < 0 || weapon_number >= NUMWEAPONS * 2)
+ {
+ DEH_Warning(context, "Invalid weapon number: %i", weapon_number);
+ return NULL;
+ }
+
+ // Because of the tome of power, we have two levels of weapons:
+
+ if (weapon_number < NUMWEAPONS)
+ {
+ return &wpnlev1info[weapon_number];
+ }
+ else
+ {
+ return &wpnlev2info[weapon_number - NUMWEAPONS];
+ }
+}
+
+static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag)
+{
+ char *variable_name, *value;
+ weaponinfo_t *weapon;
+ int ivalue;
+
+ if (tag == NULL)
+ return;
+
+ weapon = (weaponinfo_t *) tag;
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ ivalue = atoi(value);
+
+ // If this is a frame field, we need to map from Heretic 1.0 frame
+ // numbers to Heretic 1.3 frame numbers.
+
+ if (M_StrCaseStr(variable_name, "frame") != NULL)
+ {
+ ivalue = DEH_MapHereticFrameNumber(ivalue);
+ }
+
+ DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue);
+}
+
+static void DEH_WeaponMD5Sum(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMWEAPONS ;++i)
+ {
+ DEH_StructMD5Sum(context, &weapon_mapping, &wpnlev1info[i]);
+ DEH_StructMD5Sum(context, &weapon_mapping, &wpnlev2info[i]);
+ }
+}
+
+deh_section_t deh_section_weapon =
+{
+ "Weapon",
+ NULL,
+ DEH_WeaponStart,
+ DEH_WeaponParseLine,
+ NULL,
+ DEH_WeaponMD5Sum,
+};
+
diff --git a/src/heretic/doomdef.h b/src/heretic/doomdef.h
index 3f976c3d..aa1d0c0d 100644
--- a/src/heretic/doomdef.h
+++ b/src/heretic/doomdef.h
@@ -581,6 +581,7 @@ extern boolean singletics; // debug flag to cancel adaptiveness
extern boolean DebugSound; // debug flag for displaying sound info
extern int maxammo[NUMAMMO];
+extern int GetWeaponAmmo[NUMWEAPONS];
extern boolean demoplayback;
extern int skytexture;
diff --git a/src/heretic/dstrings.h b/src/heretic/dstrings.h
index aea5548e..93900f9d 100644
--- a/src/heretic/dstrings.h
+++ b/src/heretic/dstrings.h
@@ -26,42 +26,6 @@
//---------------------------------------------------------------------------
//
-// M_menu.c
-//
-//---------------------------------------------------------------------------
-#define PRESSKEY "press a key."
-#define PRESSYN "press y or n."
-#define TXT_PAUSED "PAUSED"
-#define QUITMSG "are you sure you want to\nquit this great game?"
-#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY
-#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY
-#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY
-#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY
-#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN
-#define QLPROMPT "do you want to quickload the game named"\
- "\n\n'%s'?\n\n"PRESSYN
-#define NEWGAME "you can't start a new game\n"\
- "while in a network game.\n\n"PRESSKEY
-#define NIGHTMARE "are you sure? this skill level\n"\
- "isn't even remotely fair.\n\n"PRESSYN
-#define SWSTRING "this is the shareware version of doom.\n\n"\
- "you need to order the entire trilogy.\n\n"PRESSKEY
-#define MSGOFF "Messages OFF"
-#define MSGON "Messages ON"
-#define NETEND "you can't end a netgame!\n\n"PRESSKEY
-#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN
-#define DOSY "(press y to quit to dos.)"
-#define DETAILHI "High detail"
-#define DETAILLO "Low detail"
-#define GAMMALVL0 "Gamma correction OFF"
-#define GAMMALVL1 "Gamma correction level 1"
-#define GAMMALVL2 "Gamma correction level 2"
-#define GAMMALVL3 "Gamma correction level 3"
-#define GAMMALVL4 "Gamma correction level 4"
-#define EMPTYSTRING "empty slot"
-
-//---------------------------------------------------------------------------
-//
// P_inter.c
//
//---------------------------------------------------------------------------
@@ -170,74 +134,6 @@
//---------------------------------------------------------------------------
//
-// HU_stuff.c
-//
-//---------------------------------------------------------------------------
-
-#define HUSTR_E1M1 "E1M1: Hangar"
-#define HUSTR_E1M2 "E1M2: Nuclear Plant"
-#define HUSTR_E1M3 "E1M3: Toxin Refinery"
-#define HUSTR_E1M4 "E1M4: Command Control"
-#define HUSTR_E1M5 "E1M5: Phobos Lab"
-#define HUSTR_E1M6 "E1M6: Central Processing"
-#define HUSTR_E1M7 "E1M7: Computer Station"
-#define HUSTR_E1M8 "E1M8: Phobos Anomaly"
-#define HUSTR_E1M9 "E1M9: Military Base"
-
-#define HUSTR_E2M1 "E2M1: Deimos Anomaly"
-#define HUSTR_E2M2 "E2M2: Containment Area"
-#define HUSTR_E2M3 "E2M3: Refinery"
-#define HUSTR_E2M4 "E2M4: Deimos Lab"
-#define HUSTR_E2M5 "E2M5: Command Center"
-#define HUSTR_E2M6 "E2M6: Halls of the Damned"
-#define HUSTR_E2M7 "E2M7: Spawning Vats"
-#define HUSTR_E2M8 "E2M8: Tower of Babel"
-#define HUSTR_E2M9 "E2M9: Fortress of Mystery"
-
-#define HUSTR_E3M1 "E3M1: Hell Keep"
-#define HUSTR_E3M2 "E3M2: Slough of Despair"
-#define HUSTR_E3M3 "E3M3: Pandemonium"
-#define HUSTR_E3M4 "E3M4: House of Pain"
-#define HUSTR_E3M5 "E3M5: Unholy Cathedral"
-#define HUSTR_E3M6 "E3M6: Mt. Erebus"
-#define HUSTR_E3M7 "E3M7: Limbo"
-#define HUSTR_E3M8 "E3M8: Dis"
-#define HUSTR_E3M9 "E3M9: Warrens"
-
-#define HUSTR_CHATMACRO1 "I'm ready to kick butt!"
-#define HUSTR_CHATMACRO2 "I'm OK."
-#define HUSTR_CHATMACRO3 "I'm not looking too good!"
-#define HUSTR_CHATMACRO4 "Help!"
-#define HUSTR_CHATMACRO5 "You suck!"
-#define HUSTR_CHATMACRO6 "Next time, scumbag..."
-#define HUSTR_CHATMACRO7 "Come here!"
-#define HUSTR_CHATMACRO8 "I'll take care of it."
-#define HUSTR_CHATMACRO9 "Yes"
-#define HUSTR_CHATMACRO0 "No"
-
-#define HUSTR_TALKTOSELF1 "You mumble to yourself"
-#define HUSTR_TALKTOSELF2 "Who's there?"
-#define HUSTR_TALKTOSELF3 "You scare yourself"
-#define HUSTR_TALKTOSELF4 "You start to rave"
-#define HUSTR_TALKTOSELF5 "You've lost it..."
-
-#define HUSTR_MESSAGESENT "[Message Sent]"
-
-// The following should NOT be changed unless it seems
-// just AWFULLY necessary
-
-#define HUSTR_PLRGREEN "Green: "
-#define HUSTR_PLRINDIGO "Indigo: "
-#define HUSTR_PLRBROWN "Brown: "
-#define HUSTR_PLRRED "Red: "
-
-#define HUSTR_KEYGREEN 'g'
-#define HUSTR_KEYINDIGO 'i'
-#define HUSTR_KEYBROWN 'b'
-#define HUSTR_KEYRED 'r'
-
-//---------------------------------------------------------------------------
-//
// AM_map.c
//
//---------------------------------------------------------------------------
@@ -253,26 +149,6 @@
//---------------------------------------------------------------------------
//
-// ST_stuff.c
-//
-//---------------------------------------------------------------------------
-
-#define STSTR_DQDON "Degreelessness Mode On"
-#define STSTR_DQDOFF "Degreelessness Mode Off"
-
-#define STSTR_KFAADDED "Very Happy Ammo Added"
-
-#define STSTR_NCON "No Clipping Mode ON"
-#define STSTR_NCOFF "No Clipping Mode OFF"
-
-#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"
-#define STSTR_BEHOLDX "Power-up Toggled"
-
-#define STSTR_CHOPPERS "... doesn't suck - GM"
-#define STSTR_CLEV "Changing Level..."
-
-//---------------------------------------------------------------------------
-//
// F_finale.c
//
//---------------------------------------------------------------------------
@@ -374,56 +250,3 @@
"surrender without a fight. eyes\n"\
"wide, you go to meet your fate."
-/*
-#define E1TEXT "Once you beat the big badasses and\n"\
- "clean out the moon base you're supposed\n"\
- "to win, aren't you? Aren't you? Where's\n"\
- "your fat reward and ticket home? What\n"\
- "the hell is this? It's not supposed to\n"\
- "end this way!\n"\
- "\n" \
- "It stinks like rotten meat, but looks\n"\
- "like the lost Deimos base. Looks like\n"\
- "you're stuck on The Shores of Hell.\n"\
- "The only way out is through.\n"\
- "\n"\
- "To continue the DOOM experience, play\n"\
- "The Shores of Hell and its amazing\n"\
- "sequel, Inferno!\n"
-
-#define E2TEXT "You've done it! The hideous cyber-\n"\
- "demon lord that ruled the lost Deimos\n"\
- "moon base has been slain and you\n"\
- "are triumphant! But ... where are\n"\
- "you? You clamber to the edge of the\n"\
- "moon and look down to see the awful\n"\
- "truth.\n" \
- "\n"\
- "Deimos floats above Hell itself!\n"\
- "You've never heard of anyone escaping\n"\
- "from Hell, but you'll make the bastards\n"\
- "sorry they ever heard of you! Quickly,\n"\
- "you rappel down to the surface of\n"\
- "Hell.\n"\
- "\n" \
- "Now, it's on to the final chapter of\n"\
- "DOOM! -- Inferno."
-
-#define E3TEXT "The loathsome spiderdemon that\n"\
- "masterminded the invasion of the moon\n"\
- "bases and caused so much death has had\n"\
- "its ass kicked for all time.\n"\
- "\n"\
- "A hidden doorway opens and you enter.\n"\
- "You've proven too tough for Hell to\n"\
- "contain, and now Hell at last plays\n"\
- "fair -- for you emerge from the door\n"\
- "to see the green fields of Earth!\n"\
- "Home at last.\n" \
- "\n"\
- "You wonder what's been happening on\n"\
- "Earth while you were battling evil\n"\
- "unleashed. It's good that no Hell-\n"\
- "spawn could have come through that\n"\
- "door with you ..."
-*/
diff --git a/src/heretic/f_finale.c b/src/heretic/f_finale.c
index 03806214..4077bb82 100644
--- a/src/heretic/f_finale.c
+++ b/src/heretic/f_finale.c
@@ -26,6 +26,7 @@
#include <ctype.h>
#include "doomdef.h"
+#include "deh_str.h"
#include "i_swap.h"
#include "i_video.h"
#include "s_sound.h"
@@ -37,11 +38,6 @@ int finalecount;
#define TEXTSPEED 3
#define TEXTWAIT 250
-char *e1text = E1TEXT;
-char *e2text = E2TEXT;
-char *e3text = E3TEXT;
-char *e4text = E4TEXT;
-char *e5text = E5TEXT;
char *finaletext;
char *finaleflat;
@@ -72,30 +68,30 @@ void F_StartFinale(void)
switch (gameepisode)
{
case 1:
- finaleflat = "FLOOR25";
- finaletext = e1text;
+ finaleflat = DEH_String("FLOOR25");
+ finaletext = DEH_String(E1TEXT);
break;
case 2:
- finaleflat = "FLATHUH1";
- finaletext = e2text;
+ finaleflat = DEH_String("FLATHUH1");
+ finaletext = DEH_String(E2TEXT);
break;
case 3:
- finaleflat = "FLTWAWA2";
- finaletext = e3text;
+ finaleflat = DEH_String("FLTWAWA2");
+ finaletext = DEH_String(E3TEXT);
break;
case 4:
- finaleflat = "FLOOR28";
- finaletext = e4text;
+ finaleflat = DEH_String("FLOOR28");
+ finaletext = DEH_String(E4TEXT);
break;
case 5:
- finaleflat = "FLOOR08";
- finaletext = e5text;
+ finaleflat = DEH_String("FLOOR08");
+ finaletext = DEH_String(E5TEXT);
break;
}
finalestage = 0;
finalecount = 0;
- FontABaseLump = W_GetNumForName("FONTA_S") + 1;
+ FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
// S_ChangeMusic(mus_victor, true);
S_StartSong(mus_cptd, true);
@@ -277,8 +273,8 @@ void F_DemonScroll(void)
{
return;
}
- p1 = W_CacheLumpName("FINAL1", PU_LEVEL);
- p2 = W_CacheLumpName("FINAL2", PU_LEVEL);
+ p1 = W_CacheLumpName(DEH_String("FINAL1"), PU_LEVEL);
+ p2 = W_CacheLumpName(DEH_String("FINAL2"), PU_LEVEL);
if (finalecount < 70)
{
memcpy(I_VideoBuffer, p1, SCREENHEIGHT * SCREENWIDTH);
@@ -319,8 +315,8 @@ void F_DrawUnderwater(void)
{
underwawa = true;
memset((byte *) 0xa0000, 0, SCREENWIDTH * SCREENHEIGHT);
- I_SetPalette(W_CacheLumpName("E2PAL", PU_CACHE));
- V_DrawRawScreen(W_CacheLumpName("E2END", PU_CACHE));
+ I_SetPalette(W_CacheLumpName(DEH_String("E2PAL"), PU_CACHE));
+ V_DrawRawScreen(W_CacheLumpName(DEH_String("E2END"), PU_CACHE));
}
paused = false;
MenuActive = false;
@@ -328,7 +324,7 @@ void F_DrawUnderwater(void)
break;
case 2:
- V_DrawRawScreen(W_CacheLumpName("TITLE", PU_CACHE));
+ V_DrawRawScreen(W_CacheLumpName(DEH_String("TITLE"), PU_CACHE));
//D_StartTitle(); // go to intro/demo mode.
}
}
diff --git a/src/heretic/g_game.c b/src/heretic/g_game.c
index a256b63b..b37f95e0 100644
--- a/src/heretic/g_game.c
+++ b/src/heretic/g_game.c
@@ -28,6 +28,7 @@
#include <string.h>
#include "doomdef.h"
#include "doomkeys.h"
+#include "deh_str.h"
#include "i_timer.h"
#include "i_system.h"
#include "m_controls.h"
@@ -862,12 +863,16 @@ void G_Ticker(void)
{
if (netgame)
{
- strcpy(savedescription, "NET GAME");
+ strncpy(savedescription, DEH_String("NET GAME"),
+ sizeof(savedescription));
}
else
{
- strcpy(savedescription, "SAVE GAME");
+ strncpy(savedescription, DEH_String("SAVE GAME"),
+ sizeof(savedescription));
}
+
+ savedescription[sizeof(savedescription) - 1] = '\0';
}
savegameslot =
(players[i].cmd.
@@ -1320,7 +1325,9 @@ void G_DoLoadGame(void)
save_p = savebuffer + SAVESTRINGSIZE;
// Skip the description field
memset(vcheck, 0, sizeof(vcheck));
- sprintf(vcheck, "version %i", HERETIC_VERSION);
+
+ DEH_snprintf(vcheck, VERSIONSIZE, "version %i", HERETIC_VERSION);
+
if (strcmp((char *) save_p, vcheck) != 0)
{ // Bad version
return;
@@ -1449,11 +1456,11 @@ void G_InitNew(skill_t skill, int episode, int map)
// Set the sky map
if (episode > 5)
{
- skytexture = R_TextureNumForName("SKY1");
+ skytexture = R_TextureNumForName(DEH_String("SKY1"));
}
else
{
- skytexture = R_TextureNumForName(skyLumpNames[episode - 1]);
+ skytexture = R_TextureNumForName(DEH_String(skyLumpNames[episode - 1]));
}
//
@@ -1694,7 +1701,7 @@ void G_DoSaveGame(void)
SV_Open(name);
SV_Write(description, SAVESTRINGSIZE);
memset(verString, 0, sizeof(verString));
- sprintf(verString, "version %i", HERETIC_VERSION);
+ DEH_snprintf(verString, VERSIONSIZE, "version %i", HERETIC_VERSION);
SV_Write(verString, VERSIONSIZE);
SV_WriteByte(gameskill);
SV_WriteByte(gameepisode);
@@ -1714,7 +1721,7 @@ void G_DoSaveGame(void)
gameaction = ga_nothing;
savedescription[0] = 0;
- P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
+ P_SetMessage(&players[consoleplayer], DEH_String(TXT_GAMESAVED), true);
}
//==========================================================================
diff --git a/src/heretic/in_lude.c b/src/heretic/in_lude.c
index 33b75956..3c382814 100644
--- a/src/heretic/in_lude.c
+++ b/src/heretic/in_lude.c
@@ -30,6 +30,7 @@
*/
#include "doomdef.h"
+#include "deh_str.h"
#include "s_sound.h"
#include "i_system.h"
#include "i_video.h"
@@ -161,7 +162,7 @@ extern void AM_Stop(void);
void IN_Start(void)
{
- I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+ I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE));
IN_LoadPics();
IN_InitStats();
intermission = true;
@@ -308,26 +309,26 @@ static void IN_LoadUnloadPics(void (*callback)(char *lumpname,
switch (gameepisode)
{
case 1:
- callback("MAPE1", 0, &patchINTERPIC);
+ callback(DEH_String("MAPE1"), 0, &patchINTERPIC);
break;
case 2:
- callback("MAPE2", 0, &patchINTERPIC);
+ callback(DEH_String("MAPE2"), 0, &patchINTERPIC);
break;
case 3:
- callback("MAPE3", 0, &patchINTERPIC);
+ callback(DEH_String("MAPE3"), 0, &patchINTERPIC);
break;
default:
break;
}
- callback("IN_X", 0, &patchBEENTHERE);
- callback("IN_YAH", 0, &patchGOINGTHERE);
- callback("FONTB13", 0, &FontBNegative);
+ callback(DEH_String("IN_X"), 0, &patchBEENTHERE);
+ callback(DEH_String("IN_YAH"), 0, &patchGOINGTHERE);
+ callback(DEH_String("FONTB13"), 0, &FontBNegative);
- callback("FONTB15", 0, &FontBSlash);
- callback("FONTB05", 0, &FontBPercent);
+ callback(DEH_String("FONTB15"), 0, &FontBSlash);
+ callback(DEH_String("FONTB05"), 0, &FontBPercent);
- FontBLumpBase = W_GetNumForName("FONTB16");
+ FontBLumpBase = W_GetNumForName(DEH_String("FONTB16"));
for (i = 0; i < 10; i++)
{
@@ -355,9 +356,9 @@ static void LoadLumpCallback(char *lumpname, int lumpnum, patch_t **ptr)
void IN_LoadPics(void)
{
- FontBLump = W_GetNumForName("FONTB_S") + 1;
- patchFaceOkayBase = W_GetNumForName("FACEA0");
- patchFaceDeadBase = W_GetNumForName("FACEB0");
+ FontBLump = W_GetNumForName(DEH_String("FONTB_S")) + 1;
+ patchFaceOkayBase = W_GetNumForName(DEH_String("FACEA0"));
+ patchFaceDeadBase = W_GetNumForName(DEH_String("FACEB0"));
IN_LoadUnloadPics(LoadLumpCallback);
}
@@ -580,7 +581,7 @@ void IN_DrawStatBack(void)
byte *src;
byte *dest;
- src = W_CacheLumpName("FLOOR16", PU_CACHE);
+ src = W_CacheLumpName(DEH_String("FLOOR16"), PU_CACHE);
dest = I_VideoBuffer;
for (y = 0; y < SCREENHEIGHT; y++)
@@ -612,8 +613,8 @@ void IN_DrawOldLevel(void)
x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] +
7) / 2;
IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3);
- x = 160 - MN_TextAWidth("FINISHED") / 2;
- MN_DrTextA("FINISHED", x, 25);
+ x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2;
+ MN_DrTextA(DEH_String("FINISHED"), x, 25);
if (prevmap == 9)
{
@@ -660,8 +661,8 @@ void IN_DrawYAH(void)
int i;
int x;
- x = 160 - MN_TextAWidth("NOW ENTERING:") / 2;
- MN_DrTextA("NOW ENTERING:", x, 10);
+ x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2;
+ MN_DrTextA(DEH_String("NOW ENTERING:"), x, 10);
x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] +
7) / 2;
IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] + 7, x, 20);
@@ -698,15 +699,15 @@ void IN_DrawSingleStats(void)
int x;
static int sounds;
- IN_DrTextB("KILLS", 50, 65);
- IN_DrTextB("ITEMS", 50, 90);
- IN_DrTextB("SECRETS", 50, 115);
+ IN_DrTextB(DEH_String("KILLS"), 50, 65);
+ IN_DrTextB(DEH_String("ITEMS"), 50, 90);
+ IN_DrTextB(DEH_String("SECRETS"), 50, 115);
x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] +
7) / 2;
IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3);
- x = 160 - MN_TextAWidth("FINISHED") / 2;
- MN_DrTextA("FINISHED", x, 25);
+ x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2;
+ MN_DrTextA(DEH_String("FINISHED"), x, 25);
if (intertime < 30)
{
@@ -757,13 +758,13 @@ void IN_DrawSingleStats(void)
if (gamemode != retail || gameepisode <= 3)
{
- IN_DrTextB("TIME", 85, 160);
+ IN_DrTextB(DEH_String("TIME"), 85, 160);
IN_DrawTime(155, 160, hours, minutes, seconds);
}
else
{
- x = 160 - MN_TextAWidth("NOW ENTERING:") / 2;
- MN_DrTextA("NOW ENTERING:", x, 160);
+ x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2;
+ MN_DrTextA(DEH_String("NOW ENTERING:"), x, 160);
x = 160 -
MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] +
7) / 2;
@@ -787,14 +788,14 @@ void IN_DrawCoopStats(void)
static int sounds;
- IN_DrTextB("KILLS", 95, 35);
- IN_DrTextB("BONUS", 155, 35);
- IN_DrTextB("SECRET", 232, 35);
+ IN_DrTextB(DEH_String("KILLS"), 95, 35);
+ IN_DrTextB(DEH_String("BONUS"), 155, 35);
+ IN_DrTextB(DEH_String("SECRET"), 232, 35);
x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] +
7) / 2;
IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3);
- x = 160 - MN_TextAWidth("FINISHED") / 2;
- MN_DrTextA("FINISHED", x, 25);
+ x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2;
+ MN_DrTextA(DEH_String("FINISHED"), x, 25);
ypos = 50;
for (i = 0; i < MAXPLAYERS; i++)
@@ -845,11 +846,11 @@ void IN_DrawDMStats(void)
xpos = 90;
ypos = 55;
- IN_DrTextB("TOTAL", 265, 30);
- MN_DrTextA("VICTIMS", 140, 8);
+ IN_DrTextB(DEH_String("TOTAL"), 265, 30);
+ MN_DrTextA(DEH_String("VICTIMS"), 140, 8);
for (i = 0; i < 7; i++)
{
- MN_DrTextA(KillersText[i], 10, 80 + 9 * i);
+ MN_DrTextA(DEH_String(KillersText[i]), 10, 80 + 9 * i);
}
if (intertime < 20)
{
@@ -940,7 +941,7 @@ void IN_DrawTime(int x, int y, int h, int m, int s)
if (h)
{
IN_DrawNumber(h, x, y, 2);
- IN_DrTextB(":", x + 26, y);
+ IN_DrTextB(DEH_String(":"), x + 26, y);
}
x += 34;
if (m || h)
@@ -950,7 +951,7 @@ void IN_DrawTime(int x, int y, int h, int m, int s)
x += 34;
if (s)
{
- IN_DrTextB(":", x - 8, y);
+ IN_DrTextB(DEH_String(":"), x - 8, y);
IN_DrawNumber(s, x, y, 2);
}
}
diff --git a/src/heretic/info.c b/src/heretic/info.c
index e566222c..b6dd921f 100644
--- a/src/heretic/info.c
+++ b/src/heretic/info.c
@@ -22,7 +22,7 @@
//
//-----------------------------------------------------------------------------
#include "doomdef.h"
-// generated by multigen
+#include "p_action.h"
char *sprnames[] = {
"IMPX","ACLO","PTN1","SHLD","SHD2","BAGH","SPMP","INVS","PTN2","SOAR",
@@ -41,132 +41,6 @@ char *sprnames[] = {
NULL
};
-void A_FreeTargMobj();
-void A_RestoreSpecialThing1();
-void A_RestoreSpecialThing2();
-void A_HideThing();
-void A_UnHideThing();
-void A_RestoreArtifact();
-void A_Scream();
-void A_Explode();
-void A_PodPain();
-void A_RemovePod();
-void A_MakePod();
-void A_InitKeyGizmo();
-void A_VolcanoSet();
-void A_VolcanoBlast();
-void A_BeastPuff();
-void A_VolcBallImpact();
-void A_SpawnTeleGlitter();
-void A_SpawnTeleGlitter2();
-void A_AccTeleGlitter();
-void A_Light0();
-void A_WeaponReady();
-void A_Lower();
-void A_Raise();
-void A_StaffAttackPL1();
-void A_ReFire();
-void A_StaffAttackPL2();
-void A_BeakReady();
-void A_BeakRaise();
-void A_BeakAttackPL1();
-void A_BeakAttackPL2();
-void A_GauntletAttack();
-void A_FireBlasterPL1();
-void A_FireBlasterPL2();
-void A_SpawnRippers();
-void A_FireMacePL1();
-void A_FireMacePL2();
-void A_MacePL1Check();
-void A_MaceBallImpact();
-void A_MaceBallImpact2();
-void A_DeathBallImpact();
-void A_FireSkullRodPL1();
-void A_FireSkullRodPL2();
-void A_SkullRodPL2Seek();
-void A_AddPlayerRain();
-void A_HideInCeiling();
-void A_SkullRodStorm();
-void A_RainImpact();
-void A_FireGoldWandPL1();
-void A_FireGoldWandPL2();
-void A_FirePhoenixPL1();
-void A_InitPhoenixPL2();
-void A_FirePhoenixPL2();
-void A_ShutdownPhoenixPL2();
-void A_PhoenixPuff();
-void A_FlameEnd();
-void A_FloatPuff();
-void A_FireCrossbowPL1();
-void A_FireCrossbowPL2();
-void A_BoltSpark();
-void A_Pain();
-void A_NoBlocking();
-void A_AddPlayerCorpse();
-void A_SkullPop();
-void A_FlameSnd();
-void A_CheckBurnGone();
-void A_CheckSkullFloor();
-void A_CheckSkullDone();
-void A_Feathers();
-void A_ChicLook();
-void A_ChicChase();
-void A_ChicPain();
-void A_FaceTarget();
-void A_ChicAttack();
-void A_Look();
-void A_Chase();
-void A_MummyAttack();
-void A_MummyAttack2();
-void A_MummySoul();
-void A_ContMobjSound();
-void A_MummyFX1Seek();
-void A_BeastAttack();
-void A_SnakeAttack();
-void A_SnakeAttack2();
-void A_HeadAttack();
-void A_BossDeath();
-void A_HeadIceImpact();
-void A_HeadFireGrow();
-void A_WhirlwindSeek();
-void A_ClinkAttack();
-void A_WizAtk1();
-void A_WizAtk2();
-void A_WizAtk3();
-void A_GhostOff();
-void A_ImpMeAttack();
-void A_ImpMsAttack();
-void A_ImpMsAttack2();
-void A_ImpDeath();
-void A_ImpXDeath1();
-void A_ImpXDeath2();
-void A_ImpExplode();
-void A_KnightAttack();
-void A_DripBlood();
-void A_Sor1Chase();
-void A_Sor1Pain();
-void A_Srcr1Attack();
-void A_SorZap();
-void A_SorcererRise();
-void A_SorRise();
-void A_SorSightSnd();
-void A_Srcr2Decide();
-void A_Srcr2Attack();
-void A_Sor2DthInit();
-void A_SorDSph();
-void A_Sor2DthLoop();
-void A_SorDExp();
-void A_SorDBon();
-void A_BlueSpark();
-void A_GenWizard();
-void A_MinotaurAtk1();
-void A_MinotaurDecide();
-void A_MinotaurAtk2();
-void A_MinotaurAtk3();
-void A_MinotaurCharge();
-void A_MntrFloorFire();
-void A_ESound();
-
state_t states[NUMSTATES] = {
{SPR_IMPX, 0, -1, NULL, S_NULL, 0, 0}, // S_NULL
{SPR_ACLO, 4, 1050, A_FreeTargMobj, S_NULL, 0, 0}, // S_FREETARGMOBJ
@@ -663,6 +537,9 @@ state_t states[NUMSTATES] = {
{SPR_FX08, 32773, 4, NULL, S_PHOENIXFXI1_7, 0, 0}, // S_PHOENIXFXI1_6
{SPR_FX08, 32774, 4, NULL, S_PHOENIXFXI1_8, 0, 0}, // S_PHOENIXFXI1_7
{SPR_FX08, 32775, 4, NULL, S_NULL, 0, 0}, // S_PHOENIXFXI1_8
+ {SPR_FX08, 32776, 8, NULL, S_PHOENIXFXIX_1, 0, 0 }, // S_PHOENIXFXIX_1
+ {SPR_FX08, 32777, 8, A_RemovedPhoenixFunc, S_PHOENIXFXIX_2, 0, 0 }, // S_PHOENIXFXIX_2
+ {SPR_FX08, 32778, 8, NULL, S_NULL, 0, 0 }, // S_PHOENIXFXIX_3
{SPR_FX04, 1, 4, NULL, S_PHOENIXPUFF2, 0, 0}, // S_PHOENIXPUFF1
{SPR_FX04, 2, 4, NULL, S_PHOENIXPUFF3, 0, 0}, // S_PHOENIXPUFF2
{SPR_FX04, 3, 4, NULL, S_PHOENIXPUFF4, 0, 0}, // S_PHOENIXPUFF3
@@ -3727,6 +3604,37 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = {
MF2_THRUGHOST | MF2_NOTELEPORT // flags2
},
+ // The following thing is present in the mobjinfo table from Heretic 1.0,
+ // but not in Heretic 1.3 (ie. it was removed). It has been re-inserted
+ // here to support HHE patches.
+
+ { // MT_PHOENIXFX_REMOVED
+ -1, // doomednum
+ S_PHOENIXFXIX_1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_PHOENIXFXIX_3, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
{ // MT_PHOENIXPUFF
-1, // doomednum
S_PHOENIXPUFF1, // spawnstate
diff --git a/src/heretic/info.h b/src/heretic/info.h
index f9581298..236826cb 100644
--- a/src/heretic/info.h
+++ b/src/heretic/info.h
@@ -21,7 +21,9 @@
// 02111-1307, USA.
//
//-----------------------------------------------------------------------------
-// generated by multigen
+
+#ifndef HERETIC_INFO_H
+#define HERETIC_INFO_H
typedef enum
{
@@ -653,6 +655,9 @@ typedef enum
S_PHOENIXFXI1_6,
S_PHOENIXFXI1_7,
S_PHOENIXFXI1_8,
+ S_PHOENIXFXIX_1, // [ States in Heretic 1.0 that were removed
+ S_PHOENIXFXIX_2,
+ S_PHOENIXFXIX_3, // ]
S_PHOENIXPUFF1,
S_PHOENIXPUFF2,
S_PHOENIXPUFF3,
@@ -773,8 +778,8 @@ typedef enum
S_PLAY_FDTH16,
S_PLAY_FDTH17,
S_PLAY_FDTH18,
- S_PLAY_FDTH19,
- S_PLAY_FDTH20,
+ S_PLAY_FDTH19, // < These two frames were not present in the Heretic
+ S_PLAY_FDTH20, // < 1.0 executable (fire death animation was extended)
S_BLOODYSKULL1,
S_BLOODYSKULL2,
S_BLOODYSKULL3,
@@ -1470,6 +1475,7 @@ typedef enum
MT_GOLDWANDPUFF2,
MT_WPHOENIXROD,
MT_PHOENIXFX1,
+ MT_PHOENIXFX_REMOVED, // In Heretic 1.0, but removed.
MT_PHOENIXPUFF,
MT_PHOENIXFX2,
MT_MISC15,
@@ -1575,3 +1581,6 @@ typedef struct
} mobjinfo_t;
extern mobjinfo_t mobjinfo[NUMMOBJTYPES];
+
+#endif /* #ifndef HERETIC_INFO_H */
+
diff --git a/src/heretic/mn_menu.c b/src/heretic/mn_menu.c
index 33024a96..955e0124 100644
--- a/src/heretic/mn_menu.c
+++ b/src/heretic/mn_menu.c
@@ -25,6 +25,8 @@
// MN_menu.c
#include <ctype.h>
+
+#include "deh_str.h"
#include "doomdef.h"
#include "doomkeys.h"
#include "i_system.h"
@@ -73,7 +75,7 @@ typedef struct
{
ItemType_t type;
char *text;
- boolean(*func) (int option);
+ boolean(*func) (int option);
int option;
MenuType_t menu;
} MenuItem_t;
@@ -305,7 +307,7 @@ void MN_Init(void)
InitFonts();
MenuActive = false;
messageson = true;
- SkullBaseLump = W_GetNumForName("M_SKL00");
+ SkullBaseLump = W_GetNumForName(DEH_String("M_SKL00"));
if (gamemode == retail)
{ // Add episodes 4 and 5 to the menu
@@ -322,8 +324,8 @@ void MN_Init(void)
static void InitFonts(void)
{
- FontABaseLump = W_GetNumForName("FONTA_S") + 1;
- FontBBaseLump = W_GetNumForName("FONTB_S") + 1;
+ FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
+ FontBBaseLump = W_GetNumForName(DEH_String("FONTB_S")) + 1;
}
//---------------------------------------------------------------------------
@@ -476,26 +478,28 @@ void MN_Drawer(void)
int x;
int y;
MenuItem_t *item;
+ char *message;
char *selName;
if (MenuActive == false)
{
if (askforquit)
{
- MN_DrTextA(QuitEndMsg[typeofask - 1], 160 -
- MN_TextAWidth(QuitEndMsg[typeofask - 1]) / 2, 80);
+ message = DEH_String(QuitEndMsg[typeofask - 1]);
+
+ MN_DrTextA(message, 160 - MN_TextAWidth(message) / 2, 80);
if (typeofask == 3)
{
MN_DrTextA(SlotText[quicksave - 1], 160 -
MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90);
- MN_DrTextA("?", 160 +
+ MN_DrTextA(DEH_String("?"), 160 +
MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90);
}
if (typeofask == 4)
{
MN_DrTextA(SlotText[quickload - 1], 160 -
MN_TextAWidth(SlotText[quickload - 1]) / 2, 90);
- MN_DrTextA("?", 160 +
+ MN_DrTextA(DEH_String("?"), 160 +
MN_TextAWidth(SlotText[quickload - 1]) / 2, 90);
}
UpdateState |= I_FULLSCRN;
@@ -525,13 +529,13 @@ void MN_Drawer(void)
{
if (item->type != ITT_EMPTY && item->text)
{
- MN_DrTextB(item->text, x, y);
+ MN_DrTextB(DEH_String(item->text), x, y);
}
y += ITEM_HEIGHT;
item++;
}
y = CurrentMenu->y + (CurrentItPos * ITEM_HEIGHT) + SELECTOR_YOFFSET;
- selName = MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2";
+ selName = DEH_String(MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2");
V_DrawPatch(x + SELECTOR_XOFFSET, y,
W_CacheLumpName(selName, PU_CACHE));
}
@@ -548,7 +552,7 @@ static void DrawMainMenu(void)
int frame;
frame = (MenuTime / 3) % 18;
- V_DrawPatch(88, 0, W_CacheLumpName("M_HTIC", PU_CACHE));
+ V_DrawPatch(88, 0, W_CacheLumpName(DEH_String("M_HTIC"), PU_CACHE));
V_DrawPatch(40, 10, W_CacheLumpNum(SkullBaseLump + (17 - frame),
PU_CACHE));
V_DrawPatch(232, 10, W_CacheLumpNum(SkullBaseLump + frame, PU_CACHE));
@@ -597,7 +601,11 @@ static void DrawFilesMenu(void)
static void DrawLoadMenu(void)
{
- MN_DrTextB("LOAD GAME", 160 - MN_TextBWidth("LOAD GAME") / 2, 10);
+ char *title;
+
+ title = DEH_String("LOAD GAME");
+
+ MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10);
if (!slottextloaded)
{
MN_LoadSlotText();
@@ -613,7 +621,11 @@ static void DrawLoadMenu(void)
static void DrawSaveMenu(void)
{
- MN_DrTextB("SAVE GAME", 160 - MN_TextBWidth("SAVE GAME") / 2, 10);
+ char *title;
+
+ title = DEH_String("SAVE GAME");
+
+ MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10);
if (!slottextloaded)
{
MN_LoadSlotText();
@@ -675,7 +687,7 @@ static void DrawFileSlots(Menu_t * menu)
y = menu->y;
for (i = 0; i < 6; i++)
{
- V_DrawPatch(x, y, W_CacheLumpName("M_FSLOT", PU_CACHE));
+ V_DrawPatch(x, y, W_CacheLumpName(DEH_String("M_FSLOT"), PU_CACHE));
if (SlotStatus[i])
{
MN_DrTextA(SlotText[i], x + 5, y + 5);
@@ -694,11 +706,11 @@ static void DrawOptionsMenu(void)
{
if (messageson)
{
- MN_DrTextB("ON", 196, 50);
+ MN_DrTextB(DEH_String("ON"), 196, 50);
}
else
{
- MN_DrTextB("OFF", 196, 50);
+ MN_DrTextB(DEH_String("OFF"), 196, 50);
}
DrawSlider(&OptionsMenu, 3, 10, mouseSensitivity);
}
@@ -796,11 +808,11 @@ static boolean SCMessages(int option)
messageson ^= 1;
if (messageson)
{
- P_SetMessage(&players[consoleplayer], "MESSAGES ON", true);
+ P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES ON"), true);
}
else
{
- P_SetMessage(&players[consoleplayer], "MESSAGES OFF", true);
+ P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES OFF"), true);
}
S_StartSound(NULL, sfx_chat);
return true;
@@ -1460,7 +1472,7 @@ boolean MN_Responder(event_t * event)
if (CurrentMenu->items[i].text)
{
if (toupper(charTyped)
- == toupper(CurrentMenu->items[i].text[0]))
+ == toupper(DEH_String(CurrentMenu->items[i].text)[0]))
{
CurrentItPos = i;
return (true);
@@ -1628,13 +1640,13 @@ static void DrawSlider(Menu_t * menu, int item, int width, int slot)
x = menu->x + 24;
y = menu->y + 2 + (item * ITEM_HEIGHT);
- V_DrawPatch(x - 32, y, W_CacheLumpName("M_SLDLT", PU_CACHE));
+ V_DrawPatch(x - 32, y, W_CacheLumpName(DEH_String("M_SLDLT"), PU_CACHE));
for (x2 = x, count = width; count--; x2 += 8)
{
- V_DrawPatch(x2, y, W_CacheLumpName(count & 1 ? "M_SLDMD1"
- : "M_SLDMD2", PU_CACHE));
+ V_DrawPatch(x2, y, W_CacheLumpName(DEH_String(count & 1 ? "M_SLDMD1"
+ : "M_SLDMD2"), PU_CACHE));
}
- V_DrawPatch(x2, y, W_CacheLumpName("M_SLDRT", PU_CACHE));
+ V_DrawPatch(x2, y, W_CacheLumpName(DEH_String("M_SLDRT"), PU_CACHE));
V_DrawPatch(x + 4 + slot * 8, y + 7,
- W_CacheLumpName("M_SLDKB", PU_CACHE));
+ W_CacheLumpName(DEH_String("M_SLDKB"), PU_CACHE));
}
diff --git a/src/heretic/p_action.h b/src/heretic/p_action.h
new file mode 100644
index 00000000..8d8e383c
--- /dev/null
+++ b/src/heretic/p_action.h
@@ -0,0 +1,160 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// External definitions for action pointer functions.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef HERETIC_P_ACTION_H
+#define HERETIC_P_ACTION_H
+
+void A_FreeTargMobj();
+void A_RestoreSpecialThing1();
+void A_RestoreSpecialThing2();
+void A_HideThing();
+void A_UnHideThing();
+void A_RestoreArtifact();
+void A_Scream();
+void A_Explode();
+void A_PodPain();
+void A_RemovePod();
+void A_MakePod();
+void A_InitKeyGizmo();
+void A_VolcanoSet();
+void A_VolcanoBlast();
+void A_BeastPuff();
+void A_VolcBallImpact();
+void A_SpawnTeleGlitter();
+void A_SpawnTeleGlitter2();
+void A_AccTeleGlitter();
+void A_Light0();
+void A_WeaponReady();
+void A_Lower();
+void A_Raise();
+void A_StaffAttackPL1();
+void A_ReFire();
+void A_StaffAttackPL2();
+void A_BeakReady();
+void A_BeakRaise();
+void A_BeakAttackPL1();
+void A_BeakAttackPL2();
+void A_GauntletAttack();
+void A_FireBlasterPL1();
+void A_FireBlasterPL2();
+void A_SpawnRippers();
+void A_FireMacePL1();
+void A_FireMacePL2();
+void A_MacePL1Check();
+void A_MaceBallImpact();
+void A_MaceBallImpact2();
+void A_DeathBallImpact();
+void A_FireSkullRodPL1();
+void A_FireSkullRodPL2();
+void A_SkullRodPL2Seek();
+void A_AddPlayerRain();
+void A_HideInCeiling();
+void A_SkullRodStorm();
+void A_RainImpact();
+void A_FireGoldWandPL1();
+void A_FireGoldWandPL2();
+void A_FirePhoenixPL1();
+void A_InitPhoenixPL2();
+void A_FirePhoenixPL2();
+void A_ShutdownPhoenixPL2();
+void A_PhoenixPuff();
+void A_RemovedPhoenixFunc();
+void A_FlameEnd();
+void A_FloatPuff();
+void A_FireCrossbowPL1();
+void A_FireCrossbowPL2();
+void A_BoltSpark();
+void A_Pain();
+void A_NoBlocking();
+void A_AddPlayerCorpse();
+void A_SkullPop();
+void A_FlameSnd();
+void A_CheckBurnGone();
+void A_CheckSkullFloor();
+void A_CheckSkullDone();
+void A_Feathers();
+void A_ChicLook();
+void A_ChicChase();
+void A_ChicPain();
+void A_FaceTarget();
+void A_ChicAttack();
+void A_Look();
+void A_Chase();
+void A_MummyAttack();
+void A_MummyAttack2();
+void A_MummySoul();
+void A_ContMobjSound();
+void A_MummyFX1Seek();
+void A_BeastAttack();
+void A_SnakeAttack();
+void A_SnakeAttack2();
+void A_HeadAttack();
+void A_BossDeath();
+void A_HeadIceImpact();
+void A_HeadFireGrow();
+void A_WhirlwindSeek();
+void A_ClinkAttack();
+void A_WizAtk1();
+void A_WizAtk2();
+void A_WizAtk3();
+void A_GhostOff();
+void A_ImpMeAttack();
+void A_ImpMsAttack();
+void A_ImpMsAttack2();
+void A_ImpDeath();
+void A_ImpXDeath1();
+void A_ImpXDeath2();
+void A_ImpExplode();
+void A_KnightAttack();
+void A_DripBlood();
+void A_Sor1Chase();
+void A_Sor1Pain();
+void A_Srcr1Attack();
+void A_SorZap();
+void A_SorcererRise();
+void A_SorRise();
+void A_SorSightSnd();
+void A_Srcr2Decide();
+void A_Srcr2Attack();
+void A_Sor2DthInit();
+void A_SorDSph();
+void A_Sor2DthLoop();
+void A_SorDExp();
+void A_SorDBon();
+void A_BlueSpark();
+void A_GenWizard();
+void A_MinotaurAtk1();
+void A_MinotaurDecide();
+void A_MinotaurAtk2();
+void A_MinotaurAtk3();
+void A_MinotaurCharge();
+void A_MntrFloorFire();
+void A_ESound();
+
+#endif /* #ifndef HERETIC_P_ACTION_H */
+
diff --git a/src/heretic/p_doors.c b/src/heretic/p_doors.c
index e0e82fbe..56e37119 100644
--- a/src/heretic/p_doors.c
+++ b/src/heretic/p_doors.c
@@ -25,6 +25,7 @@
// P_doors.c
#include "doomdef.h"
+#include "deh_str.h"
#include "p_local.h"
#include "s_sound.h"
#include "v_video.h"
@@ -232,7 +233,7 @@ void EV_VerticalDoor(line_t * line, mobj_t * thing)
}
if (!player->keys[key_blue])
{
- P_SetMessage(player, TXT_NEEDBLUEKEY, false);
+ P_SetMessage(player, DEH_String(TXT_NEEDBLUEKEY), false);
S_StartSound(NULL, sfx_plroof);
return;
}
@@ -245,7 +246,7 @@ void EV_VerticalDoor(line_t * line, mobj_t * thing)
}
if (!player->keys[key_yellow])
{
- P_SetMessage(player, TXT_NEEDYELLOWKEY, false);
+ P_SetMessage(player, DEH_String(TXT_NEEDYELLOWKEY), false);
S_StartSound(NULL, sfx_plroof);
return;
}
@@ -258,7 +259,7 @@ void EV_VerticalDoor(line_t * line, mobj_t * thing)
}
if (!player->keys[key_green])
{
- P_SetMessage(player, TXT_NEEDGREENKEY, false);
+ P_SetMessage(player, DEH_String(TXT_NEEDGREENKEY), false);
S_StartSound(NULL, sfx_plroof);
return;
}
diff --git a/src/heretic/p_inter.c b/src/heretic/p_inter.c
index fba6d215..afdb37f2 100644
--- a/src/heretic/p_inter.c
+++ b/src/heretic/p_inter.c
@@ -25,6 +25,7 @@
// P_inter.c
#include "doomdef.h"
+#include "deh_str.h"
#include "i_system.h"
#include "i_timer.h"
#include "m_random.h"
@@ -54,7 +55,7 @@ int maxammo[NUMAMMO] = {
150 // mace
};
-static int GetWeaponAmmo[NUMWEAPONS] = {
+int GetWeaponAmmo[NUMWEAPONS] = {
0, // staff
25, // gold wand
10, // crossbow
@@ -580,21 +581,21 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_ITEMHEALTH, false);
+ P_SetMessage(player, DEH_String(TXT_ITEMHEALTH), false);
break;
case SPR_SHLD: // Item_Shield1
if (!P_GiveArmor(player, 1))
{
return;
}
- P_SetMessage(player, TXT_ITEMSHIELD1, false);
+ P_SetMessage(player, DEH_String(TXT_ITEMSHIELD1), false);
break;
case SPR_SHD2: // Item_Shield2
if (!P_GiveArmor(player, 2))
{
return;
}
- P_SetMessage(player, TXT_ITEMSHIELD2, false);
+ P_SetMessage(player, DEH_String(TXT_ITEMSHIELD2), false);
break;
case SPR_BAGH: // Item_BagOfHolding
if (!player->backpack)
@@ -610,21 +611,21 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY);
P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY);
P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY);
- P_SetMessage(player, TXT_ITEMBAGOFHOLDING, false);
+ P_SetMessage(player, DEH_String(TXT_ITEMBAGOFHOLDING), false);
break;
case SPR_SPMP: // Item_SuperMap
if (!P_GivePower(player, pw_allmap))
{
return;
}
- P_SetMessage(player, TXT_ITEMSUPERMAP, false);
+ P_SetMessage(player, DEH_String(TXT_ITEMSUPERMAP), false);
break;
// Keys
case SPR_BKYY: // Key_Blue
if (!player->keys[key_blue])
{
- P_SetMessage(player, TXT_GOTBLUEKEY, false);
+ P_SetMessage(player, DEH_String(TXT_GOTBLUEKEY), false);
}
P_GiveKey(player, key_blue);
sound = sfx_keyup;
@@ -636,7 +637,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
case SPR_CKYY: // Key_Yellow
if (!player->keys[key_yellow])
{
- P_SetMessage(player, TXT_GOTYELLOWKEY, false);
+ P_SetMessage(player, DEH_String(TXT_GOTYELLOWKEY), false);
}
sound = sfx_keyup;
P_GiveKey(player, key_yellow);
@@ -648,7 +649,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
case SPR_AKYY: // Key_Green
if (!player->keys[key_green])
{
- P_SetMessage(player, TXT_GOTGREENKEY, false);
+ P_SetMessage(player, DEH_String(TXT_GOTGREENKEY), false);
}
sound = sfx_keyup;
P_GiveKey(player, key_green);
@@ -662,70 +663,70 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
case SPR_PTN2: // Arti_HealingPotion
if (P_GiveArtifact(player, arti_health, special))
{
- P_SetMessage(player, TXT_ARTIHEALTH, false);
+ P_SetMessage(player, DEH_String(TXT_ARTIHEALTH), false);
P_SetDormantArtifact(special);
}
return;
case SPR_SOAR: // Arti_Fly
if (P_GiveArtifact(player, arti_fly, special))
{
- P_SetMessage(player, TXT_ARTIFLY, false);
+ P_SetMessage(player, DEH_String(TXT_ARTIFLY), false);
P_SetDormantArtifact(special);
}
return;
case SPR_INVU: // Arti_Invulnerability
if (P_GiveArtifact(player, arti_invulnerability, special))
{
- P_SetMessage(player, TXT_ARTIINVULNERABILITY, false);
+ P_SetMessage(player, DEH_String(TXT_ARTIINVULNERABILITY), false);
P_SetDormantArtifact(special);
}
return;
case SPR_PWBK: // Arti_TomeOfPower
if (P_GiveArtifact(player, arti_tomeofpower, special))
{
- P_SetMessage(player, TXT_ARTITOMEOFPOWER, false);
+ P_SetMessage(player, DEH_String(TXT_ARTITOMEOFPOWER), false);
P_SetDormantArtifact(special);
}
return;
case SPR_INVS: // Arti_Invisibility
if (P_GiveArtifact(player, arti_invisibility, special))
{
- P_SetMessage(player, TXT_ARTIINVISIBILITY, false);
+ P_SetMessage(player, DEH_String(TXT_ARTIINVISIBILITY), false);
P_SetDormantArtifact(special);
}
return;
case SPR_EGGC: // Arti_Egg
if (P_GiveArtifact(player, arti_egg, special))
{
- P_SetMessage(player, TXT_ARTIEGG, false);
+ P_SetMessage(player, DEH_String(TXT_ARTIEGG), false);
P_SetDormantArtifact(special);
}
return;
case SPR_SPHL: // Arti_SuperHealth
if (P_GiveArtifact(player, arti_superhealth, special))
{
- P_SetMessage(player, TXT_ARTISUPERHEALTH, false);
+ P_SetMessage(player, DEH_String(TXT_ARTISUPERHEALTH), false);
P_SetDormantArtifact(special);
}
return;
case SPR_TRCH: // Arti_Torch
if (P_GiveArtifact(player, arti_torch, special))
{
- P_SetMessage(player, TXT_ARTITORCH, false);
+ P_SetMessage(player, DEH_String(TXT_ARTITORCH), false);
P_SetDormantArtifact(special);
}
return;
case SPR_FBMB: // Arti_FireBomb
if (P_GiveArtifact(player, arti_firebomb, special))
{
- P_SetMessage(player, TXT_ARTIFIREBOMB, false);
+ P_SetMessage(player, DEH_String(TXT_ARTIFIREBOMB), false);
P_SetDormantArtifact(special);
}
return;
case SPR_ATLP: // Arti_Teleport
if (P_GiveArtifact(player, arti_teleport, special))
{
- P_SetMessage(player, TXT_ARTITELEPORT, false);
+ P_SetMessage(player, DEH_String(TXT_ARTITELEPORT), false);
P_SetDormantArtifact(special);
}
return;
@@ -736,84 +737,84 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_AMMOGOLDWAND1, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND1), false);
break;
case SPR_AMG2: // Ammo_GoldWandHefty
if (!P_GiveAmmo(player, am_goldwand, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOGOLDWAND2, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND2), false);
break;
case SPR_AMM1: // Ammo_MaceWimpy
if (!P_GiveAmmo(player, am_mace, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOMACE1, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOMACE1), false);
break;
case SPR_AMM2: // Ammo_MaceHefty
if (!P_GiveAmmo(player, am_mace, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOMACE2, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOMACE2), false);
break;
case SPR_AMC1: // Ammo_CrossbowWimpy
if (!P_GiveAmmo(player, am_crossbow, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOCROSSBOW1, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW1), false);
break;
case SPR_AMC2: // Ammo_CrossbowHefty
if (!P_GiveAmmo(player, am_crossbow, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOCROSSBOW2, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW2), false);
break;
case SPR_AMB1: // Ammo_BlasterWimpy
if (!P_GiveAmmo(player, am_blaster, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOBLASTER1, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOBLASTER1), false);
break;
case SPR_AMB2: // Ammo_BlasterHefty
if (!P_GiveAmmo(player, am_blaster, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOBLASTER2, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOBLASTER2), false);
break;
case SPR_AMS1: // Ammo_SkullRodWimpy
if (!P_GiveAmmo(player, am_skullrod, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOSKULLROD1, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD1), false);
break;
case SPR_AMS2: // Ammo_SkullRodHefty
if (!P_GiveAmmo(player, am_skullrod, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOSKULLROD2, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD2), false);
break;
case SPR_AMP1: // Ammo_PhoenixRodWimpy
if (!P_GiveAmmo(player, am_phoenixrod, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOPHOENIXROD1, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD1), false);
break;
case SPR_AMP2: // Ammo_PhoenixRodHefty
if (!P_GiveAmmo(player, am_phoenixrod, special->health))
{
return;
}
- P_SetMessage(player, TXT_AMMOPHOENIXROD2, false);
+ P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD2), false);
break;
// Weapons
@@ -822,7 +823,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_WPNMACE, false);
+ P_SetMessage(player, DEH_String(TXT_WPNMACE), false);
sound = sfx_wpnup;
break;
case SPR_WBOW: // Weapon_Crossbow
@@ -830,7 +831,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_WPNCROSSBOW, false);
+ P_SetMessage(player, DEH_String(TXT_WPNCROSSBOW), false);
sound = sfx_wpnup;
break;
case SPR_WBLS: // Weapon_Blaster
@@ -838,7 +839,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_WPNBLASTER, false);
+ P_SetMessage(player, DEH_String(TXT_WPNBLASTER), false);
sound = sfx_wpnup;
break;
case SPR_WSKL: // Weapon_SkullRod
@@ -846,7 +847,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_WPNSKULLROD, false);
+ P_SetMessage(player, DEH_String(TXT_WPNSKULLROD), false);
sound = sfx_wpnup;
break;
case SPR_WPHX: // Weapon_PhoenixRod
@@ -854,7 +855,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_WPNPHOENIXROD, false);
+ P_SetMessage(player, DEH_String(TXT_WPNPHOENIXROD), false);
sound = sfx_wpnup;
break;
case SPR_WGNT: // Weapon_Gauntlets
@@ -862,7 +863,7 @@ void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
{
return;
}
- P_SetMessage(player, TXT_WPNGAUNTLETS, false);
+ P_SetMessage(player, DEH_String(TXT_WPNGAUNTLETS), false);
sound = sfx_wpnup;
break;
default:
diff --git a/src/heretic/p_pspr.c b/src/heretic/p_pspr.c
index 7f9660ac..bd47c9a1 100644
--- a/src/heretic/p_pspr.c
+++ b/src/heretic/p_pspr.c
@@ -1650,6 +1650,17 @@ void A_PhoenixPuff(mobj_t * actor)
puff->momz = 0;
}
+//
+// This function was present in the Heretic 1.0 executable for the
+// removed "secondary phoenix flash" object (MT_PHOENIXFX_REMOVED).
+// The purpose of this object is unknown, as is this function.
+//
+
+void A_RemovedPhoenixFunc(mobj_t *actor)
+{
+ I_Error("Action function invoked for removed Phoenix action!");
+}
+
//----------------------------------------------------------------------------
//
// PROC A_InitPhoenixPL2
diff --git a/src/heretic/p_spec.c b/src/heretic/p_spec.c
index 6227237b..49c067fa 100644
--- a/src/heretic/p_spec.c
+++ b/src/heretic/p_spec.c
@@ -25,6 +25,7 @@
// P_Spec.c
#include "doomdef.h"
+#include "deh_str.h"
#include "i_system.h"
#include "i_timer.h"
#include "m_random.h"
@@ -204,18 +205,12 @@ struct
int type;
} TerrainTypeDefs[] =
{
- {
- "FLTWAWA1", FLOOR_WATER},
- {
- "FLTFLWW1", FLOOR_WATER},
- {
- "FLTLAVA1", FLOOR_LAVA},
- {
- "FLATHUH1", FLOOR_LAVA},
- {
- "FLTSLUD1", FLOOR_SLUDGE},
- {
- "END", -1}
+ { "FLTWAWA1", FLOOR_WATER },
+ { "FLTFLWW1", FLOOR_WATER },
+ { "FLTLAVA1", FLOOR_LAVA },
+ { "FLATHUH1", FLOOR_LAVA },
+ { "FLTSLUD1", FLOOR_SLUDGE },
+ { "END", -1 }
};
mobj_t LavaInflictor;
@@ -266,35 +261,40 @@ void P_InitTerrainTypes(void)
void P_InitPicAnims(void)
{
+ char *startname;
+ char *endname;
int i;
lastanim = anims;
for (i = 0; animdefs[i].istexture != -1; i++)
{
+ startname = DEH_String(animdefs[i].startname);
+ endname = DEH_String(animdefs[i].endname);
+
if (animdefs[i].istexture)
{ // Texture animation
- if (R_CheckTextureNumForName(animdefs[i].startname) == -1)
+ if (R_CheckTextureNumForName(startname) == -1)
{ // Texture doesn't exist
continue;
}
- lastanim->picnum = R_TextureNumForName(animdefs[i].endname);
- lastanim->basepic = R_TextureNumForName(animdefs[i].startname);
+ lastanim->picnum = R_TextureNumForName(endname);
+ lastanim->basepic = R_TextureNumForName(startname);
}
else
{ // Flat animation
- if (W_CheckNumForName(animdefs[i].startname) == -1)
+ if (W_CheckNumForName(startname) == -1)
{ // Flat doesn't exist
continue;
}
- lastanim->picnum = R_FlatNumForName(animdefs[i].endname);
- lastanim->basepic = R_FlatNumForName(animdefs[i].startname);
+ lastanim->picnum = R_FlatNumForName(endname);
+ lastanim->basepic = R_FlatNumForName(startname);
}
lastanim->istexture = animdefs[i].istexture;
lastanim->numpics = lastanim->picnum - lastanim->basepic + 1;
if (lastanim->numpics < 2)
{
I_Error("P_InitPicAnims: bad cycle from %s to %s",
- animdefs[i].startname, animdefs[i].endname);
+ startname, endname);
}
lastanim->speed = animdefs[i].speed;
lastanim++;
@@ -1132,7 +1132,7 @@ void P_SpawnSpecials(void)
int episode;
episode = 1;
- if (W_CheckNumForName("texture2") >= 0)
+ if (W_CheckNumForName(DEH_String("texture2")) >= 0)
episode = 2;
//
diff --git a/src/heretic/p_switch.c b/src/heretic/p_switch.c
index cef6a74b..2ec758b6 100644
--- a/src/heretic/p_switch.c
+++ b/src/heretic/p_switch.c
@@ -23,6 +23,7 @@
//-----------------------------------------------------------------------------
#include "doomdef.h"
+#include "deh_str.h"
#include "i_system.h"
#include "p_local.h"
#include "s_sound.h"
@@ -129,9 +130,9 @@ void P_InitSwitchList(void)
if (alphSwitchList[i].episode <= episode)
{
switchlist[index++] =
- R_TextureNumForName(alphSwitchList[i].name1);
+ R_TextureNumForName(DEH_String(alphSwitchList[i].name1));
switchlist[index++] =
- R_TextureNumForName(alphSwitchList[i].name2);
+ R_TextureNumForName(DEH_String(alphSwitchList[i].name2));
}
}
}
diff --git a/src/heretic/p_user.c b/src/heretic/p_user.c
index 63368bab..16dbed49 100644
--- a/src/heretic/p_user.c
+++ b/src/heretic/p_user.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include "doomdef.h"
+#include "deh_str.h"
#include "m_random.h"
#include "p_local.h"
#include "s_sound.h"
@@ -394,7 +395,7 @@ void P_DeathThink(player_t * player)
{
if (player == &players[consoleplayer])
{
- I_SetPalette((byte *) W_CacheLumpName("PLAYPAL", PU_CACHE));
+ I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE));
inv_ptr = 0;
curpos = 0;
newtorch = 0;
diff --git a/src/heretic/r_data.c b/src/heretic/r_data.c
index ee005248..5bb79aa4 100644
--- a/src/heretic/r_data.c
+++ b/src/heretic/r_data.c
@@ -25,6 +25,8 @@
// R_data.c
#include "doomdef.h"
+#include "deh_str.h"
+
#include "i_swap.h"
#include "i_system.h"
#include "r_local.h"
@@ -316,12 +318,17 @@ void R_InitTextures(void)
int offset, maxoff, maxoff2;
int numtextures1, numtextures2;
int *directory;
+ char *texture1, *texture2, *pnames;
+
+ texture1 = DEH_String("TEXTURE1");
+ texture2 = DEH_String("TEXTURE2");
+ pnames = DEH_String("PNAMES");
//
// load the patch names from pnames.lmp
//
name[8] = 0;
- names = W_CacheLumpName("PNAMES", PU_STATIC);
+ names = W_CacheLumpName(pnames, PU_STATIC);
nummappatches = LONG(*((int *) names));
name_p = names + 4;
patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL);
@@ -330,21 +337,21 @@ void R_InitTextures(void)
strncpy(name, name_p + i * 8, 8);
patchlookup[i] = W_CheckNumForName(name);
}
- W_ReleaseLumpName("PNAMES");
+ W_ReleaseLumpName(pnames);
//
// load the map texture definitions from textures.lmp
//
- maptex = maptex1 = W_CacheLumpName("TEXTURE1", PU_STATIC);
+ maptex = maptex1 = W_CacheLumpName(texture1, PU_STATIC);
numtextures1 = LONG(*maptex);
- maxoff = W_LumpLength(W_GetNumForName("TEXTURE1"));
+ maxoff = W_LumpLength(W_GetNumForName(texture1));
directory = maptex + 1;
- if (W_CheckNumForName("TEXTURE2") != -1)
+ if (W_CheckNumForName(texture2) != -1)
{
- maptex2 = W_CacheLumpName("TEXTURE2", PU_STATIC);
+ maptex2 = W_CacheLumpName(texture2, PU_STATIC);
numtextures2 = LONG(*maptex2);
- maxoff2 = W_LumpLength(W_GetNumForName("TEXTURE2"));
+ maxoff2 = W_LumpLength(W_GetNumForName(texture2));
}
else
{
@@ -358,8 +365,11 @@ void R_InitTextures(void)
// Init the startup thermometer at this point...
//
{
+ int start, end;
int spramount;
- spramount = W_GetNumForName("S_END") - W_GetNumForName("S_START") + 1;
+ start = W_GetNumForName(DEH_String("S_START"));
+ end = W_GetNumForName(DEH_String("S_END"));
+ spramount = end - start + 1;
InitThermo(spramount + numtextures + 6);
}
@@ -427,10 +437,10 @@ void R_InitTextures(void)
Z_Free(patchlookup);
- W_ReleaseLumpName("TEXTURE1");
+ W_ReleaseLumpName(texture1);
if (maptex2)
{
- W_ReleaseLumpName("TEXTURE2");
+ W_ReleaseLumpName(texture2);
}
//
@@ -463,8 +473,8 @@ void R_InitFlats(void)
{
int i;
- firstflat = W_GetNumForName("F_START") + 1;
- lastflat = W_GetNumForName("F_END") - 1;
+ firstflat = W_GetNumForName(DEH_String("F_START")) + 1;
+ lastflat = W_GetNumForName(DEH_String("F_END")) - 1;
numflats = lastflat - firstflat + 1;
// translation table for global animation
@@ -489,8 +499,8 @@ void R_InitSpriteLumps(void)
int i;
patch_t *patch;
- firstspritelump = W_GetNumForName("S_START") + 1;
- lastspritelump = W_GetNumForName("S_END") - 1;
+ firstspritelump = W_GetNumForName(DEH_String("S_START")) + 1;
+ lastspritelump = W_GetNumForName(DEH_String("S_END")) - 1;
numspritelumps = lastspritelump - firstspritelump + 1;
spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0);
spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0);
@@ -527,7 +537,7 @@ void R_InitColormaps(void)
// load in the light tables
// 256 byte align tables
//
- lump = W_GetNumForName("COLORMAP");
+ lump = W_GetNumForName(DEH_String("COLORMAP"));
length = W_LumpLength(lump);
colormaps = Z_Malloc(length, PU_STATIC, 0);
W_ReadLump(lump, colormaps);
diff --git a/src/heretic/r_draw.c b/src/heretic/r_draw.c
index 5a20b50a..88653df1 100644
--- a/src/heretic/r_draw.c
+++ b/src/heretic/r_draw.c
@@ -24,6 +24,7 @@
// R_draw.c
#include "doomdef.h"
+#include "deh_str.h"
#include "r_local.h"
#include "i_video.h"
#include "v_video.h"
@@ -386,11 +387,11 @@ void R_DrawViewBorder(void)
if (gamemode == shareware)
{
- src = W_CacheLumpName("FLOOR04", PU_CACHE);
+ src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE);
}
else
{
- src = W_CacheLumpName("FLAT513", PU_CACHE);
+ src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE);
}
dest = I_VideoBuffer;
@@ -409,24 +410,26 @@ void R_DrawViewBorder(void)
}
for (x = viewwindowx; x < viewwindowx + viewwidth; x += 16)
{
- V_DrawPatch(x, viewwindowy - 4, W_CacheLumpName("bordt", PU_CACHE));
- V_DrawPatch(x, viewwindowy + viewheight, W_CacheLumpName("bordb",
- PU_CACHE));
+ V_DrawPatch(x, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordt"), PU_CACHE));
+ V_DrawPatch(x, viewwindowy + viewheight,
+ W_CacheLumpName(DEH_String("bordb"), PU_CACHE));
}
for (y = viewwindowy; y < viewwindowy + viewheight; y += 16)
{
- V_DrawPatch(viewwindowx - 4, y, W_CacheLumpName("bordl", PU_CACHE));
- V_DrawPatch(viewwindowx + viewwidth, y, W_CacheLumpName("bordr",
- PU_CACHE));
+ V_DrawPatch(viewwindowx - 4, y,
+ W_CacheLumpName(DEH_String("bordl"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, y,
+ W_CacheLumpName(DEH_String("bordr"), PU_CACHE));
}
- V_DrawPatch(viewwindowx - 4, viewwindowy - 4, W_CacheLumpName("bordtl",
- PU_CACHE));
+ V_DrawPatch(viewwindowx - 4, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordtl"), PU_CACHE));
V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4,
- W_CacheLumpName("bordtr", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordtr"), PU_CACHE));
V_DrawPatch(viewwindowx + viewwidth, viewwindowy + viewheight,
- W_CacheLumpName("bordbr", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordbr"), PU_CACHE));
V_DrawPatch(viewwindowx - 4, viewwindowy + viewheight,
- W_CacheLumpName("bordbl", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordbl"), PU_CACHE));
}
/*
@@ -450,11 +453,11 @@ void R_DrawTopBorder(void)
if (gamemode == shareware)
{
- src = W_CacheLumpName("FLOOR04", PU_CACHE);
+ src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE);
}
else
{
- src = W_CacheLumpName("FLAT513", PU_CACHE);
+ src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE);
}
dest = I_VideoBuffer;
@@ -476,20 +479,20 @@ void R_DrawTopBorder(void)
for (x = viewwindowx; x < viewwindowx + viewwidth; x += 16)
{
V_DrawPatch(x, viewwindowy - 4,
- W_CacheLumpName("bordt", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordt"), PU_CACHE));
}
- V_DrawPatch(viewwindowx - 4, viewwindowy, W_CacheLumpName("bordl",
- PU_CACHE));
+ V_DrawPatch(viewwindowx - 4, viewwindowy,
+ W_CacheLumpName(DEH_String("bordl"), PU_CACHE));
V_DrawPatch(viewwindowx + viewwidth, viewwindowy,
- W_CacheLumpName("bordr", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordr"), PU_CACHE));
V_DrawPatch(viewwindowx - 4, viewwindowy + 16,
- W_CacheLumpName("bordl", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordl"), PU_CACHE));
V_DrawPatch(viewwindowx + viewwidth, viewwindowy + 16,
- W_CacheLumpName("bordr", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordr"), PU_CACHE));
V_DrawPatch(viewwindowx - 4, viewwindowy - 4,
- W_CacheLumpName("bordtl", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordtl"), PU_CACHE));
V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4,
- W_CacheLumpName("bordtr", PU_CACHE));
+ W_CacheLumpName(DEH_String("bordtr"), PU_CACHE));
}
}
diff --git a/src/heretic/r_plane.c b/src/heretic/r_plane.c
index e35e2931..cc86b118 100644
--- a/src/heretic/r_plane.c
+++ b/src/heretic/r_plane.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include "doomdef.h"
+#include "deh_str.h"
#include "i_system.h"
#include "r_local.h"
@@ -90,7 +91,7 @@ fixed_t cachedystep[SCREENHEIGHT];
void R_InitSkyMap(void)
{
- skyflatnum = R_FlatNumForName("F_SKY1");
+ skyflatnum = R_FlatNumForName(DEH_String("F_SKY1"));
skytexturemid = 200 * FRACUNIT;
skyiscale = FRACUNIT;
}
diff --git a/src/heretic/r_things.c b/src/heretic/r_things.c
index 6302303e..e5ff4b4b 100644
--- a/src/heretic/r_things.c
+++ b/src/heretic/r_things.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "doomdef.h"
+#include "deh_str.h"
#include "i_swap.h"
#include "i_system.h"
#include "r_local.h"
@@ -154,7 +155,7 @@ void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation,
void R_InitSpriteDefs(char **namelist)
{
char **check;
- int i, l, intname, frame, rotation;
+ int i, l, frame, rotation;
int start, end;
// count the number of sprite names
@@ -176,17 +177,16 @@ void R_InitSpriteDefs(char **namelist)
// Just compare 4 characters as ints
for (i = 0; i < numsprites; i++)
{
- spritename = namelist[i];
+ spritename = DEH_String(namelist[i]);
memset(sprtemp, -1, sizeof(sprtemp));
maxframe = -1;
- intname = *(int *) namelist[i];
//
// scan the lumps, filling in the frames for whatever is found
//
for (l = start + 1; l < end; l++)
- if (*(int *) lumpinfo[l].name == intname)
+ if (!strncasecmp(lumpinfo[l].name, spritename, 4))
{
frame = lumpinfo[l].name[4] - 'A';
rotation = lumpinfo[l].name[5] - '0';
@@ -209,7 +209,7 @@ void R_InitSpriteDefs(char **namelist)
if (gamemode == shareware)
continue;
I_Error("R_InitSprites: No lumps found for sprite %s",
- namelist[i]);
+ spritename);
}
maxframe++;
@@ -219,7 +219,7 @@ void R_InitSpriteDefs(char **namelist)
{
case -1: // no rotations were found for that frame at all
I_Error("R_InitSprites: No patches found for %s frame %c",
- namelist[i], frame + 'A');
+ spritename, frame + 'A');
case 0: // only the first rotation is needed
break;
@@ -228,7 +228,7 @@ void R_InitSpriteDefs(char **namelist)
if (sprtemp[frame].lump[rotation] == -1)
I_Error
("R_InitSprites: Sprite %s frame %c is missing rotations",
- namelist[i], frame + 'A');
+ spritename, frame + 'A');
}
}
diff --git a/src/heretic/s_sound.c b/src/heretic/s_sound.c
index bdd24594..73942201 100644
--- a/src/heretic/s_sound.c
+++ b/src/heretic/s_sound.c
@@ -33,6 +33,8 @@
#include "r_local.h"
#include "p_local.h"
+#include "sounds.h"
+
#include "w_wad.h"
#include "z_zone.h"
@@ -55,9 +57,6 @@ int mus_lumpnum;
void *mus_sndptr;
byte *soundCurve;
-extern sfxinfo_t S_sfx[];
-extern musicinfo_t S_music[];
-
int snd_MaxVolume = 10;
int snd_MusicVolume = 10;
int snd_Channels = 16;
@@ -529,7 +528,7 @@ void S_Init(void)
{
snd_Channels = 8;
}
- I_SetMusicVolume(snd_MusicVolume);
+ I_SetMusicVolume(snd_MusicVolume * 8);
S_SetMaxVolume(true);
I_AtExit(S_ShutDown, true);
@@ -550,8 +549,16 @@ void S_GetChannelInfo(SoundInfo_t * s)
c->priority = channel[i].priority;
c->name = S_sfx[c->id].name;
c->mo = channel[i].mo;
- c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy)
- >> FRACBITS;
+
+ if (c->mo != NULL)
+ {
+ c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy)
+ >> FRACBITS;
+ }
+ else
+ {
+ c->distance = 0;
+ }
}
}
@@ -579,7 +586,7 @@ void S_SetMaxVolume(boolean fullprocess)
static boolean musicPaused;
void S_SetMusicVolume(void)
{
- I_SetMusicVolume(snd_MusicVolume);
+ I_SetMusicVolume(snd_MusicVolume * 8);
if (snd_MusicVolume == 0)
{
I_PauseSong();
diff --git a/src/heretic/sb_bar.c b/src/heretic/sb_bar.c
index 77bd40c2..51bd3b9c 100644
--- a/src/heretic/sb_bar.c
+++ b/src/heretic/sb_bar.c
@@ -25,6 +25,7 @@
// SB_bar.c
#include "doomdef.h"
+#include "deh_str.h"
#include "i_video.h"
#include "m_cheat.h"
#include "m_misc.h"
@@ -196,53 +197,53 @@ void SB_Init(void)
int i;
int startLump;
- PatchLTFACE = W_CacheLumpName("LTFACE", PU_STATIC);
- PatchRTFACE = W_CacheLumpName("RTFACE", PU_STATIC);
- PatchBARBACK = W_CacheLumpName("BARBACK", PU_STATIC);
- PatchINVBAR = W_CacheLumpName("INVBAR", PU_STATIC);
- PatchCHAIN = W_CacheLumpName("CHAIN", PU_STATIC);
+ PatchLTFACE = W_CacheLumpName(DEH_String("LTFACE"), PU_STATIC);
+ PatchRTFACE = W_CacheLumpName(DEH_String("RTFACE"), PU_STATIC);
+ PatchBARBACK = W_CacheLumpName(DEH_String("BARBACK"), PU_STATIC);
+ PatchINVBAR = W_CacheLumpName(DEH_String("INVBAR"), PU_STATIC);
+ PatchCHAIN = W_CacheLumpName(DEH_String("CHAIN"), PU_STATIC);
if (deathmatch)
{
- PatchSTATBAR = W_CacheLumpName("STATBAR", PU_STATIC);
+ PatchSTATBAR = W_CacheLumpName(DEH_String("STATBAR"), PU_STATIC);
}
else
{
- PatchSTATBAR = W_CacheLumpName("LIFEBAR", PU_STATIC);
+ PatchSTATBAR = W_CacheLumpName(DEH_String("LIFEBAR"), PU_STATIC);
}
if (!netgame)
{ // single player game uses red life gem
- PatchLIFEGEM = W_CacheLumpName("LIFEGEM2", PU_STATIC);
+ PatchLIFEGEM = W_CacheLumpName(DEH_String("LIFEGEM2"), PU_STATIC);
}
else
{
- PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("LIFEGEM0")
+ PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName(DEH_String("LIFEGEM0"))
+ consoleplayer, PU_STATIC);
}
- PatchLTFCTOP = W_CacheLumpName("LTFCTOP", PU_STATIC);
- PatchRTFCTOP = W_CacheLumpName("RTFCTOP", PU_STATIC);
- PatchSELECTBOX = W_CacheLumpName("SELECTBOX", PU_STATIC);
- PatchINVLFGEM1 = W_CacheLumpName("INVGEML1", PU_STATIC);
- PatchINVLFGEM2 = W_CacheLumpName("INVGEML2", PU_STATIC);
- PatchINVRTGEM1 = W_CacheLumpName("INVGEMR1", PU_STATIC);
- PatchINVRTGEM2 = W_CacheLumpName("INVGEMR2", PU_STATIC);
- PatchBLACKSQ = W_CacheLumpName("BLACKSQ", PU_STATIC);
- PatchARMCLEAR = W_CacheLumpName("ARMCLEAR", PU_STATIC);
- PatchCHAINBACK = W_CacheLumpName("CHAINBACK", PU_STATIC);
- startLump = W_GetNumForName("IN0");
+ PatchLTFCTOP = W_CacheLumpName(DEH_String("LTFCTOP"), PU_STATIC);
+ PatchRTFCTOP = W_CacheLumpName(DEH_String("RTFCTOP"), PU_STATIC);
+ PatchSELECTBOX = W_CacheLumpName(DEH_String("SELECTBOX"), PU_STATIC);
+ PatchINVLFGEM1 = W_CacheLumpName(DEH_String("INVGEML1"), PU_STATIC);
+ PatchINVLFGEM2 = W_CacheLumpName(DEH_String("INVGEML2"), PU_STATIC);
+ PatchINVRTGEM1 = W_CacheLumpName(DEH_String("INVGEMR1"), PU_STATIC);
+ PatchINVRTGEM2 = W_CacheLumpName(DEH_String("INVGEMR2"), PU_STATIC);
+ PatchBLACKSQ = W_CacheLumpName(DEH_String("BLACKSQ"), PU_STATIC);
+ PatchARMCLEAR = W_CacheLumpName(DEH_String("ARMCLEAR"), PU_STATIC);
+ PatchCHAINBACK = W_CacheLumpName(DEH_String("CHAINBACK"), PU_STATIC);
+ startLump = W_GetNumForName(DEH_String("IN0"));
for (i = 0; i < 10; i++)
{
PatchINumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC);
}
- PatchNEGATIVE = W_CacheLumpName("NEGNUM", PU_STATIC);
- FontBNumBase = W_GetNumForName("FONTB16");
- startLump = W_GetNumForName("SMALLIN0");
+ PatchNEGATIVE = W_CacheLumpName(DEH_String("NEGNUM"), PU_STATIC);
+ FontBNumBase = W_GetNumForName(DEH_String("FONTB16"));
+ startLump = W_GetNumForName(DEH_String("SMALLIN0"));
for (i = 0; i < 10; i++)
{
PatchSmNumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC);
}
- playpalette = W_GetNumForName("PLAYPAL");
- spinbooklump = W_GetNumForName("SPINBK0");
- spinflylump = W_GetNumForName("SPFLY0");
+ playpalette = W_GetNumForName(DEH_String("PLAYPAL"));
+ spinbooklump = W_GetNumForName(DEH_String("SPINBK0"));
+ spinflylump = W_GetNumForName(DEH_String("SPFLY0"));
}
//---------------------------------------------------------------------------
@@ -311,7 +312,7 @@ static void DrINumber(signed int val, int x, int y)
{
if (val < -9)
{
- V_DrawPatch(x + 1, y + 1, W_CacheLumpName("LAME", PU_CACHE));
+ V_DrawPatch(x + 1, y + 1, W_CacheLumpName(DEH_String("LAME"), PU_CACHE));
}
else
{
@@ -458,7 +459,7 @@ static void DrawSoundInfo(void)
if (leveltime & 16)
{
- MN_DrTextA("*** SOUND DEBUG INFO ***", xPos[0], 20);
+ MN_DrTextA(DEH_String("*** SOUND DEBUG INFO ***"), xPos[0], 20);
}
S_GetChannelInfo(&s);
if (s.channelCount == 0)
@@ -466,13 +467,13 @@ static void DrawSoundInfo(void)
return;
}
x = 0;
- MN_DrTextA("NAME", xPos[x++], 30);
- MN_DrTextA("MO.T", xPos[x++], 30);
- MN_DrTextA("MO.X", xPos[x++], 30);
- MN_DrTextA("MO.Y", xPos[x++], 30);
- MN_DrTextA("ID", xPos[x++], 30);
- MN_DrTextA("PRI", xPos[x++], 30);
- MN_DrTextA("DIST", xPos[x++], 30);
+ MN_DrTextA(DEH_String("NAME"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("MO.T"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("MO.X"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("MO.Y"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("ID"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("PRI"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("DIST"), xPos[x++], 30);
for (i = 0; i < s.channelCount; i++)
{
c = &s.chan[i];
@@ -480,7 +481,7 @@ static void DrawSoundInfo(void)
y = 40 + i * 10;
if (c->mo == NULL)
{ // Channel is unused
- MN_DrTextA("------", xPos[0], y);
+ MN_DrTextA(DEH_String("------"), xPos[0], y);
continue;
}
sprintf(text, "%s", c->name);
@@ -570,8 +571,10 @@ void SB_Drawer(void)
V_DrawPatch(0, 158, PatchBARBACK);
if (players[consoleplayer].cheats & CF_GODMODE)
{
- V_DrawPatch(16, 167, W_CacheLumpName("GOD1", PU_CACHE));
- V_DrawPatch(287, 167, W_CacheLumpName("GOD2", PU_CACHE));
+ V_DrawPatch(16, 167,
+ W_CacheLumpName(DEH_String("GOD1"), PU_CACHE));
+ V_DrawPatch(287, 167,
+ W_CacheLumpName(DEH_String("GOD2"), PU_CACHE));
}
oldhealth = -1;
}
@@ -776,8 +779,10 @@ void DrawMainBar(void)
if (ArtifactFlash)
{
V_DrawPatch(180, 161, PatchBLACKSQ);
- V_DrawPatch(182, 161, W_CacheLumpNum(W_GetNumForName("useartia")
- + ArtifactFlash - 1, PU_CACHE));
+
+ temp = W_GetNumForName(DEH_String("useartia")) + ArtifactFlash - 1;
+
+ V_DrawPatch(182, 161, W_CacheLumpNum(temp, PU_CACHE));
ArtifactFlash--;
oldarti = -1; // so that the correct artifact fills in after the flash
UpdateState |= I_STATBAR;
@@ -789,7 +794,7 @@ void DrawMainBar(void)
if (CPlayer->readyArtifact > 0)
{
V_DrawPatch(179, 160,
- W_CacheLumpName(patcharti[CPlayer->readyArtifact],
+ W_CacheLumpName(DEH_String(patcharti[CPlayer->readyArtifact]),
PU_CACHE));
DrSmallNumber(CPlayer->inventory[inv_ptr].count, 201, 182);
}
@@ -839,15 +844,15 @@ void DrawMainBar(void)
{
if (CPlayer->keys[key_yellow])
{
- V_DrawPatch(153, 164, W_CacheLumpName("ykeyicon", PU_CACHE));
+ V_DrawPatch(153, 164, W_CacheLumpName(DEH_String("ykeyicon"), PU_CACHE));
}
if (CPlayer->keys[key_green])
{
- V_DrawPatch(153, 172, W_CacheLumpName("gkeyicon", PU_CACHE));
+ V_DrawPatch(153, 172, W_CacheLumpName(DEH_String("gkeyicon"), PU_CACHE));
}
if (CPlayer->keys[key_blue])
{
- V_DrawPatch(153, 180, W_CacheLumpName("bkeyicon", PU_CACHE));
+ V_DrawPatch(153, 180, W_CacheLumpName(DEH_String("bkeyicon"), PU_CACHE));
}
oldkeys = playerkeys;
UpdateState |= I_STATBAR;
@@ -861,7 +866,7 @@ void DrawMainBar(void)
{
DrINumber(temp, 109, 162);
V_DrawPatch(111, 172,
- W_CacheLumpName(ammopic[CPlayer->readyweapon - 1],
+ W_CacheLumpName(DEH_String(ammopic[CPlayer->readyweapon - 1]),
PU_CACHE));
}
oldammo = temp;
@@ -887,6 +892,7 @@ void DrawMainBar(void)
void DrawInventoryBar(void)
{
+ char *patch;
int i;
int x;
@@ -899,10 +905,9 @@ void DrawInventoryBar(void)
if (CPlayer->inventorySlotNum > x + i
&& CPlayer->inventory[x + i].type != arti_none)
{
- V_DrawPatch(50 + i * 31, 160,
- W_CacheLumpName(patcharti
- [CPlayer->inventory[x + i].type],
- PU_CACHE));
+ patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]);
+
+ V_DrawPatch(50 + i * 31, 160, W_CacheLumpName(patch, PU_CACHE));
DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31, 182);
}
}
@@ -921,6 +926,7 @@ void DrawInventoryBar(void)
void DrawFullScreenStuff(void)
{
+ char *patch;
int i;
int x;
int temp;
@@ -950,10 +956,9 @@ void DrawFullScreenStuff(void)
{
if (CPlayer->readyArtifact > 0)
{
- V_DrawTLPatch(286, 170, W_CacheLumpName("ARTIBOX", PU_CACHE));
- V_DrawPatch(286, 170,
- W_CacheLumpName(patcharti[CPlayer->readyArtifact],
- PU_CACHE));
+ patch = DEH_String(patcharti[CPlayer->readyArtifact]);
+ V_DrawTLPatch(286, 170, W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE));
+ V_DrawPatch(286, 170, W_CacheLumpName(patch, PU_CACHE));
DrSmallNumber(CPlayer->inventory[inv_ptr].count, 307, 192);
}
}
@@ -962,15 +967,14 @@ void DrawFullScreenStuff(void)
x = inv_ptr - curpos;
for (i = 0; i < 7; i++)
{
- V_DrawTLPatch(50 + i * 31, 168, W_CacheLumpName("ARTIBOX",
- PU_CACHE));
+ V_DrawTLPatch(50 + i * 31, 168,
+ W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE));
if (CPlayer->inventorySlotNum > x + i
&& CPlayer->inventory[x + i].type != arti_none)
{
+ patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]);
V_DrawPatch(50 + i * 31, 168,
- W_CacheLumpName(patcharti
- [CPlayer->inventory[x + i].type],
- PU_CACHE));
+ W_CacheLumpName(patch, PU_CACHE));
DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31,
190);
}
@@ -1051,11 +1055,11 @@ static void CheatGodFunc(player_t * player, Cheat_t * cheat)
player->cheats ^= CF_GODMODE;
if (player->cheats & CF_GODMODE)
{
- P_SetMessage(player, TXT_CHEATGODON, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATGODON), false);
}
else
{
- P_SetMessage(player, TXT_CHEATGODOFF, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATGODOFF), false);
}
SB_state = -1;
}
@@ -1065,11 +1069,11 @@ static void CheatNoClipFunc(player_t * player, Cheat_t * cheat)
player->cheats ^= CF_NOCLIP;
if (player->cheats & CF_NOCLIP)
{
- P_SetMessage(player, TXT_CHEATNOCLIPON, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPON), false);
}
else
{
- P_SetMessage(player, TXT_CHEATNOCLIPOFF, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPOFF), false);
}
}
@@ -1102,7 +1106,7 @@ static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat)
{
player->ammo[i] = player->maxammo[i];
}
- P_SetMessage(player, TXT_CHEATWEAPONS, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATWEAPONS), false);
}
static void CheatPowerFunc(player_t * player, Cheat_t * cheat)
@@ -1110,12 +1114,12 @@ static void CheatPowerFunc(player_t * player, Cheat_t * cheat)
if (player->powers[pw_weaponlevel2])
{
player->powers[pw_weaponlevel2] = 0;
- P_SetMessage(player, TXT_CHEATPOWEROFF, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATPOWEROFF), false);
}
else
{
P_UseArtifact(player, arti_tomeofpower);
- P_SetMessage(player, TXT_CHEATPOWERON, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATPOWERON), false);
}
}
@@ -1129,7 +1133,7 @@ static void CheatHealthFunc(player_t * player, Cheat_t * cheat)
{
player->health = player->mo->health = MAXHEALTH;
}
- P_SetMessage(player, TXT_CHEATHEALTH, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATHEALTH), false);
}
static void CheatKeysFunc(player_t * player, Cheat_t * cheat)
@@ -1140,7 +1144,7 @@ static void CheatKeysFunc(player_t * player, Cheat_t * cheat)
player->keys[key_green] = true;
player->keys[key_blue] = true;
playerkeys = 7; // Key refresh flags
- P_SetMessage(player, TXT_CHEATKEYS, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATKEYS), false);
}
static void CheatSoundFunc(player_t * player, Cheat_t * cheat)
@@ -1148,11 +1152,11 @@ static void CheatSoundFunc(player_t * player, Cheat_t * cheat)
DebugSound = !DebugSound;
if (DebugSound)
{
- P_SetMessage(player, TXT_CHEATSOUNDON, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATSOUNDON), false);
}
else
{
- P_SetMessage(player, TXT_CHEATSOUNDOFF, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATSOUNDOFF), false);
}
}
@@ -1161,11 +1165,11 @@ static void CheatTickerFunc(player_t * player, Cheat_t * cheat)
DisplayTicker = !DisplayTicker;
if (DisplayTicker)
{
- P_SetMessage(player, TXT_CHEATTICKERON, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATTICKERON), false);
}
else
{
- P_SetMessage(player, TXT_CHEATTICKEROFF, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATTICKEROFF), false);
}
I_DisplayFPSDots(DisplayTicker);
@@ -1173,12 +1177,12 @@ static void CheatTickerFunc(player_t * player, Cheat_t * cheat)
static void CheatArtifact1Func(player_t * player, Cheat_t * cheat)
{
- P_SetMessage(player, TXT_CHEATARTIFACTS1, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS1), false);
}
static void CheatArtifact2Func(player_t * player, Cheat_t * cheat)
{
- P_SetMessage(player, TXT_CHEATARTIFACTS2, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS2), false);
}
static void CheatArtifact3Func(player_t * player, Cheat_t * cheat)
@@ -1206,7 +1210,7 @@ static void CheatArtifact3Func(player_t * player, Cheat_t * cheat)
P_GiveArtifact(player, i, NULL);
}
}
- P_SetMessage(player, TXT_CHEATARTIFACTS3, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false);
}
else if (type > arti_none && type < NUMARTIFACTS
&& count > 0 && count < 10)
@@ -1214,18 +1218,18 @@ static void CheatArtifact3Func(player_t * player, Cheat_t * cheat)
if (gamemode == shareware
&& (type == arti_superhealth || type == arti_teleport))
{
- P_SetMessage(player, TXT_CHEATARTIFACTSFAIL, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false);
return;
}
for (i = 0; i < count; i++)
{
P_GiveArtifact(player, type, NULL);
}
- P_SetMessage(player, TXT_CHEATARTIFACTS3, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false);
}
else
{ // Bad input
- P_SetMessage(player, TXT_CHEATARTIFACTSFAIL, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false);
}
}
@@ -1242,7 +1246,7 @@ static void CheatWarpFunc(player_t * player, Cheat_t * cheat)
if (D_ValidEpisodeMap(gamemission, gamemode, episode, map))
{
G_DeferedInitNew(gameskill, episode, map);
- P_SetMessage(player, TXT_CHEATWARP, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATWARP), false);
}
}
@@ -1254,19 +1258,19 @@ static void CheatChickenFunc(player_t * player, Cheat_t * cheat)
{
if (P_UndoPlayerChicken(player))
{
- P_SetMessage(player, TXT_CHEATCHICKENOFF, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATCHICKENOFF), false);
}
}
else if (P_ChickenMorphPlayer(player))
{
- P_SetMessage(player, TXT_CHEATCHICKENON, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATCHICKENON), false);
}
}
static void CheatMassacreFunc(player_t * player, Cheat_t * cheat)
{
P_Massacre();
- P_SetMessage(player, TXT_CHEATMASSACRE, false);
+ P_SetMessage(player, DEH_String(TXT_CHEATMASSACRE), false);
}
static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat)
@@ -1281,11 +1285,11 @@ static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat)
player->weaponowned[i] = false;
}
player->pendingweapon = wp_staff;
- P_SetMessage(player, TXT_CHEATIDKFA, true);
+ P_SetMessage(player, DEH_String(TXT_CHEATIDKFA), true);
}
static void CheatIDDQDFunc(player_t * player, Cheat_t * cheat)
{
P_DamageMobj(player->mo, NULL, player->mo, 10000);
- P_SetMessage(player, TXT_CHEATIDDQD, true);
+ P_SetMessage(player, DEH_String(TXT_CHEATIDDQD), true);
}
diff --git a/src/heretic/sounds.h b/src/heretic/sounds.h
index 707185c0..e50a6ad6 100644
--- a/src/heretic/sounds.h
+++ b/src/heretic/sounds.h
@@ -27,6 +27,8 @@
#ifndef __SOUNDSH__
#define __SOUNDSH__
+#include "i_sound.h"
+
#define MAX_SND_DIST 1600
#define MAX_CHANNELS 16
@@ -291,4 +293,7 @@ typedef enum
NUMSFX
} sfxenum_t;
+extern sfxinfo_t S_sfx[];
+extern musicinfo_t S_music[];
+
#endif
diff --git a/src/hexen/.gitignore b/src/hexen/.gitignore
index d7e732ad..d4e88e5a 100644
--- a/src/hexen/.gitignore
+++ b/src/hexen/.gitignore
@@ -1,7 +1,5 @@
Makefile
Makefile.in
.deps
-*.rc
-chocolate-doom
-chocolate-server
-*.exe
+tags
+TAGS
diff --git a/src/hexen/h2_main.c b/src/hexen/h2_main.c
index ef223c0e..abc58f15 100644
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -46,6 +46,7 @@
#include "m_controls.h"
#include "p_local.h"
#include "v_video.h"
+#include "w_main.h"
// MACROS ------------------------------------------------------------------
@@ -88,7 +89,6 @@ static void PageDrawer(void);
static void HandleArgs(void);
static void CheckRecordFrom(void);
static void DrawAndBlit(void);
-static void ExecOptionFILE(char **args, int tag);
static void ExecOptionSCRIPTS(char **args, int tag);
static void ExecOptionSKILL(char **args, int tag);
static void ExecOptionPLAYDEMO(char **args, int tag);
@@ -133,7 +133,6 @@ static int pagetic;
static char *pagename;
static execOpt_t ExecOptions[] = {
- {"-file", ExecOptionFILE, 1, 0},
{"-scripts", ExecOptionSCRIPTS, 1, 0},
{"-skill", ExecOptionSKILL, 1, 0},
{"-playdemo", ExecOptionPLAYDEMO, 1, 0},
@@ -157,6 +156,7 @@ void D_BindVariables(void)
M_BindMapControls();
M_BindMenuControls();
M_BindWeaponControls();
+ M_BindChatControls(MAXPLAYERS);
M_BindHereticControls();
M_BindHexenControls();
@@ -422,6 +422,9 @@ static void HandleArgs(void)
cmdfrag = M_ParmExists("-cmdfrag");
+ // Check WAD file command line options
+ W_ParseCommandLine();
+
// Process command line options
for (opt = ExecOptions; opt->name != NULL; opt++)
{
@@ -485,27 +488,6 @@ static void ExecOptionSKILL(char **args, int tag)
//==========================================================================
//
-// ExecOptionFILE
-//
-//==========================================================================
-
-static void ExecOptionFILE(char **args, int tag)
-{
- char *filename;
- int p;
-
- p = M_CheckParm("-file");
- while (++p != myargc && myargv[p][0] != '-')
- {
- filename = D_TryFindWADByName(myargv[p]);
-
- D_AddFile(filename);
- }
-}
-
-
-//==========================================================================
-//
// ExecOptionPLAYDEMO
//
//==========================================================================
diff --git a/src/hexen/mn_menu.c b/src/hexen/mn_menu.c
index 09a8f1ea..9f37df66 100644
--- a/src/hexen/mn_menu.c
+++ b/src/hexen/mn_menu.c
@@ -679,6 +679,32 @@ static void DrawSaveMenu(void)
DrawFileSlots(&SaveMenu);
}
+static boolean ReadDescriptionForSlot(int slot, char *description)
+{
+ FILE *fp;
+ boolean found;
+ char name[100];
+ char versionText[HXS_VERSION_TEXT_LENGTH];
+
+ sprintf(name, "%shex%d.hxs", SavePath, slot);
+
+ fp = fopen(name, "rb");
+
+ if (fp == NULL)
+ {
+ return false;
+ }
+
+ found = fread(description, HXS_DESCRIPTION_LENGTH, 1, fp) == 1
+ && fread(versionText, HXS_VERSION_TEXT_LENGTH, 1, fp) == 1;
+
+ found = found && strcmp(versionText, HXS_VERSION_TEXT) == 0;
+
+ fclose(fp);
+
+ return found;
+}
+
//===========================================================================
//
// MN_LoadSlotText
@@ -689,29 +715,12 @@ static void DrawSaveMenu(void)
void MN_LoadSlotText(void)
{
- int slot;
- FILE *fp;
- char name[100];
- char versionText[HXS_VERSION_TEXT_LENGTH];
char description[HXS_DESCRIPTION_LENGTH];
- boolean found;
+ int slot;
for (slot = 0; slot < 6; slot++)
{
- found = false;
- sprintf(name, "%shex%d.hxs", SavePath, slot);
- fp = fopen(name, "rb");
- if (fp)
- {
- fread(description, HXS_DESCRIPTION_LENGTH, 1, fp);
- fread(versionText, HXS_VERSION_TEXT_LENGTH, 1, fp);
- fclose(fp);
- if (!strcmp(versionText, HXS_VERSION_TEXT))
- {
- found = true;
- }
- }
- if (found)
+ if (ReadDescriptionForSlot(slot, description))
{
memcpy(SlotText[slot], description, SLOTTEXTLEN);
SlotStatus[slot] = 1;
diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
index 618bbd05..9ca9f6af 100644
--- a/src/hexen/p_acs.c
+++ b/src/hexen/p_acs.c
@@ -509,7 +509,7 @@ static boolean AddToACSStore(int map, int number, byte * args)
}
ACSStore[index].map = map;
ACSStore[index].script = number;
- *((int *) ACSStore[index].args) = *((int *) args);
+ memcpy(ACSStore[index].args, args, sizeof(int));
return true;
}
diff --git a/src/hexen/p_spec.c b/src/hexen/p_spec.c
index ca0bbfb8..03c98cbc 100644
--- a/src/hexen/p_spec.c
+++ b/src/hexen/p_spec.c
@@ -245,6 +245,8 @@ fixed_t P_FindNextHighestFloor(sector_t * sec, int currentheight)
fixed_t height = currentheight;
fixed_t heightlist[20]; // 20 adjoining sectors max!
+ heightlist[0] = 0;
+
for (i = 0, h = 0; i < sec->linecount; i++)
{
check = sec->lines[i];
diff --git a/src/hexen/r_things.c b/src/hexen/r_things.c
index 0517fd49..aeba11c4 100644
--- a/src/hexen/r_things.c
+++ b/src/hexen/r_things.c
@@ -158,7 +158,7 @@ void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation,
void R_InitSpriteDefs(char **namelist)
{
char **check;
- int i, l, intname, frame, rotation;
+ int i, l, frame, rotation;
int start, end;
// count the number of sprite names
@@ -184,13 +184,12 @@ void R_InitSpriteDefs(char **namelist)
memset(sprtemp, -1, sizeof(sprtemp));
maxframe = -1;
- intname = *(int *) namelist[i];
//
// scan the lumps, filling in the frames for whatever is found
//
for (l = start + 1; l < end; l++)
- if (*(int *) lumpinfo[l].name == intname)
+ if (!strncmp(lumpinfo[l].name, namelist[i], 4))
{
frame = lumpinfo[l].name[4] - 'A';
rotation = lumpinfo[l].name[5] - '0';
diff --git a/src/hexen/s_sound.c b/src/hexen/s_sound.c
index fdd01ba5..f11e4ee9 100644
--- a/src/hexen/s_sound.c
+++ b/src/hexen/s_sound.c
@@ -736,7 +736,7 @@ void S_Init(void)
{
snd_Channels = 8;
}
- I_SetMusicVolume(snd_MusicVolume);
+ I_SetMusicVolume(snd_MusicVolume * 8);
I_AtExit(S_ShutDown, true);
@@ -787,8 +787,16 @@ void S_GetChannelInfo(SoundInfo_t * s)
c->priority = Channel[i].priority;
c->name = S_sfx[c->id].name;
c->mo = Channel[i].mo;
- c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy)
- >> FRACBITS;
+
+ if (c->mo != NULL)
+ {
+ c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy)
+ >> FRACBITS;
+ }
+ else
+ {
+ c->distance = 0;
+ }
}
}
@@ -829,7 +837,7 @@ void S_SetMusicVolume(void)
}
else
{
- I_SetMusicVolume(snd_MusicVolume);
+ I_SetMusicVolume(snd_MusicVolume * 8);
}
if (snd_MusicVolume == 0)
{
diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
new file mode 100644
index 00000000..e7a42e83
--- /dev/null
+++ b/src/i_oplmusic.c
@@ -0,0 +1,1464 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// System interface for music.
+//
+//-----------------------------------------------------------------------------
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memio.h"
+#include "mus2mid.h"
+
+#include "deh_main.h"
+#include "i_sound.h"
+#include "i_swap.h"
+#include "m_misc.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#include "opl.h"
+#include "midifile.h"
+
+// #define OPL_MIDI_DEBUG
+
+#define MAXMIDLENGTH (96 * 1024)
+#define GENMIDI_NUM_INSTRS 128
+
+#define GENMIDI_HEADER "#OPL_II#"
+#define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */
+#define GENMIDI_FLAG_2VOICE 0x0004 /* double voice (OPL3) */
+
+typedef struct
+{
+ byte tremolo;
+ byte attack;
+ byte sustain;
+ byte waveform;
+ byte scale;
+ byte level;
+} PACKEDATTR genmidi_op_t;
+
+typedef struct
+{
+ genmidi_op_t modulator;
+ byte feedback;
+ genmidi_op_t carrier;
+ byte unused;
+ short base_note_offset;
+} PACKEDATTR genmidi_voice_t;
+
+typedef struct
+{
+ unsigned short flags;
+ byte fine_tuning;
+ byte fixed_note;
+
+ genmidi_voice_t voices[2];
+} PACKEDATTR genmidi_instr_t;
+
+// Data associated with a channel of a track that is currently playing.
+
+typedef struct
+{
+ // The instrument currently used for this track.
+
+ genmidi_instr_t *instrument;
+
+ // Volume level
+
+ int volume;
+
+ // Pitch bend value:
+
+ int bend;
+
+} opl_channel_data_t;
+
+// Data associated with a track that is currently playing.
+
+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;
+
+ // Tempo control variables
+
+ unsigned int ticks_per_beat;
+ unsigned int ms_per_beat;
+} opl_track_data_t;
+
+typedef struct opl_voice_s opl_voice_t;
+
+struct opl_voice_s
+{
+ // Index of this voice:
+ int index;
+
+ // The operators used by this voice:
+ int op1, op2;
+
+ // Currently-loaded instrument data
+ genmidi_instr_t *current_instr;
+
+ // The voice number in the instrument to use.
+ // This is normally set to zero; if this is a double voice
+ // instrument, it may be one.
+ unsigned int current_instr_voice;
+
+ // The channel currently using this voice.
+ opl_channel_data_t *channel;
+
+ // The midi key that this voice is playing.
+ unsigned int key;
+
+ // The note being played. This is normally the same as
+ // the key, but if the instrument is a fixed pitch
+ // instrument, it is different.
+ unsigned int note;
+
+ // The frequency value being used.
+ unsigned int freq;
+
+ // The volume of the note being played on this channel.
+ unsigned int note_volume;
+
+ // The current volume (register value) that has been set for this channel.
+ unsigned int reg_volume;
+
+ // 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.
+
+static const int voice_operators[2][OPL_NUM_VOICES] = {
+ { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 },
+ { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 }
+};
+
+// Frequency values to use for each note.
+
+static const unsigned short frequency_curve[] = {
+
+ 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1
+ 0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b,
+ 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140,
+ 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144,
+
+ 0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149, // -2
+ 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e,
+ 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153,
+ 0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158,
+
+ // These are used for the first seven MIDI note values:
+
+ 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0
+ 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162,
+ 0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167,
+ 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c,
+
+ 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, // 1
+ 0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177,
+ 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c,
+ 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182,
+
+ 0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188, // 2
+ 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d,
+ 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193,
+ 0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199,
+
+ 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, // 3
+ 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5,
+ 0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab,
+ 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1,
+
+ 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, // 4
+ 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be,
+ 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4,
+ 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb,
+
+ 0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2, // 5
+ 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8,
+ 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df,
+ 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6,
+
+ 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, // 6
+ 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5,
+ 0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc,
+ 0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203,
+
+ // First note of looped range used for all octaves:
+
+ 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7
+ 0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212,
+ 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a,
+ 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222,
+
+ 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, // 8
+ 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232,
+ 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a,
+ 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242,
+
+ 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, // 9
+ 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253,
+ 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c,
+ 0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265,
+
+ 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, // 10
+ 0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277,
+ 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280,
+ 0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289,
+
+ 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, // 11
+ 0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c,
+ 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6,
+ 0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0,
+
+ 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, // 12
+ 0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4,
+ 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce,
+ 0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9,
+
+ 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, // 13
+ 0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee,
+ 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9,
+ 0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304,
+
+ 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, // 14
+ 0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b,
+ 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327,
+ 0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332,
+
+ 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, // 15
+ 0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a,
+ 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357,
+ 0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363,
+
+ 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, // 16
+ 0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c,
+ 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389,
+ 0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397,
+
+ 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, // 17
+ 0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1,
+ 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf,
+ 0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd,
+
+ // The last note has an incomplete range, and loops round back to
+ // the start. Note that the last value is actually a buffer overrun
+ // and does not fit with the other values.
+
+ 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18
+ 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea,
+ 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8,
+ 0x3fa, 0x3fc, 0x3fe, 0x36c,
+};
+
+// Mapping from MIDI volume level to OPL level value.
+
+static const unsigned int volume_mapping_table[] = {
+ 0, 1, 3, 5, 6, 8, 10, 11,
+ 13, 14, 16, 17, 19, 20, 22, 23,
+ 25, 26, 27, 29, 30, 32, 33, 34,
+ 36, 37, 39, 41, 43, 45, 47, 49,
+ 50, 52, 54, 55, 57, 59, 60, 61,
+ 63, 64, 66, 67, 68, 69, 71, 72,
+ 73, 74, 75, 76, 77, 79, 80, 81,
+ 82, 83, 84, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 92, 93, 94, 95,
+ 96, 96, 97, 98, 99, 99, 100, 101,
+ 101, 102, 103, 103, 104, 105, 105, 106,
+ 107, 107, 108, 109, 109, 110, 110, 111,
+ 112, 112, 113, 113, 114, 114, 115, 115,
+ 116, 117, 117, 118, 118, 119, 119, 120,
+ 120, 121, 121, 122, 122, 123, 123, 123,
+ 124, 124, 125, 125, 126, 126, 127, 127
+};
+
+static boolean music_initialized = false;
+
+//static boolean musicpaused = false;
+static int current_music_volume;
+
+// GENMIDI lump instrument data:
+
+static genmidi_instr_t *main_instrs;
+static genmidi_instr_t *percussion_instrs;
+
+// Voices:
+
+static opl_voice_t voices[OPL_NUM_VOICES];
+static opl_voice_t *voice_free_list;
+static opl_voice_t *voice_alloced_list;
+
+// Track data for playing tracks:
+
+static opl_track_data_t *tracks;
+static unsigned int num_tracks = 0;
+static unsigned int running_tracks = 0;
+static boolean song_looping;
+
+// Configuration file variable, containing the port number for the
+// adlib chip.
+
+int opl_io_port = 0x388;
+
+// Load instrument table from GENMIDI lump:
+
+static boolean LoadInstrumentTable(void)
+{
+ byte *lump;
+
+ lump = W_CacheLumpName("GENMIDI", PU_STATIC);
+
+ // 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;
+
+ return true;
+}
+
+// Get the next available voice from the freelist.
+
+static opl_voice_t *GetFreeVoice(void)
+{
+ opl_voice_t *result;
+
+ // None available?
+
+ if (voice_free_list == NULL)
+ {
+ return NULL;
+ }
+
+ // Remove from free list
+
+ result = voice_free_list;
+ voice_free_list = voice_free_list->next;
+
+ // Add to allocated list
+
+ result->next = voice_alloced_list;
+ voice_alloced_list = result;
+
+ return result;
+}
+
+// Remove a voice from the allocated voices list.
+
+static void RemoveVoiceFromAllocedList(opl_voice_t *voice)
+{
+ opl_voice_t **rover;
+
+ rover = &voice_alloced_list;
+
+ // Search the list until we find the voice, then remove it.
+
+ while (*rover != NULL)
+ {
+ if (*rover == voice)
+ {
+ *rover = voice->next;
+ voice->next = NULL;
+ break;
+ }
+
+ rover = &(*rover)->next;
+ }
+}
+
+// Release a voice back to the freelist.
+
+static void ReleaseVoice(opl_voice_t *voice)
+{
+ opl_voice_t **rover;
+
+ voice->channel = NULL;
+ voice->note = 0;
+
+ // Remove from alloced list.
+
+ RemoveVoiceFromAllocedList(voice);
+
+ // Search to the end of the freelist (This is how Doom behaves!)
+
+ rover = &voice_free_list;
+
+ while (*rover != NULL)
+ {
+ rover = &(*rover)->next;
+ }
+
+ *rover = voice;
+ voice->next = NULL;
+}
+
+// Load data to the specified operator
+
+static void LoadOperatorData(int operator, genmidi_op_t *data,
+ boolean max_level)
+{
+ int level;
+
+ // 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);
+
+ if (max_level)
+ {
+ level |= 0x3f;
+ }
+
+ OPL_WriteRegister(OPL_REGS_LEVEL + operator, level);
+ OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);
+ OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack);
+ OPL_WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain);
+ OPL_WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform);
+}
+
+// Set the instrument for a particular voice.
+
+static void SetVoiceInstrument(opl_voice_t *voice,
+ genmidi_instr_t *instr,
+ unsigned int instr_voice)
+{
+ genmidi_voice_t *data;
+ unsigned int modulating;
+
+ // Instrument already set for this channel?
+
+ if (voice->current_instr == instr
+ && voice->current_instr_voice == instr_voice)
+ {
+ return;
+ }
+
+ voice->current_instr = instr;
+ voice->current_instr_voice = instr_voice;
+
+ data = &instr->voices[instr_voice];
+
+ // Are we usind modulated feedback mode?
+
+ modulating = (data->feedback & 0x01) == 0;
+
+ // Doom loads the second operator first, then the first.
+ // The carrier is set to minimum volume until the voice volume
+ // is set in SetVoiceVolume (below). If we are not using
+ // modulating mode, we must set both to minimum volume.
+
+ LoadOperatorData(voice->op2, &data->carrier, true);
+ LoadOperatorData(voice->op1, &data->modulator, !modulating);
+
+ // Set feedback register that control the connection between the
+ // two operators. Turn on bits in the upper nybble; I think this
+ // is for OPL3, where it turns on channel A/B.
+
+ OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index,
+ data->feedback | 0x30);
+
+ // Hack to force a volume update.
+
+ voice->reg_volume = 999;
+}
+
+static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)
+{
+ genmidi_voice_t *opl_voice;
+ unsigned int full_volume;
+ unsigned int op_volume;
+ unsigned int reg_volume;
+
+ voice->note_volume = volume;
+
+ opl_voice = &voice->current_instr->voices[voice->current_instr_voice];
+
+ // Multiply note volume and channel volume to get the actual volume.
+
+ full_volume = (volume_mapping_table[voice->note_volume]
+ * volume_mapping_table[voice->channel->volume]
+ * volume_mapping_table[current_music_volume]) / (127 * 127);
+
+ // The volume of each instrument can be controlled via GENMIDI:
+
+ op_volume = 0x3f - opl_voice->carrier.level;
+
+ // The volume value to use in the register:
+
+ reg_volume = (op_volume * full_volume) / 128;
+ reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale;
+
+ // Update the volume register(s) if necessary.
+
+ if (reg_volume != voice->reg_volume)
+ {
+ voice->reg_volume = reg_volume;
+
+ OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume);
+
+ // If we are using non-modulated feedback mode, we must set the
+ // volume for both voices.
+ // Note that the same register volume value is written for
+ // both voices, always calculated from the carrier's level
+ // value.
+
+ if ((opl_voice->feedback & 0x01) != 0)
+ {
+ OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume);
+ }
+ }
+}
+
+// Initialize the voice table and freelist
+
+static void InitVoices(void)
+{
+ int i;
+
+ // Start with an empty free list.
+
+ voice_free_list = NULL;
+
+ // Initialize each voice.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ voices[i].index = i;
+ voices[i].op1 = voice_operators[0][i];
+ voices[i].op2 = voice_operators[1][i];
+ voices[i].current_instr = NULL;
+
+ // Add this voice to the freelist.
+
+ ReleaseVoice(&voices[i]);
+ }
+}
+
+// Set music volume (0 - 127)
+
+static void I_OPL_SetMusicVolume(int volume)
+{
+ unsigned int i;
+
+ // Internal state variable.
+
+ current_music_volume = volume;
+
+ // Update the volume of all voices.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ if (voices[i].channel != NULL)
+ {
+ SetVoiceVolume(&voices[i], voices[i].note_volume);
+ }
+ }
+}
+
+static void VoiceKeyOff(opl_voice_t *voice)
+{
+ OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8);
+}
+
+// Get the frequency that we should be using for a voice.
+
+static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ opl_channel_data_t *channel;
+ unsigned int key;
+ unsigned int i;
+
+/*
+ printf("note off: channel %i, %i, %i\n",
+ event->data.channel.channel,
+ event->data.channel.param1,
+ event->data.channel.param2);
+*/
+
+ channel = &track->channels[event->data.channel.channel];
+ key = event->data.channel.param1;
+
+ // Turn off voices being used to play this key.
+ // If it is a double voice instrument there will be two.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ if (voices[i].channel == channel && voices[i].key == key)
+ {
+ VoiceKeyOff(&voices[i]);
+
+ // Finished with this voice now.
+
+ ReleaseVoice(&voices[i]);
+ }
+ }
+}
+
+// Compare the priorities of channels, returning either -1, 0 or 1.
+
+static int CompareChannelPriorities(opl_channel_data_t *chan1,
+ opl_channel_data_t *chan2)
+{
+ // TODO ...
+
+ return 1;
+}
+
+// When all voices are in use, we must discard an existing voice to
+// play a new note. Find and free an existing voice. The channel
+// passed to the function is the channel for the new note to be
+// played.
+
+static opl_voice_t *ReplaceExistingVoice(opl_channel_data_t *channel)
+{
+ opl_voice_t *rover;
+ opl_voice_t *result;
+
+ // Check the allocated voices, if we find an instrument that is
+ // of a lower priority to the new instrument, discard it.
+ // If a voice is being used to play the second voice of an instrument,
+ // use that, as second voices are non-essential.
+ // Lower numbered MIDI channels implicitly have a higher priority
+ // than higher-numbered channels, eg. MIDI channel 1 is never
+ // discarded for MIDI channel 2.
+
+ result = NULL;
+
+ for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+ {
+ if (rover->current_instr_voice != 0
+ || (rover->channel > channel
+ && CompareChannelPriorities(channel, rover->channel) > 0))
+ {
+ result = rover;
+ break;
+ }
+ }
+
+ // If we didn't find a voice, find an existing voice being used to
+ // play a note on the same channel, and use that.
+
+ if (result == NULL)
+ {
+ for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+ {
+ if (rover->channel == channel)
+ {
+ result = rover;
+ break;
+ }
+ }
+ }
+
+ // Still nothing found? Give up and just use the first voice in
+ // the list.
+
+ if (result == NULL)
+ {
+ result = voice_alloced_list;
+ }
+
+ // Stop playing this voice playing and release it back to the free
+ // list.
+
+ VoiceKeyOff(result);
+ ReleaseVoice(result);
+
+ // Re-allocate the voice again and return it.
+
+ return GetFreeVoice();
+}
+
+
+static unsigned int FrequencyForVoice(opl_voice_t *voice)
+{
+ genmidi_voice_t *gm_voice;
+ unsigned int freq_index;
+ unsigned int octave;
+ unsigned int sub_index;
+ unsigned int note;
+
+ note = voice->note;
+
+ // Apply note offset.
+ // Don't apply offset if the instrument is a fixed note instrument.
+
+ gm_voice = &voice->current_instr->voices[voice->current_instr_voice];
+
+ if ((voice->current_instr->flags & GENMIDI_FLAG_FIXED) == 0)
+ {
+ note += (signed short) SHORT(gm_voice->base_note_offset);
+ }
+
+ // Avoid possible overflow due to base note offset:
+
+ if (note > 0x7f)
+ {
+ note = voice->note;
+ }
+
+ freq_index = 64 + 32 * note + voice->channel->bend;
+
+ // If this is the second voice of a double voice instrument, the
+ // frequency index can be adjusted by the fine tuning field.
+
+ if (voice->current_instr_voice != 0)
+ {
+ freq_index += (voice->current_instr->fine_tuning / 2) - 64;
+ }
+
+ // The first 7 notes use the start of the table, while
+ // consecutive notes loop around the latter part.
+
+ if (freq_index < 284)
+ {
+ return frequency_curve[freq_index];
+ }
+
+ sub_index = (freq_index - 284) % (12 * 32);
+ octave = (freq_index - 284) / (12 * 32);
+
+ // Once the seventh octave is reached, things break down.
+ // We can only go up to octave 7 as a maximum anyway (the OPL
+ // register only has three bits for octave number), but for the
+ // notes in octave 7, the first five bits have octave=7, the
+ // following notes have octave=6. This 7/6 pattern repeats in
+ // following octaves (which are technically impossible to
+ // represent anyway).
+
+ if (octave >= 7)
+ {
+ if (sub_index < 5)
+ {
+ octave = 7;
+ }
+ else
+ {
+ octave = 6;
+ }
+ }
+
+ // Calculate the resulting register value to use for the frequency.
+
+ return frequency_curve[sub_index + 284] | (octave << 10);
+}
+
+// Update the frequency that a voice is programmed to use.
+
+static void UpdateVoiceFrequency(opl_voice_t *voice)
+{
+ unsigned int freq;
+
+ // Calculate the frequency to use for this voice and update it
+ // if neccessary.
+
+ freq = FrequencyForVoice(voice);
+
+ if (voice->freq != freq)
+ {
+ OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff);
+ OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20);
+
+ voice->freq = freq;
+ }
+}
+
+// Program a single voice for an instrument. For a double voice
+// instrument (GENMIDI_FLAG_2VOICE), this is called twice for each
+// key on event.
+
+static void VoiceKeyOn(opl_channel_data_t *channel,
+ genmidi_instr_t *instrument,
+ unsigned int instrument_voice,
+ unsigned int key,
+ unsigned int volume)
+{
+ opl_voice_t *voice;
+
+ // Find a voice to use for this new note.
+
+ voice = GetFreeVoice();
+
+ // If there are no more voices left, we must decide what to do.
+ // If this is the first voice of the instrument, free an existing
+ // voice and use that. Otherwise, if this is the second voice,
+ // it isn't as important; just discard it.
+
+ if (voice == NULL)
+ {
+ if (instrument_voice == 0)
+ {
+ voice = ReplaceExistingVoice(channel);
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ voice->channel = channel;
+ voice->key = key;
+
+ // Work out the note to use. This is normally the same as
+ // the key, unless it is a fixed pitch instrument.
+
+ if ((instrument->flags & GENMIDI_FLAG_FIXED) != 0)
+ {
+ voice->note = instrument->fixed_note;
+ }
+ else
+ {
+ voice->note = key;
+ }
+
+ // Program the voice with the instrument data:
+
+ SetVoiceInstrument(voice, instrument, instrument_voice);
+
+ // Set the volume level.
+
+ SetVoiceVolume(voice, volume);
+
+ // Write the frequency value to turn the note on.
+
+ voice->freq = 0;
+ UpdateVoiceFrequency(voice);
+}
+
+static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ genmidi_instr_t *instrument;
+ opl_channel_data_t *channel;
+ unsigned int key;
+ unsigned int volume;
+
+/*
+ printf("note on: channel %i, %i, %i\n",
+ event->data.channel.channel,
+ event->data.channel.param1,
+ event->data.channel.param2);
+*/
+
+ // The channel.
+
+ channel = &track->channels[event->data.channel.channel];
+ key = event->data.channel.param1;
+ volume = event->data.channel.param2;
+
+ // Percussion channel (10) is treated differently.
+
+ if (event->data.channel.channel == 9)
+ {
+ if (key < 35 || key > 81)
+ {
+ return;
+ }
+
+ instrument = &percussion_instrs[key - 35];
+ }
+ else
+ {
+ instrument = channel->instrument;
+ }
+
+ // Find and program a voice for this instrument. If this
+ // is a double voice instrument, we must do this twice.
+
+ VoiceKeyOn(channel, instrument, 0, key, volume);
+
+ if ((instrument->flags & GENMIDI_FLAG_2VOICE) != 0)
+ {
+ VoiceKeyOn(channel, instrument, 1, key, volume);
+ }
+}
+
+static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ int channel;
+ int instrument;
+
+ // Set the instrument used on this channel.
+
+ channel = event->data.channel.channel;
+ instrument = event->data.channel.param1;
+ track->channels[channel].instrument = &main_instrs[instrument];
+
+ // TODO: Look through existing voices that are turned on on this
+ // channel, and change the instrument.
+}
+
+static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume)
+{
+ unsigned int i;
+
+ channel->volume = volume;
+
+ // Update all voices that this channel is using.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ if (voices[i].channel == channel)
+ {
+ SetVoiceVolume(&voices[i], voices[i].note_volume);
+ }
+ }
+}
+
+static void ControllerEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ unsigned int controller;
+ unsigned int param;
+ opl_channel_data_t *channel;
+
+/*
+ printf("change controller: channel %i, %i, %i\n",
+ event->data.channel.channel,
+ event->data.channel.param1,
+ event->data.channel.param2);
+*/
+
+ channel = &track->channels[event->data.channel.channel];
+ controller = event->data.channel.param1;
+ param = event->data.channel.param2;
+
+ switch (controller)
+ {
+ case MIDI_CONTROLLER_MAIN_VOLUME:
+ SetChannelVolume(channel, param);
+ break;
+
+ default:
+#ifdef OPL_MIDI_DEBUG
+ fprintf(stderr, "Unknown MIDI controller type: %i\n", controller);
+#endif
+ break;
+ }
+}
+
+// Process a pitch bend event.
+
+static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ opl_channel_data_t *channel;
+ unsigned int i;
+
+ // Update the channel bend value. Only the MSB of the pitch bend
+ // value is considered: this is what Doom does.
+
+ channel = &track->channels[event->data.channel.channel];
+ channel->bend = event->data.channel.param2 - 64;
+
+ // Update all voices for this channel.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ if (voices[i].channel == channel)
+ {
+ UpdateVoiceFrequency(&voices[i]);
+ }
+ }
+}
+
+// Process a meta event.
+
+static void MetaEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ switch (event->data.meta.type)
+ {
+ // Things we can just ignore.
+
+ case MIDI_META_SEQUENCE_NUMBER:
+ case MIDI_META_TEXT:
+ case MIDI_META_COPYRIGHT:
+ case MIDI_META_TRACK_NAME:
+ case MIDI_META_INSTR_NAME:
+ case MIDI_META_LYRICS:
+ case MIDI_META_MARKER:
+ case MIDI_META_CUE_POINT:
+ case MIDI_META_SEQUENCER_SPECIFIC:
+ break;
+
+ // End of track - actually handled when we run out of events
+ // in the track, see below.
+
+ case MIDI_META_END_OF_TRACK:
+ break;
+
+ default:
+#ifdef OPL_MIDI_DEBUG
+ fprintf(stderr, "Unknown MIDI meta event type: %i\n",
+ event->data.meta.type);
+#endif
+ break;
+ }
+}
+
+// Process a MIDI event from a track.
+
+static void ProcessEvent(opl_track_data_t *track, midi_event_t *event)
+{
+ switch (event->event_type)
+ {
+ case MIDI_EVENT_NOTE_OFF:
+ KeyOffEvent(track, event);
+ break;
+
+ case MIDI_EVENT_NOTE_ON:
+ KeyOnEvent(track, event);
+ break;
+
+ case MIDI_EVENT_CONTROLLER:
+ ControllerEvent(track, event);
+ break;
+
+ case MIDI_EVENT_PROGRAM_CHANGE:
+ ProgramChangeEvent(track, event);
+ break;
+
+ case MIDI_EVENT_PITCH_BEND:
+ PitchBendEvent(track, event);
+ break;
+
+ case MIDI_EVENT_META:
+ MetaEvent(track, event);
+ break;
+
+ // SysEx events can be ignored.
+
+ case MIDI_EVENT_SYSEX:
+ case MIDI_EVENT_SYSEX_SPLIT:
+ break;
+
+ default:
+#ifdef OPL_MIDI_DEBUG
+ fprintf(stderr, "Unknown MIDI event type %i\n", event->event_type);
+#endif
+ break;
+ }
+}
+
+static void ScheduleTrack(opl_track_data_t *track);
+
+// Restart a song from the beginning.
+
+static void RestartSong(void)
+{
+ unsigned int i;
+
+ running_tracks = num_tracks;
+
+ for (i=0; i<num_tracks; ++i)
+ {
+ MIDI_RestartIterator(tracks[i].iter);
+ ScheduleTrack(&tracks[i]);
+ }
+}
+
+// Callback function invoked when another event needs to be read from
+// a track.
+
+static void TrackTimerCallback(void *arg)
+{
+ opl_track_data_t *track = arg;
+ midi_event_t *event;
+
+ // Get the next event and process it.
+
+ if (!MIDI_GetNextEvent(track->iter, &event))
+ {
+ return;
+ }
+
+ ProcessEvent(track, event);
+
+ // End of track?
+
+ if (event->event_type == MIDI_EVENT_META
+ && event->data.meta.type == MIDI_META_END_OF_TRACK)
+ {
+ --running_tracks;
+
+ // When all tracks have finished, restart the song.
+
+ if (running_tracks <= 0 && song_looping)
+ {
+ RestartSong();
+ }
+
+ return;
+ }
+
+ // Reschedule the callback for the next event in the track.
+
+ ScheduleTrack(track);
+}
+
+static void ScheduleTrack(opl_track_data_t *track)
+{
+ unsigned int nticks;
+ unsigned int ms;
+ static int total = 0;
+
+ // Get the number of milliseconds until the next event.
+
+ nticks = MIDI_GetDeltaTime(track->iter);
+ ms = (nticks * track->ms_per_beat) / track->ticks_per_beat;
+ total += ms;
+
+ // Set a timer to be invoked when the next event is
+ // ready to play.
+
+ OPL_SetCallback(ms, TrackTimerCallback, track);
+}
+
+// Initialize a channel.
+
+static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel)
+{
+ // TODO: Work out sensible defaults?
+
+ channel->instrument = &main_instrs[0];
+ channel->volume = 127;
+ channel->bend = 0;
+}
+
+// Start a MIDI track playing:
+
+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);
+ track->ticks_per_beat = MIDI_GetFileTimeDivision(file);
+
+ // Default is 120 bpm.
+ // TODO: this is wrong
+
+ track->ms_per_beat = 500 * 260;
+
+ for (i=0; i<MIDI_CHANNELS_PER_TRACK; ++i)
+ {
+ InitChannel(track, &track->channels[i]);
+ }
+
+ // Schedule the first event.
+
+ ScheduleTrack(track);
+}
+
+// Start playing a mid
+
+static void I_OPL_PlaySong(void *handle, boolean looping)
+{
+ midi_file_t *file;
+ unsigned int i;
+
+ if (!music_initialized || handle == NULL)
+ {
+ return;
+ }
+
+ file = handle;
+
+ // Allocate track data.
+
+ tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t));
+
+ num_tracks = MIDI_NumTracks(file);
+ running_tracks = num_tracks;
+ song_looping = looping;
+
+ for (i=0; i<num_tracks; ++i)
+ {
+ StartTrack(file, i);
+ }
+}
+
+static void I_OPL_PauseSong(void)
+{
+ unsigned int i;
+
+ if (!music_initialized)
+ {
+ return;
+ }
+
+ // Pause OPL callbacks.
+
+ OPL_SetPaused(1);
+
+ // Turn off all main instrument voices (not percussion).
+ // This is what Vanilla does.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ if (voices[i].channel != NULL
+ && voices[i].current_instr < percussion_instrs)
+ {
+ VoiceKeyOff(&voices[i]);
+ }
+ }
+}
+
+static void I_OPL_ResumeSong(void)
+{
+ if (!music_initialized)
+ {
+ return;
+ }
+
+ OPL_SetPaused(0);
+}
+
+static void I_OPL_StopSong(void)
+{
+ unsigned int i;
+
+ if (!music_initialized)
+ {
+ return;
+ }
+
+ OPL_Lock();
+
+ // Stop all playback.
+
+ OPL_ClearCallbacks();
+
+ // Free all voices.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ if (voices[i].channel != NULL)
+ {
+ VoiceKeyOff(&voices[i]);
+ ReleaseVoice(&voices[i]);
+ }
+ }
+
+ // Free all track data.
+
+ for (i=0; i<num_tracks; ++i)
+ {
+ MIDI_FreeIterator(tracks[i].iter);
+ }
+
+ free(tracks);
+
+ tracks = NULL;
+ num_tracks = 0;
+
+ OPL_Unlock();
+}
+
+static void I_OPL_UnRegisterSong(void *handle)
+{
+ if (!music_initialized)
+ {
+ return;
+ }
+
+ if (handle != NULL)
+ {
+ MIDI_FreeFile(handle);
+ }
+}
+
+// Determine whether memory block is a .mid file
+
+static boolean IsMid(byte *mem, int len)
+{
+ return len > 4 && !memcmp(mem, "MThd", 4);
+}
+
+static boolean ConvertMus(byte *musdata, int len, char *filename)
+{
+ MEMFILE *instream;
+ MEMFILE *outstream;
+ void *outbuf;
+ size_t outbuf_len;
+ int result;
+
+ instream = mem_fopen_read(musdata, len);
+ outstream = mem_fopen_write();
+
+ result = mus2mid(instream, outstream);
+
+ if (result == 0)
+ {
+ mem_get_buf(outstream, &outbuf, &outbuf_len);
+
+ M_WriteFile(filename, outbuf, outbuf_len);
+ }
+
+ mem_fclose(instream);
+ mem_fclose(outstream);
+
+ return result;
+}
+
+static void *I_OPL_RegisterSong(void *data, int len)
+{
+ midi_file_t *result;
+ char *filename;
+
+ if (!music_initialized)
+ {
+ return NULL;
+ }
+
+ // MUS files begin with "MUS"
+ // Reject anything which doesnt have this signature
+
+ filename = M_TempFile("doom.mid");
+
+ if (IsMid(data, len) && len < MAXMIDLENGTH)
+ {
+ M_WriteFile(filename, data, len);
+ }
+ else
+ {
+ // Assume a MUS file and try to convert
+
+ ConvertMus(data, len, filename);
+ }
+
+ result = MIDI_LoadFile(filename);
+
+ if (result == NULL)
+ {
+ fprintf(stderr, "I_OPL_RegisterSong: Failed to load MID.\n");
+ }
+
+ // remove file now
+
+ remove(filename);
+
+ Z_Free(filename);
+
+ return result;
+}
+
+// Is the song playing?
+
+static boolean I_OPL_MusicIsPlaying(void)
+{
+ if (!music_initialized)
+ {
+ return false;
+ }
+
+ return num_tracks > 0;
+}
+
+// Shutdown music
+
+static void I_OPL_ShutdownMusic(void)
+{
+ if (music_initialized)
+ {
+ // Stop currently-playing track, if there is one:
+
+ I_OPL_StopSong();
+
+ OPL_Shutdown();
+
+ // Release GENMIDI lump
+
+ W_ReleaseLumpName("GENMIDI");
+
+ music_initialized = false;
+ }
+}
+
+// Initialize music subsystem
+
+static boolean I_OPL_InitMusic(void)
+{
+ OPL_SetSampleRate(snd_samplerate);
+
+ if (!OPL_Init(opl_io_port))
+ {
+ printf("Dude. The Adlib isn't responding.\n");
+ return false;
+ }
+
+ // Load instruments from GENMIDI lump:
+
+ if (!LoadInstrumentTable())
+ {
+ OPL_Shutdown();
+ return false;
+ }
+
+ InitVoices();
+
+ tracks = NULL;
+ num_tracks = 0;
+ music_initialized = true;
+
+ return true;
+}
+
+static snddevice_t music_opl_devices[] =
+{
+ SNDDEVICE_ADLIB,
+ SNDDEVICE_SB,
+};
+
+music_module_t music_opl_module =
+{
+ music_opl_devices,
+ arrlen(music_opl_devices),
+ I_OPL_InitMusic,
+ I_OPL_ShutdownMusic,
+ I_OPL_SetMusicVolume,
+ I_OPL_PauseSong,
+ I_OPL_ResumeSong,
+ I_OPL_RegisterSong,
+ I_OPL_UnRegisterSong,
+ I_OPL_PlaySong,
+ I_OPL_StopSong,
+ I_OPL_MusicIsPlaying,
+};
+
diff --git a/src/i_scale.c b/src/i_scale.c
index 4f08f5d6..2c0b718c 100644
--- a/src/i_scale.c
+++ b/src/i_scale.c
@@ -56,11 +56,11 @@ static int dest_pitch;
// stretch_tables[1] : 40% / 60%
// All other combinations can be reached from these two tables.
-static byte *stretch_tables[2];
+static byte *stretch_tables[2] = { NULL, NULL };
// 50%/50% stretch table, for 800x600 squash mode
-static byte *half_stretch_table;
+static byte *half_stretch_table = NULL;
// Called to set the source and destination buffers before doing the
// scale.
@@ -367,6 +367,11 @@ static byte *GenerateStretchTable(byte *palette, int pct)
static void I_InitStretchTables(byte *palette)
{
+ if (stretch_tables[0] != NULL)
+ {
+ return;
+ }
+
// We only actually need two lookup tables:
//
// mix 0% = just write line 1
@@ -388,6 +393,11 @@ static void I_InitStretchTables(byte *palette)
static void I_InitSquashTable(byte *palette)
{
+ if (half_stretch_table != NULL)
+ {
+ return;
+ }
+
printf("I_InitSquashTable: Generating lookup table..");
fflush(stdout);
half_stretch_table = GenerateStretchTable(palette, 50);
diff --git a/src/i_sdlmusic.c b/src/i_sdlmusic.c
index 1e907739..0418e025 100644
--- a/src/i_sdlmusic.c
+++ b/src/i_sdlmusic.c
@@ -335,8 +335,6 @@ static boolean I_SDL_MusicIsPlaying(void)
static snddevice_t music_sdl_devices[] =
{
- SNDDEVICE_ADLIB,
- SNDDEVICE_SB,
SNDDEVICE_PAS,
SNDDEVICE_GUS,
SNDDEVICE_WAVEBLASTER,
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index 0b3f8aa3..7deb683d 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -25,7 +25,6 @@
//
//-----------------------------------------------------------------------------
-
#include "config.h"
#include <stdio.h>
@@ -42,6 +41,7 @@
#include "deh_str.h"
#include "i_sound.h"
#include "i_system.h"
+#include "i_swap.h"
#include "m_argv.h"
#include "w_wad.h"
#include "z_zone.h"
@@ -49,6 +49,7 @@
#include "doomtype.h"
#define LOW_PASS_FILTER
+//#define DEBUG_DUMP_WAVS
#define MAX_SOUND_SLICE_TIME 70 /* ms */
#define NUM_CHANNELS 16
@@ -288,6 +289,56 @@ static boolean ConvertibleRatio(int freq1, int freq2)
}
}
+#ifdef DEBUG_DUMP_WAVS
+
+// Debug code to dump resampled sound effects to WAV files for analysis.
+
+static void WriteWAV(char *filename, byte *data,
+ uint32_t length, int samplerate)
+{
+ FILE *wav;
+ unsigned int i;
+ unsigned short s;
+
+ wav = fopen(filename, "wb");
+
+ // Header
+
+ fwrite("RIFF", 1, 4, wav);
+ i = LONG(36 + samplerate);
+ fwrite(&i, 4, 1, wav);
+ fwrite("WAVE", 1, 4, wav);
+
+ // Subchunk 1
+
+ fwrite("fmt ", 1, 4, wav);
+ i = LONG(16);
+ fwrite(&i, 4, 1, wav); // Length
+ s = SHORT(1);
+ fwrite(&s, 2, 1, wav); // Format (PCM)
+ s = SHORT(2);
+ fwrite(&s, 2, 1, wav); // Channels (2=stereo)
+ i = LONG(samplerate);
+ fwrite(&i, 4, 1, wav); // Sample rate
+ i = LONG(samplerate * 2 * 2);
+ fwrite(&i, 4, 1, wav); // Byte rate (samplerate * stereo * 16 bit)
+ s = SHORT(2 * 2);
+ fwrite(&s, 2, 1, wav); // Block align (stereo * 16 bit)
+ s = SHORT(16);
+ fwrite(&s, 2, 1, wav); // Bits per sample (16 bit)
+
+ // Data subchunk
+
+ fwrite("data", 1, 4, wav);
+ i = LONG(length);
+ fwrite(&i, 4, 1, wav); // Data length
+ fwrite(data, 1, length, wav); // Data
+
+ fclose(wav);
+}
+
+#endif
+
// Generic sound expansion function for any sample rate.
// Returns number of clipped samples (always 0).
@@ -313,7 +364,7 @@ static void ExpandSoundData_SDL(sfxinfo_t *sfxinfo,
chunk = AllocateChunk(sfxinfo, expanded_length);
// If we can, use the standard / optimized SDL conversion routines.
-
+
if (samplerate <= mixer_freq
&& ConvertibleRatio(samplerate, mixer_freq)
&& SDL_BuildAudioCVT(&convertor,
@@ -379,9 +430,12 @@ static void ExpandSoundData_SDL(sfxinfo_t *sfxinfo,
rc = 1.0f / (3.14f * samplerate);
alpha = dt / (rc + dt);
- for (i=1; i<expanded_length; ++i)
+ // Both channels are processed in parallel, hence [i-2]:
+
+ for (i=2; i<expanded_length * 2; ++i)
{
- expanded[i] = (Sint16) (alpha * expanded[i] + (1 - alpha) * expanded[i-1]);
+ expanded[i] = (Sint16) (alpha * expanded[i]
+ + (1 - alpha) * expanded[i-2]);
}
}
#endif /* #ifdef LOW_PASS_FILTER */
@@ -432,6 +486,16 @@ static boolean CacheSFX(sfxinfo_t *sfxinfo)
ExpandSoundData(sfxinfo, data + 8, samplerate, length);
+#ifdef DEBUG_DUMP_WAVS
+ {
+ char filename[16];
+
+ sprintf(filename, "%s.wav", DEH_String(S_sfx[sound].name));
+ WriteWAV(filename, sound_chunks[sound].abuf,
+ sound_chunks[sound].alen, mixer_freq);
+ }
+#endif
+
// don't need the original lump any more
W_ReleaseLumpNum(lumpnum);
diff --git a/src/i_sound.c b/src/i_sound.c
index a9e953ce..0c771dc2 100644
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -46,7 +46,7 @@ int snd_samplerate = 44100;
static sound_module_t *sound_module;
static music_module_t *music_module;
-int snd_musicdevice = SNDDEVICE_SB;
+int snd_musicdevice = SNDDEVICE_GENMIDI;
int snd_sfxdevice = SNDDEVICE_SB;
// Sound modules
@@ -54,6 +54,11 @@ int snd_sfxdevice = SNDDEVICE_SB;
extern sound_module_t sound_sdl_module;
extern sound_module_t sound_pcsound_module;
extern music_module_t music_sdl_module;
+extern music_module_t music_opl_module;
+
+// For OPL module:
+
+extern int opl_io_port;
// DOS-specific options: These are unused but should be maintained
// so that the config file can be shared between chocolate
@@ -81,6 +86,7 @@ static music_module_t *music_modules[] =
{
#ifdef FEATURE_SOUND
&music_sdl_module,
+ &music_opl_module,
#endif
NULL,
};
@@ -245,7 +251,6 @@ void I_UpdateSound(void)
static void CheckVolumeSeparation(int *sep, int *vol)
{
-return;
if (*sep < 0)
{
*sep = 0;
@@ -407,6 +412,7 @@ void I_BindSoundVariables(void)
M_BindVariable("snd_sbdma", &snd_sbdma);
M_BindVariable("snd_mport", &snd_mport);
M_BindVariable("snd_samplerate", &snd_samplerate);
+ M_BindVariable("opl_io_port", &opl_io_port);
#ifdef FEATURE_SOUND
M_BindVariable("use_libsamplerate", &use_libsamplerate);
#endif
diff --git a/src/i_video.c b/src/i_video.c
index a370fc08..733af899 100644
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -144,8 +144,6 @@ static char *window_title = "";
static SDL_Color palette[256];
static boolean palette_to_set;
-static int windowwidth, windowheight;
-
// display has been set up?
static boolean initialized = false;
@@ -155,6 +153,10 @@ static boolean initialized = false;
static boolean nomouse = false;
int usemouse = 1;
+// Bit mask of mouse button state.
+
+static unsigned int mouse_button_state = 0;
+
// Disallow mouse and joystick movement to cause forward/backward
// motion. Specified with the '-novert' command line parameter.
// This is an int to allow saving to config file
@@ -235,6 +237,12 @@ static SDL_Cursor *cursors[2];
static screen_mode_t *screen_mode;
+// Window resize state.
+
+static boolean need_resize = false;
+static unsigned int resize_w, resize_h;
+static unsigned int last_resize_time;
+
// If true, keyboard mapping is ignored, like in Vanilla Doom.
// The sensible thing to do is to disable this if you have a non-US
// keyboard.
@@ -261,6 +269,8 @@ int mouse_threshold = 10;
int usegamma = 0;
+static void ApplyWindowResize(unsigned int w, unsigned int h);
+
static boolean MouseShouldBeGrabbed()
{
// never grab the mouse when in screensaver mode
@@ -524,29 +534,56 @@ void I_StartFrame (void)
}
-static int MouseButtonState(void)
+static void UpdateMouseButtonState(unsigned int button, boolean on)
{
- Uint8 state;
- int result = 0;
+ event_t event;
-#if SDL_VERSION_ATLEAST(1, 3, 0)
- state = SDL_GetMouseState(0, NULL, NULL);
-#else
- state = SDL_GetMouseState(NULL, NULL);
-#endif
+ if (button < SDL_BUTTON_LEFT || button > MAX_MOUSE_BUTTONS)
+ {
+ return;
+ }
// Note: button "0" is left, button "1" is right,
// button "2" is middle for Doom. This is different
// to how SDL sees things.
- if (state & SDL_BUTTON(1))
- result |= 1;
- if (state & SDL_BUTTON(3))
- result |= 2;
- if (state & SDL_BUTTON(2))
- result |= 4;
+ switch (button)
+ {
+ case SDL_BUTTON_LEFT:
+ button = 0;
+ break;
- return result;
+ case SDL_BUTTON_RIGHT:
+ button = 1;
+ break;
+
+ case SDL_BUTTON_MIDDLE:
+ button = 2;
+ break;
+
+ default:
+ // SDL buttons are indexed from 1.
+ --button;
+ break;
+ }
+
+ // Turn bit representing this button on or off.
+
+ if (on)
+ {
+ mouse_button_state |= (1 << button);
+ }
+ else
+ {
+ mouse_button_state &= ~(1 << button);
+ }
+
+ // Post an event with the new button state.
+
+ event.type = ev_mouse;
+ event.data1 = mouse_button_state;
+ event.data2 = event.data3 = 0;
+ D_PostEvent(&event);
}
static int AccelerateMouse(int val)
@@ -692,7 +729,7 @@ void I_GetEvent(void)
/*
case SDL_MOUSEMOTION:
event.type = ev_mouse;
- event.data1 = MouseButtonState();
+ event.data1 = mouse_button_state;
event.data2 = AccelerateMouse(sdlevent.motion.xrel);
event.data3 = -AccelerateMouse(sdlevent.motion.yrel);
D_PostEvent(&event);
@@ -702,20 +739,14 @@ void I_GetEvent(void)
case SDL_MOUSEBUTTONDOWN:
if (usemouse && !nomouse)
{
- event.type = ev_mouse;
- event.data1 = MouseButtonState();
- event.data2 = event.data3 = 0;
- D_PostEvent(&event);
+ UpdateMouseButtonState(sdlevent.button.button, true);
}
break;
case SDL_MOUSEBUTTONUP:
if (usemouse && !nomouse)
{
- event.type = ev_mouse;
- event.data1 = MouseButtonState();
- event.data2 = event.data3 = 0;
- D_PostEvent(&event);
+ UpdateMouseButtonState(sdlevent.button.button, false);
}
break;
@@ -733,6 +764,13 @@ void I_GetEvent(void)
palette_to_set = true;
break;
+ case SDL_RESIZABLE:
+ need_resize = true;
+ resize_w = sdlevent.resize.w;
+ resize_h = sdlevent.resize.h;
+ last_resize_time = SDL_GetTicks();
+ break;
+
default:
break;
}
@@ -777,7 +815,7 @@ static void I_ReadMouse(void)
if (x != 0 || y != 0)
{
ev.type = ev_mouse;
- ev.data1 = MouseButtonState();
+ ev.data1 = mouse_button_state;
ev.data2 = AccelerateMouse(x);
if (!novert)
@@ -976,14 +1014,20 @@ void I_FinishUpdate (void)
static int lasttic;
int tics;
int i;
- // UNUSED static unsigned char *bigscreen=0;
if (!initialized)
return;
if (noblit)
return;
-
+
+ if (need_resize && SDL_GetTicks() > last_resize_time + 500)
+ {
+ ApplyWindowResize(resize_w, resize_h);
+ need_resize = false;
+ palette_to_set = true;
+ }
+
UpdateGrab();
// Don't update the screen if the window isn't visible.
@@ -1692,11 +1736,96 @@ static char *WindowBoxType(screen_mode_t *mode, int w, int h)
}
}
+static void SetVideoMode(screen_mode_t *mode, int w, int h)
+{
+ byte *doompal;
+ int flags = 0;
+
+ doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
+
+ // Generate lookup tables before setting the video mode.
+
+ if (mode != NULL && mode->InitMode != NULL)
+ {
+ mode->InitMode(doompal);
+ }
+
+ // Set the video mode.
+
+ flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
+
+ if (fullscreen)
+ {
+ flags |= SDL_FULLSCREEN;
+ }
+ else
+ {
+ flags |= SDL_RESIZABLE;
+ }
+
+ screen = SDL_SetVideoMode(w, h, 8, flags);
+
+ if (screen == NULL)
+ {
+ I_Error("Error setting video mode: %s\n", SDL_GetError());
+ }
+
+ // If mode was not set, it must be set now that we know the
+ // screen size.
+
+ if (mode == NULL)
+ {
+ mode = I_FindScreenMode(screen->w, screen->h);
+
+ if (mode == NULL)
+ {
+ I_Error("I_InitGraphics: Unable to find a screen mode small "
+ "enough for %ix%i", screen->w, screen->h);
+ }
+
+ // Generate lookup tables before setting the video mode.
+
+ if (mode->InitMode != NULL)
+ {
+ mode->InitMode(doompal);
+ }
+ }
+
+ // Save screen mode.
+
+ screen_mode = mode;
+}
+
+static void ApplyWindowResize(unsigned int w, unsigned int h)
+{
+ screen_mode_t *mode;
+
+ // Find the biggest screen mode that will fall within these
+ // dimensions, falling back to the smallest mode possible if
+ // none is found.
+
+ mode = I_FindScreenMode(w, h);
+
+ if (mode == NULL)
+ {
+ mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT);
+ }
+
+ // Reset mode to resize window.
+
+ printf("Resize to %ix%i\n", mode->width, mode->height);
+ SetVideoMode(mode, mode->width, mode->height);
+
+ // Save settings.
+
+ screen_width = mode->width;
+ screen_height = mode->height;
+}
+
void I_InitGraphics(void)
{
SDL_Event dummy;
byte *doompal;
- int flags = 0;
char *env;
// Pass through the XSCREENSAVER_WINDOW environment variable to
@@ -1727,70 +1856,53 @@ void I_InitGraphics(void)
CheckCommandLine();
- doompal = W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE);
+ // Set up title and icon. Windows cares about the ordering; this
+ // has to be done before the call to SDL_SetVideoMode.
+
+ I_InitWindowTitle();
+#if !SDL_VERSION_ATLEAST(1, 3, 0)
+ I_InitWindowIcon();
+#endif
+
+ //
+ // Enter into graphics mode.
+ //
+ // When in screensaver mode, run full screen and auto detect
+ // screen dimensions (don't change video mode)
+ //
if (screensaver_mode)
{
- windowwidth = 0;
- windowheight = 0;
+ SetVideoMode(NULL, 0, 0);
}
else
{
+ int w, h;
+
if (autoadjust_video_settings)
{
I_AutoAdjustSettings();
}
- windowwidth = screen_width;
- windowheight = screen_height;
+ w = screen_width;
+ h = screen_height;
- screen_mode = I_FindScreenMode(windowwidth, windowheight);
+ screen_mode = I_FindScreenMode(w, h);
if (screen_mode == NULL)
{
I_Error("I_InitGraphics: Unable to find a screen mode small "
- "enough for %ix%i", windowwidth, windowheight);
+ "enough for %ix%i", w, h);
}
- if (windowwidth != screen_mode->width
- || windowheight != screen_mode->height)
+ if (w != screen_mode->width || h != screen_mode->height)
{
printf("I_InitGraphics: %s (%ix%i within %ix%i)\n",
- WindowBoxType(screen_mode, windowwidth, windowheight),
- screen_mode->width, screen_mode->height,
- windowwidth, windowheight);
+ WindowBoxType(screen_mode, w, h),
+ screen_mode->width, screen_mode->height, w, h);
}
- // Generate lookup tables before setting the video mode.
-
- if (screen_mode->InitMode != NULL)
- {
- screen_mode->InitMode(doompal);
- }
- }
-
- // Set up title and icon. Windows cares about the ordering; this
- // has to be done before the call to SDL_SetVideoMode.
-
- I_InitWindowTitle();
-#if !SDL_VERSION_ATLEAST(1, 3, 0)
- I_InitWindowIcon();
-#endif
-
- // Set the video mode.
-
- flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
-
- if (fullscreen)
- {
- flags |= SDL_FULLSCREEN;
- }
-
- screen = SDL_SetVideoMode(windowwidth, windowheight, 8, flags);
-
- if (screen == NULL)
- {
- I_Error("Error setting video mode: %s\n", SDL_GetError());
+ SetVideoMode(screen_mode, w, h);
}
// Start with a clear black screen
@@ -1811,6 +1923,7 @@ void I_InitGraphics(void)
// Set the palette
+ doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
I_SetPalette(doompal);
SDL_SetColors(screen, palette, 0, 256);
@@ -1819,26 +1932,6 @@ void I_InitGraphics(void)
UpdateFocus();
UpdateGrab();
- // In screensaver mode, now find a screen_mode to use.
-
- if (screensaver_mode)
- {
- screen_mode = I_FindScreenMode(screen->w, screen->h);
-
- if (screen_mode == NULL)
- {
- I_Error("I_InitGraphics: Unable to find a screen mode small "
- "enough for %ix%i", screen->w, screen->h);
- }
-
- // Generate lookup tables before setting the video mode.
-
- if (screen_mode->InitMode != NULL)
- {
- screen_mode->InitMode(doompal);
- }
- }
-
// On some systems, it takes a second or so for the screen to settle
// after changing modes. We include the option to add a delay when
// setting the screen mode, so that the game doesn't start immediately
@@ -1854,12 +1947,12 @@ void I_InitGraphics(void)
// Likewise if the screen pitch is not the same as the width
// If we have to multiply, drawing is done to a separate 320x200 buf
- native_surface = !SDL_MUSTLOCK(screen)
+ native_surface = !SDL_MUSTLOCK(screen)
&& screen_mode == &mode_scale_1x
&& screen->pitch == SCREENWIDTH
&& aspect_ratio_correct;
- // If not, allocate a buffer and copy from that buffer to the
+ // If not, allocate a buffer and copy from that buffer to the
// screen when we do an update
if (native_surface)
diff --git a/src/i_video.h b/src/i_video.h
index af865287..82368967 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -43,7 +43,9 @@
#define SCREENHEIGHT_4_3 240
-typedef struct
+#define MAX_MOUSE_BUTTONS 8
+
+typedef struct
{
// Screen width and height
diff --git a/src/m_argv.c b/src/m_argv.c
index e2b551f1..79702e56 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -101,7 +101,7 @@ static void LoadResponseFile(int argv_index)
size = M_FileLength(handle);
// Read in the entire file
- // Allocate one byte extra - this is incase there is an argument
+ // Allocate one byte extra - this is in case there is an argument
// at the end of the response file, in which case a '\0' will be
// needed.
diff --git a/src/m_config.c b/src/m_config.c
index f3e6e913..d3085b05 100644
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -60,6 +60,7 @@ static char *default_extra_config;
typedef enum
{
DEFAULT_INT,
+ DEFAULT_INT_HEX,
DEFAULT_STRING,
DEFAULT_FLOAT,
DEFAULT_KEY,
@@ -99,14 +100,19 @@ typedef struct
char *filename;
} default_collection_t;
+#define CONFIG_VARIABLE_GENERIC(name, type) \
+ { #name, NULL, type, 0, 0, false }
+
#define CONFIG_VARIABLE_KEY(name) \
- { #name, NULL, DEFAULT_KEY, 0, 0, false }
+ CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY)
#define CONFIG_VARIABLE_INT(name) \
- { #name, NULL, DEFAULT_INT, 0, 0, false }
+ CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT)
+#define CONFIG_VARIABLE_INT_HEX(name) \
+ CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX)
#define CONFIG_VARIABLE_FLOAT(name) \
- { #name, NULL, DEFAULT_FLOAT, 0, 0, false }
+ CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT)
#define CONFIG_VARIABLE_STRING(name) \
- { #name, NULL, DEFAULT_STRING, 0, 0, false }
+ CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING)
//! @begin_config_file default.cfg
@@ -664,20 +670,27 @@ static default_t extra_defaults_list[] =
//!
// Mouse acceleration threshold. When the speed of mouse movement
- // exceeds this threshold value, the speed is multiplied by an
+ // exceeds this threshold value, the speed is multiplied by an
// acceleration factor (mouse_acceleration).
//
CONFIG_VARIABLE_INT(mouse_threshold),
//!
- // Sound output sample rate, in Hz. Typical values to use are
+ // Sound output sample rate, in Hz. Typical values to use are
// 11025, 22050, 44100 and 48000.
//
CONFIG_VARIABLE_INT(snd_samplerate),
//!
+ // 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),
+
+ //!
// If non-zero, the ENDOOM screen is displayed when exiting the
// game. If zero, the ENDOOM screen is not displayed.
//
@@ -772,6 +785,18 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_INT(joyb_straferight),
//!
+ // Joystick button to cycle to the previous weapon.
+ //
+
+ CONFIG_VARIABLE_INT(joyb_prevweapon),
+
+ //!
+ // Joystick button to cycle to the next weapon.
+ //
+
+ CONFIG_VARIABLE_INT(joyb_nextweapon),
+
+ //!
// Mouse button to strafe left.
//
@@ -796,6 +821,18 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_INT(mouseb_backward),
//!
+ // Mouse button to cycle to the previous weapon.
+ //
+
+ CONFIG_VARIABLE_INT(mouseb_prevweapon),
+
+ //!
+ // Mouse button to cycle to the next weapon.
+ //
+
+ CONFIG_VARIABLE_INT(mouseb_nextweapon),
+
+ //!
// If non-zero, double-clicking a mouse button acts like pressing
// the "use" key to use an object in-game, eg. a door or switch.
//
@@ -948,6 +985,12 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_KEY(key_menu_gamma),
//!
+ // Keyboard shortcut to switch view in multiplayer.
+ //
+
+ CONFIG_VARIABLE_KEY(key_spy),
+
+ //!
// Keyboard shortcut to increase the screen size.
//
@@ -1080,10 +1123,82 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_KEY(key_weapon8),
//!
+ // Key to cycle to the previous weapon.
+ //
+
+ CONFIG_VARIABLE_KEY(key_prevweapon),
+
+ //!
+ // Key to cycle to the next weapon.
+ //
+
+ CONFIG_VARIABLE_KEY(key_nextweapon),
+
+ //!
// Key to re-display last message.
//
CONFIG_VARIABLE_KEY(key_message_refresh),
+
+ //!
+ // Key to quit the game when recording a demo.
+ //
+
+ CONFIG_VARIABLE_KEY(key_demo_quit),
+
+ //!
+ // Key to send a message during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msg),
+
+ //!
+ // Key to send a message to player 1 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer1),
+
+ //!
+ // Key to send a message to player 2 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer2),
+
+ //!
+ // Key to send a message to player 3 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer3),
+
+ //!
+ // Key to send a message to player 4 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer4),
+
+ //!
+ // Key to send a message to player 5 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer5),
+
+ //!
+ // Key to send a message to player 6 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer6),
+
+ //!
+ // Key to send a message to player 7 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer7),
+
+ //!
+ // Key to send a message to player 8 during multiplayer games.
+ //
+
+ CONFIG_VARIABLE_KEY(key_multi_msgplayer8)
};
static default_collection_t extra_defaults =
@@ -1205,6 +1320,10 @@ static void SaveDefaultCollection(default_collection_t *collection)
fprintf(f, "%i", * (int *) defaults[i].location);
break;
+ case DEFAULT_INT_HEX:
+ fprintf(f, "0x%x", * (int *) defaults[i].location);
+ break;
+
case DEFAULT_FLOAT:
fprintf(f, "%f", * (float *) defaults[i].location);
break;
@@ -1294,6 +1413,7 @@ static void LoadDefaultCollection(default_collection_t *collection)
break;
case DEFAULT_INT:
+ case DEFAULT_INT_HEX:
* (int *) def->location = ParseIntParameter(strparm);
break;
diff --git a/src/m_controls.c b/src/m_controls.c
index 0f64249d..8b8af160 100644
--- a/src/m_controls.c
+++ b/src/m_controls.c
@@ -22,6 +22,8 @@
//
//-----------------------------------------------------------------------------
+#include <stdio.h>
+
#include "doomtype.h"
#include "doomkeys.h"
@@ -100,8 +102,19 @@ int mousebstraferight = -1;
int mousebbackward = -1;
int mousebuse = -1;
+int mousebprevweapon = -1;
+int mousebnextweapon = -1;
+
+
int key_message_refresh = KEY_ENTER;
int key_pause = KEY_PAUSE;
+int key_demo_quit = 'q';
+int key_spy = KEY_F12;
+
+// Multiplayer chat keys:
+
+int key_multi_msg = 't';
+int key_multi_msgplayer[8];
// Weapon selection keys:
@@ -113,8 +126,10 @@ int key_weapon5 = '5';
int key_weapon6 = '6';
int key_weapon7 = '7';
int key_weapon8 = '8';
+int key_prevweapon = 0;
+int key_nextweapon = 0;
-// Map controls keys:
+// Map control keys:
int key_map_north = KEY_UPARROW;
int key_map_south = KEY_DOWNARROW;
@@ -170,6 +185,9 @@ int joybstraferight = -1;
int joybjump = -1;
+int joybprevweapon = -1;
+int joybnextweapon = -1;
+
// Control whether if a mouse button is double clicked, it acts like
// "use" has been pressed
@@ -202,7 +220,7 @@ void M_BindBaseControls(void)
M_BindVariable("joyb_speed", &joybspeed),
// Extra controls that are not in the Vanilla versions:
-
+
M_BindVariable("joyb_strafeleft", &joybstrafeleft);
M_BindVariable("joyb_straferight", &joybstraferight);
M_BindVariable("mouseb_strafeleft", &mousebstrafeleft);
@@ -278,6 +296,15 @@ void M_BindWeaponControls(void)
M_BindVariable("key_weapon6", &key_weapon6);
M_BindVariable("key_weapon7", &key_weapon7);
M_BindVariable("key_weapon8", &key_weapon8);
+
+ M_BindVariable("key_prevweapon", &key_prevweapon);
+ M_BindVariable("key_nextweapon", &key_nextweapon);
+
+ M_BindVariable("joyb_prevweapon", &joybprevweapon);
+ M_BindVariable("joyb_nextweapon", &joybnextweapon);
+
+ M_BindVariable("mouseb_prevweapon", &mousebprevweapon);
+ M_BindVariable("mouseb_nextweapon", &mousebnextweapon);
}
void M_BindMapControls(void)
@@ -322,6 +349,22 @@ void M_BindMenuControls(void)
M_BindVariable("key_menu_incscreen", &key_menu_incscreen);
M_BindVariable("key_menu_decscreen", &key_menu_decscreen);
+ M_BindVariable("key_demo_quit", &key_demo_quit);
+ M_BindVariable("key_spy", &key_spy);
+}
+
+void M_BindChatControls(unsigned int num_players)
+{
+ char name[20];
+ int i;
+
+ M_BindVariable("key_multi_msg", &key_multi_msg);
+
+ for (i=0; i<num_players; ++i)
+ {
+ sprintf(name, "key_multi_msgplayer%i", i + 1);
+ M_BindVariable(name, &key_multi_msgplayer[i]);
+ }
}
#ifdef _WIN32_WCE
diff --git a/src/m_controls.h b/src/m_controls.h
index 10ca7a0c..3e50d557 100644
--- a/src/m_controls.h
+++ b/src/m_controls.h
@@ -63,6 +63,9 @@ extern int key_invdrop;
extern int key_message_refresh;
extern int key_pause;
+extern int key_multi_msg;
+extern int key_multi_msgplayer[8];
+
extern int key_weapon1;
extern int key_weapon2;
extern int key_weapon3;
@@ -72,6 +75,11 @@ extern int key_weapon6;
extern int key_weapon7;
extern int key_weapon8;
+extern int key_demo_quit;
+extern int key_spy;
+extern int key_prevweapon;
+extern int key_nextweapon;
+
extern int key_map_north;
extern int key_map_south;
extern int key_map_east;
@@ -136,6 +144,9 @@ extern int mousebstraferight;
extern int mousebbackward;
extern int mousebuse;
+extern int mousebprevweapon;
+extern int mousebnextweapon;
+
extern int joybfire;
extern int joybstrafe;
extern int joybuse;
@@ -146,8 +157,11 @@ extern int joybjump;
extern int joybstrafeleft;
extern int joybstraferight;
+extern int joybprevweapon;
+extern int joybnextweapon;
+
extern int dclick_use;
-
+
void M_BindBaseControls(void);
void M_BindHereticControls(void);
void M_BindHexenControls(void);
@@ -155,6 +169,7 @@ void M_BindStrifeControls(void);
void M_BindWeaponControls(void);
void M_BindMapControls(void);
void M_BindMenuControls(void);
+void M_BindChatControls(unsigned int num_players);
void M_ApplyPlatformDefaults(void);
diff --git a/src/m_misc.c b/src/m_misc.c
index 5847f1a2..ed41b5f1 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -255,3 +255,37 @@ void M_ForceUppercase(char *text)
}
}
+//
+// M_StrCaseStr
+//
+// Case-insensitive version of strstr()
+//
+
+char *M_StrCaseStr(char *haystack, char *needle)
+{
+ unsigned int haystack_len;
+ unsigned int needle_len;
+ unsigned int len;
+ unsigned int i;
+
+ haystack_len = strlen(haystack);
+ needle_len = strlen(needle);
+
+ if (haystack_len < needle_len)
+ {
+ return NULL;
+ }
+
+ len = haystack_len - needle_len;
+
+ for (i = 0; i <= len; ++i)
+ {
+ if (!strncasecmp(haystack + i, needle, needle_len))
+ {
+ return haystack + i;
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/src/m_misc.h b/src/m_misc.h
index c92ddde8..c6be6ccb 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -42,7 +42,7 @@ long M_FileLength(FILE *handle);
boolean M_StrToInt(const char *str, int *result);
void M_ExtractFileBase(char *path, char *dest);
void M_ForceUppercase(char *text);
-
+char *M_StrCaseStr(char *haystack, char *needle);
#endif
diff --git a/src/midifile.c b/src/midifile.c
new file mode 100644
index 00000000..1990dcdc
--- /dev/null
+++ b/src/midifile.c
@@ -0,0 +1,814 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Reading of MIDI files.
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "doomtype.h"
+#include "i_swap.h"
+#include "midifile.h"
+
+#define HEADER_CHUNK_ID "MThd"
+#define TRACK_CHUNK_ID "MTrk"
+#define MAX_BUFFER_SIZE 0x10000
+
+typedef struct
+{
+ byte chunk_id[4];
+ unsigned int chunk_size;
+} PACKEDATTR chunk_header_t;
+
+typedef struct
+{
+ chunk_header_t chunk_header;
+ unsigned short format_type;
+ unsigned short num_tracks;
+ unsigned short time_division;
+} PACKEDATTR midi_header_t;
+
+typedef struct
+{
+ // Length in bytes:
+
+ unsigned int data_len;
+
+ // Events in this track:
+
+ midi_event_t *events;
+ int num_events;
+} midi_track_t;
+
+struct midi_track_iter_s
+{
+ midi_track_t *track;
+ unsigned int position;
+};
+
+struct midi_file_s
+{
+ midi_header_t header;
+
+ // All tracks in this file:
+ midi_track_t *tracks;
+ unsigned int num_tracks;
+
+ // Data buffer used to store data read for SysEx or meta events:
+ byte *buffer;
+ unsigned int buffer_size;
+};
+
+// Check the header of a chunk:
+
+static boolean CheckChunkHeader(chunk_header_t *chunk,
+ char *expected_id)
+{
+ boolean result;
+
+ result = (memcmp((char *) chunk->chunk_id, expected_id, 4) == 0);
+
+ if (!result)
+ {
+ fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header, "
+ "got '%c%c%c%c'\n",
+ expected_id,
+ chunk->chunk_id[0], chunk->chunk_id[1],
+ chunk->chunk_id[2], chunk->chunk_id[3]);
+ }
+
+ return result;
+}
+
+// Read a single byte. Returns false on error.
+
+static boolean ReadByte(byte *result, FILE *stream)
+{
+ int c;
+
+ c = fgetc(stream);
+
+ if (c == EOF)
+ {
+ fprintf(stderr, "ReadByte: Unexpected end of file\n");
+ return false;
+ }
+ else
+ {
+ *result = (byte) c;
+
+ return true;
+ }
+}
+
+// Read a variable-length value.
+
+static boolean ReadVariableLength(unsigned int *result, FILE *stream)
+{
+ int i;
+ byte b;
+
+ *result = 0;
+
+ for (i=0; i<4; ++i)
+ {
+ if (!ReadByte(&b, stream))
+ {
+ fprintf(stderr, "ReadVariableLength: Error while reading "
+ "variable-length value\n");
+ return false;
+ }
+
+ // Insert the bottom seven bits from this byte.
+
+ *result <<= 7;
+ *result |= b & 0x7f;
+
+ // If the top bit is not set, this is the end.
+
+ if ((b & 0x80) == 0)
+ {
+ return true;
+ }
+ }
+
+ fprintf(stderr, "ReadVariableLength: Variable-length value too "
+ "long: maximum of four bytes\n");
+ return false;
+}
+
+// Read a byte sequence into the data buffer.
+
+static void *ReadByteSequence(unsigned int num_bytes, FILE *stream)
+{
+ unsigned int i;
+ byte *result;
+
+ // Allocate a buffer:
+
+ result = malloc(num_bytes);
+
+ if (result == NULL)
+ {
+ fprintf(stderr, "ReadByteSequence: Failed to allocate buffer\n");
+ return NULL;
+ }
+
+ // Read the data:
+
+ for (i=0; i<num_bytes; ++i)
+ {
+ if (!ReadByte(&result[i], stream))
+ {
+ fprintf(stderr, "ReadByteSequence: Error while reading byte %u\n",
+ i);
+ free(result);
+ return NULL;
+ }
+ }
+
+ return result;
+}
+
+// Read a MIDI channel event.
+// two_param indicates that the event type takes two parameters
+// (three byte) otherwise it is single parameter (two byte)
+
+static boolean ReadChannelEvent(midi_event_t *event,
+ byte event_type, boolean two_param,
+ FILE *stream)
+{
+ byte b;
+
+ // Set basics:
+
+ event->event_type = event_type & 0xf0;
+ event->data.channel.channel = event_type & 0x0f;
+
+ // Read parameters:
+
+ if (!ReadByte(&b, stream))
+ {
+ fprintf(stderr, "ReadChannelEvent: Error while reading channel "
+ "event parameters\n");
+ return false;
+ }
+
+ event->data.channel.param1 = b;
+
+ // Second parameter:
+
+ if (two_param)
+ {
+ if (!ReadByte(&b, stream))
+ {
+ fprintf(stderr, "ReadChannelEvent: Error while reading channel "
+ "event parameters\n");
+ return false;
+ }
+
+ event->data.channel.param2 = b;
+ }
+
+ return true;
+}
+
+// Read sysex event:
+
+static boolean ReadSysExEvent(midi_event_t *event, int event_type,
+ FILE *stream)
+{
+ event->event_type = event_type;
+
+ if (!ReadVariableLength(&event->data.sysex.length, stream))
+ {
+ fprintf(stderr, "ReadSysExEvent: Failed to read length of "
+ "SysEx block\n");
+ return false;
+ }
+
+ // Read the byte sequence:
+
+ event->data.sysex.data = ReadByteSequence(event->data.sysex.length, stream);
+
+ if (event->data.sysex.data == NULL)
+ {
+ fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n");
+ return false;
+ }
+
+ return true;
+}
+
+// Read meta event:
+
+static boolean ReadMetaEvent(midi_event_t *event, FILE *stream)
+{
+ byte b;
+
+ event->event_type = MIDI_EVENT_META;
+
+ // Read meta event type:
+
+ if (!ReadByte(&b, stream))
+ {
+ fprintf(stderr, "ReadMetaEvent: Failed to read meta event type\n");
+ return false;
+ }
+
+ event->data.meta.type = b;
+
+ // Read length of meta event data:
+
+ if (!ReadVariableLength(&event->data.meta.length, stream))
+ {
+ fprintf(stderr, "ReadSysExEvent: Failed to read length of "
+ "SysEx block\n");
+ return false;
+ }
+
+ // Read the byte sequence:
+
+ event->data.meta.data = ReadByteSequence(event->data.meta.length, stream);
+
+ if (event->data.meta.data == NULL)
+ {
+ fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n");
+ return false;
+ }
+
+ return true;
+}
+
+static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type,
+ FILE *stream)
+{
+ byte event_type;
+
+ if (!ReadVariableLength(&event->delta_time, stream))
+ {
+ fprintf(stderr, "ReadEvent: Failed to read event timestamp\n");
+ return false;
+ }
+
+ if (!ReadByte(&event_type, stream))
+ {
+ fprintf(stderr, "ReadEvent: Failed to read event type\n");
+ return false;
+ }
+
+ // All event types have their top bit set. Therefore, if
+ // the top bit is not set, it is because we are using the "same
+ // as previous event type" shortcut to save a byte. Skip back
+ // a byte so that we read this byte again.
+
+ if ((event_type & 0x80) == 0)
+ {
+ event_type = *last_event_type;
+
+ if (fseek(stream, -1, SEEK_CUR) < 0)
+ {
+ fprintf(stderr, "ReadEvent: Unable to seek in stream\n");
+ return false;
+ }
+ }
+ else
+ {
+ *last_event_type = event_type;
+ }
+
+ // Check event type:
+
+ switch (event_type & 0xf0)
+ {
+ // Two parameter channel events:
+
+ case MIDI_EVENT_NOTE_OFF:
+ case MIDI_EVENT_NOTE_ON:
+ case MIDI_EVENT_AFTERTOUCH:
+ case MIDI_EVENT_CONTROLLER:
+ case MIDI_EVENT_PITCH_BEND:
+ return ReadChannelEvent(event, event_type, true, stream);
+
+ // Single parameter channel events:
+
+ case MIDI_EVENT_PROGRAM_CHANGE:
+ case MIDI_EVENT_CHAN_AFTERTOUCH:
+ return ReadChannelEvent(event, event_type, false, stream);
+
+ default:
+ break;
+ }
+
+ // Specific value?
+
+ switch (event_type)
+ {
+ case MIDI_EVENT_SYSEX:
+ case MIDI_EVENT_SYSEX_SPLIT:
+ return ReadSysExEvent(event, event_type, stream);
+
+ case MIDI_EVENT_META:
+ return ReadMetaEvent(event, stream);
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "ReadEvent: Unknown MIDI event type: 0x%x\n", event_type);
+ return false;
+}
+
+// Free an event:
+
+static void FreeEvent(midi_event_t *event)
+{
+ // Some event types have dynamically allocated buffers assigned
+ // to them that must be freed.
+
+ switch (event->event_type)
+ {
+ case MIDI_EVENT_SYSEX:
+ case MIDI_EVENT_SYSEX_SPLIT:
+ free(event->data.sysex.data);
+ break;
+
+ case MIDI_EVENT_META:
+ free(event->data.meta.data);
+ break;
+
+ default:
+ // Nothing to do.
+ break;
+ }
+}
+
+// Read and check the track chunk header
+
+static boolean ReadTrackHeader(midi_track_t *track, FILE *stream)
+{
+ size_t records_read;
+ chunk_header_t chunk_header;
+
+ records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream);
+
+ if (records_read < 1)
+ {
+ return false;
+ }
+
+ if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID))
+ {
+ return false;
+ }
+
+ track->data_len = SDL_SwapBE32(chunk_header.chunk_size);
+
+ return true;
+}
+
+static boolean ReadTrack(midi_track_t *track, FILE *stream)
+{
+ midi_event_t *new_events;
+ midi_event_t *event;
+ unsigned int last_event_type;
+
+ track->num_events = 0;
+ track->events = NULL;
+
+ // Read the header:
+
+ if (!ReadTrackHeader(track, stream))
+ {
+ return false;
+ }
+
+ // Then the events:
+
+ last_event_type = 0;
+
+ for (;;)
+ {
+ // Resize the track slightly larger to hold another event:
+
+ new_events = realloc(track->events,
+ sizeof(midi_event_t) * (track->num_events + 1));
+
+ if (new_events == NULL)
+ {
+ return false;
+ }
+
+ track->events = new_events;
+
+ // Read the next event:
+
+ event = &track->events[track->num_events];
+ if (!ReadEvent(event, &last_event_type, stream))
+ {
+ return false;
+ }
+
+ ++track->num_events;
+
+ // End of track?
+
+ if (event->event_type == MIDI_EVENT_META
+ && event->data.meta.type == MIDI_META_END_OF_TRACK)
+ {
+ break;
+ }
+ }
+
+ return true;
+}
+
+// Free a track:
+
+static void FreeTrack(midi_track_t *track)
+{
+ unsigned int i;
+
+ for (i=0; i<track->num_events; ++i)
+ {
+ FreeEvent(&track->events[i]);
+ }
+
+ free(track->events);
+}
+
+static boolean ReadAllTracks(midi_file_t *file, FILE *stream)
+{
+ unsigned int i;
+
+ // Allocate list of tracks and read each track:
+
+ file->tracks = malloc(sizeof(midi_track_t) * file->num_tracks);
+
+ if (file->tracks == NULL)
+ {
+ return false;
+ }
+
+ memset(file->tracks, 0, sizeof(midi_track_t) * file->num_tracks);
+
+ // Read each track:
+
+ for (i=0; i<file->num_tracks; ++i)
+ {
+ if (!ReadTrack(&file->tracks[i], stream))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Read and check the header chunk.
+
+static boolean ReadFileHeader(midi_file_t *file, FILE *stream)
+{
+ size_t records_read;
+ unsigned int format_type;
+
+ records_read = fread(&file->header, sizeof(midi_header_t), 1, stream);
+
+ if (records_read < 1)
+ {
+ return false;
+ }
+
+ if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID)
+ || SDL_SwapBE32(file->header.chunk_header.chunk_size) != 6)
+ {
+ fprintf(stderr, "ReadFileHeader: Invalid MIDI chunk header! "
+ "chunk_size=%i\n",
+ SDL_SwapBE32(file->header.chunk_header.chunk_size));
+ return false;
+ }
+
+ format_type = SDL_SwapBE16(file->header.format_type);
+ file->num_tracks = SDL_SwapBE16(file->header.num_tracks);
+
+ if ((format_type != 0 && format_type != 1)
+ || file->num_tracks < 1)
+ {
+ fprintf(stderr, "ReadFileHeader: Only type 0/1 "
+ "MIDI files supported!\n");
+ return false;
+ }
+
+ return true;
+}
+
+void MIDI_FreeFile(midi_file_t *file)
+{
+ int i;
+
+ if (file->tracks != NULL)
+ {
+ for (i=0; i<file->num_tracks; ++i)
+ {
+ FreeTrack(&file->tracks[i]);
+ }
+
+ free(file->tracks);
+ }
+
+ free(file);
+}
+
+midi_file_t *MIDI_LoadFile(char *filename)
+{
+ midi_file_t *file;
+ FILE *stream;
+
+ file = malloc(sizeof(midi_file_t));
+
+ if (file == NULL)
+ {
+ return NULL;
+ }
+
+ file->tracks = NULL;
+ file->num_tracks = 0;
+ file->buffer = NULL;
+ file->buffer_size = 0;
+
+ // Open file
+
+ stream = fopen(filename, "rb");
+
+ if (stream == NULL)
+ {
+ fprintf(stderr, "MIDI_LoadFile: Failed to open '%s'\n", filename);
+ MIDI_FreeFile(file);
+ return NULL;
+ }
+
+ // Read MIDI file header
+
+ if (!ReadFileHeader(file, stream))
+ {
+ fclose(stream);
+ MIDI_FreeFile(file);
+ return NULL;
+ }
+
+ // Read all tracks:
+
+ if (!ReadAllTracks(file, stream))
+ {
+ fclose(stream);
+ MIDI_FreeFile(file);
+ return NULL;
+ }
+
+ fclose(stream);
+
+ return file;
+}
+
+// Get the number of tracks in a MIDI file.
+
+unsigned int MIDI_NumTracks(midi_file_t *file)
+{
+ return file->num_tracks;
+}
+
+// Start iterating over the events in a track.
+
+midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track)
+{
+ midi_track_iter_t *iter;
+
+ assert(track < file->num_tracks);
+
+ iter = malloc(sizeof(*iter));
+ iter->track = &file->tracks[track];
+ iter->position = 0;
+
+ return iter;
+}
+
+void MIDI_FreeIterator(midi_track_iter_t *iter)
+{
+ free(iter);
+}
+
+// Get the time until the next MIDI event in a track.
+
+unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter)
+{
+ if (iter->position < iter->track->num_events)
+ {
+ midi_event_t *next_event;
+
+ next_event = &iter->track->events[iter->position];
+
+ return next_event->delta_time;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Get a pointer to the next MIDI event.
+
+int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event)
+{
+ if (iter->position < iter->track->num_events)
+ {
+ *event = &iter->track->events[iter->position];
+ ++iter->position;
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+unsigned int MIDI_GetFileTimeDivision(midi_file_t *file)
+{
+ return file->header.time_division;
+}
+
+void MIDI_RestartIterator(midi_track_iter_t *iter)
+{
+ iter->position = 0;
+}
+
+#ifdef TEST
+
+static char *MIDI_EventTypeToString(midi_event_type_t event_type)
+{
+ switch (event_type)
+ {
+ case MIDI_EVENT_NOTE_OFF:
+ return "MIDI_EVENT_NOTE_OFF";
+ case MIDI_EVENT_NOTE_ON:
+ return "MIDI_EVENT_NOTE_ON";
+ case MIDI_EVENT_AFTERTOUCH:
+ return "MIDI_EVENT_AFTERTOUCH";
+ case MIDI_EVENT_CONTROLLER:
+ return "MIDI_EVENT_CONTROLLER";
+ case MIDI_EVENT_PROGRAM_CHANGE:
+ return "MIDI_EVENT_PROGRAM_CHANGE";
+ case MIDI_EVENT_CHAN_AFTERTOUCH:
+ return "MIDI_EVENT_CHAN_AFTERTOUCH";
+ case MIDI_EVENT_PITCH_BEND:
+ return "MIDI_EVENT_PITCH_BEND";
+ case MIDI_EVENT_SYSEX:
+ return "MIDI_EVENT_SYSEX";
+ case MIDI_EVENT_SYSEX_SPLIT:
+ return "MIDI_EVENT_SYSEX_SPLIT";
+ case MIDI_EVENT_META:
+ return "MIDI_EVENT_META";
+
+ default:
+ return "(unknown)";
+ }
+}
+
+void PrintTrack(midi_track_t *track)
+{
+ midi_event_t *event;
+ unsigned int i;
+
+ for (i=0; i<track->num_events; ++i)
+ {
+ event = &track->events[i];
+
+ if (event->delta_time > 0)
+ {
+ printf("Delay: %i ticks\n", event->delta_time);
+ }
+
+ printf("Event type: %s (%i)\n",
+ MIDI_EventTypeToString(event->event_type),
+ event->event_type);
+
+ switch(event->event_type)
+ {
+ case MIDI_EVENT_NOTE_OFF:
+ case MIDI_EVENT_NOTE_ON:
+ case MIDI_EVENT_AFTERTOUCH:
+ case MIDI_EVENT_CONTROLLER:
+ case MIDI_EVENT_PROGRAM_CHANGE:
+ case MIDI_EVENT_CHAN_AFTERTOUCH:
+ case MIDI_EVENT_PITCH_BEND:
+ printf("\tChannel: %i\n", event->data.channel.channel);
+ printf("\tParameter 1: %i\n", event->data.channel.param1);
+ printf("\tParameter 2: %i\n", event->data.channel.param2);
+ break;
+
+ case MIDI_EVENT_SYSEX:
+ case MIDI_EVENT_SYSEX_SPLIT:
+ printf("\tLength: %i\n", event->data.sysex.length);
+ break;
+
+ case MIDI_EVENT_META:
+ printf("\tMeta type: %i\n", event->data.meta.type);
+ printf("\tLength: %i\n", event->data.meta.length);
+ break;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ midi_file_t *file;
+ unsigned int i;
+
+ if (argc < 2)
+ {
+ printf("Usage: %s <filename>\n", argv[0]);
+ exit(1);
+ }
+
+ file = MIDI_LoadFile(argv[1]);
+
+ if (file == NULL)
+ {
+ fprintf(stderr, "Failed to open %s\n", argv[1]);
+ exit(1);
+ }
+
+ for (i=0; i<file->num_tracks; ++i)
+ {
+ printf("\n== Track %i ==\n\n", i);
+
+ PrintTrack(&file->tracks[i]);
+ }
+
+ return 0;
+}
+
+#endif
+
diff --git a/src/midifile.h b/src/midifile.h
new file mode 100644
index 00000000..4ee0ddb2
--- /dev/null
+++ b/src/midifile.h
@@ -0,0 +1,175 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2009 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// MIDI file parsing.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef MIDIFILE_H
+#define MIDIFILE_H
+
+typedef struct midi_file_s midi_file_t;
+typedef struct midi_track_iter_s midi_track_iter_t;
+
+#define MIDI_CHANNELS_PER_TRACK 16
+
+typedef enum
+{
+ MIDI_EVENT_NOTE_OFF = 0x80,
+ MIDI_EVENT_NOTE_ON = 0x90,
+ MIDI_EVENT_AFTERTOUCH = 0xa0,
+ MIDI_EVENT_CONTROLLER = 0xb0,
+ MIDI_EVENT_PROGRAM_CHANGE = 0xc0,
+ MIDI_EVENT_CHAN_AFTERTOUCH = 0xd0,
+ MIDI_EVENT_PITCH_BEND = 0xe0,
+
+ MIDI_EVENT_SYSEX = 0xf0,
+ MIDI_EVENT_SYSEX_SPLIT = 0xf7,
+ MIDI_EVENT_META = 0xff,
+} midi_event_type_t;
+
+typedef enum
+{
+ MIDI_CONTROLLER_BANK_SELECT = 0x0,
+ MIDI_CONTROLLER_MODULATION = 0x1,
+ MIDI_CONTROLLER_BREATH_CONTROL = 0x2,
+ MIDI_CONTROLLER_FOOT_CONTROL = 0x3,
+ MIDI_CONTROLLER_PORTAMENTO = 0x4,
+ MIDI_CONTROLLER_DATA_ENTRY = 0x5,
+
+ MIDI_CONTROLLER_MAIN_VOLUME = 0x7,
+ MIDI_CONTROLLER_PAN = 0xa
+} midi_controller_t;
+
+typedef enum
+{
+ MIDI_META_SEQUENCE_NUMBER = 0x0,
+
+ MIDI_META_TEXT = 0x1,
+ MIDI_META_COPYRIGHT = 0x2,
+ MIDI_META_TRACK_NAME = 0x3,
+ MIDI_META_INSTR_NAME = 0x4,
+ MIDI_META_LYRICS = 0x5,
+ MIDI_META_MARKER = 0x6,
+ MIDI_META_CUE_POINT = 0x7,
+
+ MIDI_META_CHANNEL_PREFIX = 0x20,
+ MIDI_META_END_OF_TRACK = 0x2f,
+
+ MIDI_META_SET_TEMPO = 0x51,
+ MIDI_META_SMPTE_OFFSET = 0x54,
+ MIDI_META_TIME_SIGNATURE = 0x58,
+ MIDI_META_KEY_SIGNATURE = 0x59,
+ MIDI_META_SEQUENCER_SPECIFIC = 0x7f,
+} midi_meta_event_type_t;
+
+typedef struct
+{
+ // Meta event type:
+
+ unsigned int type;
+
+ // Length:
+
+ unsigned int length;
+
+ // Meta event data:
+
+ byte *data;
+} midi_meta_event_data_t;
+
+typedef struct
+{
+ // Length:
+
+ unsigned int length;
+
+ // Event data:
+
+ byte *data;
+} midi_sysex_event_data_t;
+
+typedef struct
+{
+ // The channel number to which this applies:
+
+ unsigned int channel;
+
+ // Extra parameters:
+
+ unsigned int param1;
+ unsigned int param2;
+} midi_channel_event_data_t;
+
+typedef struct
+{
+ // Time between the previous event and this event.
+ unsigned int delta_time;
+
+ // Type of event:
+ midi_event_type_t event_type;
+
+ union
+ {
+ midi_channel_event_data_t channel;
+ midi_meta_event_data_t meta;
+ midi_sysex_event_data_t sysex;
+ } data;
+} midi_event_t;
+
+// Load a MIDI file.
+
+midi_file_t *MIDI_LoadFile(char *filename);
+
+// Free a MIDI file.
+
+void MIDI_FreeFile(midi_file_t *file);
+
+// Get the time division value from the MIDI header.
+
+unsigned int MIDI_GetFileTimeDivision(midi_file_t *file);
+
+// Get the number of tracks in a MIDI file.
+
+unsigned int MIDI_NumTracks(midi_file_t *file);
+
+// Start iterating over the events in a track.
+
+midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track_num);
+
+// Free an iterator.
+
+void MIDI_FreeIterator(midi_track_iter_t *iter);
+
+// Get the time until the next MIDI event in a track.
+
+unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter);
+
+// Get a pointer to the next MIDI event.
+
+int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event);
+
+// Reset an iterator to the beginning of a track.
+
+void MIDI_RestartIterator(midi_track_iter_t *iter);
+
+#endif /* #ifndef MIDIFILE_H */
+
diff --git a/src/net_client.c b/src/net_client.c
index 131397e9..a1697944 100644
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -196,7 +196,8 @@ static void NET_CL_PlayerQuitGame(player_t *player)
// Do this the same way as Vanilla Doom does, to allow dehacked
// replacements of this message
- strcpy(exitmsg, DEH_String("Player 1 left the game"));
+ strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
+ exitmsg[sizeof(exitmsg) - 1] = '\0';
exitmsg[7] += player - players;
diff --git a/src/setup/.gitignore b/src/setup/.gitignore
index 37c8e4c1..f41d11c7 100644
--- a/src/setup/.gitignore
+++ b/src/setup/.gitignore
@@ -1,8 +1,7 @@
Makefile.in
Makefile
.deps
-chocolate-setup
+setup-manifest.xml
*.rc
-*.exe
tags
TAGS
diff --git a/src/setup/joystick.c b/src/setup/joystick.c
index 0094dd81..fbe3a3f3 100644
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -425,6 +425,8 @@ void ConfigJoystick(void)
AddJoystickControl(button_table, "Strafe Left", &joybstrafeleft);
AddJoystickControl(button_table, "Strafe Right", &joybstraferight);
+ AddJoystickControl(button_table, "Previous weapon", &joybprevweapon);
+ AddJoystickControl(button_table, "Next weapon", &joybnextweapon);
if (gamemission == hexen)
{
diff --git a/src/setup/keyboard.c b/src/setup/keyboard.c
index ce3865b3..7ee523bb 100644
--- a/src/setup/keyboard.c
+++ b/src/setup/keyboard.c
@@ -49,7 +49,7 @@ static int *controls[] = { &key_left, &key_right, &key_up, &key_down,
&key_weapon1, &key_weapon2, &key_weapon3,
&key_weapon4, &key_weapon5, &key_weapon6,
&key_weapon7, &key_weapon8,
- NULL };
+ &key_prevweapon, &key_nextweapon, NULL };
static int *menu_nav[] = { &key_menu_activate, &key_menu_up, &key_menu_down,
&key_menu_left, &key_menu_right, &key_menu_back,
@@ -57,10 +57,12 @@ static int *menu_nav[] = { &key_menu_activate, &key_menu_up, &key_menu_down,
static int *shortcuts[] = { &key_menu_help, &key_menu_save, &key_menu_load,
&key_menu_volume, &key_menu_detail, &key_menu_qsave,
- &key_menu_endgame, &key_menu_messages,
+ &key_menu_endgame, &key_menu_messages, &key_spy,
&key_menu_qload, &key_menu_quit, &key_menu_gamma,
&key_menu_incscreen, &key_menu_decscreen,
- &key_message_refresh, NULL };
+ &key_message_refresh, &key_multi_msg,
+ &key_multi_msgplayer[0], &key_multi_msgplayer[1],
+ &key_multi_msgplayer[2], &key_multi_msgplayer[3] };
static int *map_keys[] = { &key_map_north, &key_map_south, &key_map_east,
&key_map_west, &key_map_zoomin, &key_map_zoomout,
@@ -240,6 +242,8 @@ static void ConfigExtraKeys(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
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);
}
static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
@@ -280,14 +284,15 @@ static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
AddKeyControl(table, "Quick load", &key_menu_qload);
AddKeyControl(table, "Quit game", &key_menu_quit);
AddKeyControl(table, "Toggle gamma", &key_menu_gamma);
+ AddKeyControl(table, "Multiplayer spy", &key_spy);
AddKeyControl(table, "Increase screen size", &key_menu_incscreen);
AddKeyControl(table, "Decrease screen size", &key_menu_decscreen);
AddKeyControl(table, "Display last message", &key_message_refresh);
+ AddKeyControl(table, "Finish recording demo", &key_demo_quit);
AddSectionLabel(table, "Map", true);
-
AddKeyControl(table, "Toggle map", &key_map_toggle);
AddKeyControl(table, "Zoom in", &key_map_zoomin);
AddKeyControl(table, "Zoom out", &key_map_zoomout);
@@ -301,6 +306,20 @@ static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
AddKeyControl(table, "Mark location", &key_map_mark);
AddKeyControl(table, "Clear all marks", &key_map_clearmark);
+ AddSectionLabel(table, "Multiplayer", true);
+
+ AddKeyControl(table, "Send message", &key_multi_msg);
+ AddKeyControl(table, "- to green", &key_multi_msgplayer[0]);
+ AddKeyControl(table, "- to indigo", &key_multi_msgplayer[1]);
+ AddKeyControl(table, "- to brown", &key_multi_msgplayer[2]);
+ AddKeyControl(table, "- to red", &key_multi_msgplayer[3]);
+
+ TXT_AddWidgets(table, TXT_NewStrut(0, 1),
+ TXT_NewStrut(0, 1),
+ TXT_NewLabel(" - Map - "),
+ TXT_NewStrut(0, 0),
+ NULL);
+
scrollpane = TXT_NewScrollPane(0, 13, table);
TXT_AddWidget(window, scrollpane);
diff --git a/src/setup/keyboard.h b/src/setup/keyboard.h
index 2797ba8f..12059bf8 100644
--- a/src/setup/keyboard.h
+++ b/src/setup/keyboard.h
@@ -22,59 +22,6 @@
#ifndef SETUP_KEYBOARD_H
#define SETUP_KEYBOARD_H
-// Menu keys:
-
-extern int key_menu_activate;
-extern int key_menu_up;
-extern int key_menu_down;
-extern int key_menu_left;
-extern int key_menu_right;
-extern int key_menu_back;
-extern int key_menu_forward;
-extern int key_menu_confirm;
-extern int key_menu_abort;
-
-extern int key_menu_help;
-extern int key_menu_save;
-extern int key_menu_load;
-extern int key_menu_volume;
-extern int key_menu_detail;
-extern int key_menu_qsave;
-extern int key_menu_endgame;
-extern int key_menu_messages;
-extern int key_menu_qload;
-extern int key_menu_quit;
-extern int key_menu_gamma;
-
-extern int key_menu_incscreen;
-extern int key_menu_decscreen;
-
-// Automap keys:
-
-extern int key_map_north;
-extern int key_map_south;
-extern int key_map_east;
-extern int key_map_west;
-extern int key_map_zoomin;
-extern int key_map_zoomout;
-extern int key_map_toggle;
-extern int key_map_maxzoom;
-extern int key_map_follow;
-extern int key_map_grid;
-extern int key_map_mark;
-extern int key_map_clearmark;
-
-// Weapon keys:
-
-extern int key_weapon1;
-extern int key_weapon2;
-extern int key_weapon3;
-extern int key_weapon4;
-extern int key_weapon5;
-extern int key_weapon6;
-extern int key_weapon7;
-extern int key_weapon8;
-
void ConfigKeyboard(void);
void BindKeyboardVariables(void);
diff --git a/src/setup/mainmenu.c b/src/setup/mainmenu.c
index 46e4b4e6..c3cb7db5 100644
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -46,6 +46,64 @@
#include "multiplayer.h"
#include "sound.h"
+static const int cheat_sequence[] =
+{
+ KEY_UPARROW, KEY_UPARROW, KEY_DOWNARROW, KEY_DOWNARROW,
+ KEY_LEFTARROW, KEY_RIGHTARROW, KEY_LEFTARROW, KEY_RIGHTARROW,
+ 'b', 'a', KEY_ENTER, 0
+};
+
+static unsigned int cheat_sequence_index = 0;
+
+// I think these are good "sensible" defaults:
+
+static void SensibleDefaults(void)
+{
+#if 0
+ // TODO for raven-branch
+ key_up = 'w';
+ key_down = 's';
+ key_strafeleft = 'a';
+ key_straferight = 'd';
+ mousebprevweapon = 4;
+ mousebnextweapon = 3;
+ snd_musicdevice = 3;
+ joybspeed = 29;
+ vanilla_savegame_limit = 0;
+ vanilla_keyboard_mapping = 0;
+ vanilla_demo_limit = 0;
+ show_endoom = 0;
+ dclick_use = 0;
+ novert = 1;
+#endif
+}
+
+static int MainMenuKeyPress(txt_window_t *window, int key, void *user_data)
+{
+ if (key == cheat_sequence[cheat_sequence_index])
+ {
+ ++cheat_sequence_index;
+
+ if (cheat_sequence[cheat_sequence_index] == 0)
+ {
+ SensibleDefaults();
+ cheat_sequence_index = 0;
+
+ window = TXT_NewWindow(NULL);
+ TXT_AddWidget(window, TXT_NewLabel(" \x01 "));
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL);
+
+ return 1;
+ }
+ }
+ else
+ {
+ cheat_sequence_index = 0;
+ }
+
+ return 0;
+}
+
static void DoQuit(void *widget, void *dosave)
{
if (dosave != NULL)
@@ -174,6 +232,8 @@ void MainMenu(void)
quit_action = TXT_NewWindowAction(KEY_ESCAPE, "Quit");
TXT_SignalConnect(quit_action, "pressed", QuitConfirm, NULL);
TXT_SetWindowAction(window, TXT_HORIZ_LEFT, quit_action);
+
+ TXT_SetKeyListener(window, MainMenuKeyPress, NULL);
}
//
diff --git a/src/setup/mouse.c b/src/setup/mouse.c
index d464261f..5b555f88 100644
--- a/src/setup/mouse.c
+++ b/src/setup/mouse.c
@@ -36,7 +36,7 @@ static int usemouse = 1;
static int novert = 0;
static int mouseSensitivity = 5;
-static float mouse_acceleration = 1.0;
+static float mouse_acceleration = 2.0;
static int mouse_threshold = 10;
static int grabmouse = 1;
@@ -48,7 +48,9 @@ static int *all_mouse_buttons[] = {
&mousebstraferight,
&mousebbackward,
&mousebuse,
- &mousebjump
+ &mousebjump,
+ &mousebprevweapon,
+ &mousebnextweapon
};
static void MouseSetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable))
@@ -103,6 +105,9 @@ static void ConfigExtraButtons(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
{
AddMouseControl(buttons_table, "Jump", &mousebjump);
}
+
+ AddMouseControl(buttons_table, "Previous weapon", &mousebprevweapon);
+ AddMouseControl(buttons_table, "Next weapon", &mousebnextweapon);
}
void ConfigMouse(void)
diff --git a/src/setup/multiplayer.c b/src/setup/multiplayer.c
index f3b3221d..24cd0670 100644
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@ -29,6 +29,7 @@
#include "d_iwad.h"
#include "m_config.h"
#include "doom/d_englsh.h"
+#include "m_controls.h"
#include "multiplayer.h"
#include "mode.h"
@@ -863,5 +864,39 @@ void BindMultiplayerVariables(void)
sprintf(buf, "chatmacro%i", i);
M_BindVariable(buf, &chat_macros[i]);
}
+
+ switch (gamemission)
+ {
+ case doom:
+ M_BindChatControls(4);
+ key_multi_msgplayer[0] = 'g';
+ key_multi_msgplayer[1] = 'i';
+ key_multi_msgplayer[2] = 'b';
+ key_multi_msgplayer[3] = 'r';
+ break;
+
+ case heretic:
+ M_BindChatControls(4);
+ key_multi_msgplayer[0] = 'g';
+ key_multi_msgplayer[1] = 'y';
+ key_multi_msgplayer[2] = 'r';
+ key_multi_msgplayer[3] = 'b';
+ break;
+
+ case hexen:
+ M_BindChatControls(8);
+ key_multi_msgplayer[0] = 'b';
+ key_multi_msgplayer[1] = 'r';
+ key_multi_msgplayer[2] = 'y';
+ key_multi_msgplayer[3] = 'g';
+ key_multi_msgplayer[4] = 'j';
+ key_multi_msgplayer[5] = 'w';
+ key_multi_msgplayer[6] = 'h';
+ key_multi_msgplayer[7] = 'p';
+ break;
+
+ default:
+ break;
+ }
}
diff --git a/src/setup/sound.c b/src/setup/sound.c
index 45787eba..d8dc129e 100644
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -40,17 +40,18 @@ typedef enum
NUM_SFXMODES
} sfxmode_t;
-static char *sfxmode_strings[] =
+static char *sfxmode_strings[] =
{
"Disabled",
"Digital",
"PC speaker"
};
-typedef enum
+typedef enum
{
MUSICMODE_DISABLED,
- MUSICMODE_MIDI,
+ MUSICMODE_OPL,
+ MUSICMODE_NATIVE,
MUSICMODE_CD,
NUM_MUSICMODES
} musicmode_t;
@@ -58,15 +59,17 @@ typedef enum
static char *musicmode_strings[] =
{
"Disabled",
- "MIDI",
+ "OPL (Adlib/SB)",
+ "Native MIDI",
"CD audio"
};
// Config file variables:
int snd_sfxdevice = SNDDEVICE_SB;
-int snd_musicdevice = SNDDEVICE_SB;
+int snd_musicdevice = SNDDEVICE_GENMIDI;
int snd_samplerate = 22050;
+int opl_io_port = 0x388;
static int numChannels = 8;
static int sfxVolume = 15;
@@ -108,7 +111,10 @@ static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
case MUSICMODE_DISABLED:
snd_musicdevice = SNDDEVICE_NONE;
break;
- case MUSICMODE_MIDI:
+ case MUSICMODE_NATIVE:
+ snd_musicdevice = SNDDEVICE_GENMIDI;
+ break;
+ case MUSICMODE_OPL:
snd_musicdevice = SNDDEVICE_SB;
break;
case MUSICMODE_CD:
@@ -139,20 +145,26 @@ void ConfigSound(void)
{
snd_sfxmode = SFXMODE_DISABLED;
}
-
+
// Is music enabled?
- if (snd_musicdevice == SNDDEVICE_NONE)
+ if (snd_musicdevice == SNDDEVICE_GENMIDI)
{
- snd_musicmode = MUSICMODE_DISABLED;
+ snd_musicmode = MUSICMODE_NATIVE;
}
else if (snd_musicmode == SNDDEVICE_CD)
{
snd_musicmode = MUSICMODE_CD;
}
+ else if (snd_musicdevice == SNDDEVICE_SB
+ || snd_musicdevice == SNDDEVICE_ADLIB
+ || snd_musicdevice == SNDDEVICE_AWE32)
+ {
+ snd_musicmode = MUSICMODE_OPL;
+ }
else
{
- snd_musicmode = MUSICMODE_MIDI;
+ snd_musicmode = MUSICMODE_DISABLED;
}
// Doom has PC speaker sound effects, but others do not:
@@ -188,7 +200,7 @@ void ConfigSound(void)
music_table = TXT_NewTable(2),
NULL);
- TXT_SetColumnWidths(sfx_table, 20, 5);
+ TXT_SetColumnWidths(sfx_table, 20, 14);
TXT_AddWidgets(sfx_table,
TXT_NewLabel("Sound effects"),
@@ -209,7 +221,7 @@ void ConfigSound(void)
NULL);
}
- TXT_SetColumnWidths(music_table, 20, 5);
+ TXT_SetColumnWidths(music_table, 20, 14);
TXT_AddWidgets(music_table,
TXT_NewLabel("Music"),
@@ -222,7 +234,6 @@ void ConfigSound(void)
TXT_SignalConnect(sfx_mode_control, "changed", UpdateSndDevices, NULL);
TXT_SignalConnect(music_mode_control, "changed", UpdateSndDevices, NULL);
-
}
void BindSoundVariables(void)
diff --git a/src/setup/txt_keyinput.c b/src/setup/txt_keyinput.c
index e385cc59..483c325f 100644
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -111,7 +111,7 @@ static void TXT_KeyInputDrawer(TXT_UNCAST_ARG(key_input), int selected)
if (*key_input->variable == 0)
{
- strcpy(buf, "");
+ strcpy(buf, "(none)");
}
else
{
diff --git a/src/setup/txt_mouseinput.c b/src/setup/txt_mouseinput.c
index 05c89b39..8b87e651 100644
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -91,7 +91,7 @@ static void GetMouseButtonDescription(int button, char *buf)
strcpy(buf, "MID");
break;
default:
- sprintf(buf, "BUTTON #%i", button);
+ sprintf(buf, "BUTTON #%i", button + 1);
break;
}
}
@@ -153,7 +153,7 @@ static int TXT_MouseInputKeyPress(TXT_UNCAST_ARG(mouse_input), int mouse)
static void TXT_MouseInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
{
TXT_CAST_ARG(txt_mouse_input_t, widget);
-
+
// Clicking is like pressing enter
if (b == TXT_MOUSE_LEFT)
diff --git a/src/strife/.gitignore b/src/strife/.gitignore
index 973a0073..d4e88e5a 100644
--- a/src/strife/.gitignore
+++ b/src/strife/.gitignore
@@ -1,9 +1,5 @@
Makefile
Makefile.in
.deps
-*.rc
-chocolate-doom
-chocolate-server
-*.exe
tags
TAGS
diff --git a/src/strife/am_map.c b/src/strife/am_map.c
index b1eaa216..c9d0f659 100644
--- a/src/strife/am_map.c
+++ b/src/strife/am_map.c
@@ -502,7 +502,7 @@ void AM_loadPics(void)
for (i=0;i<10;i++)
{
- sprintf(namebuf, DEH_String("PLMNUM%d"), i);
+ DEH_snprintf(namebuf, 9, "PLMNUM%d", i);
marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
}
@@ -515,7 +515,7 @@ void AM_unloadPics(void)
for (i=0;i<10;i++)
{
- sprintf(namebuf, DEH_String("PLMNUM%d"), i); // haleyjd: Choco change.
+ DEH_snprintf(namebuf, 9, "PLMNUM%d", i);
W_ReleaseLumpName(namebuf);
}
}
@@ -1022,7 +1022,7 @@ AM_drawFline
|| fl->b.x < 0 || fl->b.x >= f_w
|| fl->b.y < 0 || fl->b.y >= f_h)
{
- fprintf(stderr, DEH_String("fuck %d \r"), fuck++);
+ DEH_fprintf(stderr, "fuck %d \r", fuck++);
return;
}
diff --git a/src/strife/d_main.c b/src/strife/d_main.c
index 8dcbcf8c..e7ed01be 100644
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -45,8 +45,8 @@
#include "d_iwad.h"
#include "z_zone.h"
+#include "w_main.h"
#include "w_wad.h"
-#include "w_merge.h"
#include "s_sound.h"
#include "v_video.h"
@@ -384,6 +384,12 @@ void D_BindVariables(void)
M_BindMapControls();
M_BindMenuControls();
M_BindStrifeControls(); // haleyjd 09/01/10: [STRIFE]
+ M_BindChatControls(MAXPLAYERS);
+
+ key_multi_msgplayer[0] = HUSTR_KEYGREEN;
+ key_multi_msgplayer[1] = HUSTR_KEYINDIGO;
+ key_multi_msgplayer[2] = HUSTR_KEYBROWN;
+ key_multi_msgplayer[3] = HUSTR_KEYRED;
#ifdef FEATURE_MULTIPLAYER
NET_BindVariables();
@@ -849,7 +855,6 @@ static boolean CheckChex(char *iwadname)
// print title for every printed line
char title[128];
-
static boolean D_AddFile(char *filename)
{
wad_file_t *handle;
@@ -1126,7 +1131,7 @@ void D_DoomMain (void)
I_PrintBanner(PACKAGE_STRING);
- printf (DEH_String("Z_Init: Init zone memory allocation daemon. \n"));
+ DEH_printf("Z_Init: Init zone memory allocation daemon. \n");
Z_Init ();
#ifdef FEATURE_MULTIPLAYER
@@ -1245,7 +1250,7 @@ void D_DoomMain (void)
deathmatch = 2;
if (devparm)
- printf(DEH_String(D_DEVSTR));
+ DEH_printf(D_DEVSTR);
// find which dir to use for config files
@@ -1294,7 +1299,7 @@ void D_DoomMain (void)
scale = 10;
if (scale > 400)
scale = 400;
- printf (DEH_String("turbo scale: %i%%\n"),scale);
+ DEH_printf("turbo scale: %i%%\n", scale);
forwardmove[0] = forwardmove[0]*scale/100;
forwardmove[1] = forwardmove[1]*scale/100;
sidemove[0] = sidemove[0]*scale/100;
@@ -1302,12 +1307,12 @@ void D_DoomMain (void)
}
// init subsystems
- printf(DEH_String("V_Init: allocate screens.\n"));
- V_Init();
+ DEH_printf("V_Init: allocate screens.\n");
+ V_Init ();
// Load configuration files before initialising other subsystems.
// haleyjd 08/22/2010: [STRIFE] - use strife.cfg
- printf(DEH_String("M_LoadDefaults: Load system defaults.\n"));
+ DEH_printf("M_LoadDefaults: Load system defaults.\n");
M_SetConfigFilenames("strife.cfg", PROGRAM_PREFIX "strife.cfg");
D_BindVariables();
M_LoadDefaults();
@@ -1315,160 +1320,9 @@ void D_DoomMain (void)
// Save configuration at exit.
I_AtExit(M_SaveDefaults, false);
- printf (DEH_String("W_Init: Init WADfiles.\n"));
+ DEH_printf("W_Init: Init WADfiles.\n");
D_AddFile(iwadfile);
-
-#ifdef FEATURE_WAD_MERGE
-
- // Merged PWADs are loaded first, because they are supposed to be
- // modified IWADs.
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of deutex's -merge option, merging a PWAD
- // into the main IWAD. Multiple files may be specified.
- //
-
- p = M_CheckParm("-merge");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging %s\n", filename);
- W_MergeFile(filename);
- }
- }
-
- // NWT-style merging:
-
- // NWT's -merge option:
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of NWT's -merge option. Multiple files
- // may be specified.
-
- p = M_CheckParm("-nwtmerge");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" performing NWT-style merge of %s\n", filename);
- W_NWTDashMerge(filename);
- }
- }
-
- // Add flats
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of NWT's -af option, merging flats into
- // the main IWAD directory. Multiple files may be specified.
- //
-
- p = M_CheckParm("-af");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging flats from %s\n", filename);
- W_NWTMergeFile(filename, W_NWT_MERGE_FLATS);
- }
- }
-
- //!
- // @arg <files>
- // @category mod
- //
- // Simulates the behavior of NWT's -as option, merging sprites
- // into the main IWAD directory. Multiple files may be specified.
- //
-
- p = M_CheckParm("-as");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging sprites from %s\n", filename);
- W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES);
- }
- }
-
- //!
- // @arg <files>
- // @category mod
- //
- // Equivalent to "-af <files> -as <files>".
- //
-
- p = M_CheckParm("-aa");
-
- if (p > 0)
- {
- for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- printf(" merging sprites and flats from %s\n", filename);
- W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS);
- }
- }
-
-#endif
-
- //!
- // @arg <files>
- // @vanilla
- //
- // Load the specified PWAD files.
- //
-
- p = M_CheckParm ("-file");
- if (p)
- {
- // the parms after p are wadfile/lump names,
- // until end of parms or another - preceded parm
- modifiedgame = true; // homebrew levels
- while (++p != myargc && myargv[p][0] != '-')
- {
- char *filename;
-
- filename = D_TryFindWADByName(myargv[p]);
-
- D_AddFile(filename);
- }
- }
-
- // Debug:
-// W_PrintDirectory();
+ modifiedgame = W_ParseCommandLine();
// add any files specified on the command line with -file wadfile
// to the wad list
@@ -1664,8 +1518,8 @@ void D_DoomMain (void)
if (p && p < myargc-1 && deathmatch)
{
- printf(DEH_String("Austin Virtual Gaming: Levels will end "
- "after 20 minutes\n"));
+ DEH_printf("Austin Virtual Gaming: Levels will end "
+ "after 20 minutes\n");
timelimit = 20;
}
@@ -1750,17 +1604,17 @@ void D_DoomMain (void)
// haleyjd 08/28/10: Init Choco Strife stuff.
D_InitChocoStrife();
- printf (DEH_String("M_Init: Init miscellaneous info.\n"));
+ DEH_printf("M_Init: Init miscellaneous info.\n");
M_Init ();
// haleyjd 08/22/2010: [STRIFE] Modified string to match binary
- printf (DEH_String("R_Init: Loading Graphics - "));
+ DEH_printf("R_Init: Loading Graphics - ");
R_Init ();
- printf (DEH_String("\nP_Init: Init Playloop state.\n"));
+ DEH_printf("\nP_Init: Init Playloop state.\n");
P_Init ();
- printf (DEH_String("I_Init: Setting up machine state.\n"));
+ DEH_printf("I_Init: Setting up machine state.\n");
I_CheckIsScreensaver();
I_InitTimer();
I_InitJoystick();
@@ -1770,18 +1624,18 @@ void D_DoomMain (void)
NET_Init ();
#endif
- printf (DEH_String("S_Init: Setting up sound.\n"));
+ DEH_printf("S_Init: Setting up sound.\n");
S_Init (sfxVolume * 8, musicVolume * 8);
- printf (DEH_String("D_CheckNetGame: Checking network game status.\n"));
+ DEH_printf("D_CheckNetGame: Checking network game status.\n");
D_CheckNetGame ();
PrintGameVersion();
- printf (DEH_String("HU_Init: Setting up heads up display.\n"));
+ DEH_printf("HU_Init: Setting up heads up display.\n");
HU_Init ();
- printf (DEH_String("ST_Init: Init status bar.\n"));
+ DEH_printf("ST_Init: Init status bar.\n");
ST_Init ();
// If Doom II without a MAP01 lump, this is a store demo.
diff --git a/src/strife/d_net.c b/src/strife/d_net.c
index c8742c71..bacc4e23 100644
--- a/src/strife/d_net.c
+++ b/src/strife/d_net.c
@@ -374,17 +374,17 @@ void D_CheckNetGame (void)
++num_players;
}
- printf (DEH_String("startskill %i deathmatch: %i startmap: %i startepisode: %i\n"),
- startskill, deathmatch, startmap, startepisode);
+ DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
+ startskill, deathmatch, startmap, startepisode);
- printf(DEH_String("player %i of %i (%i nodes)\n"),
- consoleplayer+1, num_players, num_players);
+ DEH_printf("player %i of %i (%i nodes)\n",
+ consoleplayer+1, num_players, num_players);
// Show players here; the server might have specified a time limit
if (timelimit > 0)
{
- printf(DEH_String("Levels will end after %d minute"),timelimit);
+ DEH_printf("Levels will end after %d minute", timelimit);
if (timelimit > 1)
printf("s");
printf(".\n");
diff --git a/src/strife/f_finale.c b/src/strife/f_finale.c
index 9e0aca93..68c07af1 100644
--- a/src/strife/f_finale.c
+++ b/src/strife/f_finale.c
@@ -665,7 +665,7 @@ void F_BunnyScroll (void)
laststage = stage;
}
- sprintf (name, DEH_String("END%i"), stage);
+ DEH_snprintf(name, 10, "END%i", stage);
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
(SCREENHEIGHT - 8 * 8) / 2,
W_CacheLumpName (name,PU_CACHE));
diff --git a/src/strife/g_game.c b/src/strife/g_game.c
index 25838468..42c8bdd3 100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -183,14 +183,40 @@ static int *weapon_keys[] = {
&key_weapon8
};
+// Set to -1 or +1 to switch to the previous or next weapon.
+
+static int next_weapon = 0;
+
+// Used for prev/next weapon keys.
+// STRIFE-TODO: Check this table makes sense.
+
+static const struct
+{
+ weapontype_t weapon;
+ weapontype_t weapon_num;
+} weapon_order_table[] = {
+ { wp_fist, wp_fist },
+ { wp_elecbow, wp_elecbow },
+ { wp_poisonbow, wp_elecbow },
+ { wp_rifle, wp_rifle },
+ { wp_missile, wp_missile },
+ { wp_hegrenade, wp_hegrenade },
+ { wp_wpgrenade, wp_hegrenade },
+ { wp_flame, wp_flame },
+ { wp_mauler, wp_mauler },
+ { wp_torpedo, wp_mauler },
+ { wp_sigil, wp_sigil },
+};
+
#define SLOWTURNTICS 6
#define NUMKEYS 256
+#define MAX_JOY_BUTTONS 20
static boolean gamekeydown[NUMKEYS];
static int turnheld; // for accelerative turning
-static boolean mousearray[4];
+static boolean mousearray[MAX_MOUSE_BUTTONS + 1];
static boolean *mousebuttons = &mousearray[1]; // allow [-1]
// mouse values are used once
@@ -204,8 +230,6 @@ static int dclicktime2;
static boolean dclickstate2;
static int dclicks2;
-#define MAX_JOY_BUTTONS 20
-
// joystick values are repeated
static int joyxmove;
static int joyymove;
@@ -344,7 +368,55 @@ int G_CmdChecksum (ticcmd_t* cmd)
return sum;
}
-
+
+static boolean WeaponSelectable(weapontype_t weapon)
+{
+ // Can't select a weapon if we don't own it.
+
+ if (!players[consoleplayer].weaponowned[weapon])
+ {
+ return false;
+ }
+
+ // STRIFE-TODO: Special weapon cycling rules?
+
+ return true;
+}
+
+static int G_NextWeapon(int direction)
+{
+ weapontype_t weapon;
+ int i;
+
+ // Find index in the table.
+
+ if (players[consoleplayer].pendingweapon == wp_nochange)
+ {
+ weapon = players[consoleplayer].readyweapon;
+ }
+ else
+ {
+ weapon = players[consoleplayer].pendingweapon;
+ }
+
+ for (i=0; i<arrlen(weapon_order_table); ++i)
+ {
+ if (weapon_order_table[i].weapon == weapon)
+ {
+ break;
+ }
+ }
+
+ // Switch weapon.
+
+ do
+ {
+ i += direction;
+ i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table);
+ } while (!WeaponSelectable(weapon_order_table[i].weapon));
+
+ return weapon_order_table[i].weapon_num;
+}
//
// G_BuildTiccmd
@@ -503,20 +575,34 @@ void G_BuildTiccmd (ticcmd_t* cmd)
dclicks = 0;
}
- // chainsaw overrides
+ // If the previous or next weapon button is pressed, the
+ // next_weapon variable is set to change weapons when
+ // we generate a ticcmd. Choose a new weapon.
- for (i=0; i<arrlen(weapon_keys); ++i)
+ if (next_weapon != 0)
{
- int key = *weapon_keys[i];
+ i = G_NextWeapon(next_weapon);
+ cmd->buttons |= BT_CHANGE;
+ cmd->buttons |= i << BT_WEAPONSHIFT;
+ next_weapon = 0;
+ }
+ else
+ {
+ // Check weapon keys.
- if (gamekeydown[key])
+ for (i=0; i<arrlen(weapon_keys); ++i)
{
- cmd->buttons |= BT_CHANGE;
- cmd->buttons |= i<<BT_WEAPONSHIFT;
- break;
+ int key = *weapon_keys[i];
+
+ if (gamekeydown[key])
+ {
+ cmd->buttons |= BT_CHANGE;
+ cmd->buttons |= i<<BT_WEAPONSHIFT;
+ break;
+ }
}
}
-
+
// mouse
if (mousebuttons[mousebforward])
{
@@ -697,7 +783,6 @@ void G_DoLoadLevel (void)
P_DialogLoad(); // villsa [STRIFE]
}
-
static void SetJoyButtons(unsigned int buttons_mask)
{
@@ -705,10 +790,54 @@ static void SetJoyButtons(unsigned int buttons_mask)
for (i=0; i<MAX_JOY_BUTTONS; ++i)
{
- joybuttons[i] = (buttons_mask & (1 << i)) != 0;
+ int button_on = (buttons_mask & (1 << i)) != 0;
+
+ // Detect button press:
+
+ if (!joybuttons[i] && button_on)
+ {
+ // Weapon cycling:
+
+ if (i == joybprevweapon)
+ {
+ next_weapon = -1;
+ }
+ else if (i == joybnextweapon)
+ {
+ next_weapon = 1;
+ }
+ }
+
+ joybuttons[i] = button_on;
}
}
-
+
+static void SetMouseButtons(unsigned int buttons_mask)
+{
+ int i;
+
+ for (i=0; i<MAX_MOUSE_BUTTONS; ++i)
+ {
+ unsigned int button_on = (buttons_mask & (1 << i)) != 0;
+
+ // Detect button press:
+
+ if (!mousebuttons[i] && button_on)
+ {
+ if (i == mousebprevweapon)
+ {
+ next_weapon = -1;
+ }
+ else if (i == mousebnextweapon)
+ {
+ next_weapon = 1;
+ }
+ }
+
+ mousebuttons[i] = button_on;
+ }
+}
+
//
// G_Responder
// Get info needed to make ticcmd_ts for the players.
@@ -717,7 +846,7 @@ boolean G_Responder (event_t* ev)
{
// allow spy mode changes even during the demo
if (gamestate == GS_LEVEL && ev->type == ev_keydown
- && ev->data1 == KEY_F12 && (singledemo || !deathmatch) )
+ && ev->data1 == key_spy && (singledemo || !deathmatch) )
{
// spy mode
do
@@ -777,6 +906,18 @@ boolean G_Responder (event_t* ev)
testcontrols_mousespeed = abs(ev->data2);
}
+ // If the next/previous weapon keys are pressed, set the next_weapon
+ // variable to change weapons when the next ticcmd is generated.
+
+ if (ev->type == ev_keydown && ev->data1 == key_prevweapon)
+ {
+ next_weapon = -1;
+ }
+ else if (ev->type == ev_keydown && ev->data1 == key_nextweapon)
+ {
+ next_weapon = 1;
+ }
+
switch (ev->type)
{
case ev_keydown:
@@ -797,9 +938,7 @@ boolean G_Responder (event_t* ev)
return false; // always let key up events filter down
case ev_mouse:
- mousebuttons[0] = ev->data1 & 1;
- mousebuttons[1] = ev->data1 & 2;
- mousebuttons[2] = ev->data1 & 4;
+ SetMouseButtons(ev->data1);
mousex = ev->data2*(mouseSensitivity+5)/10;
mousey = ev->data3*(mouseSensitivity+5)/10;
return true; // eat events
@@ -1416,6 +1555,8 @@ void G_DoLoadGame (void)
return;
}
+ savegame_error = false;
+
if (!P_ReadSaveGameHeader())
{
fclose(save_stream);
@@ -1483,6 +1624,8 @@ void G_DoSaveGame (void)
return;
}
+ savegame_error = false;
+
P_WriteSaveGameHeader(savedescription);
P_ArchivePlayers ();
@@ -1778,7 +1921,7 @@ void G_WriteDemoTiccmd (ticcmd_t* cmd)
{
byte *demo_start;
- if (gamekeydown['q']) // press q to end demo recording
+ if (gamekeydown[key_demo_quit]) // press q to end demo recording
G_CheckDemoStatus ();
demo_start = demo_p;
diff --git a/src/strife/hu_stuff.c b/src/strife/hu_stuff.c
index 3ddf402e..0358bd63 100644
--- a/src/strife/hu_stuff.c
+++ b/src/strife/hu_stuff.c
@@ -178,7 +178,7 @@ void HU_Init(void)
j = HU_FONTSTART;
for (i=0;i<HU_FONTSIZE;i++)
{
- sprintf(buffer, DEH_String("STCFN%.3d"), j++);
+ DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
}
@@ -383,14 +383,6 @@ boolean HU_Responder(event_t *ev)
int i;
int numplayers;
- static char destination_keys[MAXPLAYERS] =
- {
- HUSTR_KEYGREEN,
- HUSTR_KEYINDIGO,
- HUSTR_KEYBROWN,
- HUSTR_KEYRED
- };
-
static int num_nobrainers = 0;
numplayers = 0;
@@ -419,7 +411,7 @@ boolean HU_Responder(event_t *ev)
message_counter = HU_MSGTIMEOUT;
eatkey = true;
}
- else if (netgame && ev->data2 == HU_INPUTTOGGLE)
+ else if (netgame && ev->data2 == key_multi_msg)
{
eatkey = chat_on = true;
HUlib_resetIText(&w_chat);
@@ -429,7 +421,7 @@ boolean HU_Responder(event_t *ev)
{
for (i=0; i<MAXPLAYERS ; i++)
{
- if (ev->data2 == destination_keys[i])
+ if (ev->data2 == key_multi_msgplayer[i])
{
if (playeringame[i] && i!=consoleplayer)
{
diff --git a/src/strife/m_menu.c b/src/strife/m_menu.c
index 337c663b..ef868236 100644
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@ -715,7 +715,7 @@ void M_QuickSave(void)
quickSaveSlot = -2; // means to pick a slot now
return;
}
- sprintf(tempstring,DEH_String(QSPROMPT),savegamestrings[quickSaveSlot]);
+ DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]);
M_StartMessage(tempstring,M_QuickSaveResponse,true);
}
@@ -747,7 +747,7 @@ void M_QuickLoad(void)
M_StartMessage(DEH_String(QSAVESPOT),NULL,false);
return;
}
- sprintf(tempstring,DEH_String(QLPROMPT),savegamestrings[quickSaveSlot]);
+ DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]);
M_StartMessage(tempstring,M_QuickLoadResponse,true);
}
diff --git a/src/strife/p_saveg.c b/src/strife/p_saveg.c
index 6da095e5..52c6bcd5 100644
--- a/src/strife/p_saveg.c
+++ b/src/strife/p_saveg.c
@@ -44,6 +44,7 @@
FILE *save_stream;
int savegamelength;
+boolean savegame_error;
// Get the filename of a temporary file to write the savegame to. After
// the file has been successfully saved, it will be renamed to the
@@ -75,7 +76,7 @@ char *P_SaveGameFile(int slot)
filename = malloc(strlen(savegamedir) + 32);
}
- sprintf(basename, DEH_String(SAVEGAMENAME "%d.dsg"), slot);
+ DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot);
sprintf(filename, "%s%s", savegamedir, basename);
@@ -88,14 +89,31 @@ static byte saveg_read8(void)
{
byte result;
- fread(&result, 1, 1, save_stream);
+ if (fread(&result, 1, 1, save_stream) < 1)
+ {
+ if (!savegame_error)
+ {
+ fprintf(stderr, "saveg_read8: Unexpected end of file while "
+ "reading save game\n");
+
+ savegame_error = true;
+ }
+ }
return result;
}
static void saveg_write8(byte value)
{
- fwrite(&value, 1, 1, save_stream);
+ if (fwrite(&value, 1, 1, save_stream) < 1)
+ {
+ if (!savegame_error)
+ {
+ fprintf(stderr, "saveg_write8: Error while writing save game\n");
+
+ savegame_error = true;
+ }
+ }
}
static short saveg_read16(void)
diff --git a/src/strife/p_saveg.h b/src/strife/p_saveg.h
index 3a96cc3e..5488289c 100644
--- a/src/strife/p_saveg.h
+++ b/src/strife/p_saveg.h
@@ -64,6 +64,7 @@ void P_ArchiveSpecials (void);
void P_UnArchiveSpecials (void);
extern FILE *save_stream;
+extern boolean savegame_error;
#endif
diff --git a/src/strife/p_setup.c b/src/strife/p_setup.c
index 652d86df..2145c5d5 100644
--- a/src/strife/p_setup.c
+++ b/src/strife/p_setup.c
@@ -33,6 +33,7 @@
#include "deh_main.h"
#include "i_swap.h"
+#include "m_argv.h"
#include "m_bbox.h"
#include "g_game.h"
@@ -76,6 +77,7 @@ line_t* lines;
int numsides;
side_t* sides;
+static int totallines;
// BLOCKMAP
// Created from axis aligned bounding box
@@ -554,7 +556,6 @@ void P_GroupLines (void)
line_t** linebuffer;
int i;
int j;
- int total;
line_t* li;
sector_t* sector;
subsector_t* ss;
@@ -572,21 +573,21 @@ void P_GroupLines (void)
// count number of lines in each sector
li = lines;
- total = 0;
+ totallines = 0;
for (i=0 ; i<numlines ; i++, li++)
{
- total++;
+ totallines++;
li->frontsector->linecount++;
if (li->backsector && li->backsector != li->frontsector)
{
li->backsector->linecount++;
- total++;
+ totallines++;
}
}
// build line tables for each sector
- linebuffer = Z_Malloc (total*sizeof(line_t *), PU_LEVEL, 0);
+ linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0);
for (i=0; i<numsectors; ++i)
{
@@ -663,6 +664,87 @@ void P_GroupLines (void)
}
+// Pad the REJECT lump with extra data when the lump is too small,
+// to simulate a REJECT buffer overflow in Vanilla Doom.
+
+static void PadRejectArray(byte *array, unsigned int len)
+{
+ unsigned int i;
+ unsigned int byte_num;
+ byte *dest;
+ unsigned int padvalue;
+
+ // Values to pad the REJECT array with:
+
+ unsigned int rejectpad[4] =
+ {
+ ((totallines * 4 + 3) & ~3) + 24, // Size
+ 0, // Part of z_zone block header
+ 50, // PU_LEVEL
+ 0x1d4a11 // DOOM_CONST_ZONEID
+ };
+
+ // Copy values from rejectpad into the destination array.
+
+ dest = array;
+
+ for (i=0; i<len && i<sizeof(rejectpad); ++i)
+ {
+ byte_num = i % 4;
+ *dest = (rejectpad[i / 4] >> (byte_num * 8)) & 0xff;
+ ++dest;
+ }
+
+ // We only have a limited pad size. Print a warning if the
+ // REJECT lump is too small.
+
+ if (len > sizeof(rejectpad))
+ {
+ fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%i > %i)\n",
+ len, sizeof(rejectpad));
+
+ // Pad remaining space with 0 (or 0xff, if specified on command line).
+
+ if (M_CheckParm("-reject_pad_with_ff"))
+ {
+ padvalue = 0xff;
+ }
+ else
+ {
+ padvalue = 0xf00;
+ }
+
+ memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad));
+ }
+}
+
+static void P_LoadReject(int lumpnum)
+{
+ int minlength;
+ int lumplen;
+
+ // Calculate the size that the REJECT lump *should* be.
+
+ minlength = (numsectors * numsectors + 7) / 8;
+
+ // If the lump meets the minimum length, it can be loaded directly.
+ // Otherwise, we need to allocate a buffer of the correct size
+ // and pad it with appropriate data.
+
+ lumplen = W_LumpLength(lumpnum);
+
+ if (lumplen >= minlength)
+ {
+ rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL);
+ }
+ else
+ {
+ rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix);
+ W_ReadLump(lumpnum, rejectmatrix);
+
+ PadRejectArray(rejectmatrix + lumplen, minlength - lumplen);
+ }
+}
//
// P_SetupLevel
@@ -712,9 +794,9 @@ P_SetupLevel
if ( gamemode == commercial)
{
if (map<10)
- sprintf (lumpname, DEH_String("map0%i"), map);
+ DEH_snprintf(lumpname, 9, "map0%i", map);
else
- sprintf (lumpname, DEH_String("map%i"), map);
+ DEH_snprintf(lumpname, 9, "map%i", map);
}
else
{
@@ -739,9 +821,9 @@ P_SetupLevel
P_LoadSubsectors (lumpnum+ML_SSECTORS);
P_LoadNodes (lumpnum+ML_NODES);
P_LoadSegs (lumpnum+ML_SEGS);
-
- rejectmatrix = W_CacheLumpNum (lumpnum+ML_REJECT,PU_LEVEL);
+
P_GroupLines ();
+ P_LoadReject (lumpnum+ML_REJECT);
bodyqueslot = 0;
deathmatch_p = deathmatchstarts;
diff --git a/src/strife/r_draw.c b/src/strife/r_draw.c
index a09032c7..79c571fe 100644
--- a/src/strife/r_draw.c
+++ b/src/strife/r_draw.c
@@ -600,48 +600,54 @@ int dscount;
// Draws the actual span.
void R_DrawSpan (void)
{
- fixed_t xfrac;
- fixed_t yfrac;
- byte* dest;
- int count;
- int spot;
-
-#ifdef RANGECHECK
+ unsigned int position, step;
+ byte *dest;
+ int count;
+ int spot;
+ unsigned int xtemp, ytemp;
+
+#ifdef RANGECHECK
if (ds_x2 < ds_x1
|| ds_x1<0
- || ds_x2>=SCREENWIDTH
+ || ds_x2>=SCREENWIDTH
|| (unsigned)ds_y>SCREENHEIGHT)
{
I_Error( "R_DrawSpan: %i to %i at %i",
ds_x1,ds_x2,ds_y);
}
-// dscount++;
-#endif
+// dscount++;
+#endif
+
+ // Pack position and step variables into a single 32-bit integer,
+ // with x in the top 16 bits and y in the bottom 16 bits. For
+ // each 16-bit part, the top 6 bits are the integer part and the
+ // bottom 10 bits are the fractional part of the pixel position.
+
+ position = ((ds_xfrac << 10) & 0xffff0000)
+ | ((ds_yfrac >> 6) & 0x0000ffff);
+ step = ((ds_xstep << 10) & 0xffff0000)
+ | ((ds_ystep >> 6) & 0x0000ffff);
-
- xfrac = ds_xfrac;
- yfrac = ds_yfrac;
-
dest = ylookup[ds_y] + columnofs[ds_x1];
// We do not check for zero spans here?
- count = ds_x2 - ds_x1;
+ count = ds_x2 - ds_x1;
- do
+ do
{
- // Current texture index in u,v.
- spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
+ // Calculate current texture index in u,v.
+ ytemp = (position >> 4) & 0x0fc0;
+ xtemp = (position >> 26);
+ spot = xtemp | ytemp;
// Lookup pixel from flat texture tile,
// re-index using light/colormap.
*dest++ = ds_colormap[ds_source[spot]];
- // Next step in u,v.
- xfrac += ds_xstep;
- yfrac += ds_ystep;
-
- } while (count--);
-}
+ position += step;
+
+ } while (count--);
+}
@@ -721,49 +727,54 @@ void R_DrawSpan (void)
//
// Again..
//
-void R_DrawSpanLow (void)
-{
- fixed_t xfrac;
- fixed_t yfrac;
- byte* dest;
- int count;
- int spot;
-
-#ifdef RANGECHECK
+void R_DrawSpanLow (void)
+{
+ unsigned int position, step;
+ unsigned int xtemp, ytemp;
+ byte *dest;
+ int count;
+ int spot;
+
+#ifdef RANGECHECK
if (ds_x2 < ds_x1
|| ds_x1<0
- || ds_x2>=SCREENWIDTH
+ || ds_x2>=SCREENWIDTH
|| (unsigned)ds_y>SCREENHEIGHT)
{
I_Error( "R_DrawSpan: %i to %i at %i",
ds_x1,ds_x2,ds_y);
}
// dscount++;
-#endif
-
- xfrac = ds_xfrac;
- yfrac = ds_yfrac;
-
- count = (ds_x2 - ds_x1);
+#endif
+
+ position = ((ds_xfrac << 10) & 0xffff0000)
+ | ((ds_yfrac >> 6) & 0x0000ffff);
+ step = ((ds_xstep << 10) & 0xffff0000)
+ | ((ds_ystep >> 6) & 0x0000ffff);
+
+ count = (ds_x2 - ds_x1);
// Blocky mode, need to multiply by 2.
ds_x1 <<= 1;
ds_x2 <<= 1;
-
+
dest = ylookup[ds_y] + columnofs[ds_x1];
-
- do
- {
- spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
+
+ do
+ {
+ // Calculate current texture index in u,v.
+ ytemp = (position >> 4) & 0x0fc0;
+ xtemp = (position >> 26);
+ spot = xtemp | ytemp;
+
// Lowres/blocky mode does it twice,
// while scale is adjusted appropriately.
- *dest++ = ds_colormap[ds_source[spot]];
*dest++ = ds_colormap[ds_source[spot]];
-
- xfrac += ds_xstep;
- yfrac += ds_ystep;
+ *dest++ = ds_colormap[ds_source[spot]];
- } while (count--);
+ position += step;
+
+ } while (count--);
}
//
diff --git a/src/strife/st_stuff.c b/src/strife/st_stuff.c
index 3b4ff0fb..1af8a485 100644
--- a/src/strife/st_stuff.c
+++ b/src/strife/st_stuff.c
@@ -767,10 +767,10 @@ static void ST_loadUnloadGraphics(load_callback_t callback)
// Load the numbers, green and yellow
for (i=0;i<10;i++)
{
- sprintf(namebuf, DEH_String("INVFONG%d"), i);
+ DEH_snprintf(namebuf, 9, "INVFONG%d", i);
callback(namebuf, &invfontg[i]);
- sprintf(namebuf, DEH_String("INVFONY%d"), i);
+ DEH_snprintf(namebuf, 9, "INVFONY%d", i);
callback(namebuf, &invfonty[i]);
}
diff --git a/src/strife/wi_stuff.c b/src/strife/wi_stuff.c
index 1ae8c9e2..f322e31f 100644
--- a/src/strife/wi_stuff.c
+++ b/src/strife/wi_stuff.c
@@ -1572,16 +1572,16 @@ static void WI_loadUnloadData(load_callback_t callback)
if (gamemode == commercial)
{
for (i=0 ; i<NUMCMAPS ; i++)
- {
- sprintf(name, DEH_String("CWILV%2.2d"), i);
+ {
+ DEH_snprintf(name, 9, "CWILV%2.2d", i);
callback(name, &lnames[i]);
- }
+ }
}
else
{
for (i=0 ; i<NUMMAPS ; i++)
{
- sprintf(name, DEH_String("WILV%d%d"), wbs->epsd, i);
+ DEH_snprintf(name, 9, "WILV%d%d", wbs->epsd, i);
callback(name, &lnames[i]);
}
@@ -1593,7 +1593,7 @@ static void WI_loadUnloadData(load_callback_t callback)
// splat
callback(DEH_String("WISPLAT"), &splat[0]);
-
+
if (wbs->epsd < 3)
{
for (j=0;j<NUMANIMS[wbs->epsd];j++)
@@ -1602,17 +1602,16 @@ static void WI_loadUnloadData(load_callback_t callback)
for (i=0;i<a->nanims;i++)
{
// MONDO HACK!
- if (wbs->epsd != 1 || j != 8)
+ if (wbs->epsd != 1 || j != 8)
{
// animations
- sprintf(name, DEH_String("WIA%d%.2d%.2d"),
- wbs->epsd, j, i);
+ DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i);
callback(name, &a->p[i]);
}
else
{
// HACK ALERT!
- a->p[i] = anims[1][4].p[i];
+ a->p[i] = anims[1][4].p[i];
}
}
}
@@ -1625,7 +1624,7 @@ static void WI_loadUnloadData(load_callback_t callback)
for (i=0;i<10;i++)
{
// numbers 0-9
- sprintf(name, DEH_String("WINUM%d"), i);
+ DEH_snprintf(name, 9, "WINUM%d", i);
callback(name, &num[i]);
}
@@ -1666,13 +1665,13 @@ static void WI_loadUnloadData(load_callback_t callback)
callback(DEH_String("WICOLON"), &colon);
// "time"
- callback(DEH_String("WITIME"), &timepatch);
+ callback(DEH_String("WITIME"), &timepatch);
// "sucks"
- callback(DEH_String("WISUCKS"), &sucks);
+ callback(DEH_String("WISUCKS"), &sucks);
// "par"
- callback(DEH_String("WIPAR"), &par);
+ callback(DEH_String("WIPAR"), &par);
// "killers" (vertical)
callback(DEH_String("WIKILRS"), &killers);
@@ -1681,16 +1680,16 @@ static void WI_loadUnloadData(load_callback_t callback)
callback(DEH_String("WIVCTMS"), &victims);
// "total"
- callback(DEH_String("WIMSTT"), &total);
+ callback(DEH_String("WIMSTT"), &total);
for (i=0 ; i<MAXPLAYERS ; i++)
{
// "1,2,3,4"
- sprintf(name, DEH_String("STPB%d"), i);
+ DEH_snprintf(name, 9, "STPB%d", i);
callback(name, &p[i]);
// "1,2,3,4"
- sprintf(name, DEH_String("WIBP%d"), i+1);
+ DEH_snprintf(name, 9, "WIBP%d", i+1);
callback(name, &bp[i]);
}
@@ -1698,19 +1697,21 @@ static void WI_loadUnloadData(load_callback_t callback)
if (gamemode == commercial)
{
- strcpy(name, DEH_String("INTERPIC"));
+ strncpy(name, DEH_String("INTERPIC"), 9);
+ name[8] = '\0';
}
else if (gamemode == retail && wbs->epsd == 3)
{
- strcpy(name, DEH_String("INTERPIC"));
+ strncpy(name, DEH_String("INTERPIC"), 9);
+ name[8] = '\0';
}
- else
+ else
{
- sprintf(name, DEH_String("WIMAP%d"), wbs->epsd);
+ DEH_snprintf(name, 9, "WIMAP%d", wbs->epsd);
}
-
+
// Draw backdrop and save to a temporary buffer
-
+
callback(name, &background);
}
@@ -1723,7 +1724,7 @@ void WI_loadData(void)
{
if (gamemode == commercial)
{
- NUMCMAPS = 32;
+ NUMCMAPS = 32;
lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS,
PU_STATIC, NULL);
}
diff --git a/src/w_main.c b/src/w_main.c
new file mode 100644
index 00000000..92a394dd
--- /dev/null
+++ b/src/w_main.c
@@ -0,0 +1,206 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 2005-2010 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Common code to parse command line, identifying WAD files to load.
+//
+//-----------------------------------------------------------------------------
+
+#include "doomfeatures.h"
+#include "d_iwad.h"
+#include "m_argv.h"
+#include "w_main.h"
+#include "w_merge.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+// 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;
+ int p;
+
+#ifdef FEATURE_WAD_MERGE
+
+ // Merged PWADs are loaded first, because they are supposed to be
+ // modified IWADs.
+
+ //!
+ // @arg <files>
+ // @category mod
+ //
+ // Simulates the behavior of deutex's -merge option, merging a PWAD
+ // into the main IWAD. Multiple files may be specified.
+ //
+
+ p = M_CheckParm("-merge");
+
+ if (p > 0)
+ {
+ for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
+ {
+ char *filename;
+
+ modifiedgame = true;
+
+ filename = D_TryFindWADByName(myargv[p]);
+
+ printf(" merging %s\n", filename);
+ W_MergeFile(filename);
+ }
+ }
+
+ // NWT-style merging:
+
+ // NWT's -merge option:
+
+ //!
+ // @arg <files>
+ // @category mod
+ //
+ // Simulates the behavior of NWT's -merge option. Multiple files
+ // may be specified.
+
+ p = M_CheckParm("-nwtmerge");
+
+ if (p > 0)
+ {
+ for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
+ {
+ char *filename;
+
+ modifiedgame = true;
+
+ filename = D_TryFindWADByName(myargv[p]);
+
+ printf(" performing NWT-style merge of %s\n", filename);
+ W_NWTDashMerge(filename);
+ }
+ }
+
+ // Add flats
+
+ //!
+ // @arg <files>
+ // @category mod
+ //
+ // Simulates the behavior of NWT's -af option, merging flats into
+ // the main IWAD directory. Multiple files may be specified.
+ //
+
+ p = M_CheckParm("-af");
+
+ if (p > 0)
+ {
+ for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
+ {
+ char *filename;
+
+ modifiedgame = true;
+
+ filename = D_TryFindWADByName(myargv[p]);
+
+ printf(" merging flats from %s\n", filename);
+ W_NWTMergeFile(filename, W_NWT_MERGE_FLATS);
+ }
+ }
+
+ //!
+ // @arg <files>
+ // @category mod
+ //
+ // Simulates the behavior of NWT's -as option, merging sprites
+ // into the main IWAD directory. Multiple files may be specified.
+ //
+
+ p = M_CheckParm("-as");
+
+ if (p > 0)
+ {
+ for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
+ {
+ char *filename;
+
+ modifiedgame = true;
+ filename = D_TryFindWADByName(myargv[p]);
+
+ printf(" merging sprites from %s\n", filename);
+ W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES);
+ }
+ }
+
+ //!
+ // @arg <files>
+ // @category mod
+ //
+ // Equivalent to "-af <files> -as <files>".
+ //
+
+ p = M_CheckParm("-aa");
+
+ if (p > 0)
+ {
+ for (p = p + 1; p<myargc && myargv[p][0] != '-'; ++p)
+ {
+ char *filename;
+
+ modifiedgame = true;
+
+ filename = D_TryFindWADByName(myargv[p]);
+
+ printf(" merging sprites and flats from %s\n", filename);
+ W_NWTMergeFile(filename, W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS);
+ }
+ }
+
+#endif
+
+ //!
+ // @arg <files>
+ // @vanilla
+ //
+ // Load the specified PWAD files.
+ //
+
+ p = M_CheckParm ("-file");
+ if (p)
+ {
+ // the parms after p are wadfile/lump names,
+ // until end of parms or another - preceded parm
+ modifiedgame = true; // homebrew levels
+ while (++p != myargc && myargv[p][0] != '-')
+ {
+ char *filename;
+
+ filename = D_TryFindWADByName(myargv[p]);
+
+ printf(" adding %s\n", filename);
+ W_AddFile(filename);
+ }
+ }
+
+// W_PrintDirectory();
+
+ return modifiedgame;
+}
+
diff --git a/src/w_main.h b/src/w_main.h
new file mode 100644
index 00000000..73047b16
--- /dev/null
+++ b/src/w_main.h
@@ -0,0 +1,32 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// DESCRIPTION:
+// Common code to parse command line, identifying WAD files to load.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef W_MAIN_H
+#define W_MAIN_H
+
+boolean W_ParseCommandLine(void);
+
+#endif /* #ifndef W_MAIN_H */
+
diff --git a/src/z_zone.c b/src/z_zone.c
index a3900f5b..c7425290 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -41,6 +41,7 @@
// because it will get overwritten automatically if needed.
//
+#define MEM_ALIGN sizeof(void *)
#define ZONEID 0x1d4a11
typedef struct memblock_s
@@ -201,7 +202,7 @@ Z_Malloc
memblock_t* base;
void *result;
- size = (size + 3) & ~3;
+ size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1);
// scan through the block list,
// looking for the first free block
diff --git a/textscreen/Makefile.am b/textscreen/Makefile.am
index 51b3af13..c1c6f392 100644
--- a/textscreen/Makefile.am
+++ b/textscreen/Makefile.am
@@ -9,6 +9,8 @@ SUBDIRS= . examples
noinst_LIBRARIES=libtextscreen.a
+EXTRA_DIST=Doxyfile
+
libtextscreen_a_SOURCES = \
textscreen.h \
txt_checkbox.c txt_checkbox.h \
@@ -21,11 +23,11 @@ libtextscreen_a_SOURCES = \
txt_button.c txt_button.h \
txt_label.c txt_label.h \
txt_radiobutton.c txt_radiobutton.h \
- txt_scrollpane.c txt_scrollpane.h \
+ txt_scrollpane.c txt_scrollpane.h \
txt_separator.c txt_separator.h \
txt_spinctrl.c txt_spinctrl.h \
txt_sdl.c txt_sdl.h \
- txt_smallfont.h \
+ txt_smallfont.h \
txt_strut.c txt_strut.h \
txt_table.c txt_table.h \
txt_widget.c txt_widget.h \