IntriguingTiles / HLFixes

Fixing bugs in GoldSrc
https://hgrunt.xyz/hlfixes.html
MIT License
21 stars 2 forks source link

Music fix introduces another problem (& fix) #3

Open anzz1 opened 7 months ago

anzz1 commented 7 months ago

As the music fix always lets music continue playing on connect/transition if it's not gamestartup.mp3, this introduces an issue where a server can play some music file and it continues playing after map changes.

To fix this, apply the music fix on single player only. One way is to check for (server_state_t) svs.maxclients == 1 in the function and act accordingly. There is also field intermission in client_state_t which probably would also work, but I find check for maxclients==1 to check for SP/MP works just fine.

Here is some example code. Note also that you can't install the hook for CGameUI::ConnectToServer before CBaseUI::Start has been called as otherwise the GameUI.dll module isn't loaded yet. This can be done for example in CL_Init.

Offsets are hardcoded for steam_legacy branch (build 8684) but it can be adapted to use signatures.


typedef ULONG_PTR (__cdecl *_GetInteralCDAudio)(); // sic!
_GetInteralCDAudio GetInteralCDAudio = 0;

ULONG_PTR CL_Init = 0;
DWORD* pdwMaxClients = 0; // server_state_t svs.maxclients

static DWORD calc_disp32(ULONG_PTR src, ULONG_PTR dst)
{
  ULONG_PTR diff = 0;
  if (src > dst)
    diff = src-dst;
  else
    diff = dst-src;

  //if (diff > 0xFFFFFFF0) // no need in 32-bit
    //return 0;

  // disp32 = target - disp32_ptr - 4
  return (DWORD)(dst-src-4);
}

static char* get_current_track()
{
  ULONG_PTR pCDAudio = GetInteralCDAudio();
  if (pCDAudio) {
    return (char*)(pCDAudio + 0x3D3);
  }
  return "";
}

// 1=mp3 stop 0=not
int __cdecl CGameUI__ConnectToServer_Detour()
{
  return *pdwMaxClients != 1 || !stricmp(get_current_track(), "media\\gamestartup.mp3");
}

static int Hook_CGameUI()
{
  DWORD oldProtect, newProtect = PAGE_EXECUTE_READWRITE;
  ULONG_PTR cgui = (ULONG_PTR)GetModuleHandleA("GameUI.dll");
  if (!cgui) return 0;

  if (*(ULONGLONG*)((ULONG_PTR)(cgui + 0x000233F0)) != 0x244C8B32EBFFFE93) return 0;
  if (!VirtualProtect((void*)((ULONG_PTR)(cgui + 0x000233F0)), 21, newProtect, &oldProtect)) return 0;

  *(BYTE*)((ULONG_PTR)(cgui + 0x000233F5)) = 0xE8;
  *(ULONG_PTR*)((ULONG_PTR)(cgui + 0x000233F6)) = calc_disp32((cgui + 0x000233F6), (ULONG_PTR)CGameUI__ConnectToServer_Detour);
  *(ULONGLONG*)((ULONG_PTR)(cgui + 0x000233FA)) = 0x9090909090909090;
  *(ULONGLONG*)((ULONG_PTR)(cgui + 0x00023402)) = 0x74C0859090909090;

  VirtualProtect((void*)((ULONG_PTR)(cgui + 0x000233F0)), 21, oldProtect, &newProtect);

  return 1;
}

void __cdecl My_CL_Init()
{
  Hook_CGameUI();
  ((void (__cdecl *)(void)) (void*)(CL_Init))();
}

static int Hook_CL_Init(ULONG_PTR hw)
{
  DWORD oldProtect, newProtect = PAGE_EXECUTE_READWRITE;

  if (*(ULONGLONG*)((ULONG_PTR)(hw+0x00019DB0)) != 0xF6330008B12AE856) return 0;
  CL_Init = hw+0x00019DB0;

  if (*(ULONGLONG*)((ULONG_PTR)(hw+0x00057FD1)) != 0x54680DEBFFFC1DDB) return 0;
  if (!VirtualProtect((void*)((ULONG_PTR)(hw+0x00057FD1)), 4, newProtect, &oldProtect)) return 0;

  *(ULONG_PTR*)((ULONG_PTR)(hw+0x00057FD1)) = calc_disp32((hw+0x00057FD1), (ULONG_PTR)My_CL_Init);

  VirtualProtect((void*)((ULONG_PTR)(hw+0x00057FD1)), 4, oldProtect, &newProtect);

  return 1;
}

int hook_funcs(void)
{
  ULONG_PTR hw = (ULONG_PTR)GetModuleHandleA("hw.dll");
  if (!hw) return -1;

  GetInteralCDAudio = (_GetInteralCDAudio)(hw+0x00009000);
  pdwMaxClients = (DWORD*)(*(ULONG_PTR*)((ULONG_PTR)(hw+0x0005A11D)));

  if (!Hook_CL_Init(hw)) return 0;

  return 1;
}

So for clarification, this stops playing the music on connect/transition if either 1) current file is gamestartup.mp3 2) is playing MP game

This way sound doesn't cut off in singleplayer game map transitions, but also doesn't keep playing in multiplayer servers after map (or server) change.

IntriguingTiles commented 7 months ago

Interesting. I think music persisting between maps in multiplayer is technically correct behavior and is what would've probably happened in ancient versions of GoldSrc, but I can see how multiplayer mods might rely on the music stopping.

I'll look into implementing the fix at some point.