diff options
author | Simon Howard | 2010-09-09 23:13:06 +0000 |
---|---|---|
committer | Simon Howard | 2010-09-09 23:13:06 +0000 |
commit | 120d90c67b2a4aa0a8883c4897241dee2222acd2 (patch) | |
tree | b439e48483ebbd698c2daa6e24eeaaab1ba97470 /src | |
parent | 79268587fc730e17cbd974a5583c7185604b59a3 (diff) | |
parent | 22fc405736dc4796958de221c07d52432f1b271b (diff) | |
download | chocolate-doom-120d90c67b2a4aa0a8883c4897241dee2222acd2.tar.gz chocolate-doom-120d90c67b2a4aa0a8883c4897241dee2222acd2.tar.bz2 chocolate-doom-120d90c67b2a4aa0a8883c4897241dee2222acd2.zip |
Merge from raven-branch.
Subversion-branch: /branches/strife-branch
Subversion-revision: 2051
Diffstat (limited to 'src')
107 files changed, 7211 insertions, 1570 deletions
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 |