diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d76ab753f..fdee9ee083 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: build +name: Continuous Integration on: push: @@ -11,32 +11,132 @@ on: - "master" paths-ignore: - "**.md" - - "**.yml" workflow_dispatch: jobs: build: - runs-on: ${{ matrix.os }} - + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + defaults: + run: + shell: ${{ matrix.config.shell }} strategy: + fail-fast: false matrix: - compiler: [clang, gcc] - os: [ubuntu-latest] + config: + - name: Linux GCC + os: ubuntu-latest + compiler: gcc + shell: bash + + - name: macOS Clang + os: macos-latest + compiler: clang + shell: bash + + - name: MSYS2 UCRT64 + os: windows-latest + compiler: gcc + shell: 'msys2 {0}' + msystem: ucrt64 + msys-env: mingw-w64-ucrt-x86_64 + + - name: MSYS2 MINGW32 + os: windows-latest + compiler: gcc + shell: 'msys2 {0}' + msystem: mingw32 + msys-env: mingw-w64-i686 steps: - - name: Install dependencies - run: sudo apt-get update && sudo apt-get install libfluidsynth-dev libpng-dev libsamplerate0-dev libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev ninja-build + - name: Install pandoc + if: runner.os == 'Windows' + shell: cmd + run: choco install pandoc + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install \ + libsdl2-dev \ + libsdl2-mixer-dev \ + libsdl2-net-dev \ + libpng-dev \ + libsamplerate0-dev \ + libfluidsynth-dev + + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: | + brew install \ + automake \ + dylibbundler \ + sdl2 \ + sdl2_mixer \ + sdl2_net \ + libpng \ + libsamplerate \ + fluid-synth + + - name: Install dependencies (MSYS2) + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.config.msystem }} + update: false + path-type: inherit + install: >- + autotools + zip + ${{ matrix.config.msys-env }}-gcc + ${{ matrix.config.msys-env }}-SDL2 + ${{ matrix.config.msys-env }}-SDL2_mixer + ${{ matrix.config.msys-env }}-SDL2_net + ${{ matrix.config.msys-env }}-libpng + ${{ matrix.config.msys-env }}-libsamplerate + ${{ matrix.config.msys-env }}-fluidsynth - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - - name: CMake + - name: Configure env: - CC: ${{ matrix.compiler }} - run: cmake -S . -B build -G Ninja && ninja -C build + CC: ${{ matrix.config.compiler }} + run: | + ./autogen.sh - - name: Make - env: - CC: ${{ matrix.compiler }} - run: $GITHUB_WORKSPACE/.travis.sh + - name: Build + run: make -j4 + + - name: Test + run: | + PREFIX=`sed -n '/PROGRAM_PREFIX/p' ${PWD}/config.h | cut -d '"' -f 2` + make -j4 -C quickcheck check SOURCE_PORT=$PWD/src/${PREFIX}doom + + - name: Package (macOS) + if: runner.os == 'macOS' + run: | + cd pkg/osx + make + + - name: Package (MSYS2) + if: runner.os == 'Windows' + run: | + cd pkg/win32 + make + + - name: Upload artifacts (macOS) + if: runner.os == 'macOS' + uses: actions/upload-artifact@v3 + with: + name: macOS + path: pkg/osx/*.dmg + + - name: Upload artifacts (MSYS2) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v3 + with: + name: Win-${{ matrix.config.msystem }} + path: pkg/win32/*.zip diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml deleted file mode 100644 index bd2ea3a850..0000000000 --- a/.github/workflows/msys2.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: msys2 - -on: - push: - branches: - - "master" - tags: - - "*" - paths-ignore: - - "**.md" - pull_request: - branches: - - "master" - paths-ignore: - - "**.md" - - "**.yml" - -jobs: - build: - runs-on: windows-latest - - defaults: - run: - shell: msys2 {0} - - strategy: - fail-fast: false - matrix: - include: - - { sys: mingw64, env: x86_64 } - - { sys: mingw32, env: i686 } - - steps: - - name: Install pandoc - shell: cmd - run: choco install pandoc - - - name: Extract Version Number - shell: bash - run: echo "VERSION=${GITHUB_REF##*-}" >> $GITHUB_ENV - - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - update: true - msystem: ${{ matrix.sys }} - path-type: inherit - install: >- - autotools - zip - mingw-w64-${{matrix.env}}-gcc - mingw-w64-${{matrix.env}}-SDL2 - mingw-w64-${{matrix.env}}-SDL2_mixer - mingw-w64-${{matrix.env}}-SDL2_net - mingw-w64-${{matrix.env}}-libpng - mingw-w64-${{matrix.env}}-libsamplerate - mingw-w64-${{matrix.env}}-fluidsynth - - - uses: actions/checkout@v3 - - - name: Configure - run: | - ./autogen.sh - - - name: Build - run: make -j4 - - - name: Package - run: | - cd pkg/win32 - make - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Win-${{ matrix.env }} - path: pkg/win32/*.zip - - - name: Release - if: ${{ contains(github.ref, 'tags') }} - uses: ncipollo/release-action@v1 - with: - name: Crispy Doom ${{ env.VERSION }} - bodyFile: CHANGELOG.md - allowUpdates: true - artifacts: pkg/win32/*.zip diff --git a/pkg/osx/GNUmakefile b/pkg/osx/GNUmakefile index 1647954b81..188490d915 100644 --- a/pkg/osx/GNUmakefile +++ b/pkg/osx/GNUmakefile @@ -12,10 +12,17 @@ DMG=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).dmg TOPLEVEL=../.. TOPLEVEL_DOCS=$(patsubst %,../../%,$(DOC_FILES)) +ifeq (, $(shell which dylibbundler)) + CP=./cp-with-libs +else + CP=cp +endif + # DMG file containing package: $(DMG) : tmp.dmg rm -f $@ + sleep 2 # pause to workaround occasional "Can’t get disk" (-1728) issues ./dmgfix "$(realpath tmp.dmg)" "$(PACKAGE_STRING)" "$(PACKAGE_NAME).app" hdiutil convert -format UDBZ -o $@ tmp.dmg rm -f tmp.dmg @@ -62,17 +69,26 @@ $(STAGING_DIR): launcher $(TOPLEVEL_DOCS) app.icns wadfile.icns cp launcher "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/launcher" - ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)doom "$(APP_BIN_DIR)" + $(CP) $(TOPLEVEL)/src/$(PROGRAM_PREFIX)doom "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)doom" - ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)heretic "$(APP_BIN_DIR)" + $(CP) $(TOPLEVEL)/src/$(PROGRAM_PREFIX)heretic "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)heretic" - ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)hexen "$(APP_BIN_DIR)" + $(CP) $(TOPLEVEL)/src/$(PROGRAM_PREFIX)hexen "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)hexen" - ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)strife "$(APP_BIN_DIR)" + $(CP) $(TOPLEVEL)/src/$(PROGRAM_PREFIX)strife "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)strife" - ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)setup "$(APP_BIN_DIR)" + $(CP) $(TOPLEVEL)/src/$(PROGRAM_PREFIX)setup "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)setup" + -dylibbundler --bundle-deps --overwrite-files \ + --install-path "@executable_path/" \ + --dest-dir "$(APP_BIN_DIR)" \ + --fix-file "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)doom" \ + --fix-file "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)heretic" \ + --fix-file "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)hexen" \ + --fix-file "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)strife" \ + --fix-file "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)setup" + $(TOPLEVEL)/man/simplecpp -DPRECOMPILED -D__MACOSX__ \ -DDOOM -DHERETIC -DHEXEN -DSTRIFE \ < $(TOPLEVEL)/man/INSTALL.template \ diff --git a/src/doom/am_map.c b/src/doom/am_map.c index acbd454b2b..23ee526a1e 100644 --- a/src/doom/am_map.c +++ b/src/doom/am_map.c @@ -249,8 +249,6 @@ static mline_t square_mark[] = { static int cheating = 0; static int grid = 0; -static int leveljuststarted = 1; // kluge until AM_LevelInit() is called - boolean automapactive = false; //static int finit_width = SCREENWIDTH; //static int finit_height = SCREENHEIGHT - (ST_HEIGHT << crispy->hires); @@ -630,8 +628,6 @@ void AM_LevelInit(boolean reinit) // [crispy] Only need to precalculate color lookup tables once static int precalc_once; - leveljuststarted = 0; - f_x = f_y = 0; f_w = SCREENWIDTH; f_h = SCREENHEIGHT - (ST_HEIGHT << crispy->hires); diff --git a/src/doom/d_main.c b/src/doom/d_main.c index 8b37c574e2..b6e30ef4ea 100644 --- a/src/doom/d_main.c +++ b/src/doom/d_main.c @@ -124,9 +124,6 @@ boolean storedemo; // If true, the main game loop has started. boolean main_loop_started = false; -char wadfile[1024]; // primary wad file -char mapdir[1024]; // directory of development maps - int show_endoom = 0; // [crispy] disable int show_diskicon = 1; @@ -2111,7 +2108,7 @@ void D_DoomMain (void) I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); - I_InitSound(true); + I_InitSound(doom); I_InitMusic(); // [crispy] check for SSG resources diff --git a/src/doom/g_game.c b/src/doom/g_game.c index 82d71dce89..8bc3d565fa 100644 --- a/src/doom/g_game.c +++ b/src/doom/g_game.c @@ -36,6 +36,7 @@ #include "m_misc.h" #include "m_menu.h" #include "m_random.h" +#include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "i_input.h" @@ -492,11 +493,20 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) // fprintf(stderr, "strafe left\n"); side -= sidemove[speed]; } - if (joyxmove > 0) - side += sidemove[speed]; - if (joyxmove < 0) - side -= sidemove[speed]; - + if (use_analog && joyxmove) + { + joyxmove = joyxmove * joystick_move_sensitivity / 10; + joyxmove = (joyxmove > FRACUNIT) ? FRACUNIT : joyxmove; + joyxmove = (joyxmove < -FRACUNIT) ? -FRACUNIT : joyxmove; + side += FixedMul(sidemove[speed], joyxmove); + } + else if (joystick_move_sensitivity) + { + if (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + } } else { @@ -504,10 +514,21 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) cmd->angleturn += angleturn[tspeed]; - if (joyxmove > 0) - cmd->angleturn -= angleturn[tspeed]; - if (joyxmove < 0) - cmd->angleturn += angleturn[tspeed]; + if (use_analog && joyxmove) + { + // Cubic response curve allows for finer control when stick + // deflection is small. + joyxmove = FixedMul(FixedMul(joyxmove, joyxmove), joyxmove); + joyxmove = joyxmove * joystick_turn_sensitivity / 10; + cmd->angleturn -= FixedMul(angleturn[1], joyxmove); + } + else if (joystick_turn_sensitivity) + { + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } } if (gamekeydown[key_up] || gamekeydown[key_alt_up]) // [crispy] add key_alt_* @@ -521,27 +542,50 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) forward -= forwardmove[speed]; } - if (joyymove < 0) - forward += forwardmove[speed]; - if (joyymove > 0) - forward -= forwardmove[speed]; + if (use_analog && joyymove) + { + joyymove = joyymove * joystick_move_sensitivity / 10; + joyymove = (joyymove > FRACUNIT) ? FRACUNIT : joyymove; + joyymove = (joyymove < -FRACUNIT) ? -FRACUNIT : joyymove; + forward -= FixedMul(forwardmove[speed], joyymove); + } + else if (joystick_move_sensitivity) + { + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + } if (gamekeydown[key_strafeleft] || gamekeydown[key_alt_strafeleft] // [crispy] add key_alt_* || joybuttons[joybstrafeleft] - || mousebuttons[mousebstrafeleft] - || joystrafemove < 0) + || mousebuttons[mousebstrafeleft]) { side -= sidemove[speed]; } if (gamekeydown[key_straferight] || gamekeydown[key_alt_straferight] // [crispy] add key_alt_* || joybuttons[joybstraferight] - || mousebuttons[mousebstraferight] - || joystrafemove > 0) + || mousebuttons[mousebstraferight]) { side += sidemove[speed]; } + if (use_analog && joystrafemove) + { + joystrafemove = joystrafemove * joystick_move_sensitivity / 10; + joystrafemove = (joystrafemove > FRACUNIT) ? FRACUNIT : joystrafemove; + joystrafemove = (joystrafemove < -FRACUNIT) ? -FRACUNIT : joystrafemove; + side += FixedMul(sidemove[speed], joystrafemove); + } + else if (joystick_move_sensitivity) + { + if (joystrafemove < 0) + side -= sidemove[speed]; + if (joystrafemove > 0) + side += sidemove[speed]; + } + // [crispy] look up/down/center keys if (crispy->freelook) { diff --git a/src/doom/st_stuff.c b/src/doom/st_stuff.c index 58574ff1da..9b5294b9e1 100644 --- a/src/doom/st_stuff.c +++ b/src/doom/st_stuff.c @@ -81,13 +81,6 @@ extern boolean inhelpscreens; // [crispy] prevent palette changes // Radiation suit, green shift. #define RADIATIONPAL 13 -// N/256*100% probability -// that the normal face state will change -#define ST_FACEPROBABILITY 96 - -// For Responder -#define ST_TOGGLECHAT KEY_ENTER - // Location of status bar #define ST_X 0 #define ST_X2 104 @@ -95,10 +88,6 @@ extern boolean inhelpscreens; // [crispy] prevent palette changes #define ST_FX 143 #define ST_FY 169 -// Should be set to patch width -// for tall numbers later on -#define ST_TALLNUMWIDTH (tallnum[0]->width) - // Number of status faces. #define ST_NUMPAINFACES 5 #define ST_NUMSTRAIGHTFACES 3 @@ -211,62 +200,8 @@ extern boolean inhelpscreens; // [crispy] prevent palette changes #define ST_MAXAMMO3X (314 + ST_WIDESCREENDELTA) #define ST_MAXAMMO3Y 185 -// pistol -#define ST_WEAPON0X 110 -#define ST_WEAPON0Y 172 - -// shotgun -#define ST_WEAPON1X 122 -#define ST_WEAPON1Y 172 - -// chain gun -#define ST_WEAPON2X 134 -#define ST_WEAPON2Y 172 - -// missile launcher -#define ST_WEAPON3X 110 -#define ST_WEAPON3Y 181 - -// plasma gun -#define ST_WEAPON4X 122 -#define ST_WEAPON4Y 181 - - // bfg -#define ST_WEAPON5X 134 -#define ST_WEAPON5Y 181 - -// WPNS title -#define ST_WPNSX 109 -#define ST_WPNSY 191 - - // DETH title -#define ST_DETHX 109 -#define ST_DETHY 191 - -//Incoming messages window location -//UNUSED -// #define ST_MSGTEXTX (viewwindowx) -// #define ST_MSGTEXTY (viewwindowy+viewheight-18) -#define ST_MSGTEXTX 0 -#define ST_MSGTEXTY 0 // Dimensions given in characters. #define ST_MSGWIDTH 52 -// Or shall I say, in lines? -#define ST_MSGHEIGHT 1 - -#define ST_OUTTEXTX 0 -#define ST_OUTTEXTY 6 - -// Width, in characters again. -#define ST_OUTWIDTH 52 - // Height, in lines. -#define ST_OUTHEIGHT 1 - -#define ST_MAPTITLEX \ - (ORIGWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH) - -#define ST_MAPTITLEY 0 -#define ST_MAPHEIGHT 1 // graphics are drawn to a backing screen and blitted to the real screen pixel_t *st_backing_screen; @@ -280,18 +215,9 @@ static boolean st_firsttime; // lump number for PLAYPAL static int lu_palette; -// used for timing -static unsigned int st_clock; - // used for making messages go away static int st_msgcounter=0; -// used when in chat -static st_chatstateenum_t st_chatstate; - -// whether in automap or first-person -static st_stateenum_t st_gamestate; - // whether left-side main status bar is active static boolean st_statusbaron; @@ -306,9 +232,6 @@ static boolean st_chat; // value of st_chat before message popped up static boolean st_oldchat; -// whether chat window has the cursor on -static boolean st_cursoron; - // !deathmatch static boolean st_notdeathmatch; @@ -735,13 +658,11 @@ ST_Responder (event_t* ev) switch(ev->data1) { case AM_MSGENTERED: - st_gamestate = AutomapState; st_firsttime = true; break; case AM_MSGEXITED: // fprintf(stderr, "AM exited\n"); - st_gamestate = FirstPersonState; break; } } @@ -1679,7 +1600,6 @@ static int st_widescreendelta; void ST_Ticker (void) { - st_clock++; st_randomnumber = M_Random(); ST_updateWidgets(); st_oldhealth = plyr->health; @@ -2229,13 +2149,8 @@ void ST_initData(void) st_firsttime = true; plyr = &players[displayplayer]; - st_clock = 0; - st_chatstate = StartChatState; - st_gamestate = FirstPersonState; - st_statusbaron = true; st_oldchat = st_chat = false; - st_cursoron = false; faceindex = 0; // [crispy] fix status bar face hysteresis across level changes st_faceindex = 0; diff --git a/src/doom/st_stuff.h b/src/doom/st_stuff.h index c3c49ac48a..1e18f4723a 100644 --- a/src/doom/st_stuff.h +++ b/src/doom/st_stuff.h @@ -63,26 +63,6 @@ void ST_Init (void); extern void ST_refreshBackground(boolean force); -// States for status bar code. -typedef enum -{ - AutomapState, - FirstPersonState - -} st_stateenum_t; - - -// States for the chat code. -typedef enum -{ - StartChatState, - WaitDestState, - GetChatState - -} st_chatstateenum_t; - - - extern pixel_t *st_backing_screen; extern cheatseq_t cheat_mus; extern cheatseq_t cheat_god; diff --git a/src/heretic/am_map.c b/src/heretic/am_map.c index 345cc59b32..039453e03c 100644 --- a/src/heretic/am_map.c +++ b/src/heretic/am_map.c @@ -94,8 +94,6 @@ const char *LevelNames[] = { static int cheating = 0; static int grid = 0; -static int leveljuststarted = 1; // kluge until AM_LevelInit() is called - boolean automapactive = false; static int finit_width;// = SCREENWIDTH; static int finit_height;// = SCREENHEIGHT - (42 << crispy->hires); @@ -546,8 +544,6 @@ void AM_LevelInit(boolean reinit) // [crispy] Used for reinit static int f_h_old; - leveljuststarted = 0; - finit_width = SCREENWIDTH; finit_height = SCREENHEIGHT - (42 << crispy->hires); f_x = f_y = 0; diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c index ecce1ff4fd..9957c033db 100644 --- a/src/heretic/d_main.c +++ b/src/heretic/d_main.c @@ -1315,7 +1315,7 @@ void D_DoomMain(void) } I_InitTimer(); - I_InitSound(false); + I_InitSound(heretic); I_InitMusic(); tprintf("NET_Init: Init network subsystem.\n", 1); diff --git a/src/heretic/g_game.c b/src/heretic/g_game.c index f8c616857a..c17b240806 100644 --- a/src/heretic/g_game.c +++ b/src/heretic/g_game.c @@ -23,6 +23,7 @@ #include "doomkeys.h" #include "deh_str.h" #include "i_input.h" +#include "i_joystick.h" #include "i_timer.h" #include "i_system.h" #include "i_swap.h" @@ -415,10 +416,20 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) side += sidemove[speed]; if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) side -= sidemove[speed]; - if (joyxmove > 0) - side += sidemove[speed]; - if (joyxmove < 0) - side -= sidemove[speed]; + if (use_analog && joyxmove) + { + joyxmove = joyxmove * joystick_move_sensitivity / 10; + joyxmove = (joyxmove > FRACUNIT) ? FRACUNIT : joyxmove; + joyxmove = (joyxmove < -FRACUNIT) ? -FRACUNIT : joyxmove; + side += FixedMul(sidemove[speed], joyxmove); + } + else if (joystick_move_sensitivity) + { + if (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + } } else { @@ -426,41 +437,98 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) cmd->angleturn += angleturn[tspeed]; - if (joyxmove > 0) - cmd->angleturn -= angleturn[tspeed]; - if (joyxmove < 0) - cmd->angleturn += angleturn[tspeed]; + if (use_analog && joyxmove) + { + // Cubic response curve allows for finer control when stick + // deflection is small. + joyxmove = FixedMul(FixedMul(joyxmove, joyxmove), joyxmove); + joyxmove = joyxmove * joystick_turn_sensitivity / 10; + cmd->angleturn -= FixedMul(angleturn[1], joyxmove); + } + else if (joystick_turn_sensitivity) + { + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } } if (gamekeydown[key_up] || gamekeydown[key_alt_up]) // [crispy] add key_alt_* forward += forwardmove[speed]; if (gamekeydown[key_down] || gamekeydown[key_alt_down]) // [crispy] add key_alt_* forward -= forwardmove[speed]; - if (joyymove < 0) - forward += forwardmove[speed]; - if (joyymove > 0) - forward -= forwardmove[speed]; + if (use_analog && joyymove) + { + joyymove = joyymove * joystick_move_sensitivity / 10; + joyymove = (joyymove > FRACUNIT) ? FRACUNIT : joyymove; + joyymove = (joyymove < -FRACUNIT) ? FRACUNIT : joyymove; + forward -= FixedMul(forwardmove[speed], joyymove); + } + else if (joystick_move_sensitivity) + { + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + } if (gamekeydown[key_straferight] || gamekeydown[key_alt_straferight] || mousebuttons[mousebstraferight] // [crispy] add key_alt_* - || joybuttons[joybstraferight] || joystrafemove > 0) + || joybuttons[joybstraferight]) side += sidemove[speed]; if (gamekeydown[key_strafeleft] || gamekeydown[key_alt_strafeleft] || mousebuttons[mousebstrafeleft] // [crispy] add key_alt_* - || joybuttons[joybstrafeleft] || joystrafemove < 0) + || joybuttons[joybstrafeleft]) side -= sidemove[speed]; + if (use_analog && joystrafemove) + { + joystrafemove = joystrafemove * joystick_move_sensitivity / 10; + joystrafemove = (joystrafemove > FRACUNIT) ? FRACUNIT : joystrafemove; + joystrafemove = (joystrafemove < -FRACUNIT) ? -FRACUNIT : joystrafemove; + side += FixedMul(sidemove[speed], joystrafemove); + } + else if (joystick_move_sensitivity) + { + if (joystrafemove < 0) + side -= sidemove[speed]; + if (joystrafemove > 0) + side += sidemove[speed]; + } // Look up/down/center keys // [crispy] Keyboard lookspring if (crispy->freelook_hh == FREELOOK_HH_SPRING) { - if (gamekeydown[key_lookup] || joylook < 0) + if (gamekeydown[key_lookup]) { look = lspeed; kbdlookctrl += ticdup; } - else if (gamekeydown[key_lookdown] || joylook > 0) + else if (gamekeydown[key_lookdown]) { look = -lspeed; kbdlookctrl += ticdup; } + else if (use_analog && joylook) + { + joylook = joylook * joystick_look_sensitivity / 10; + joylook = (joylook > FRACUNIT) ? FRACUNIT : joylook; + joylook = (joylook < -FRACUNIT) ? -FRACUNIT : joylook; + look = -FixedMul(2, joylook); + kbdlookctrl += ticdup; + } + else if (joystick_look_sensitivity && joylook) + { + if (joylook < 0) + { + look = lspeed; + kbdlookctrl += ticdup; + } + + if (joylook > 0) + { + look = -lspeed; + kbdlookctrl += ticdup; + } + } else if (gamekeydown[key_lookcenter] || kbdlookctrl) { look = TOCENTER; @@ -469,14 +537,33 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) } else { - if (gamekeydown[key_lookup] || joylook < 0) + if (gamekeydown[key_lookup]) { look = lspeed; } - if (gamekeydown[key_lookdown] || joylook > 0) + if (gamekeydown[key_lookdown]) { look = -lspeed; } + if (use_analog && joylook) + { + joylook = joylook * joystick_look_sensitivity / 10; + joylook = (joylook > FRACUNIT) ? FRACUNIT : joylook; + joylook = (joylook < -FRACUNIT) ? -FRACUNIT : joylook; + look = -FixedMul(2, joylook); + } + else if (joystick_look_sensitivity) + { + if (joylook < 0) + { + look = lspeed; + } + + if (joylook > 0) + { + look = -lspeed; + } + } // haleyjd: removed externdriver crap if (gamekeydown[key_lookcenter]) { diff --git a/src/hexen/am_map.c b/src/hexen/am_map.c index f520931801..7f32b8e09d 100644 --- a/src/hexen/am_map.c +++ b/src/hexen/am_map.c @@ -33,8 +33,6 @@ int cheating = 0; static int grid = 0; -static int leveljuststarted = 1; // kluge until AM_LevelInit() is called - boolean automapactive = false; static int finit_width;// = SCREENWIDTH; static int finit_height;// = SCREENHEIGHT - SBARHEIGHT - (3 << crispy->hires); @@ -460,8 +458,6 @@ void AM_LevelInit(boolean reinit) // [crispy] Used for reinit static int f_h_old; - leveljuststarted = 0; - finit_width = SCREENWIDTH; finit_height = SCREENHEIGHT - ((ORIGSBARHEIGHT + 3) << crispy->hires); diff --git a/src/hexen/g_game.c b/src/hexen/g_game.c index ca05c6741a..00f027e8d9 100644 --- a/src/hexen/g_game.c +++ b/src/hexen/g_game.c @@ -21,6 +21,7 @@ #include "s_sound.h" #include "doomkeys.h" #include "i_input.h" +#include "i_joystick.h" #include "i_video.h" #include "i_system.h" #include "i_timer.h" @@ -323,13 +324,23 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) { side -= sidemove[pClass][speed]; } - if (joyxmove > 0) + if (use_analog && joyxmove) { - side += sidemove[pClass][speed]; + joyxmove = joyxmove * joystick_move_sensitivity / 10; + joyxmove = (joyxmove > FRACUNIT) ? FRACUNIT : joyxmove; + joyxmove = (joyxmove < -FRACUNIT) ? -FRACUNIT : joyxmove; + side += FixedMul(sidemove[pClass][speed], joyxmove); } - if (joyxmove < 0) + else if (joystick_move_sensitivity) { - side -= sidemove[pClass][speed]; + if (joyxmove > 0) + { + side += sidemove[pClass][speed]; + } + if (joyxmove < 0) + { + side -= sidemove[pClass][speed]; + } } } else @@ -338,10 +349,21 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) cmd->angleturn += angleturn[tspeed]; - if (joyxmove > 0) - cmd->angleturn -= angleturn[tspeed]; - if (joyxmove < 0) - cmd->angleturn += angleturn[tspeed]; + if (use_analog && joyxmove) + { + // Cubic response curve allows for finer control when stick + // deflection is small. + joyxmove = FixedMul(FixedMul(joyxmove, joyxmove), joyxmove); + joyxmove = joyxmove * joystick_turn_sensitivity / 10; + cmd->angleturn -= FixedMul(angleturn[1], joyxmove); + } + else if (joystick_turn_sensitivity) + { + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } } if (gamekeydown[key_up] || gamekeydown[key_alt_up]) // [crispy] add key_alt_* @@ -352,39 +374,86 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) { forward -= forwardmove[pClass][speed]; } - if (joyymove < 0) + if (use_analog && joyymove) { - forward += forwardmove[pClass][speed]; + joyymove = joyymove * joystick_move_sensitivity / 10; + joyymove = (joyymove > FRACUNIT) ? FRACUNIT : joyymove; + joyymove = (joyymove < -FRACUNIT) ? FRACUNIT : joyymove; + forward -= FixedMul(forwardmove[pClass][speed], joyymove); } - if (joyymove > 0) + else if (joystick_move_sensitivity) { - forward -= forwardmove[pClass][speed]; + if (joyymove < 0) + { + forward += forwardmove[pClass][speed]; + } + if (joyymove > 0) + { + forward -= forwardmove[pClass][speed]; + } } if (gamekeydown[key_straferight] || gamekeydown[key_alt_straferight] || mousebuttons[mousebstraferight] // [crispy] add key_alt_* - || joystrafemove > 0 || joybuttons[joybstraferight]) + || joybuttons[joybstraferight]) { side += sidemove[pClass][speed]; } if (gamekeydown[key_strafeleft] || gamekeydown[key_alt_strafeleft] || mousebuttons[mousebstrafeleft] // [crispy] add key_alt_* - || joystrafemove < 0 || joybuttons[joybstrafeleft]) + || joybuttons[joybstrafeleft]) { side -= sidemove[pClass][speed]; } + if (use_analog && joystrafemove) + { + joystrafemove = joystrafemove * joystick_move_sensitivity / 10; + joystrafemove = (joystrafemove > FRACUNIT) ? FRACUNIT : joystrafemove; + joystrafemove = (joystrafemove < -FRACUNIT) ? -FRACUNIT : joystrafemove; + side += FixedMul(sidemove[pClass][speed], joystrafemove); + } + else if (joystick_move_sensitivity) + { + if (joystrafemove < 0) + side -= sidemove[pClass][speed]; + if (joystrafemove > 0) + side += sidemove[pClass][speed]; + } + // Look up/down/center keys // [crispy] Keyboard lookspring if (crispy->freelook_hh == FREELOOK_HH_SPRING) { - if (gamekeydown[key_lookup] || joylook < 0) + if (gamekeydown[key_lookup]) { look = lspeed; kbdlookctrl += ticdup; } - else if (gamekeydown[key_lookdown] || joylook > 0) + else if (gamekeydown[key_lookdown]) { look = -lspeed; kbdlookctrl += ticdup; } + else if (use_analog && joylook) + { + joylook = joylook * joystick_look_sensitivity / 10; + joylook = (joylook > FRACUNIT) ? FRACUNIT : joylook; + joylook = (joylook < -FRACUNIT) ? -FRACUNIT : joylook; + look = -FixedMul(2, joylook); + kbdlookctrl += ticdup; + } + else if (joystick_look_sensitivity && joylook) + { + if (joylook < 0) + { + look = lspeed; + kbdlookctrl += ticdup; + } + + if (joylook > 0) + { + look = -lspeed; + kbdlookctrl += ticdup; + } + } else if (gamekeydown[key_lookcenter] || kbdlookctrl) { look = TOCENTER; @@ -393,14 +462,33 @@ void G_BuildTiccmd(ticcmd_t *cmd, int maketic) } else { - if (gamekeydown[key_lookup] || joylook < 0) + if (gamekeydown[key_lookup]) { look = lspeed; } - if (gamekeydown[key_lookdown] || joylook > 0) + if (gamekeydown[key_lookdown]) { look = -lspeed; } + if (use_analog && joylook) + { + joylook = joylook * joystick_look_sensitivity / 10; + joylook = (joylook > FRACUNIT) ? FRACUNIT : joylook; + joylook = (joylook < -FRACUNIT) ? -FRACUNIT : joylook; + look = -FixedMul(2, joylook); + } + else if (joystick_look_sensitivity) + { + if (joylook < 0) + { + look = lspeed; + } + + if (joylook > 0) + { + look = -lspeed; + } + } // haleyjd: removed externdriver crap if (gamekeydown[key_lookcenter]) { diff --git a/src/hexen/h2_main.c b/src/hexen/h2_main.c index 87fad45b5a..2c37fd2c7e 100644 --- a/src/hexen/h2_main.c +++ b/src/hexen/h2_main.c @@ -530,7 +530,7 @@ void D_DoomMain(void) I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); - I_InitSound(false); + I_InitSound(hexen); I_InitMusic(); ST_Message("NET_Init: Init networking subsystem.\n"); diff --git a/src/i_joystick.c b/src/i_joystick.c index abf2177c0c..575073bd1d 100644 --- a/src/i_joystick.c +++ b/src/i_joystick.c @@ -30,6 +30,7 @@ #include "i_system.h" #include "m_config.h" +#include "m_fixed.h" #include "m_misc.h" static SDL_GameController *gamepad = NULL; @@ -80,6 +81,12 @@ static int joystick_y_dead_zone = 33; static int joystick_strafe_dead_zone = 33; static int joystick_look_dead_zone = 33; +int use_analog = 0; + +int joystick_turn_sensitivity = 10; +int joystick_move_sensitivity = 10; +int joystick_look_sensitivity = 10; + // Virtual to physical button joystick button mapping. By default this // is a straight mapping. static int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = { @@ -294,7 +301,7 @@ static int GetAxisStateGamepad(int axis, int invert, int dead_zone) if (result < dead_zone && result > -dead_zone) { - result = 0; + return 0; } if (invert) @@ -302,6 +309,8 @@ static int GetAxisStateGamepad(int axis, int invert, int dead_zone) result = -result; } + result *= FRACUNIT / 32768; // Want FXP number between -1 and 1 + return result; } @@ -616,6 +625,8 @@ static int GetAxisState(int axis, int invert, int dead_zone) result = -result; } + result *= FRACUNIT / 32768; // Want FXP number between -1 and 1 + return result; } @@ -667,6 +678,10 @@ void I_BindJoystickVariables(void) M_BindIntVariable("joystick_y_dead_zone", &joystick_y_dead_zone); M_BindIntVariable("joystick_strafe_dead_zone", &joystick_strafe_dead_zone); M_BindIntVariable("joystick_look_dead_zone", &joystick_look_dead_zone); + M_BindIntVariable("use_analog", &use_analog); + M_BindIntVariable("joystick_turn_sensitivity", &joystick_turn_sensitivity); + M_BindIntVariable("joystick_move_sensitivity", &joystick_move_sensitivity); + M_BindIntVariable("joystick_look_sensitivity", &joystick_look_sensitivity); for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) { diff --git a/src/i_joystick.h b/src/i_joystick.h index b99b8d24d0..9831ee1c0d 100644 --- a/src/i_joystick.h +++ b/src/i_joystick.h @@ -82,6 +82,11 @@ enum GAMEPAD_BUTTON_MAX }; +extern int use_analog; +extern int joystick_turn_sensitivity; +extern int joystick_move_sensitivity; +extern int joystick_look_sensitivity; + void I_InitJoystick(void); void I_ShutdownJoystick(void); void I_UpdateJoystick(void); diff --git a/src/i_pcsound.c b/src/i_pcsound.c index 9bff98f4fe..b4249c2c94 100644 --- a/src/i_pcsound.c +++ b/src/i_pcsound.c @@ -33,7 +33,7 @@ static boolean pcs_initialized = false; static SDL_mutex *sound_lock; -static boolean use_sfx_prefix; +static GameMission_t gamemission; static uint8_t *current_sound_lump = NULL; static uint8_t *current_sound_pos = NULL; @@ -158,6 +158,7 @@ static boolean IsDisabledSound(sfxinfo_t *sfxinfo) "dmpain", "popain", "sawidl", + "rifle", }; for (i=0; iname)); + + if (gamemission == strife && W_CheckNumForName(namebuf) == -1) + { + // Missing sounds replaced with DPRIFLE. + M_snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String("rifle")); + } } else { @@ -273,9 +280,9 @@ static boolean I_PCS_SoundIsPlaying(int handle) return current_sound_lump != NULL && current_sound_remaining > 0; } -static boolean I_PCS_InitSound(boolean _use_sfx_prefix) +static boolean I_PCS_InitSound(GameMission_t mission) { - use_sfx_prefix = _use_sfx_prefix; + gamemission = mission; // Use the sample rate from the configuration file diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c index baa464714b..df8bb27958 100644 --- a/src/i_sdlsound.c +++ b/src/i_sdlsound.c @@ -1128,11 +1128,11 @@ static int GetSliceSize(void) return 1024; } -static boolean I_SDL_InitSound(boolean _use_sfx_prefix) +static boolean I_SDL_InitSound(GameMission_t mission) { int i; - use_sfx_prefix = _use_sfx_prefix; + use_sfx_prefix = (mission == doom || mission == strife); // No sounds yet for (i=0; iInit(use_sfx_prefix)) + if (sound_modules[i]->Init(mission)) { sound_module = sound_modules[i]; return; @@ -203,7 +203,7 @@ static void InitMusicModule(void) // allocates channel buffer, sets S_sfx lookup. // -void I_InitSound(boolean use_sfx_prefix) +void I_InitSound(GameMission_t mission) { boolean nosound, nosfx, nomusic, nomusicpacks; @@ -258,7 +258,7 @@ void I_InitSound(boolean use_sfx_prefix) if (!nosfx) { - InitSfxModule(use_sfx_prefix); + InitSfxModule(mission); } if (!nomusic) diff --git a/src/i_sound.h b/src/i_sound.h index 9cc647fc78..7eaca5b158 100644 --- a/src/i_sound.h +++ b/src/i_sound.h @@ -21,6 +21,7 @@ #define __I_SOUND__ #include "doomtype.h" +#include "d_mode.h" // so that the individual game logic and sound driver code agree #define NORM_PITCH 127 @@ -115,7 +116,7 @@ typedef struct // Initialise sound module // Returns true if successfully initialised - boolean (*Init)(boolean use_sfx_prefix); + boolean (*Init)(GameMission_t mission); // Shutdown sound module @@ -152,7 +153,7 @@ typedef struct } sound_module_t; -void I_InitSound(boolean use_sfx_prefix); +void I_InitSound(GameMission_t mission); void I_ShutdownSound(void); int I_GetSfxLumpNum(sfxinfo_t *sfxinfo); void I_UpdateSound(void); diff --git a/src/i_system.c b/src/i_system.c index 0bf3c48183..7b0e0ee9b9 100644 --- a/src/i_system.c +++ b/src/i_system.c @@ -135,7 +135,7 @@ byte *I_ZoneBase (int *size) // @category obscure // @arg // - // Specify the heap size, in MiB (default 16). + // Specify the heap size, in MiB. // p = M_CheckParmWithArgs("-mb", 1); @@ -147,7 +147,21 @@ byte *I_ZoneBase (int *size) } else { - default_ram = DEFAULT_RAM; + // Because of the 8-byte pointer size in a 64-bit build, the default + // heap size (16 MiB) is insufficient compared to a 32-bit build. For + // example, the Alien Vendetta avm62402.lmp demo completes successfully + // on a 32-bit build, but terminates with an out of memory error on a + // 64-bit build. Therefore, to maintain consistency with a 32-bit + // build, the heap size should be increased. + + if (sizeof(void *) == 8) + { + default_ram = DEFAULT_RAM * 2; + } + else + { + default_ram = DEFAULT_RAM; + } min_ram = MIN_RAM; } diff --git a/src/i_winmusic.c b/src/i_winmusic.c index bc4845f98a..758b4f81b5 100644 --- a/src/i_winmusic.c +++ b/src/i_winmusic.c @@ -78,9 +78,12 @@ static boolean update_volume = false; typedef enum { + STATE_STARTUP, + STATE_SHUTDOWN, + STATE_EXIT, + STATE_STOPPING, STATE_STOPPED, STATE_PLAYING, - STATE_PAUSING, STATE_PAUSED } win_midi_state_t; @@ -93,8 +96,11 @@ 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; + +#define EMIDI_DEVICE (1U << EMIDI_DEVICE_GENERAL_MIDI) // This is a reduced Windows MIDIEVENT structure for MEVT_F_SHORT // type of events. @@ -157,8 +163,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(const char *prefix, DWORD dwError) @@ -191,6 +195,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 AllocateBuffer(const unsigned int size) { MIDIHDR *hdr = &MidiStreamHdr; @@ -222,6 +232,10 @@ static void AllocateBuffer(const unsigned int size) } } +// 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); @@ -229,6 +243,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) @@ -240,6 +257,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; @@ -255,6 +275,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) { @@ -265,6 +288,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) { @@ -273,6 +299,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) { @@ -285,6 +314,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; @@ -294,6 +327,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; @@ -303,6 +339,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). @@ -310,6 +349,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; @@ -323,6 +365,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) { @@ -341,12 +387,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; @@ -357,6 +411,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; @@ -367,6 +425,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; @@ -378,6 +441,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; @@ -395,6 +462,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; @@ -415,11 +486,12 @@ 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) { - // Send notes/sound off prior to reset to prevent volume spikes. - SendNotesSoundOff(); - MIDI_ResetFallback(); use_fallback = false; @@ -447,10 +519,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(); } @@ -464,6 +540,8 @@ 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) { @@ -491,6 +569,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) @@ -594,6 +676,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; @@ -650,6 +735,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) { @@ -673,6 +762,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; @@ -686,6 +778,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) && @@ -701,6 +797,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) { @@ -708,7 +807,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) @@ -722,7 +821,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; } } @@ -746,7 +845,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); @@ -838,13 +937,12 @@ static void SendEMIDI(unsigned int delta_time, const midi_event_t *event, } SendNOPMsg(delta_time); break; - - default: - SendNOPMsg(delta_time); - break; } } +// 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) { @@ -873,6 +971,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) @@ -941,6 +1042,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) @@ -970,8 +1074,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); @@ -1156,10 +1259,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; @@ -1173,6 +1285,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; @@ -1191,6 +1306,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; @@ -1229,52 +1350,15 @@ 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; int num_events; - buffer.position = 0; - - if (initial_playback) - { - ResetDevice(); - StreamOut(); - song.rpg_loop = IsRPGLoop(); - initial_playback = false; - return; - } - - if (update_volume) - { - update_volume = false; - UpdateVolume(); - StreamOut(); - return; - } - - switch (win_midi_state) - { - 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_STOPPED: - return; - } - for (num_events = 0; num_events < STREAM_MAX_EVENTS; ) { midi_event_t *event = NULL; @@ -1356,68 +1440,141 @@ 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) { - HANDLE events[2] = { hBufferReturnEvent, hExitEvent }; + boolean keep_going = true; - while (1) + while (keep_going) { - switch (WaitForMultipleObjects(2, events, FALSE, INFINITE)) + if (WaitForSingleObject(hBufferReturnEvent, INFINITE) != WAIT_OBJECT_0) { - case 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; + + 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: + keep_going = false; + break; + + case STATE_PLAYING: + if (update_volume) + { + UpdateVolume(); + StreamOut(); + update_volume = false; + break; + } FillBuffer(); break; - case WAIT_OBJECT_0 + 1: - return 0; + case STATE_STOPPING: + // Send notes/sound off to prevent hanging notes. + SendNotesSoundOff(); + StreamOut(); + win_midi_state = STATE_STOPPED; + break; + + case STATE_STOPPED: + SetEvent(hStoppedEvent); + break; + + case STATE_PAUSED: + break; } + + 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; + + SetEvent(hBufferReturnEvent); + + mmr = midiStreamRestart(hMidiStream); + if (mmr != MMSYSERR_NOERROR) + { + MidiError("midiStreamRestart", mmr); + } +} + +// 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; + + mmr = midiStreamStop(hMidiStream); + if (mmr != MMSYSERR_NOERROR) + { + MidiError("midiStreamStop", mmr); + } + + ResetEvent(hBufferReturnEvent); + ResetEvent(hStoppedEvent); +} + static boolean I_WIN_InitMusic(void) { - int all_devices; + const int all_devices = midiOutGetNumDevs(); int i; - MIDIOUTCAPS mcaps; MMRESULT mmr; - // find the midi device that matches the saved one - if (winmm_midi_device != NULL) + if (winmm_midi_device == NULL) { - all_devices = midiOutGetNumDevs() + 1; // include MIDI_MAPPER - for (i = 0; i < all_devices; ++i) - { - // start from device id -1 (MIDI_MAPPER) - mmr = midiOutGetDevCaps(i - 1, &mcaps, sizeof(mcaps)); - if (mmr == MMSYSERR_NOERROR) - { - if (strstr(winmm_midi_device, mcaps.szPname)) - { - MidiDevice = i - 1; - break; - } - } + winmm_midi_device = M_StringDuplicate(""); + } - if (i == all_devices - 1) - { - // give up and use MIDI_MAPPER - free(winmm_midi_device); - winmm_midi_device = NULL; - } + for (i = 0; i < all_devices; i++) + { + MIDIOUTCAPS caps; + + if (midiOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR && + !strncasecmp(winmm_midi_device, caps.szPname, MAXPNAMELEN)) + { + MidiDevice = i; + break; } } - if (winmm_midi_device == NULL) + if (i == all_devices) { + free(winmm_midi_device); + winmm_midi_device = M_StringDuplicate("Microsoft MIDI Mapper"); MidiDevice = MIDI_MAPPER; - mmr = midiOutGetDevCaps(MIDI_MAPPER, &mcaps, sizeof(mcaps)); - if (mmr == MMSYSERR_NOERROR) - { - winmm_midi_device = M_StringDuplicate(mcaps.szPname); - } } mmr = midiStreamOpen(&hMidiStream, &MidiDevice, (DWORD)1, @@ -1433,14 +1590,16 @@ static boolean I_WIN_InitMusic(void) AllocateBuffer(BUFFER_INITIAL_SIZE); 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; - return true; } @@ -1453,66 +1612,43 @@ static void I_WIN_SetMusicVolume(int volume) // Ignore holding key down in volume menu. return; } - last_volume = volume; + EnterCriticalSection(&CriticalSection); volume_factor = sqrtf((float)volume / 120); - update_volume = song.registered; + LeaveCriticalSection(&CriticalSection); } static void I_WIN_StopSong(void) { - MMRESULT mmr; - - if (!hPlayerThread) - { - return; - } - - SetEvent(hExitEvent); - WaitForSingleObject(hPlayerThread, PLAYER_THREAD_WAIT_TIME); - CloseHandle(hPlayerThread); - hPlayerThread = NULL; - win_midi_state = STATE_STOPPED; - if (!hMidiStream) { return; } - mmr = midiStreamStop(hMidiStream); - if (mmr != MMSYSERR_NOERROR) - { - MidiError("midiStreamStop", mmr); - } + EnterCriticalSection(&CriticalSection); + StreamStop(); + win_midi_state = STATE_STOPPING; + StreamStart(); + LeaveCriticalSection(&CriticalSection); + + WaitForSingleObject(hStoppedEvent, PLAYER_THREAD_WAIT_TIME); + StreamStop(); } static void I_WIN_PlaySong(void *handle, boolean looping) { - MMRESULT mmr; - if (!hMidiStream) { return; } + EnterCriticalSection(&CriticalSection); song.looping = looping; - - hPlayerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlayerProc, - 0, 0, 0); - SetThreadPriority(hPlayerThread, THREAD_PRIORITY_TIME_CRITICAL); - - initial_playback = true; - win_midi_state = STATE_PLAYING; - - SetEvent(hBufferReturnEvent); - - mmr = midiStreamRestart(hMidiStream); - if (mmr != MMSYSERR_NOERROR) - { - MidiError("midiStreamRestart", mmr); - } + win_midi_state = STATE_STARTUP; + StreamStart(); + LeaveCriticalSection(&CriticalSection); } static void I_WIN_PauseSong(void) @@ -1522,7 +1658,11 @@ static void I_WIN_PauseSong(void) return; } - win_midi_state = STATE_PAUSING; + I_WIN_StopSong(); + + EnterCriticalSection(&CriticalSection); + win_midi_state = STATE_PAUSED; + LeaveCriticalSection(&CriticalSection); } static void I_WIN_ResumeSong(void) @@ -1532,7 +1672,13 @@ static void I_WIN_ResumeSong(void) return; } - win_midi_state = STATE_PLAYING; + EnterCriticalSection(&CriticalSection); + if (win_midi_state == STATE_PAUSED) + { + win_midi_state = STATE_PLAYING; + StreamStart(); + } + LeaveCriticalSection(&CriticalSection); } static boolean ConvertMus(byte *musdata, int len, const char *filename) @@ -1634,14 +1780,16 @@ static void *I_WIN_RegisterSong(void *data, int len) } song.registered = true; - ResetEvent(hBufferReturnEvent); - ResetEvent(hExitEvent); - return file; } static void I_WIN_UnRegisterSong(void *handle) { + if (!hMidiStream) + { + return; + } + if (song.tracks) { unsigned int i; @@ -1676,24 +1824,20 @@ static void I_WIN_ShutdownMusic(void) return; } - I_WIN_StopSong(); + EnterCriticalSection(&CriticalSection); + StreamStop(); I_WIN_UnRegisterSong(NULL); + win_midi_state = STATE_SHUTDOWN; + StreamStart(); + LeaveCriticalSection(&CriticalSection); - // Reset device at shutdown. - buffer.position = 0; - ResetDevice(); - StreamOut(); - mmr = midiStreamRestart(hMidiStream); - if (mmr != MMSYSERR_NOERROR) + if (WaitForSingleObject(hPlayerThread, PLAYER_THREAD_WAIT_TIME) == + WAIT_OBJECT_0) { - MidiError("midiStreamRestart", mmr); - } - WaitForSingleObject(hBufferReturnEvent, PLAYER_THREAD_WAIT_TIME); - mmr = midiStreamStop(hMidiStream); - if (mmr != MMSYSERR_NOERROR) - { - MidiError("midiStreamStop", mmr); + CloseHandle(hPlayerThread); + hPlayerThread = NULL; } + StreamStop(); // Don't free the buffer to avoid calling midiOutUnprepareHeader() which // contains a memory error (detected by ASan). @@ -1706,7 +1850,8 @@ static void I_WIN_ShutdownMusic(void) hMidiStream = NULL; CloseHandle(hBufferReturnEvent); - CloseHandle(hExitEvent); + CloseHandle(hStoppedEvent); + DeleteCriticalSection(&CriticalSection); } static boolean I_WIN_MusicIsPlaying(void) diff --git a/src/m_argv.c b/src/m_argv.c index b4f0722055..07b11f1ebe 100644 --- a/src/m_argv.c +++ b/src/m_argv.c @@ -47,7 +47,10 @@ int M_CheckParmWithArgs(const char *check, int num_args) { int i; - for (i = 1; i < myargc - num_args; i++) + // Check if myargv[i] has been set to NULL in LoadResponseFile(), + // which may call I_Error(), which in turn calls M_ParmExists("-nogui"). + + for (i = 1; i < myargc - num_args && myargv[i]; i++) { if (!strcasecmp(check, myargv[i])) return i; @@ -129,6 +132,11 @@ static void LoadResponseFile(int argv_index, const char *filename) // Copy all the arguments in the list up to the response file + if (argv_index >= MAXARGVS) + { + I_Error("Too many arguments up to the response file!"); + } + for (i=0; i= MAXARGVS) + { + I_Error("Too many arguments in the response file!"); + } + newargv[newargc++] = M_StringDuplicate(argstart); } else @@ -199,14 +213,24 @@ static void LoadResponseFile(int argv_index, const char *filename) // Cut off the end of the argument at the first space infile[k] = '\0'; - ++k; + + if (newargc >= MAXARGVS) + { + I_Error("Too many arguments in the response file!"); + } + newargv[newargc++] = M_StringDuplicate(argstart); } } // Add arguments following the response file argument + if (newargc + myargc - (argv_index + 1) >= MAXARGVS) + { + I_Error("Too many arguments following the response file!"); + } + for (i=argv_index + 1; i 0) - { - for (i = 0; i < midi_num_devices; ++i) - { - free(midi_names[i]); - midi_names[i] = NULL; - } - free(midi_names); - midi_names = NULL; - } - midi_num_devices = 0; + midi_index = 0; + free(midi_names[0]); + midi_names[0] = M_StringDuplicate("Microsoft MIDI Mapper"); - // get the number of midi devices on this system - all_devices = midiOutGetNumDevs() + 1; // include MIDI_MAPPER - if (all_devices > MAX_MIDI_DEVICES) + if (all_devices > MAX_MIDI_DEVICES - num_devices) { - all_devices = MAX_MIDI_DEVICES; + all_devices = MAX_MIDI_DEVICES - num_devices; } - // get the valid device ids only, starting from -1 (MIDI_MAPPER) - for (i = 0; i < all_devices; ++i) + for (i = 0; i < all_devices; i++) { - mmr = midiOutGetDevCaps(i - 1, &mcaps, sizeof(mcaps)); - if (mmr == MMSYSERR_NOERROR) - { - device_ids[midi_num_devices] = i - 1; - midi_num_devices++; - } - } + MIDIOUTCAPS caps; - // get the device names - midi_names = malloc(midi_num_devices * sizeof(char *)); - for (i = 0; i < midi_num_devices; ++i) - { - mmr = midiOutGetDevCaps(device_ids[i], &mcaps, sizeof(mcaps)); - if (mmr == MMSYSERR_NOERROR) + if (midiOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { - midi_names[i] = M_StringDuplicate(mcaps.szPname); - } - } + free(midi_names[num_devices]); + midi_names[num_devices] = M_StringDuplicate(caps.szPname); - // set the dropdown list index to the previously selected device - for (i = 0; i < midi_num_devices; ++i) - { - if (winmm_midi_device != NULL && - strstr(winmm_midi_device, midi_names[i])) - { - midi_index = i; - break; - } - else if (winmm_midi_device == NULL || i == midi_num_devices - 1) - { - // give up and use MIDI_MAPPER - midi_index = 0; - free(winmm_midi_device); - winmm_midi_device = M_StringDuplicate(midi_names[0]); - break; + if (!strncasecmp(winmm_midi_device, midi_names[num_devices], + MAXPNAMELEN)) + { + // Set the dropdown list index to the saved device. + midi_index = num_devices; + } + + num_devices++; } } - result = TXT_NewDropdownList(&midi_index, (const char **)midi_names, - midi_num_devices); + free(winmm_midi_device); + winmm_midi_device = M_StringDuplicate(midi_names[midi_index]); + + result = TXT_NewDropdownList(&midi_index, (const char **) midi_names, + num_devices); TXT_SignalConnect(result, "changed", UpdateMidiDevice, NULL); return result; @@ -268,7 +237,7 @@ void ConfigSound(TXT_UNCAST_ARG(widget), void *user_data) TXT_AddWidgets(window, TXT_NewSeparator("Sound effects"), TXT_NewRadioButton("Disabled", &snd_sfxdevice, SNDDEVICE_NONE), - TXT_If(gamemission == doom, + TXT_If(gamemission == doom || gamemission == strife, TXT_NewRadioButton("PC speaker effects", &snd_sfxdevice, SNDDEVICE_PCSPEAKER)), TXT_NewRadioButton("Digital sound effects", @@ -407,6 +376,10 @@ void BindSoundVariables(void) timidity_cfg_path = M_StringDuplicate(""); gus_patch_path = M_StringDuplicate(""); +#ifdef _WIN32 + winmm_midi_device = M_StringDuplicate(""); +#endif + #ifdef HAVE_FLUIDSYNTH fsynth_sf_path = M_StringDuplicate(""); #endif diff --git a/src/strife/am_map.c b/src/strife/am_map.c index 2afcd8dba0..0a8ea1450b 100644 --- a/src/strife/am_map.c +++ b/src/strife/am_map.c @@ -199,8 +199,6 @@ mline_t thintriangle_guy[] = { static int cheating = 0; //static int grid = 0; [STRIFE]: no such variable -static int leveljuststarted = 1; // kluge until AM_LevelInit() is called - boolean automapactive = false; //static int finit_width = SCREENWIDTH; //static int finit_height = SCREENHEIGHT - (32 << crispy->hires); @@ -572,8 +570,6 @@ void AM_LevelInit(boolean reinit) // [crispy] Only need to precalculate color lookup tables once static int precalc_once; - leveljuststarted = 0; - f_x = f_y = 0; f_w = SCREENWIDTH; f_h = SCREENHEIGHT - (ST_HEIGHT << crispy->hires); diff --git a/src/strife/d_main.c b/src/strife/d_main.c index ec9dc32a9c..337abbe5ea 100644 --- a/src/strife/d_main.c +++ b/src/strife/d_main.c @@ -137,10 +137,7 @@ boolean isdemoversion; //boolean storedemo; -char wadfile[1024]; // primary wad file -char mapdir[1024]; // directory of development maps - -int show_endoom = 0; +int show_endoom = 1; int show_diskicon = 1; int graphical_startup = 0; static boolean using_text_startup; @@ -2013,7 +2010,7 @@ void D_DoomMain (void) // fraggle 20130405: I_InitTimer is needed here for the netgame // startup. Start low-level sound init here too. I_InitTimer(); - I_InitSound(true); + I_InitSound(strife); I_InitMusic(); if(devparm) // [STRIFE] diff --git a/src/strife/g_game.c b/src/strife/g_game.c index 7bc40b8349..356bce6f78 100644 --- a/src/strife/g_game.c +++ b/src/strife/g_game.c @@ -35,6 +35,7 @@ #include "m_saves.h" // STRIFE #include "m_random.h" #include "i_input.h" +#include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" @@ -362,12 +363,12 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) // [crispy] center view key and lookspring support if (crispy->freelook_hh == FREELOOK_HH_SPRING) { - if (gamekeydown[key_lookup] || joylook < 0) + if (gamekeydown[key_lookup] || (joylook < 0 && joystick_look_sensitivity)) { cmd->buttons2 |= BT2_LOOKUP; kbdlookctrl += ticdup; } - else if (gamekeydown[key_lookdown] || joylook > 0) + else if (gamekeydown[key_lookdown] || (joylook > 0 && joystick_look_sensitivity)) { cmd->buttons2 |= BT2_LOOKDOWN; kbdlookctrl += ticdup; @@ -381,10 +382,10 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) } else { - if (gamekeydown[key_lookup] || joylook < 0) + if (gamekeydown[key_lookup] || (joylook < 0 && joystick_look_sensitivity)) cmd->buttons2 |= BT2_LOOKUP; - if (gamekeydown[key_lookdown] || joylook > 0) + if (gamekeydown[key_lookdown] || (joylook > 0 && joystick_look_sensitivity)) cmd->buttons2 |= BT2_LOOKDOWN; if (gamekeydown[key_lookcenter]) @@ -517,10 +518,20 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) // fprintf(stderr, "strafe left\n"); side -= sidemove[speed]; } - if (joyxmove > 0) - side += sidemove[speed]; - if (joyxmove < 0) - side -= sidemove[speed]; + if (use_analog && joyxmove) + { + joyxmove = joyxmove * joystick_move_sensitivity / 10; + joyxmove = (joyxmove > FRACUNIT) ? FRACUNIT : joyxmove; + joyxmove = (joyxmove < -FRACUNIT) ? -FRACUNIT : joyxmove; + side += FixedMul(sidemove[speed], joyxmove); + } + else if (joystick_move_sensitivity) + { + if (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + } } else @@ -529,10 +540,21 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) cmd->angleturn += angleturn[tspeed]; - if (joyxmove > 0) - cmd->angleturn -= angleturn[tspeed]; - if (joyxmove < 0) - cmd->angleturn += angleturn[tspeed]; + if (use_analog && joyxmove) + { + // Cubic response curve allows for finer control when stick + // deflection is small. + joyxmove = FixedMul(FixedMul(joyxmove, joyxmove), joyxmove); + joyxmove = joyxmove * joystick_turn_sensitivity / 10; + cmd->angleturn -= FixedMul(angleturn[1], joyxmove); + } + else if (joystick_turn_sensitivity) + { + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } } if (gamekeydown[key_up] || gamekeydown[key_alt_up]) // [crispy] add key_alt_* @@ -546,27 +568,49 @@ void G_BuildTiccmd (ticcmd_t* cmd, int maketic) forward -= forwardmove[speed]; } - if (joyymove < 0) - forward += forwardmove[speed]; - if (joyymove > 0) - forward -= forwardmove[speed]; + if (use_analog && joyymove) + { + joyymove = joyymove * joystick_move_sensitivity / 10; + joyymove = (joyymove > FRACUNIT) ? FRACUNIT : joyymove; + joyymove = (joyymove < -FRACUNIT) ? FRACUNIT : joyymove; + forward -= FixedMul(forwardmove[speed], joyymove); + } + else if (joystick_move_sensitivity) + { + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + } if (gamekeydown[key_strafeleft] || gamekeydown[key_alt_strafeleft] // [crispy] add key_alt_* || joybuttons[joybstrafeleft] - || mousebuttons[mousebstrafeleft] - || joystrafemove < 0) + || mousebuttons[mousebstrafeleft]) { side -= sidemove[speed]; } if (gamekeydown[key_straferight] || gamekeydown[key_alt_straferight] // [crispy] add key_alt_* || joybuttons[joybstraferight] - || mousebuttons[mousebstraferight] - || joystrafemove > 0) + || mousebuttons[mousebstraferight]) { side += sidemove[speed]; } + if (use_analog && joystrafemove) + { + joystrafemove = joystrafemove * joystick_move_sensitivity / 10; + joystrafemove = (joystrafemove > FRACUNIT) ? FRACUNIT : joystrafemove; + joystrafemove = (joystrafemove < -FRACUNIT) ? -FRACUNIT : joystrafemove; + side += FixedMul(sidemove[speed], joystrafemove); + } + else if (joystick_move_sensitivity) + { + if (joystrafemove < 0) + side -= sidemove[speed]; + if (joystrafemove > 0) + side += sidemove[speed]; + } // buttons cmd->chatchar = HU_dequeueChatChar(); diff --git a/src/strife/st_stuff.c b/src/strife/st_stuff.c index 8f3b1254af..288a8c7b8f 100644 --- a/src/strife/st_stuff.c +++ b/src/strife/st_stuff.c @@ -162,9 +162,6 @@ static boolean st_firsttime; // lump number for PLAYPAL static int lu_palette; -// whether in automap or first-person -static st_stateenum_t st_gamestate; - // whether left-side main status bar is active static boolean st_statusbaron; @@ -327,12 +324,10 @@ boolean ST_Responder(event_t* ev) switch(ev->data1) { case AM_MSGENTERED: - st_gamestate = AutomapState; st_firsttime = true; break; case AM_MSGEXITED: - st_gamestate = FirstPersonState; break; } @@ -1748,8 +1743,6 @@ void ST_initData(void) st_firsttime = true; plyr = &players[consoleplayer]; - st_gamestate = FirstPersonState; - st_statusbaron = true; st_palette = -1; diff --git a/src/strife/st_stuff.h b/src/strife/st_stuff.h index 07cc3ac4d3..17554d1182 100644 --- a/src/strife/st_stuff.h +++ b/src/strife/st_stuff.h @@ -67,16 +67,6 @@ typedef enum } st_stateenum_t; -// States for the chat code. -typedef enum -{ - StartChatState, - WaitDestState, - GetChatState - -} st_chatstateenum_t; - - // [crispy] For status bar background in widescreen mode and widgets. extern pixel_t *st_backing_screen;