From 74549609d36588724919c9cc576e7402ea9dac4e Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sun, 10 Dec 2023 18:20:03 +0700 Subject: [PATCH 01/28] fix zero length sounds (#1315) * fix zero length sounds DSSAWUP sound in "Eviternity II RC1.wad" * better fix * another variant (cherry picked from commit 626021e0eb3a54e18c2f5c72cf13e6278717ed7e) --- src/i_oalsound.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_oalsound.c b/src/i_oalsound.c index e5342c63a..0d2a42fc7 100644 --- a/src/i_oalsound.c +++ b/src/i_oalsound.c @@ -580,7 +580,7 @@ boolean I_OAL_CacheSound(sfxinfo_t *sfx) lumplen = W_LumpLength(lumpnum); // Check the header, and ensure this is a valid sound - if (lumpdata[0] == 0x03 && lumpdata[1] == 0x00) + if (lumplen > DMXHDRSIZE && lumpdata[0] == 0x03 && lumpdata[1] == 0x00) { freq = (lumpdata[3] << 8) | lumpdata[2]; size = (lumpdata[7] << 24) | (lumpdata[6] << 16) | From 05328ff57308e1d0a63ff6f777f7e11e1ba7a2f0 Mon Sep 17 00:00:00 2001 From: Fabian Greffrath Date: Sat, 9 Dec 2023 13:30:03 +0100 Subject: [PATCH 02/28] do not clear the entire musinfo.items[] array when loading a savegame (#1309) So it is not necessary to parse the MUSINFO lump again. Fixes #1308 (cherry picked from commit 0813541417e3234fda0431f1fceb2df43d505d42) --- src/g_game.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index e818ab1c9..1fcf83983 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2171,7 +2171,9 @@ static void G_DoLoadGame(void) if (lump[0] && i > 0) { - memset(&musinfo, 0, sizeof(musinfo)); + musinfo.mapthing = NULL; + musinfo.lastmapthing = NULL; + musinfo.tics = 0; musinfo.current_item = i; musinfo.from_savegame = true; S_ChangeMusInfoMusic(i, true); From 6e47bff64c4c4fbba31f41e371011d13b31d58bc Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 8 Dec 2023 21:07:36 +0700 Subject: [PATCH 03/28] don't parse lumpname as identifier, use indexes instead of pointers (#1311) * remove redundant '.' (cherry picked from commit 702b6439bb121936260330bd0b83f78fdfb8cee8) --- src/r_bmaps.c | 52 +++++++++++++++++++++++++++++-------------------- src/u_scanner.c | 2 +- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/r_bmaps.c b/src/r_bmaps.c index d1b1ff779..b2e3c90c4 100644 --- a/src/r_bmaps.c +++ b/src/r_bmaps.c @@ -36,7 +36,8 @@ static const byte nobrightmap[COLORMASK_SIZE] = { 0 }; const byte *dc_brightmap = nobrightmap; -typedef struct { +typedef struct +{ const char *name; byte colormask[COLORMASK_SIZE]; } brightmap_t; @@ -92,7 +93,7 @@ static void AddBrightmap(brightmap_t *brightmap) typedef struct { - brightmap_t *brightmap; + int idx; const char *name; int num; } elem_t; @@ -123,15 +124,15 @@ static void AddElem(array_t *array, elem_t *elem) array->num_elems++; } -static brightmap_t *GetBrightmap(const char *name) +static int GetBrightmap(const char *name) { int i; for (i = 0; i < num_brightmaps; ++i) { if (!strcasecmp(brightmaps_array[i].name, name)) - return &brightmaps_array[i]; + return i; } - return NULL; + return -1; } enum @@ -144,15 +145,15 @@ enum static boolean ParseProperty(u_scanner_t *s, elem_t *elem) { char *name; - brightmap_t *brightmap; + int idx; int game = DOOM1AND2; - U_MustGetToken(s, TK_Identifier); + U_GetString(s); name = M_StringDuplicate(s->string); U_MustGetToken(s, TK_Identifier); - brightmap = GetBrightmap(s->string); - if (!brightmap) + idx = GetBrightmap(s->string); + if (idx < 0) { U_Error(s, "brightmap '%s' not found", s->string); free(name); @@ -186,28 +187,31 @@ static boolean ParseProperty(u_scanner_t *s, elem_t *elem) } elem->name = name; - elem->brightmap = brightmap; + elem->idx = idx; return true; } void R_InitFlatBrightmaps(void) { int i; + elem_t *elems = flats_bm.elems; + for (i = 0; i < flats_bm.num_elems; ++i) { - flats_bm.elems[i].num = R_FlatNumForName(flats_bm.elems[i].name); + elems[i].num = R_FlatNumForName(elems[i].name); } } const byte *R_BrightmapForTexName(const char *texname) { int i; + const elem_t *elems = textures_bm.elems; for (i = textures_bm.num_elems - 1; i >= 0; i--) { - if (!strncasecmp(textures_bm.elems[i].name, texname, 8)) + if (!strncasecmp(elems[i].name, texname, 8)) { - return textures_bm.elems[i].brightmap->colormask; + return brightmaps_array[elems[i].idx].colormask; } } @@ -219,11 +223,13 @@ const byte *R_BrightmapForSprite(const int type) if (STRICTMODE(brightmaps) || force_brightmaps) { int i; + const elem_t *elems = sprites_bm.elems; + for (i = sprites_bm.num_elems - 1; i >= 0 ; i--) { - if (sprites_bm.elems[i].num == type) + if (elems[i].num == type) { - return sprites_bm.elems[i].brightmap->colormask; + return brightmaps_array[elems[i].idx].colormask; } } } @@ -236,11 +242,13 @@ const byte *R_BrightmapForFlatNum(const int num) if (STRICTMODE(brightmaps) || force_brightmaps) { int i; + const elem_t *elems = flats_bm.elems; + for (i = flats_bm.num_elems - 1; i >= 0; i--) { - if (flats_bm.elems[i].num == num) + if (elems[i].num == num) { - return flats_bm.elems[i].brightmap->colormask; + return brightmaps_array[elems[i].idx].colormask; } } } @@ -253,11 +261,13 @@ const byte *R_BrightmapForState(const int state) if (STRICTMODE(brightmaps) || force_brightmaps) { int i; + const elem_t *elems = states_bm.elems; + for (i = states_bm.num_elems - 1; i >= 0; i--) { - if (states_bm.elems[i].num == state) + if (elems[i].num == state) { - return states_bm.elems[i].brightmap->colormask; + return brightmaps_array[elems[i].idx].colormask; } } } @@ -342,8 +352,8 @@ void R_ParseBrightmaps(int lumpnum) U_Error(s, "state '%d' not found", elem.num); } U_MustGetToken(s, TK_Identifier); - elem.brightmap = GetBrightmap(s->string); - if (elem.brightmap) + elem.idx = GetBrightmap(s->string); + if (elem.idx >= 0) { AddElem(&states_bm, &elem); } diff --git a/src/u_scanner.c b/src/u_scanner.c index c7f9243bc..573745973 100644 --- a/src/u_scanner.c +++ b/src/u_scanner.c @@ -494,7 +494,7 @@ void PRINTF_ATTR(2, 0) U_Error(u_scanner_t* s, const char *msg, ...) va_start(ap, msg); M_vsnprintf(buffer, 1024, msg, ap); va_end(ap); - I_Error("%s:%d:%d:%s.", s->name, s->tokenLine, s->tokenLinePosition, buffer); + I_Error("%s:%d:%d:%s", s->name, s->tokenLine, s->tokenLinePosition, buffer); } boolean U_MustGetToken(u_scanner_t* s, char token) From 7fe853bea0e4b7dca1c7c34220e840ea3301e007 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Tue, 28 Nov 2023 12:04:15 +0700 Subject: [PATCH 04/28] add U_GetString function (#1289) Revert '-' in TK_Identifier. U_GetString accepts all data except whitespace. (cherry picked from commit 286e0c375091d0ebfb08001c9c7321e56bab3ea9) --- src/s_musinfo.c | 2 +- src/u_scanner.c | 43 +++++++++++++++++++++++++++++++++++++++++-- src/u_scanner.h | 1 + 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/s_musinfo.c b/src/s_musinfo.c index c17e92cec..f792e2827 100644 --- a/src/s_musinfo.c +++ b/src/s_musinfo.c @@ -75,7 +75,7 @@ void S_ParseMusInfo (const char *mapid) // Check number in range if (num > 0 && num < MAX_MUS_ENTRIES) { - U_MustGetToken(s, TK_Identifier); + U_GetString(s); lumpnum = W_CheckNumForName(s->string); if (lumpnum > 0) { diff --git a/src/u_scanner.c b/src/u_scanner.c index 573745973..7831c3a45 100644 --- a/src/u_scanner.c +++ b/src/u_scanner.c @@ -166,7 +166,7 @@ boolean U_CheckToken(u_scanner_t* s, char token) if(s->needNext) { if(!U_GetNextToken(s, false)) - return false; + return false; } // An int can also be a float. @@ -216,6 +216,46 @@ static void U_RestoreState(u_scanner_t* s, u_scanner_t savedstate) } } +boolean U_GetString(u_scanner_t* scanner) +{ + unsigned int start; + char cur; + u_parserstate_t* nextState = &scanner->nextState; + + if(!scanner->needNext) + { + scanner->needNext = true; + U_ExpandState(scanner); + return true; + } + + nextState->tokenLine = scanner->line; + nextState->tokenLinePosition = scanner->scanPos - scanner->lineStart; + nextState->token = TK_NoToken; + if(scanner->scanPos >= scanner->length) + { + U_ExpandState(scanner); + return false; + } + + start = scanner->scanPos; + cur = scanner->data[scanner->scanPos++]; + + while(scanner->scanPos < scanner->length) + { + cur = scanner->data[scanner->scanPos]; + + if(cur == ' ' || cur == '\t' || cur == '\n' || cur == '\r' || cur == 0) + break; + else + scanner->scanPos++; + } + + U_SetString(&(nextState->string), scanner->data + start, scanner->scanPos - start); + U_ExpandState(scanner); + return true; +} + boolean U_GetNextToken(u_scanner_t* scanner, boolean expandState) { unsigned int start; @@ -464,7 +504,6 @@ boolean U_GetNextLineToken(u_scanner_t* scanner) return retval; } - void U_ErrorToken(u_scanner_t* s, int token) { if (token < TK_NumSpecialTokens && s->token >= TK_Identifier && s->token < TK_NumSpecialTokens) diff --git a/src/u_scanner.h b/src/u_scanner.h index 83f8417d9..1bf1d64e2 100644 --- a/src/u_scanner.h +++ b/src/u_scanner.h @@ -101,6 +101,7 @@ boolean U_CheckToken(u_scanner_t* scanner, char token); boolean U_CheckInteger(u_scanner_t* s); boolean U_CheckFloat(u_scanner_t* s); void U_Unget(u_scanner_t* s); +boolean U_GetString(u_scanner_t* s); void PRINTF_ATTR(2, 0) U_Error(u_scanner_t* s, const char *msg, ...); void U_ErrorToken(u_scanner_t* s, int token); From de285a82891465907f4451fff782998eb7e93f77 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 10 Nov 2023 17:49:41 +0700 Subject: [PATCH 05/28] fix buffer overflow (cherry picked from commit b1d4988e4460fe652082f24701afd922ceadf45d) --- src/d_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index d3c575fbb..16ef564fb 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -668,9 +668,11 @@ void D_AddFile(const char *file) if (D_AddZipFile(path)) return; - if (numwadfiles >= numwadfiles_alloc) - wadfiles = I_Realloc(wadfiles, (numwadfiles_alloc = numwadfiles_alloc ? - numwadfiles_alloc * 2 : 8)*sizeof*wadfiles); + if (numwadfiles == numwadfiles_alloc - 1 || !numwadfiles_alloc) + { + numwadfiles_alloc = (numwadfiles_alloc ? numwadfiles_alloc * 2 : 8); + wadfiles = I_Realloc(wadfiles, numwadfiles_alloc * sizeof(*wadfiles)); + } // [FG] search for PWADs by their filename wadfiles[numwadfiles++] = path; wadfiles[numwadfiles] = NULL; From 4651ca89b083268bad31e4965367c13c83e70d5f Mon Sep 17 00:00:00 2001 From: Fabian Greffrath Date: Sun, 17 Sep 2023 22:20:33 +0200 Subject: [PATCH 06/28] Fluidsynth: support relative paths in soundfont_dir to enable the AppImage to find its own bundled soundfont file (cherry picked from commit 60030a8d4f5100fbc29c137c734830ee33e346d2) --- src/i_flmusic.c | 21 +++++++++++++++++++-- src/m_misc.c | 4 +++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/i_flmusic.c b/src/i_flmusic.c index 2f04cb4e2..e1949456c 100644 --- a/src/i_flmusic.c +++ b/src/i_flmusic.c @@ -36,6 +36,7 @@ #include "w_wad.h" #include "z_zone.h" #include "i_glob.h" +#include "d_iwad.h" // [FG] D_DoomExeDir() char *soundfont_path = ""; char *soundfont_dir = ""; @@ -124,8 +125,19 @@ static void AddSoundFont(const char *path) static void ScanDir(const char *dir) { - glob_t *glob = I_StartMultiGlob(dir, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED, - "*.sf2", "*.sf3", NULL); + char *rel = NULL; + glob_t *glob; + + // [FG] relative to the executable directory + if (dir[0] == '.') + { + rel = M_StringJoin(D_DoomExeDir(), DIR_SEPARATOR_S, dir, NULL); + dir = rel; + } + + glob = I_StartMultiGlob(dir, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED, + "*.sf2", "*.sf3", NULL); + while(1) { const char *filename = I_NextGlob(glob); @@ -139,6 +151,11 @@ static void ScanDir(const char *dir) } I_EndGlob(glob); + + if (rel) + { + free(rel); + } } static void GetSoundFonts(void) diff --git a/src/m_misc.c b/src/m_misc.c index 44b127433..e9ed6e752 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -484,7 +484,9 @@ default_t defaults[] = { {.s = "/usr/share/soundfonts:" /* Debian/Ubuntu/OpenSUSE */ "/usr/share/sounds/sf2:" - "/usr/share/sounds/sf3"}, + "/usr/share/sounds/sf3:" + /* AppImage */ + "../share/" PROJECT_SHORTNAME "/soundfonts"}, #endif {0}, string, ss_none, wad_no, "FluidSynth soundfont directories" From 7031d830f55110fbcfc0965e42b9d84931ec31dc Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Wed, 20 Sep 2023 19:56:04 +0700 Subject: [PATCH 07/28] fix semicolon (cherry picked from commit e1e8e06e96c4ee3920ff69b3b2f12b522dc5a5df) --- src/i_printf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_printf.c b/src/i_printf.c index 49146cfe2..96872e0df 100644 --- a/src/i_printf.c +++ b/src/i_printf.c @@ -74,7 +74,7 @@ static void EnableVTMode(void) } if (!SetConsoleMode(hConsole, ENABLE_PROCESSED_OUTPUT | - ENABLE_VIRTUAL_TERMINAL_PROCESSING)); + ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { return; } From 526ab1d3bbc96164b8a7697e282579e62cc8e466 Mon Sep 17 00:00:00 2001 From: Fabian Greffrath Date: Fri, 15 Sep 2023 14:25:21 +0200 Subject: [PATCH 08/28] document the new release in the metainfo file (cherry picked from commit ef34e6f7698244d31f7c972ec889cd58444aa152) --- data/io.github.fabiangreffrath.woof.metainfo.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/io.github.fabiangreffrath.woof.metainfo.xml b/data/io.github.fabiangreffrath.woof.metainfo.xml index dd5b7b11e..bd06406c9 100644 --- a/data/io.github.fabiangreffrath.woof.metainfo.xml +++ b/data/io.github.fabiangreffrath.woof.metainfo.xml @@ -33,7 +33,7 @@ source port should still look very familiar to the original MBF.

- In summary, this project's goal is to forward-port MBF.EXE from + In summary, this project's goal is to fast-forward MBF.EXE from DOS to 21st century and remove all the stumbling blocks on the way. Furthermore, just as MBF was ahead of its time, this project dedicates itself to early adoption of new modding features such as @@ -48,6 +48,7 @@ intense + From e3e4186e1b24065823e247789381d599e4c97671 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Tue, 10 Oct 2023 23:19:10 +0700 Subject: [PATCH 09/28] remove escape strings in wad filenames in the setup tool (#1222) It seems that `_wopen()` does not open escaped filenames in Windows. (cherry picked from commit 51ce2f38f46ea595666946baa337f8df6cbea6bc) --- setup/multiplayer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/multiplayer.c b/setup/multiplayer.c index 40a013f5e..ef0a60d96 100644 --- a/setup/multiplayer.c +++ b/setup/multiplayer.c @@ -139,7 +139,7 @@ static void AddWADs(execute_context_t *exec) have_wads = 1; } - AddCmdLineParameter(exec, "\"%s\"", wads[i]); + AddCmdLineParameter(exec, "%s", wads[i]); } } } From a8eb033576590113306572490c3e07122d1c1e10 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Wed, 11 Oct 2023 16:20:21 +0700 Subject: [PATCH 10/28] win midi: fallback if no devices found (cherry picked from commit 51ccc03461c9a34b48fe8b05d69896f52204c1ac) --- src/i_winmusic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/i_winmusic.c b/src/i_winmusic.c index 3e1f2bb05..643edc210 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -1730,6 +1730,12 @@ static int I_WIN_DeviceList(const char *devices[], int size, int *current_device GetDevices(); + if (winmm_devices_num == 0 && size > 0) + { + devices[0] = "MIDI Mapper"; + return 1; + } + for (i = 0; i < winmm_devices_num && i < size; ++i) { devices[i] = winmm_devices[i]; From d785b3bfc2d10219fcddb7bfe27de2d50b879034 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 13 Oct 2023 15:04:46 +0700 Subject: [PATCH 11/28] do not print control characters if VT mode is not enabled (cherry picked from commit 3c546497117231910fccba0979a18640f4e64df3) --- src/i_printf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/i_printf.c b/src/i_printf.c index 96872e0df..8cf8eb6c8 100644 --- a/src/i_printf.c +++ b/src/i_printf.c @@ -58,7 +58,7 @@ verbosity_t cfg_verbosity; #ifdef _WIN32 static HANDLE hConsole; static DWORD OldMode; -static boolean restore_mode = false; +static boolean vt_mode_enabled = false; static void EnableVTMode(void) { @@ -79,12 +79,12 @@ static void EnableVTMode(void) return; } - restore_mode = true; + vt_mode_enabled = true; } static void RestoreOldMode(void) { - if (!restore_mode) + if (!vt_mode_enabled) { return; } @@ -142,7 +142,11 @@ void I_Printf(verbosity_t prio, const char *msg, ...) break; } - if (I_ConsoleStdout()) + if (I_ConsoleStdout() +#ifdef _WIN32 + && vt_mode_enabled +#endif + ) { switch (prio) { From f8e36adcd4f50417f1fb4a9f586e533b00354c05 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 13 Oct 2023 23:17:33 +0700 Subject: [PATCH 12/28] fix global variable usage (cherry picked from commit e4b3be3590cf82d2b772b00167b9067eff759462) --- src/i_printf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_printf.c b/src/i_printf.c index 8cf8eb6c8..5636ada19 100644 --- a/src/i_printf.c +++ b/src/i_printf.c @@ -62,7 +62,7 @@ static boolean vt_mode_enabled = false; static void EnableVTMode(void) { - HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { return; From 0f85d36522c7e1663e1c6c52c9a80c5272c58dbd Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Sat, 14 Oct 2023 00:43:06 +0700 Subject: [PATCH 13/28] use static version of MSVC CRT Fixed a linker warning. (cherry picked from commit a19eaca101fc137a5b3c3a8a9dca9a8f08b5b96c) --- .github/workflows/win_msvc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/win_msvc.yml b/.github/workflows/win_msvc.yml index 18663d0f4..80249308e 100644 --- a/.github/workflows/win_msvc.yml +++ b/.github/workflows/win_msvc.yml @@ -63,6 +63,7 @@ jobs: -DCMAKE_TOOLCHAIN_FILE="${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" ` -DVCPKG_TARGET_TRIPLET="${{ matrix.config.arch }}-windows-static-release" ` -DVCPKG_OVERLAY_TRIPLETS="cmake/triplets" ` + -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ` -DCMAKE_IGNORE_PATH="C:/Strawberry/perl/bin;C:/Strawberry/c/lib" - name: Build From a5fee03c03edc6a76675d3ac1922d283a4555739 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Tue, 31 Oct 2023 17:07:49 +0700 Subject: [PATCH 14/28] fix "you need a key" messages in multiplayer (#1244) (cherry picked from commit 02d4fde2a50fccea405b5f6b3b247a47788f0dfd) --- src/p_doors.c | 12 ++++++------ src/p_spec.c | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/p_doors.c b/src/p_doors.c index ee31f44e3..32a711b9c 100644 --- a/src/p_doors.c +++ b/src/p_doors.c @@ -248,7 +248,7 @@ int EV_DoLockedDoor(line_t *line, vldoor_e type, mobj_t *thing) case 133: if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) { - displaymsg("%s", s_PD_BLUEO); // Ty 03/27/98 - externalized + doomprintf(p, MESSAGES_NONE, "%s", s_PD_BLUEO); // Ty 03/27/98 - externalized S_StartSound(p->mo,sfx_oof); // killough 3/20/98 return 0; } @@ -258,7 +258,7 @@ int EV_DoLockedDoor(line_t *line, vldoor_e type, mobj_t *thing) case 135: if (!p->cards[it_redcard] && !p->cards[it_redskull]) { - displaymsg("%s", s_PD_REDO); // Ty 03/27/98 - externalized + doomprintf(p, MESSAGES_NONE, "%s", s_PD_REDO); // Ty 03/27/98 - externalized S_StartSound(p->mo,sfx_oof); // killough 3/20/98 return 0; } @@ -268,7 +268,7 @@ int EV_DoLockedDoor(line_t *line, vldoor_e type, mobj_t *thing) case 137: if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) { - displaymsg("%s", s_PD_YELLOWO); // Ty 03/27/98 - externalized + doomprintf(p, MESSAGES_NONE, "%s", s_PD_YELLOWO); // Ty 03/27/98 - externalized S_StartSound(p->mo,sfx_oof); // killough 3/20/98 return 0; } @@ -395,7 +395,7 @@ int EV_VerticalDoor(line_t *line, mobj_t *thing) return 0; if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) { - displaymsg("%s", s_PD_BLUEK); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", s_PD_BLUEK); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } @@ -407,7 +407,7 @@ int EV_VerticalDoor(line_t *line, mobj_t *thing) return 0; if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) { - displaymsg("%s", s_PD_YELLOWK); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", s_PD_YELLOWK); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } @@ -419,7 +419,7 @@ int EV_VerticalDoor(line_t *line, mobj_t *thing) return 0; if (!player->cards[it_redcard] && !player->cards[it_redskull]) { - displaymsg("%s", s_PD_REDK); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", s_PD_REDK); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } diff --git a/src/p_spec.c b/src/p_spec.c index 9d0b781c7..354bf48a6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -800,7 +800,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) !player->cards[it_yellowcard] && !player->cards[it_yellowskull]) { - displaymsg("%s", s_PD_ANY); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", s_PD_ANY); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -809,7 +809,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) if (!player->cards[it_redcard] && (!skulliscard || !player->cards[it_redskull])) { - displaymsg("%s", skulliscard? s_PD_REDK : s_PD_REDC); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", skulliscard? s_PD_REDK : s_PD_REDC); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -818,7 +818,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) if (!player->cards[it_bluecard] && (!skulliscard || !player->cards[it_blueskull])) { - displaymsg("%s", skulliscard? s_PD_BLUEK : s_PD_BLUEC); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", skulliscard? s_PD_BLUEK : s_PD_BLUEC); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -827,7 +827,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) if (!player->cards[it_yellowcard] && (!skulliscard || !player->cards[it_yellowskull])) { - displaymsg("%s", skulliscard? s_PD_YELLOWK : s_PD_YELLOWC); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", skulliscard? s_PD_YELLOWK : s_PD_YELLOWC); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -836,7 +836,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) if (!player->cards[it_redskull] && (!skulliscard || !player->cards[it_redcard])) { - displaymsg("%s", skulliscard? s_PD_REDK : s_PD_REDS); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", skulliscard? s_PD_REDK : s_PD_REDS); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -845,7 +845,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) if (!player->cards[it_blueskull] && (!skulliscard || !player->cards[it_bluecard])) { - displaymsg("%s", skulliscard? s_PD_BLUEK : s_PD_BLUES); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", skulliscard? s_PD_BLUEK : s_PD_BLUES); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -854,7 +854,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) if (!player->cards[it_yellowskull] && (!skulliscard || !player->cards[it_yellowcard])) { - displaymsg("%s", skulliscard? s_PD_YELLOWK : s_PD_YELLOWS); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", skulliscard? s_PD_YELLOWK : s_PD_YELLOWS); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -868,7 +868,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) !player->cards[it_yellowcard] || !player->cards[it_yellowskull])) { - displaymsg("%s", s_PD_ALL6); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", s_PD_ALL6); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } @@ -879,7 +879,7 @@ boolean P_CanUnlockGenDoor(line_t *line, player_t *player) // http://prboom.sourceforge.net/mbf-bugs.html !(player->cards[it_yellowcard] | (demo_version == 203 ? !player->cards[it_yellowskull] : player->cards[it_yellowskull])))) { - displaymsg("%s", s_PD_ALL3); // Ty 03/27/98 - externalized + doomprintf(player, MESSAGES_NONE, "%s", s_PD_ALL3); // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return false; } From 112f90f9e2facfba211325131212799e8c113216 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 10 Nov 2023 17:56:39 +0700 Subject: [PATCH 15/28] win midi: introduce more states (#1248) * reworked shutdown sequence to avoid possible crashes * use critial section * do not close the thread when stopping the song. * add STATE_EXIT, remove hExitEvent * send SendNotesSoundOff() when the stream stops, refactoring * Add STATE_STOPPING state and hStoppedEvent event. * Add StreamStart() and StreamStop() functions. * Remove SendNotesSoundOff() from ResetDevice(). (cherry picked from commit b46d80df4540f5b2558be507327b12aa96df25ff) --- src/i_winmusic.c | 240 +++++++++++++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 89 deletions(-) diff --git a/src/i_winmusic.c b/src/i_winmusic.c index 643edc210..963229ae3 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -79,6 +79,10 @@ static boolean update_volume = false; typedef enum { + STATE_STARTUP, + STATE_SHUTDOWN, + STATE_EXIT, + STATE_STOPPING, STATE_STOPPED, STATE_PLAYING, STATE_PAUSING, @@ -94,8 +98,9 @@ static UINT MidiDevice; static HMIDISTRM hMidiStream; static MIDIHDR MidiStreamHdr; static HANDLE hBufferReturnEvent; -static HANDLE hExitEvent; +static HANDLE hStoppedEvent; static HANDLE hPlayerThread; +static CRITICAL_SECTION CriticalSection; static char **winmm_devices; static int winmm_devices_num; @@ -141,7 +146,7 @@ static win_midi_song_t song; #define BUFFER_INITIAL_SIZE 8192 -#define PLAYER_THREAD_WAIT_TIME 3000 +#define PLAYER_THREAD_WAIT_TIME 4000 typedef struct { @@ -161,8 +166,6 @@ static buffer_t buffer; #define PADDED_SIZE(x) (((x) + sizeof(DWORD) - 1) & ~(sizeof(DWORD) - 1)) -static boolean initial_playback = false; - // Check for midiStream errors. static void MidiError(verbosity_t severity, const char *prefix, DWORD dwError) @@ -426,9 +429,6 @@ static void ResetPitchBendSensitivity(void) static void ResetDevice(void) { - // Send notes/sound off prior to reset to prevent volume spikes. - SendNotesSoundOff(); - MIDI_ResetFallback(); use_fallback = false; @@ -456,10 +456,14 @@ static void ResetDevice(void) ResetPitchBendSensitivity(); // Reset volume (initial playback or on shutdown if no SysEx reset). - if (initial_playback || winmm_reset_type == RESET_TYPE_NONE) + // Scale by slider on initial playback, max on shutdown. + if (win_midi_state == STATE_STARTUP) + { + ResetVolume(); + } + else if (winmm_reset_type == RESET_TYPE_NONE) { - // Scale by slider on initial playback, max on shutdown. - volume_factor = initial_playback ? volume_factor : 1.0f; + volume_factor = 1.0f; ResetVolume(); } @@ -1238,25 +1242,23 @@ static void FillBuffer(void) buffer.position = 0; - if (initial_playback) + switch (win_midi_state) { - ResetDevice(); - StreamOut(); - song.rpg_loop = IsRPGLoop(); - initial_playback = false; - return; - } + case STATE_STARTUP: + ResetDevice(); + StreamOut(); + song.rpg_loop = IsRPGLoop(); + win_midi_state = STATE_PLAYING; + return; - if (update_volume) - { - update_volume = false; - UpdateVolume(); - StreamOut(); - return; - } + case STATE_SHUTDOWN: + // Send notes/sound off prior to reset to prevent volume spikes. + SendNotesSoundOff(); + ResetDevice(); + StreamOut(); + win_midi_state = STATE_EXIT; + return; - switch (win_midi_state) - { case STATE_PLAYING: break; @@ -1273,10 +1275,24 @@ static void FillBuffer(void) StreamOut(); return; - case STATE_STOPPED: + case STATE_STOPPING: + SendNotesSoundOff(); + StreamOut(); + win_midi_state = STATE_STOPPED; + return; + + default: return; } + if (update_volume) + { + UpdateVolume(); + StreamOut(); + update_volume = false; + return; + } + for (num_events = 0; num_events < STREAM_MAX_EVENTS; ) { midi_event_t *event = NULL; @@ -1362,23 +1378,77 @@ static void FillBuffer(void) static DWORD WINAPI PlayerProc(void) { - HANDLE events[2] = { hBufferReturnEvent, hExitEvent }; - - while (1) + while (true) { - switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) + if (WaitForSingleObject(hBufferReturnEvent, INFINITE) == WAIT_OBJECT_0) { - case WAIT_OBJECT_0: - FillBuffer(); - break; + EnterCriticalSection(&CriticalSection); - case WAIT_OBJECT_0 + 1: - return 0; + switch (win_midi_state) + { + case STATE_STOPPED: + SetEvent(hStoppedEvent); + break; + + case STATE_EXIT: + LeaveCriticalSection(&CriticalSection); + return 0; + + default: + break; + } + + FillBuffer(); + + LeaveCriticalSection(&CriticalSection); } } return 0; } +static void StreamStart(void) +{ + MMRESULT mmr; + + if (!hMidiStream) + { + return; + } + + SetEvent(hBufferReturnEvent); + + mmr = midiStreamRestart(hMidiStream); + if (mmr != MMSYSERR_NOERROR) + { + MidiError(VB_WARNING, "midiStreamRestart", mmr); + } +} + +static void StreamStop(void) +{ + MMRESULT mmr; + + if (!hMidiStream) + { + return; + } + + EnterCriticalSection(&CriticalSection); + + win_midi_state = STATE_STOPPED; + + LeaveCriticalSection(&CriticalSection); + + mmr = midiStreamStop(hMidiStream); + if (mmr != MMSYSERR_NOERROR) + { + MidiError(VB_WARNING, "midiStreamStop", mmr); + } + + ResetEvent(hBufferReturnEvent); + ResetEvent(hStoppedEvent); +} + static void GetDevices(void) { int i; @@ -1461,14 +1531,16 @@ static boolean I_WIN_InitMusic(int device) } hBufferReturnEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + hStoppedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + hPlayerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlayerProc, + 0, 0, 0); + SetThreadPriority(hPlayerThread, THREAD_PRIORITY_TIME_CRITICAL); + InitializeCriticalSectionAndSpinCount(&CriticalSection, 1024); AddToBuffer = (winmm_complevel == COMP_VANILLA) ? AddToBuffer_Vanilla : AddToBuffer_Standard; MIDI_InitFallback(); - win_midi_state = STATE_STOPPED; - I_Printf(VB_INFO, "Windows MIDI Init: Using '%s'.", winmm_device); return true; @@ -1488,40 +1560,35 @@ static void I_WIN_SetMusicVolume(int volume) volume_factor = sqrtf((float)volume / 15); + EnterCriticalSection(&CriticalSection); + update_volume = (song.file != NULL); + + LeaveCriticalSection(&CriticalSection); } static void I_WIN_StopSong(void *handle) { - MMRESULT mmr; - - if (!hPlayerThread) + if (!hMidiStream) { return; } - SetEvent(hExitEvent); - WaitForSingleObject(hPlayerThread, PLAYER_THREAD_WAIT_TIME); - CloseHandle(hPlayerThread); - hPlayerThread = NULL; - win_midi_state = STATE_STOPPED; + StreamStop(); - if (!hMidiStream) - { - return; - } + EnterCriticalSection(&CriticalSection); - mmr = midiStreamStop(hMidiStream); - if (mmr != MMSYSERR_NOERROR) - { - MidiError(VB_ERROR, "midiStreamStop", mmr); - } + win_midi_state = STATE_STOPPING; + + LeaveCriticalSection(&CriticalSection); + + StreamStart(); + WaitForSingleObject(hStoppedEvent, PLAYER_THREAD_WAIT_TIME); + StreamStop(); } static void I_WIN_PlaySong(void *handle, boolean looping) { - MMRESULT mmr; - if (!hMidiStream) { return; @@ -1529,20 +1596,13 @@ static void I_WIN_PlaySong(void *handle, boolean looping) song.looping = looping; - hPlayerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlayerProc, - 0, 0, 0); - SetThreadPriority(hPlayerThread, THREAD_PRIORITY_TIME_CRITICAL); + EnterCriticalSection(&CriticalSection); - initial_playback = true; - win_midi_state = STATE_PLAYING; + win_midi_state = STATE_STARTUP; - SetEvent(hBufferReturnEvent); + LeaveCriticalSection(&CriticalSection); - mmr = midiStreamRestart(hMidiStream); - if (mmr != MMSYSERR_NOERROR) - { - MidiError(VB_ERROR, "midiStreamRestart", mmr); - } + StreamStart(); } static void I_WIN_PauseSong(void *handle) @@ -1552,7 +1612,11 @@ static void I_WIN_PauseSong(void *handle) return; } + EnterCriticalSection(&CriticalSection); + win_midi_state = STATE_PAUSING; + + LeaveCriticalSection(&CriticalSection); } static void I_WIN_ResumeSong(void *handle) @@ -1562,7 +1626,11 @@ static void I_WIN_ResumeSong(void *handle) return; } + EnterCriticalSection(&CriticalSection); + win_midi_state = STATE_PLAYING; + + LeaveCriticalSection(&CriticalSection); } static void *I_WIN_RegisterSong(void *data, int len) @@ -1643,9 +1711,6 @@ static void *I_WIN_RegisterSong(void *data, int len) song.tracks[i].iter = MIDI_IterateTrack(song.file, i); } - ResetEvent(hBufferReturnEvent); - ResetEvent(hExitEvent); - return (void *)1; } @@ -1690,26 +1755,22 @@ static void I_WIN_ShutdownMusic(void) return; } - I_WIN_StopSong(NULL); + StreamStop(); I_WIN_UnRegisterSong(NULL); - // Reset device at shutdown. - buffer.position = 0; - ResetDevice(); - StreamOut(); - mmr = midiStreamRestart(hMidiStream); - if (mmr != MMSYSERR_NOERROR) - { - MidiError(VB_WARNING, "midiStreamRestart", mmr); - } - WaitForSingleObject(hBufferReturnEvent, PLAYER_THREAD_WAIT_TIME); - mmr = midiStreamStop(hMidiStream); - if (mmr != MMSYSERR_NOERROR) + EnterCriticalSection(&CriticalSection); + + win_midi_state = STATE_SHUTDOWN; + + LeaveCriticalSection(&CriticalSection); + + StreamStart(); + if (WaitForSingleObject(hPlayerThread, PLAYER_THREAD_WAIT_TIME) == WAIT_OBJECT_0) { - MidiError(VB_WARNING, "midiStreamStop", mmr); + CloseHandle(hPlayerThread); + hPlayerThread = NULL; } - - buffer.position = 0; + StreamStop(); mmr = midiStreamClose(hMidiStream); if (mmr != MMSYSERR_NOERROR) @@ -1719,7 +1780,8 @@ static void I_WIN_ShutdownMusic(void) hMidiStream = NULL; CloseHandle(hBufferReturnEvent); - CloseHandle(hExitEvent); + CloseHandle(hStoppedEvent); + DeleteCriticalSection(&CriticalSection); } static int I_WIN_DeviceList(const char *devices[], int size, int *current_device) From d994f26013fd23d713f7e5492f45f728243c5baf Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 10 Nov 2023 18:56:14 +0700 Subject: [PATCH 16/28] win midi: make all errors VB_ERROR verbosity level (cherry picked from commit 8c1fea93a65a6311d6d06415f8d4b7872a9e730b) --- src/i_winmusic.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/i_winmusic.c b/src/i_winmusic.c index 963229ae3..b56bfd52b 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -168,7 +168,7 @@ static buffer_t buffer; // Check for midiStream errors. -static void MidiError(verbosity_t severity, const char *prefix, DWORD dwError) +static void MidiError(const char *prefix, DWORD dwError) { wchar_t werror[MAXERRORLENGTH]; MMRESULT mmr; @@ -177,12 +177,12 @@ static void MidiError(verbosity_t severity, const char *prefix, DWORD dwError) if (mmr == MMSYSERR_NOERROR) { char *error = M_ConvertWideToUtf8(werror); - I_Printf(severity, "%s: %s.", prefix, error); + I_Printf(VB_ERROR, "%s: %s.", prefix, error); free(error); } else { - I_Printf(severity, "%s: Unknown midiStream error.", prefix); + I_Printf(VB_ERROR, "%s: Unknown midiStream error.", prefix); } } @@ -209,7 +209,7 @@ static void PrepareHeader(void) mmr = midiOutPrepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_ERROR, "midiOutPrepareHeader", mmr); + MidiError("midiOutPrepareHeader", mmr); } } @@ -224,7 +224,7 @@ static void AllocateBuffer(const unsigned int size) mmr = midiOutUnprepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_ERROR, "midiOutUnprepareHeader", mmr); + MidiError("midiOutUnprepareHeader", mmr); } } @@ -263,7 +263,7 @@ static void StreamOut(void) mmr = midiStreamOut(hMidiStream, hdr, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_ERROR, "midiStreamOut", mmr); + MidiError("midiStreamOut", mmr); } } @@ -1420,7 +1420,7 @@ static void StreamStart(void) mmr = midiStreamRestart(hMidiStream); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_WARNING, "midiStreamRestart", mmr); + MidiError("midiStreamRestart", mmr); } } @@ -1442,7 +1442,7 @@ static void StreamStop(void) mmr = midiStreamStop(hMidiStream); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_WARNING, "midiStreamStop", mmr); + MidiError("midiStreamStop", mmr); } ResetEvent(hBufferReturnEvent); @@ -1516,7 +1516,7 @@ static boolean I_WIN_InitMusic(int device) CALLBACK_FUNCTION); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_ERROR, "midiStreamOpen", mmr); + MidiError("midiStreamOpen", mmr); hMidiStream = NULL; return false; } @@ -1687,7 +1687,7 @@ static void *I_WIN_RegisterSong(void *data, int len) MIDIPROP_SET | MIDIPROP_TIMEDIV); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_ERROR, "midiStreamProperty", mmr); + MidiError("midiStreamProperty", mmr); return NULL; } timediv = prop_timediv.dwTimeDiv; @@ -1699,7 +1699,7 @@ static void *I_WIN_RegisterSong(void *data, int len) MIDIPROP_SET | MIDIPROP_TEMPO); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_ERROR, "midiStreamProperty", mmr); + MidiError("midiStreamProperty", mmr); return NULL; } tempo = prop_tempo.dwTempo; @@ -1775,7 +1775,7 @@ static void I_WIN_ShutdownMusic(void) mmr = midiStreamClose(hMidiStream); if (mmr != MMSYSERR_NOERROR) { - MidiError(VB_WARNING, "midiStreamClose", mmr); + MidiError("midiStreamClose", mmr); } hMidiStream = NULL; From c0bb2dfeccca8a7b26f1cb03f834928b16c47ca0 Mon Sep 17 00:00:00 2001 From: ceski <56656010+ceski-1@users.noreply.github.com> Date: Sat, 11 Nov 2023 08:15:18 -0800 Subject: [PATCH 17/28] win midi: Misc. fix roundup (#1251) * win midi: Faster pause/resume * win midi: Refactor states * win midi: Use strncasecmp * win midi: Improve thread synchronization * win midi: Fix EMIDI events and designation (cherry picked from commit f566602caa73dd27ec7da0e43f02d8a3cd004ee2) --- src/i_winmusic.c | 190 ++++++++++++++++++----------------------------- 1 file changed, 71 insertions(+), 119 deletions(-) diff --git a/src/i_winmusic.c b/src/i_winmusic.c index b56bfd52b..bc4032052 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -85,7 +85,6 @@ typedef enum STATE_STOPPING, STATE_STOPPED, STATE_PLAYING, - STATE_PAUSING, STATE_PAUSED } win_midi_state_t; @@ -102,6 +101,8 @@ static HANDLE hStoppedEvent; static HANDLE hPlayerThread; static CRITICAL_SECTION CriticalSection; +#define EMIDI_DEVICE (1 << EMIDI_DEVICE_GENERAL_MIDI) + static char **winmm_devices; static int winmm_devices_num; @@ -716,7 +717,7 @@ static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, unsigned int flag; int count; - switch ((int)event->event_type) + switch (event->data.channel.param1) { case EMIDI_CONTROLLER_TRACK_DESIGNATION: if (track->elapsed_time < timediv) @@ -844,10 +845,6 @@ static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, } SendNOPMsg(delta_time); break; - - default: - SendNOPMsg(delta_time); - break; } } @@ -976,8 +973,7 @@ static boolean AddToBuffer_Standard(unsigned int delta_time, return true; } - if (track->emidi_designated && - (EMIDI_DEVICE_GENERAL_MIDI & ~track->emidi_device_flags)) + if (track->emidi_designated && (EMIDI_DEVICE & ~track->emidi_device_flags)) { // Send NOP if this device has been excluded from this track. SendNOPMsg(delta_time); @@ -1240,59 +1236,6 @@ static void FillBuffer(void) unsigned int i; int num_events; - buffer.position = 0; - - switch (win_midi_state) - { - case STATE_STARTUP: - ResetDevice(); - StreamOut(); - song.rpg_loop = IsRPGLoop(); - win_midi_state = STATE_PLAYING; - return; - - case STATE_SHUTDOWN: - // Send notes/sound off prior to reset to prevent volume spikes. - SendNotesSoundOff(); - ResetDevice(); - StreamOut(); - win_midi_state = STATE_EXIT; - return; - - case STATE_PLAYING: - break; - - case STATE_PAUSING: - // Send notes/sound off to prevent hanging notes. - SendNotesSoundOff(); - StreamOut(); - win_midi_state = STATE_PAUSED; - return; - - case STATE_PAUSED: - // Send a NOP every 100 ms while paused. - SendDelayMsg(100); - StreamOut(); - return; - - case STATE_STOPPING: - SendNotesSoundOff(); - StreamOut(); - win_midi_state = STATE_STOPPED; - return; - - default: - return; - } - - if (update_volume) - { - UpdateVolume(); - StreamOut(); - update_volume = false; - return; - } - for (num_events = 0; num_events < STREAM_MAX_EVENTS; ) { midi_event_t *event = NULL; @@ -1380,28 +1323,63 @@ static DWORD WINAPI PlayerProc(void) { while (true) { - if (WaitForSingleObject(hBufferReturnEvent, INFINITE) == WAIT_OBJECT_0) + if (WaitForSingleObject(hBufferReturnEvent, INFINITE) != WAIT_OBJECT_0) { - EnterCriticalSection(&CriticalSection); + continue; + } - switch (win_midi_state) - { - case STATE_STOPPED: - SetEvent(hStoppedEvent); - break; + EnterCriticalSection(&CriticalSection); - case STATE_EXIT: - LeaveCriticalSection(&CriticalSection); - return 0; + buffer.position = 0; - default: + switch (win_midi_state) + { + case STATE_STARTUP: + ResetDevice(); + StreamOut(); + song.rpg_loop = IsRPGLoop(); + win_midi_state = STATE_PLAYING; + break; + + case STATE_SHUTDOWN: + // Send notes/sound off prior to reset to prevent volume spikes. + SendNotesSoundOff(); + ResetDevice(); + StreamOut(); + win_midi_state = STATE_EXIT; + break; + + case STATE_EXIT: + LeaveCriticalSection(&CriticalSection); + return 0; + + case STATE_PLAYING: + if (update_volume) + { + UpdateVolume(); + StreamOut(); + update_volume = false; break; - } + } + FillBuffer(); + break; + + case STATE_STOPPING: + // Send notes/sound off to prevent hanging notes. + SendNotesSoundOff(); + StreamOut(); + win_midi_state = STATE_STOPPED; + break; - FillBuffer(); + case STATE_STOPPED: + SetEvent(hStoppedEvent); + break; - LeaveCriticalSection(&CriticalSection); + case STATE_PAUSED: + break; } + + LeaveCriticalSection(&CriticalSection); } return 0; } @@ -1410,11 +1388,6 @@ static void StreamStart(void) { MMRESULT mmr; - if (!hMidiStream) - { - return; - } - SetEvent(hBufferReturnEvent); mmr = midiStreamRestart(hMidiStream); @@ -1428,17 +1401,6 @@ static void StreamStop(void) { MMRESULT mmr; - if (!hMidiStream) - { - return; - } - - EnterCriticalSection(&CriticalSection); - - win_midi_state = STATE_STOPPED; - - LeaveCriticalSection(&CriticalSection); - mmr = midiStreamStop(hMidiStream); if (mmr != MMSYSERR_NOERROR) { @@ -1489,7 +1451,7 @@ static boolean I_WIN_InitMusic(int device) for (i = 0; i < winmm_devices_num; ++i) { - if (!strcasecmp(winmm_devices[i], winmm_device)) + if (!strncasecmp(winmm_devices[i], winmm_device, MAXPNAMELEN)) { device = i; break; @@ -1555,15 +1517,11 @@ static void I_WIN_SetMusicVolume(int volume) // Ignore holding key down in volume menu. return; } - last_volume = volume; - volume_factor = sqrtf((float)volume / 15); - EnterCriticalSection(&CriticalSection); - + volume_factor = sqrtf((float)volume / 15); update_volume = (song.file != NULL); - LeaveCriticalSection(&CriticalSection); } @@ -1574,15 +1532,12 @@ static void I_WIN_StopSong(void *handle) return; } - StreamStop(); - EnterCriticalSection(&CriticalSection); - + StreamStop(); win_midi_state = STATE_STOPPING; - + StreamStart(); LeaveCriticalSection(&CriticalSection); - StreamStart(); WaitForSingleObject(hStoppedEvent, PLAYER_THREAD_WAIT_TIME); StreamStop(); } @@ -1594,15 +1549,11 @@ static void I_WIN_PlaySong(void *handle, boolean looping) return; } - song.looping = looping; - EnterCriticalSection(&CriticalSection); - + song.looping = looping; win_midi_state = STATE_STARTUP; - - LeaveCriticalSection(&CriticalSection); - StreamStart(); + LeaveCriticalSection(&CriticalSection); } static void I_WIN_PauseSong(void *handle) @@ -1612,10 +1563,10 @@ static void I_WIN_PauseSong(void *handle) return; } - EnterCriticalSection(&CriticalSection); - - win_midi_state = STATE_PAUSING; + I_WIN_StopSong(NULL); + EnterCriticalSection(&CriticalSection); + win_midi_state = STATE_PAUSED; LeaveCriticalSection(&CriticalSection); } @@ -1627,9 +1578,13 @@ static void I_WIN_ResumeSong(void *handle) } EnterCriticalSection(&CriticalSection); - + if (win_midi_state != STATE_PAUSED) + { + LeaveCriticalSection(&CriticalSection); + return; + } win_midi_state = STATE_PLAYING; - + StreamStart(); LeaveCriticalSection(&CriticalSection); } @@ -1755,16 +1710,13 @@ static void I_WIN_ShutdownMusic(void) return; } + EnterCriticalSection(&CriticalSection); StreamStop(); I_WIN_UnRegisterSong(NULL); - - EnterCriticalSection(&CriticalSection); - win_midi_state = STATE_SHUTDOWN; - + StreamStart(); LeaveCriticalSection(&CriticalSection); - StreamStart(); if (WaitForSingleObject(hPlayerThread, PLAYER_THREAD_WAIT_TIME) == WAIT_OBJECT_0) { CloseHandle(hPlayerThread); @@ -1801,7 +1753,7 @@ static int I_WIN_DeviceList(const char *devices[], int size, int *current_device for (i = 0; i < winmm_devices_num && i < size; ++i) { devices[i] = winmm_devices[i]; - if (!strcasecmp(winmm_devices[i], winmm_device)) + if (!strncasecmp(winmm_devices[i], winmm_device, MAXPNAMELEN)) { *current_device = i; } From ea97a18c6db205140a39fdac1ceb08452594737f Mon Sep 17 00:00:00 2001 From: ceski <56656010+ceski-1@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:01:52 -0800 Subject: [PATCH 18/28] win midi: Sync with Chocolate Doom (#1261) (cherry picked from commit 7024ffcef591505b89a65c299788a56f2f48feb2) --- src/i_winmusic.c | 172 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 159 insertions(+), 13 deletions(-) diff --git a/src/i_winmusic.c b/src/i_winmusic.c index bc4032052..d89689342 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -101,7 +101,7 @@ static HANDLE hStoppedEvent; static HANDLE hPlayerThread; static CRITICAL_SECTION CriticalSection; -#define EMIDI_DEVICE (1 << EMIDI_DEVICE_GENERAL_MIDI) +#define EMIDI_DEVICE (1U << EMIDI_DEVICE_GENERAL_MIDI) static char **winmm_devices; static int winmm_devices_num; @@ -199,6 +199,12 @@ static void CALLBACK MidiStreamProc(HMIDIOUT hMidi, UINT uMsg, } } +// Allocates the buffer and prepares the MIDI header. Set during initialization +// by the main thread. BUFFER_INITIAL_SIZE should be large enough to avoid +// reallocation by the MIDI thread during playback, due to a known memory bug +// with midiOutUnprepareHeader() (detected by ASan). The calling thread must +// have exclusive access to the shared resources in this function. + static void PrepareHeader(void) { MIDIHDR *hdr = &MidiStreamHdr; @@ -221,6 +227,9 @@ static void AllocateBuffer(const unsigned int size) MIDIHDR *hdr = &MidiStreamHdr; MMRESULT mmr; + // Windows doesn't always immediately clear the MHDR_INQUEUE flag, even + // after midiStreamStop() is called. There doesn't seem to be any side + // effect to just forcing the flag off. hdr->dwFlags &= ~MHDR_INQUEUE; mmr = midiOutUnprepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) @@ -235,6 +244,10 @@ static void AllocateBuffer(const unsigned int size) PrepareHeader(); } +// Pads the buffer with zeros so that an integral number of DWORDs are stored. +// Required for long messages (SysEx). Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static void WriteBufferPad(void) { unsigned int padding = PADDED_SIZE(buffer.position); @@ -242,6 +255,9 @@ static void WriteBufferPad(void) buffer.position = padding; } +// Writes message data to buffer. Call this function from the MIDI thread only, +// with exclusive access to shared resources. + static void WriteBuffer(const byte *ptr, unsigned int size) { if (buffer.position + size >= buffer.size) @@ -253,6 +269,9 @@ static void WriteBuffer(const byte *ptr, unsigned int size) buffer.position += size; } +// Streams out the current buffer. Call this function from the MIDI thread only, +// with exclusive access to shared resources. + static void StreamOut(void) { MIDIHDR *hdr = &MidiStreamHdr; @@ -268,6 +287,9 @@ static void StreamOut(void) } } +// Writes a short MIDI message. Call this function from the MIDI thread only, +// with exclusive access to shared resources. + static void SendShortMsg(unsigned int delta_time, byte status, byte channel, byte param1, byte param2) { @@ -278,6 +300,9 @@ static void SendShortMsg(unsigned int delta_time, byte status, byte channel, WriteBuffer((byte *)&native_event, sizeof(native_event_t)); } +// Writes a short MIDI message (from an event). Call this function from the MIDI +// thread only, with exclusive access to shared resources. + static void SendChannelMsg(unsigned int delta_time, const midi_event_t *event, boolean use_param2) { @@ -286,6 +311,9 @@ static void SendChannelMsg(unsigned int delta_time, const midi_event_t *event, use_param2 ? event->data.channel.param2 : 0); } +// Writes a long MIDI message (SysEx). Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static void SendLongMsg(unsigned int delta_time, const byte *ptr, unsigned int length) { @@ -298,6 +326,10 @@ static void SendLongMsg(unsigned int delta_time, const byte *ptr, WriteBufferPad(); } +// Writes an RPN message set to NULL (0x7F). Prevents accidental data entry. +// Call this function from the MIDI thread only, with exclusive access to shared +// resources. + static void SendNullRPN(unsigned int delta_time, const midi_event_t *event) { const byte channel = event->data.channel.channel; @@ -307,6 +339,9 @@ static void SendNullRPN(unsigned int delta_time, const midi_event_t *event) MIDI_CONTROLLER_RPN_MSB, MIDI_RPN_NULL); } +// Writes a NOP message (ticks). Call this function from the MIDI thread only, +// with exclusive access to shared resources. + static void SendNOPMsg(unsigned int delta_time) { native_event_t native_event; @@ -316,6 +351,9 @@ static void SendNOPMsg(unsigned int delta_time) WriteBuffer((byte *)&native_event, sizeof(native_event_t)); } +// Writes a NOP message (milliseconds). Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static void SendDelayMsg(unsigned int time_ms) { // Convert ms to ticks (see "Standard MIDI Files 1.0" page 14). @@ -323,6 +361,9 @@ static void SendDelayMsg(unsigned int time_ms) SendNOPMsg(ticks); } +// Writes a tempo MIDI meta message. Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static void UpdateTempo(unsigned int delta_time, const midi_event_t *event) { native_event_t native_event; @@ -336,6 +377,10 @@ static void UpdateTempo(unsigned int delta_time, const midi_event_t *event) WriteBuffer((byte *)&native_event, sizeof(native_event_t)); } +// Writes a MIDI volume message. The value is scaled by the volume slider. Call +// this function from the MIDI thread only, with exclusive access to shared +// resources. + static void SendManualVolumeMsg(unsigned int delta_time, byte channel, byte volume) { @@ -354,12 +399,20 @@ static void SendManualVolumeMsg(unsigned int delta_time, byte channel, channel_volume[channel] = volume; } +// Writes a MIDI volume message (from an event). The value is scaled by the +// volume slider. Call this function from the MIDI thread only, with exclusive +// access to shared resources. + static void SendVolumeMsg(unsigned int delta_time, const midi_event_t *event) { SendManualVolumeMsg(delta_time, event->data.channel.channel, event->data.channel.param2); } +// Sets each channel to its saved volume level, scaled by the volume slider. +// Call this function from the MIDI thread only, with exclusive access to shared +// resources. + static void UpdateVolume(void) { int i; @@ -370,6 +423,10 @@ static void UpdateVolume(void) } } +// Sets each channel to the default volume level, scaled by the volume slider. +// Call this function from the MIDI thread only, with exclusive access to shared +// resources. + static void ResetVolume(void) { int i; @@ -380,6 +437,11 @@ static void ResetVolume(void) } } +// Writes "notes off" and "sound off" messages for each channel. Some devices +// may support only one or the other. Held notes (sustained, etc.) are released +// to prevent hanging notes. Call this function from the MIDI thread only, with +// exclusive access to shared resources. + static void SendNotesSoundOff(void) { int i; @@ -391,6 +453,10 @@ static void SendNotesSoundOff(void) } } +// Resets commonly used controllers. This is only for a reset type of "none" for +// devices that don't support SysEx resets. Call this function from the MIDI +// thread only, with exclusive access to shared resources. + static void ResetControllers(void) { int i; @@ -408,6 +474,10 @@ static void ResetControllers(void) } } +// Resets the pitch bend sensitivity for each channel. This must be sent during +// a reset due to an MS GS Wavetable Synth bug. Call this function from the MIDI +// thread only, with exclusive access to shared resources. + static void ResetPitchBendSensitivity(void) { int i; @@ -428,6 +498,10 @@ static void ResetPitchBendSensitivity(void) } } +// Resets the MIDI device. Call this function before each song starts and once +// at shut down. Call this function from the MIDI thread only, with exclusive +// access to shared resources. + static void ResetDevice(void) { MIDI_ResetFallback(); @@ -475,6 +549,12 @@ static void ResetDevice(void) } } +// Normally, volume is controlled by channel volume messages. Roland defined a +// special SysEx message called "part level" that is equivalent to this. MS GS +// Wavetable Synth ignores these messages, but other MIDI devices support them. +// Returns true if there is a match. Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static boolean IsPartLevel(const byte *msg, unsigned int length) { if (length == 10 && @@ -500,6 +580,10 @@ static boolean IsPartLevel(const byte *msg, unsigned int length) return false; } +// Checks if the current SysEx message matches any known SysEx reset message. +// Returns true if there is a match. Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static boolean IsSysExReset(const byte *msg, unsigned int length) { if (length < 5) @@ -603,6 +687,9 @@ static boolean IsSysExReset(const byte *msg, unsigned int length) return false; } +// Writes a MIDI SysEx message. Call this function from the MIDI thread only, +// with exclusive access to shared resources. + static void SendSysExMsg(unsigned int delta_time, const midi_event_t *event) { native_event_t native_event; @@ -659,6 +746,10 @@ static void SendSysExMsg(unsigned int delta_time, const midi_event_t *event) } } +// Writes a MIDI program change message. If applicable, emulates capital tone +// fallback to fix invalid instruments. Call this function from the MIDI thread +// only, with exclusive access to shared resources. + static void SendProgramMsg(unsigned int delta_time, byte channel, byte program, const midi_fallback_t *fallback) { @@ -682,6 +773,9 @@ static void SendProgramMsg(unsigned int delta_time, byte channel, byte program, } } +// Sets a Final Fantasy or RPG Maker loop point. Call this function from the +// MIDI thread only, with exclusive access to shared resources. + static void SetLoopPoint(void) { unsigned int i; @@ -695,6 +789,10 @@ static void SetLoopPoint(void) song.saved_elapsed_time = song.elapsed_time; } +// Checks if the MIDI meta message contains a Final Fantasy loop marker. Call +// this function from the MIDI thread only, with exclusive access to shared +// resources. + static void CheckFFLoop(const midi_event_t *event) { if (event->data.meta.length == sizeof(ff_loopStart) && @@ -710,6 +808,9 @@ static void CheckFFLoop(const midi_event_t *event) } } +// Writes an EMIDI message. Call this function from the MIDI thread only, with +// exclusive access to shared resources. + static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, win_midi_track_t *track, const midi_fallback_t *fallback) { @@ -731,7 +832,7 @@ static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, } else if (flag <= EMIDI_DEVICE_ULTRASOUND) { - track->emidi_device_flags |= 1 << flag; + track->emidi_device_flags |= 1U << flag; track->emidi_designated = true; } } @@ -755,7 +856,7 @@ static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, if (flag <= EMIDI_DEVICE_ULTRASOUND) { - track->emidi_device_flags &= ~(1 << flag); + track->emidi_device_flags &= ~(1U << flag); } } SendNOPMsg(delta_time); @@ -848,6 +949,9 @@ static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, } } +// Writes a MIDI meta message. Call this function from the MIDI thread only, +// with exclusive access to shared resources. + static void SendMetaMsg(unsigned int delta_time, const midi_event_t *event, win_midi_track_t *track) { @@ -876,6 +980,9 @@ static void SendMetaMsg(unsigned int delta_time, const midi_event_t *event, } } +// AddToBuffer function for vanilla (DMX MPU-401) compatibility level. Do not +// call this function directly. See the AddToBuffer function pointer. + static boolean AddToBuffer_Vanilla(unsigned int delta_time, const midi_event_t *event, win_midi_track_t *track) @@ -944,6 +1051,9 @@ static boolean AddToBuffer_Vanilla(unsigned int delta_time, return true; } +// AddToBuffer function for standard and full MIDI compatibility levels. Do not +// call this function directly. See the AddToBuffer function pointer. + static boolean AddToBuffer_Standard(unsigned int delta_time, const midi_event_t *event, win_midi_track_t *track) @@ -1158,10 +1268,19 @@ static boolean AddToBuffer_Standard(unsigned int delta_time, return true; } +// Function pointer determined by the desired MIDI compatibility level. Set +// during initialization by the main thread, then called from the MIDI thread +// only. The calling thread must have exclusive access to the shared resources +// in this function. + static boolean (*AddToBuffer)(unsigned int delta_time, const midi_event_t *event, win_midi_track_t *track) = AddToBuffer_Standard; +// Restarts a song that uses a Final Fantasy or RPG Maker loop point. Call this +// function from the MIDI thread only, with exclusive access to shared +// resources. + static void RestartLoop(void) { unsigned int i; @@ -1175,6 +1294,9 @@ static void RestartLoop(void) song.elapsed_time = song.saved_elapsed_time; } +// Restarts a song that uses standard looping. Call this function from the MIDI +// thread only, with exclusive access to shared resources. + static void RestartTracks(void) { unsigned int i; @@ -1193,6 +1315,12 @@ static void RestartTracks(void) song.elapsed_time = 0; } +// The controllers "EMIDI track exclusion" and "RPG Maker loop point" share the +// same number (CC#111) and are not compatible with each other. As a workaround, +// allow an RPG Maker loop point only if no other EMIDI events are present. Call +// this function from the MIDI thread only, before the song starts, with +// exclusive access to shared resources. + static boolean IsRPGLoop(void) { unsigned int i; @@ -1231,6 +1359,10 @@ static boolean IsRPGLoop(void) return (num_rpg_events == 1 && num_emidi_events == 0); } +// Fills the output buffer with events from the current song and then streams it +// out. Call this function from the MIDI thread only, with exclusive access to +// shared resources. + static void FillBuffer(void) { unsigned int i; @@ -1317,17 +1449,21 @@ static void FillBuffer(void) // The Windows API documentation states: "Applications should not call any // multimedia functions from inside the callback function, as doing so can -// cause a deadlock." We use thread to avoid possible deadlocks. +// cause a deadlock." We use a thread to avoid possible deadlocks. static DWORD WINAPI PlayerProc(void) { - while (true) + boolean keep_going = true; + + while (keep_going) { if (WaitForSingleObject(hBufferReturnEvent, INFINITE) != WAIT_OBJECT_0) { continue; } + // The MIDI thread must have exclusive access to shared resources until + // the end of the current loop iteration or when the thread exits. EnterCriticalSection(&CriticalSection); buffer.position = 0; @@ -1350,8 +1486,8 @@ static DWORD WINAPI PlayerProc(void) break; case STATE_EXIT: - LeaveCriticalSection(&CriticalSection); - return 0; + keep_going = false; + break; case STATE_PLAYING: if (update_volume) @@ -1381,9 +1517,13 @@ static DWORD WINAPI PlayerProc(void) LeaveCriticalSection(&CriticalSection); } + return 0; } +// Restarts the MIDI stream. Call this function from the main thread only, with +// exclusive access to shared resources. + static void StreamStart(void) { MMRESULT mmr; @@ -1397,6 +1537,11 @@ static void StreamStart(void) } } +// Turns off notes but does not release all held ones (use SendNotesSoundOff() +// to prevent hanging notes). The output buffer is returned to the callback +// function and flagged as MHDR_DONE. Call this function from the main thread +// only, with exclusive access to shared resources. + static void StreamStop(void) { MMRESULT mmr; @@ -1578,13 +1723,11 @@ static void I_WIN_ResumeSong(void *handle) } EnterCriticalSection(&CriticalSection); - if (win_midi_state != STATE_PAUSED) + if (win_midi_state == STATE_PAUSED) { - LeaveCriticalSection(&CriticalSection); - return; + win_midi_state = STATE_PLAYING; + StreamStart(); } - win_midi_state = STATE_PLAYING; - StreamStart(); LeaveCriticalSection(&CriticalSection); } @@ -1724,6 +1867,9 @@ static void I_WIN_ShutdownMusic(void) } StreamStop(); + // Don't free the buffer to avoid calling midiOutUnprepareHeader() which + // contains a memory error (detected by ASan). + mmr = midiStreamClose(hMidiStream); if (mmr != MMSYSERR_NOERROR) { @@ -1746,7 +1892,7 @@ static int I_WIN_DeviceList(const char *devices[], int size, int *current_device if (winmm_devices_num == 0 && size > 0) { - devices[0] = "MIDI Mapper"; + devices[0] = "Microsoft MIDI Mapper"; return 1; } From 52f6484b13b58aa01ad8743002094e212520f873 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Mon, 20 Nov 2023 11:02:49 +0700 Subject: [PATCH 19/28] safe exit when console is closed on Windows (#1269) (cherry picked from commit ec600f80c3be34733209270c3772596ef1794991) --- src/i_main.c | 9 ++++++++- win32/win_launcher.c | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/i_main.c b/src/i_main.c index 9ae269841..4bf3d7b6a 100644 --- a/src/i_main.c +++ b/src/i_main.c @@ -17,12 +17,12 @@ // //----------------------------------------------------------------------------- -#include #include "config.h" #include "SDL.h" // haleyjd #include "i_printf.h" +#include "i_system.h" #include "m_argv.h" #include "version.h" @@ -34,6 +34,13 @@ void D_DoomMain(void); +#if defined(WIN_LAUNCHER) +__declspec(dllexport) void Woof_Exit(void) +{ + I_SafeExit(0); +} +#endif + #if defined(WIN_LAUNCHER) __declspec(dllexport) int Woof_Main(int argc, char **argv) #else diff --git a/win32/win_launcher.c b/win32/win_launcher.c index d623fb26b..9b8693132 100644 --- a/win32/win_launcher.c +++ b/win32/win_launcher.c @@ -13,10 +13,25 @@ // #include "SDL.h" +#define WIN32_LEAN_AND_MEAN +#include __declspec(dllexport) extern int Woof_Main(int argc, char **argv); +__declspec(dllexport) extern void Woof_Exit(void); + +BOOL CtrlHandler(DWORD event) +{ + if (event == CTRL_CLOSE_EVENT) + { + Woof_Exit(); + return TRUE; + } + return FALSE; +} int main(int argc, char **argv) { + SetConsoleCtrlHandler((PHANDLER_ROUTINE)(CtrlHandler), TRUE); + return Woof_Main(argc, argv); } From 5de01bf3024332fcde4035e7c264ba479befe499 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Wed, 29 Nov 2023 09:29:34 +0700 Subject: [PATCH 20/28] win midi: bring back unprepare header (#1268) * always unprepare header before modifying buffer * add exception for ASan * sync with Chocolate Doom (cherry picked from commit 4ffb49aedaa2bec0c9fd2288f325a01e8ba603f8) --- src/i_winmusic.c | 92 +++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/src/i_winmusic.c b/src/i_winmusic.c index d89689342..d48f0db33 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -154,6 +154,7 @@ typedef struct byte *data; unsigned int size; unsigned int position; + boolean prepared; } buffer_t; static buffer_t buffer; @@ -199,49 +200,39 @@ static void CALLBACK MidiStreamProc(HMIDIOUT hMidi, UINT uMsg, } } -// Allocates the buffer and prepares the MIDI header. Set during initialization -// by the main thread. BUFFER_INITIAL_SIZE should be large enough to avoid -// reallocation by the MIDI thread during playback, due to a known memory bug -// with midiOutUnprepareHeader() (detected by ASan). The calling thread must -// have exclusive access to the shared resources in this function. +// Unprepare MIDI header. The calling thread must have exclusive access to the +// shared resources in this function. -static void PrepareHeader(void) +static void UnprepareHeader(void) { + // Avoid ASan detection. Commentary by Microsoft: "It looks like + // midiOutPrepareHeader() allocates with HeapAlloc(), and then + // midiOutUnprepareHeader() deallocates with GlobalFree(GlobalHandle + // (...)). By design, this kind of allocator mismatch is an issue that ASan + // is designed to catch. It is theoretically possible for us to support + // this kind of code, but it’s not very high priority since it is undefined + // behavior, though it happens to work right now outside of ASan." + // https://developercommunity.visualstudio.com/t/1597288 + +#ifndef __SANITIZE_ADDRESS__ MIDIHDR *hdr = &MidiStreamHdr; MMRESULT mmr; - hdr->lpData = (LPSTR)buffer.data; - hdr->dwBytesRecorded = 0; - hdr->dwBufferLength = buffer.size; - mmr = midiOutPrepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); + mmr = midiOutUnprepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { - MidiError("midiOutPrepareHeader", mmr); + MidiError("midiOutUnprepareHeader", mmr); } +#endif } +// Allocate buffer. The calling thread must have exclusive access to the shared +// resources in this function. + static void AllocateBuffer(const unsigned int size) { - if (buffer.data) - { - MIDIHDR *hdr = &MidiStreamHdr; - MMRESULT mmr; - - // Windows doesn't always immediately clear the MHDR_INQUEUE flag, even - // after midiStreamStop() is called. There doesn't seem to be any side - // effect to just forcing the flag off. - hdr->dwFlags &= ~MHDR_INQUEUE; - mmr = midiOutUnprepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); - if (mmr != MMSYSERR_NOERROR) - { - MidiError("midiOutUnprepareHeader", mmr); - } - } - buffer.size = PADDED_SIZE(size); buffer.data = I_Realloc(buffer.data, buffer.size); - - PrepareHeader(); } // Pads the buffer with zeros so that an integral number of DWORDs are stored. @@ -260,6 +251,12 @@ static void WriteBufferPad(void) static void WriteBuffer(const byte *ptr, unsigned int size) { + if (buffer.prepared) + { + UnprepareHeader(); + buffer.prepared = false; + } + if (buffer.position + size >= buffer.size) { AllocateBuffer(size + buffer.size * 2); @@ -277,8 +274,22 @@ static void StreamOut(void) MIDIHDR *hdr = &MidiStreamHdr; MMRESULT mmr; + memset(hdr, 0, sizeof(*hdr)); hdr->lpData = (LPSTR)buffer.data; hdr->dwBytesRecorded = buffer.position; + hdr->dwBufferLength = buffer.size; + + // Reset buffer position even if midiStreamOut fails. + buffer.position = 0; + + mmr = midiOutPrepareHeader((HMIDIOUT)hMidiStream, hdr, sizeof(MIDIHDR)); + if (mmr != MMSYSERR_NOERROR) + { + MidiError("midiOutPrepareHeader", mmr); + return; + } + + buffer.prepared = true; mmr = midiStreamOut(hMidiStream, hdr, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) @@ -1466,8 +1477,6 @@ static DWORD WINAPI PlayerProc(void) // the end of the current loop iteration or when the thread exits. EnterCriticalSection(&CriticalSection); - buffer.position = 0; - switch (win_midi_state) { case STATE_STARTUP: @@ -1628,14 +1637,7 @@ static boolean I_WIN_InitMusic(int device) return false; } - if (buffer.data == NULL) - { - AllocateBuffer(BUFFER_INITIAL_SIZE); - } - else - { - PrepareHeader(); - } + AllocateBuffer(BUFFER_INITIAL_SIZE); hBufferReturnEvent = CreateEvent(NULL, FALSE, FALSE, NULL); hStoppedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -1867,8 +1869,11 @@ static void I_WIN_ShutdownMusic(void) } StreamStop(); - // Don't free the buffer to avoid calling midiOutUnprepareHeader() which - // contains a memory error (detected by ASan). + if (buffer.prepared) + { + UnprepareHeader(); + buffer.prepared = false; + } mmr = midiStreamClose(hMidiStream); if (mmr != MMSYSERR_NOERROR) @@ -1877,6 +1882,11 @@ static void I_WIN_ShutdownMusic(void) } hMidiStream = NULL; + free(buffer.data); + buffer.data = NULL; + buffer.size = 0; + buffer.position = 0; + CloseHandle(hBufferReturnEvent); CloseHandle(hStoppedEvent); DeleteCriticalSection(&CriticalSection); From ec2dc7797ca0bb766067a61410ee3c02d611add3 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Wed, 29 Nov 2023 16:38:01 +0700 Subject: [PATCH 21/28] load complevel from save (#1294) (cherry picked from commit 525f3441709579c3cd5c237bff313a69b079006c) --- src/g_game.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1fcf83983..c93121d0a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2028,7 +2028,6 @@ static void G_DoLoadGame(void) int length, i; char vcheck[VERSIONSIZE]; uint64_t checksum; - byte saveg_complevel = 203; int tmp_compat, tmp_skill, tmp_epi, tmp_map; I_SetFastdemoTimer(false); @@ -2070,7 +2069,11 @@ static void G_DoLoadGame(void) if (saveg_compat > saveg_woof510) { - saveg_complevel = *save_p++; + demo_version = *save_p++; + } + else + { + demo_version = 203; } // killough 2/14/98: load compatibility mode @@ -2115,7 +2118,7 @@ static void G_DoLoadGame(void) idmusnum = *(signed char *) save_p++; /* cph 2001/05/23 - Must read options before we set up the level */ - if (saveg_complevel == 221) + if (mbf21) G_ReadOptionsMBF21(save_p); else G_ReadOptions(save_p); @@ -2127,7 +2130,7 @@ static void G_DoLoadGame(void) // killough 11/98: move down to here /* cph - MBF needs to reread the savegame options because G_InitNew * rereads the WAD options. The demo playback code does this too. */ - if (saveg_complevel == 221) + if (mbf21) save_p = G_ReadOptionsMBF21(save_p); else save_p = G_ReadOptions(save_p); From bb983387360ee17303e083a722d32ca0834faf30 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Mon, 11 Dec 2023 17:19:52 +0700 Subject: [PATCH 22/28] update CHANGELOG.md --- CHANGELOG.md | 45 +++++++++------------------------------------ 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5397fbf49..cc0eb15ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,39 +1,12 @@ **New Features and Improvements** -* 3D audio support (@ceski-1) - - Stereo and up to 7.1 surround sound. - - HRTF mode ("Headphones mode" in the General menu). - - Air absorption and Doppler effects settings in the config. -* PC Speaker emulation sound module (taken from Chocolate Doom). -* Various HUD additions and fixes: - - Optional widescreen widget arrangement. - - Bring back three-lined coords/stats widgets. - - Optionally draw bar graphs in Boom HUD widgets. - - Ability to position "message" and "secret" text widgets by WOOFHUD lump. -* Add obituaries from ZDoom, enabled by default (Options->Setup->Messages->Show Obituaries option). -* Add support for XGLN/ZGLN nodes. -* Color console messages and optional verbosity level (`default_verbosity` in config). -* Allow separate key binding for the numeric keypad. -* Replace and extend crosshair patches with the shaded variants from Nugget Doom. -* Ignore DMX sound padding (@ceski-1). -* Implement sky top color algorithm from Eternity Engine (@tomas7770). -* Attempt to play `demo4` also for Doom 2 (Final Doom). -* Clean screenshots are taken with the base palette. +* Fluidsynth: Support relative paths in soundfont directory. +* Windows Native MIDI: Improvements to shutdown sequence, fix stability issues. +* Load complevel from save files. **Bug Fixes** -* Fix a savegame loading crash related to the use of MUSINFO. -* Consistently rename `-nodehlump` command line parameter to `-nodeh`. -* Fix mouselook/padlook checks for direct vertical aiming (@ceski-1). -* Fix sector movement doesn't render sometimes using Boom fake floors (Line action 242), when uncapped framerate is enabled. -* Fix automap marks in non-follow mode. -* Various fixes to weapon lowering and switching animation (thanks to @MrAlaux). -* Disable returning to the episodes menu if only one episode is present. -* Fix ESC reset with mini-thermo menu items affects multi-choice select items. -* Reset menu string editing with ESC. -* Fix `PIT_ApplyTorque` when line has no length (from DSDA-Doom). -* Reorder sprites rendering, so that objects with higher map indices appear in front (thanks to @JNechaevsky). -* Various brightmaps fixes (@JNechaevsky). -* Skip "hidden" files in ZIP archives (fixes opening archives created by MacOS). -* Reinitialize automap if screen size changes while it is enabled (thanks to @MrAlaux). - -**Miscellaneous** -* Add Linux distribution package in AppImage format (@qurious-pixel). +* Fix zero length sound lumps (fix "Evirenity II RC1.wad"). +* Fix MUSINFO fails sometimes when loading saves (thanks to @DRON12261). +* BRGHTMPS: Fix parsing lump names, crash when overwrite brightmaps (thanks to @andrikpowell). +* Fix parsing lump names in MUSINFO. +* Fix "you need a key" messages in multiplayer. +* Fix WAD paths in setup tool on Windows. From 97c42f268dae3dae7531595b43376504cce48aec Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Mon, 11 Dec 2023 17:20:10 +0700 Subject: [PATCH 23/28] update metainfo --- data/io.github.fabiangreffrath.woof.metainfo.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/io.github.fabiangreffrath.woof.metainfo.xml b/data/io.github.fabiangreffrath.woof.metainfo.xml index bd06406c9..39068ecb9 100644 --- a/data/io.github.fabiangreffrath.woof.metainfo.xml +++ b/data/io.github.fabiangreffrath.woof.metainfo.xml @@ -48,6 +48,7 @@ intense + From feee7644bcc7252ee19d97946409d6e26c78a7a3 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Tue, 12 Dec 2023 00:03:04 +0700 Subject: [PATCH 24/28] non power of two skies (#1321) (cherry picked from commit cc079ce043391a8d044260e6d36182ea74f2e1ca) --- src/r_plane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_plane.c b/src/r_plane.c index 07781be80..004a786a1 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -419,8 +419,8 @@ static void do_draw_plane(visplane_t *pl) for (x = pl->minx; (dc_x = x) <= pl->maxx; x++) if ((dc_yl = pl->top[x]) != USHRT_MAX && dc_yl <= (dc_yh = pl->bottom[x])) { - dc_source = R_GetColumn(texture, ((an + xtoskyangle[x])^flip) >> - ANGLETOSKYSHIFT); + dc_source = R_GetColumnMod(texture, ((an + xtoskyangle[x])^flip) >> + ANGLETOSKYSHIFT); colfunc(); } From 04fd0a8f73bb33c4788ae2593316125fe28460d2 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 13 Oct 2023 13:53:27 +0700 Subject: [PATCH 25/28] redo interpolation of scrollers animation (#1219) * add old* fields * remove R_InterpolateTextureOffets() function, move code to r_bsp.c (cherry picked from commit 54eb621036c0e5b9abe57fec8aad2eb3f3908367) --- src/p_saveg.c | 4 -- src/p_setup.c | 8 ++++ src/p_spec.c | 108 +++++++++----------------------------------------- src/p_spec.h | 4 -- src/r_bsp.c | 25 ++++++++++++ src/r_defs.h | 8 ++++ src/r_main.c | 3 -- 7 files changed, 59 insertions(+), 101 deletions(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index a0d7f1616..5f76de52a 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2565,8 +2565,6 @@ void P_UnArchiveSpecials (void) { byte tclass; - P_FreeScrollers(); - // read in saved thinkers while ((tclass = saveg_read8()) != tc_endspecials) // killough 2/14/98 switch (tclass) @@ -2686,8 +2684,6 @@ void P_UnArchiveSpecials (void) saveg_read_scroll_t(scroll); scroll->thinker.function.p1 = (actionf_p1)T_Scroll; P_AddThinker(&scroll->thinker); - if (scroll->type >= sc_side && scroll->type <= sc_ceiling) - P_AddScroller(scroll); break; } diff --git a/src/p_setup.c b/src/p_setup.c index 9d6b62c72..e8196ec9e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -304,8 +304,12 @@ void P_LoadSectors (int lump) // killough 3/7/98: ss->floor_xoffs = 0; ss->floor_yoffs = 0; // floor and ceiling flats offsets + ss->old_floor_xoffs = ss->base_floor_xoffs = 0; + ss->old_floor_yoffs = ss->base_floor_yoffs = 0; ss->ceiling_xoffs = 0; ss->ceiling_yoffs = 0; + ss->old_ceiling_xoffs = ss->base_ceiling_xoffs = 0; + ss->old_ceiling_yoffs = ss->base_ceiling_yoffs = 0; ss->heightsec = -1; // sector used to get floor and ceiling height ss->floorlightsec = -1; // sector used to get floor lighting // killough 3/7/98: end changes @@ -329,6 +333,7 @@ void P_LoadSectors (int lump) // [FG] inhibit sector interpolation during the 0th gametic ss->oldceilgametic = -1; ss->oldfloorgametic = -1; + ss->oldscrollgametic = -1; } Z_Free (data); @@ -579,8 +584,11 @@ void P_LoadSideDefs2(int lump) sd->textureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<oldtextureoffset = sd->textureoffset; + sd->oldrowoffset = sd->rowoffset; sd->basetextureoffset = sd->textureoffset; sd->baserowoffset = sd->rowoffset; + sd->oldgametic = -1; // killough 4/4/98: allow sidedef texture names to be overloaded // killough 4/11/98: refined to allow colormaps to work as wall diff --git a/src/p_spec.c b/src/p_spec.c index 354bf48a6..78f359a84 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2642,6 +2642,12 @@ void T_Scroll(scroll_t *s) case sc_side: // killough 3/7/98: Scroll wall texture side = sides + s->affectee; + if (side->oldgametic != gametic) + { + side->oldtextureoffset = side->basetextureoffset; + side->oldrowoffset = side->baserowoffset; + side->oldgametic = gametic; + } side->basetextureoffset += dx; side->baserowoffset += dy; side->textureoffset = side->basetextureoffset; @@ -2650,6 +2656,12 @@ void T_Scroll(scroll_t *s) case sc_floor: // killough 3/7/98: Scroll floor texture sec = sectors + s->affectee; + if (sec->oldscrollgametic != gametic) + { + sec->old_floor_xoffs = sec->base_floor_xoffs; + sec->old_floor_yoffs = sec->base_floor_yoffs; + sec->oldscrollgametic = gametic; + } sec->base_floor_xoffs += dx; sec->base_floor_yoffs += dy; sec->floor_xoffs = sec->base_floor_xoffs; @@ -2658,6 +2670,12 @@ void T_Scroll(scroll_t *s) case sc_ceiling: // killough 3/7/98: Scroll ceiling texture sec = sectors + s->affectee; + if (sec->oldscrollgametic != gametic) + { + sec->old_ceiling_xoffs = sec->base_ceiling_xoffs; + sec->old_ceiling_yoffs = sec->base_ceiling_yoffs; + sec->oldscrollgametic = gametic; + } sec->base_ceiling_xoffs += dx; sec->base_ceiling_yoffs += dy; sec->ceiling_xoffs = sec->base_ceiling_xoffs; @@ -2695,91 +2713,6 @@ void T_Scroll(scroll_t *s) } } -// [crispy] smooth texture scrolling - -static int maxscrollers, numscrollers; -static scroll_t **scrollers; - -void P_AddScroller (scroll_t *s) -{ - if (numscrollers == maxscrollers) - { - maxscrollers = maxscrollers ? 2 * maxscrollers : 32; - scrollers = I_Realloc(scrollers, maxscrollers * sizeof(*scrollers)); - } - scrollers[numscrollers++] = s; -} - -void P_FreeScrollers (void) -{ - maxscrollers = 0; - numscrollers = 0; - if (scrollers) - free(scrollers); - scrollers = NULL; -} - -void R_InterpolateTextureOffsets (void) -{ - if (uncapped && leveltime > oldleveltime && !frozen_mode) - { - int i; - - for (i = 0; i < numscrollers; i++) - { - scroll_t *s = scrollers[i]; - int dx, dy; - - if (s->accel) - { - dx = s->vdx; - dy = s->vdy; - } - else - { - dx = s->dx; - dy = s->dy; - - if (s->control != -1) - { // compute scroll amounts based on a sector's height changes - fixed_t height = sectors[s->control].floorheight + - sectors[s->control].ceilingheight; - fixed_t delta = height - s->last_height; - dx = FixedMul(dx, delta); - dy = FixedMul(dy, delta); - } - } - - if (!dx && !dy) - continue; - - switch(s->type) - { - side_t *side; - sector_t *sec; - - case sc_side: - side = sides + s->affectee; - side->textureoffset = side->basetextureoffset + FixedMul(dx, fractionaltic); - side->rowoffset = side->baserowoffset + FixedMul(dy, fractionaltic); - break; - case sc_floor: - sec = sectors + s->affectee; - sec->floor_xoffs = sec->base_floor_xoffs + FixedMul(dx, fractionaltic); - sec->floor_yoffs = sec->base_floor_yoffs + FixedMul(dy, fractionaltic); - break; - case sc_ceiling: - sec = sectors + s->affectee; - sec->ceiling_xoffs = sec->base_ceiling_xoffs + FixedMul(dx, fractionaltic); - sec->ceiling_yoffs = sec->base_ceiling_yoffs + FixedMul(dy, fractionaltic); - break; - default: - break; - } - } - } -} - // // Add_Scroller() // @@ -2813,9 +2746,6 @@ static void Add_Scroller(int type, fixed_t dx, fixed_t dy, sectors[control].floorheight + sectors[control].ceilingheight; s->affectee = affectee; P_AddThinker(&s->thinker); - - if (type >= sc_side && type <= sc_ceiling) - P_AddScroller(s); } // Adds wall scroller. Scroll amount is rotated with respect to wall's @@ -2855,8 +2785,6 @@ static void P_SpawnScrollers(void) int i; line_t *l = lines; - P_FreeScrollers(); - for (i=0;idx >> SCROLL_SHIFT; // direction and speed of scrolling diff --git a/src/p_spec.h b/src/p_spec.h index f01c33c1e..df54b965e 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -810,10 +810,6 @@ boolean P_WasSecret(sector_t *sec); void P_ChangeSwitchTexture(line_t *line, int useAgain); -void P_FreeScrollers(void); - -void P_AddScroller(scroll_t *s); - //////////////////////////////////////////////////////////////// // // Linedef and sector special action function prototypes diff --git a/src/r_bsp.c b/src/r_bsp.c index 2d8f4f6e2..dc6b83549 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -294,6 +294,18 @@ static void R_MaybeInterpolateSector(sector_t* sector) { sector->interpceilingheight = sector->ceilingheight; } + + if (sector->oldscrollgametic == gametic - 1) + { + sector->floor_xoffs = sector->old_floor_xoffs + + FixedMul(sector->base_floor_xoffs - sector->old_floor_xoffs, fractionaltic); + sector->floor_yoffs = sector->old_floor_yoffs + + FixedMul(sector->base_floor_yoffs - sector->old_floor_yoffs, fractionaltic); + sector->ceiling_xoffs = sector->old_ceiling_xoffs + + FixedMul(sector->base_ceiling_xoffs - sector->old_ceiling_xoffs, fractionaltic); + sector->ceiling_yoffs = sector->old_ceiling_yoffs + + FixedMul(sector->base_ceiling_yoffs - sector->old_ceiling_yoffs, fractionaltic); + } } else { @@ -302,6 +314,17 @@ static void R_MaybeInterpolateSector(sector_t* sector) } } +static void R_MaybeInterpolateTextureOffsets(side_t *side) +{ + if (uncapped && side->oldgametic == gametic - 1) + { + side->textureoffset = side->oldtextureoffset + + FixedMul(side->basetextureoffset - side->oldtextureoffset, fractionaltic); + side->rowoffset = side->oldrowoffset + + FixedMul(side->baserowoffset - side->oldrowoffset, fractionaltic); + } +} + // // R_AddLine // Clips the given segment @@ -372,6 +395,8 @@ static void R_AddLine (seg_t *line) if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness return; + R_MaybeInterpolateTextureOffsets(line->sidedef); + backsector = line->backsector; // Single sided line? diff --git a/src/r_defs.h b/src/r_defs.h index 1ebe2e123..0a81f47e2 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -153,6 +153,7 @@ typedef struct // if old values were not updated recently. int oldceilgametic; int oldfloorgametic; + int oldscrollgametic; // [AM] Interpolated floor and ceiling height. // Calculated once per tic and used inside @@ -162,8 +163,12 @@ typedef struct fixed_t base_floor_xoffs; fixed_t base_floor_yoffs; + fixed_t old_floor_xoffs; + fixed_t old_floor_yoffs; fixed_t base_ceiling_xoffs; fixed_t base_ceiling_yoffs; + fixed_t old_ceiling_xoffs; + fixed_t old_ceiling_yoffs; } sector_t; // @@ -186,8 +191,11 @@ typedef struct int special; // [crispy] smooth texture scrolling + fixed_t oldtextureoffset; + fixed_t oldrowoffset; fixed_t basetextureoffset; fixed_t baserowoffset; + int oldgametic; } side_t; diff --git a/src/r_main.c b/src/r_main.c index fd3a3f77e..c05b51673 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -816,9 +816,6 @@ void R_RenderPlayerView (player_t* player) // check for new console commands. NetUpdate (); - // [crispy] smooth texture scrolling - R_InterpolateTextureOffsets(); - // The head node is the last node output. R_RenderBSPNode (numnodes-1); From a18b90878e019aa87db5e315e6e7c81b431ba6e6 Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Fri, 20 Oct 2023 14:04:10 +0700 Subject: [PATCH 26/28] do not apply the single color fill to vertically scrolling skies (#1231) (cherry picked from commit 2473a6ef9302603f9bdd947cbf5c235f9808684e) --- src/r_plane.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/r_plane.c b/src/r_plane.c index 004a786a1..f8cdc25a0 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -338,6 +338,7 @@ static void do_draw_plane(visplane_t *pl) { int texture; angle_t an, flip; + boolean vertically_scrolling = false; // killough 10/98: allow skies to come from sidedefs. // Allows scrolling and/or animated skies, as well as @@ -354,6 +355,9 @@ static void do_draw_plane(visplane_t *pl) // Sky transferred from first sidedef const side_t *s = *l->sidenum + sides; + if (s->baserowoffset - s->oldrowoffset) + vertically_scrolling = true; + // Texture comes from upper texture of reference sidedef texture = texturetranslation[s->toptexture]; @@ -401,7 +405,7 @@ static void do_draw_plane(visplane_t *pl) dc_texturemid = dc_texturemid * dc_texheight / SKYSTRETCH_HEIGHT; colfunc = R_DrawColumn; } - else + else if (!vertically_scrolling) { // Make sure the fade-to-color effect doesn't happen too early fixed_t diff = dc_texturemid - ORIGHEIGHT / 2 * FRACUNIT; From 44198a508c1998ccbbcd8ed21b2a2fa5d7f243ce Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Tue, 12 Dec 2023 00:13:41 +0700 Subject: [PATCH 27/28] update CHANGELOG.md --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0eb15ad..177cdc9f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ **New Features and Improvements** -* Fluidsynth: Support relative paths in soundfont directory. +* Support for non power of two sky textures sizes (fix sky in Sigil II). * Windows Native MIDI: Improvements to shutdown sequence, fix stability issues. +* Fluidsynth: Support relative paths in soundfont directory. * Load complevel from save files. **Bug Fixes** -* Fix zero length sound lumps (fix "Evirenity II RC1.wad"). +* Ignore zero length sound lumps (fix "Evirenity II RC1.wad"). +* Do not apply the single color fill to vertically scrolling skies. * Fix MUSINFO fails sometimes when loading saves (thanks to @DRON12261). * BRGHTMPS: Fix parsing lump names, crash when overwrite brightmaps (thanks to @andrikpowell). * Fix parsing lump names in MUSINFO. From d896eba4a367e816160bf25f67746caf20a7c32a Mon Sep 17 00:00:00 2001 From: Roman Fomin Date: Tue, 12 Dec 2023 00:39:17 +0700 Subject: [PATCH 28/28] release Woof! 12.0.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b8a44f59..c72902f8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ set(X_VCPKG_APPLOCAL_DEPS_INSTALL ON) cmake_minimum_required(VERSION 3.9) project("Woof" - VERSION 12.0.0 + VERSION 12.0.1 LANGUAGES C) set(CMAKE_C_STANDARD 99)