Skip to content

Supply Dock is black/shadowed in Replay playback #439

Closed

Description

@xezon

Supply Dock is black/shadowed in Replay playback. GenTool fixes this issue with a hack.

Image

Activity

  1. added
    BugSomething is not working right, typically is user facing
    GenToolRelates to GenTool
    MinorSeverity: Minor < Major < Critical < Blocker
    on Mar 17, 2025
  2. added theissue type on Mar 17, 2025
  3. xezon commented on Aug 14, 2025

    @xezon
    Author

    This is how GenTool "fixes" it:

    dptr_GhostObject = 0xA2C280
    fptr_CleanupBuilding = 0x774830
    
    //////////////////////////////////////////////////////////////////////////////////////
    
    bool CGameAccess::Hack_ClearBuggedBuildings() const
    {
    	if (dptr_GhostObject.Address() && fptr_CleanupBuilding.Address())
    	{
    		void (__stdcall* ClearBuggedBuilding)(DWORD) = reinterpret_cast<void (__stdcall*)(DWORD)>(fptr_CleanupBuilding.Address());
    		const DWORD copyAddress = fptr_CleanupBuilding.Address() + 0xDC;
    		const DWORD copyNumBytes = 0x44;
    		BYTE copy[copyNumBytes];
    		::memcpy(copy, (const void*)copyAddress, copyNumBytes);
    
    		// Write no operation instructions to copy address
    		Hack_Nop(copyAddress, copyNumBytes);
    
    		DWORD object = *(DWORD*)(*(DWORD*)dptr_GhostObject.Address() + 0x10);
    		while (object)
    		{
    			ClearBuggedBuilding(object);
    			object = *(DWORD*)(object + 0x7C);
    		}
    
    		// Write back original bytes to copy address
    		Hack_WriteBytes(copyAddress, copy, copyNumBytes);
    		return true;
    	}
    	return false;
    }
    
    //////////////////////////////////////////////////////////////////////////////////////
    
    // CPU Disasm (Zero Hour 1.04)
    // Address   Hex dump          Command                                  Comments
    // 004A46DA  |.  A1 88B6A200   MOV EAX,DWORD PTR DS:[0A2B688]
    // 004A46DF  |.  8B40 0C       MOV EAX,DWORD PTR DS:[EAX+0C]
    // 004A46E2  |.  8B40 24       MOV EAX,DWORD PTR DS:[EAX+24]
    // 004A46E5  |.  8B0D 80C2A200 MOV ECX,DWORD PTR DS:[0A2C280]
    // 004A46EB  |.  8B11          MOV EDX,DWORD PTR DS:[ECX]
    // 004A46ED  |.  50            PUSH EAX
    // 004A46EE  |.  FF52 1C       CALL DWORD PTR DS:[EDX+1C]
    
    // Calls game function 0x774A80 and spawns new meshes for the previous deleted ones
    // Also changes the actual replay master player to the new one
    
    void CGameAccess::Hack_SpawnFixedBuildings() const
    {
    	if (!dptr_Players.Address())
    		return;
    
    	if (!dptr_GhostObject.Address())
    		return;
    
    	DWORD playerNum = *(DWORD*)(*(DWORD*)(*(DWORD*)dptr_Players.Address()+0xC)+0x24);
    	DWORD pGhostObject = *(DWORD*)dptr_GhostObject.Address();
    	DWORD pSomething = *(DWORD*)pGhostObject;
    
    	const DWORD spawnAllNewObjectsPtr = *(DWORD*)(pSomething+0x1C);
    	void (__fastcall* SpawnAllNewObjects)(DWORD, DWORD, DWORD) = reinterpret_cast<void (__fastcall*)(DWORD, DWORD, DWORD)>(spawnAllNewObjectsPtr);
    	SpawnAllNewObjects(pGhostObject, pSomething, playerNum);
    }
    
    //////////////////////////////////////////////////////////////////////////////////////
    
    // Loops through all world buildings and sets their render state to 0
    // Causes object refresh through function 0x539C50 or similar
    
    void CGameAccess::Hack_RefreshBuildingFogStates() const
    {
    	if (!dptr_GhostObject.Address())
    		return;
    
    	DWORD offset = *(DWORD*)(*(DWORD*)dptr_GhostObject.Address() + 0x4);
    	DWORD pBuilding = *(DWORD*)(*(DWORD*)dptr_GhostObject.Address() + 0x10);
    
    	while (pBuilding)
    	{
    		DWORD pFogStates = *(DWORD*)(pBuilding + 0x28);
    		*(EFogState*)(offset*4+pFogStates+0x30) = eFogState_None;
    		*(EFogState*)(offset*4+pFogStates+0x70) = eFogState_GoingFog;
    
    		pBuilding = *(DWORD*)(pBuilding + 0x7C);
    	}
    }
    
    
    update loop:
    
    	// Refresh buildings on first frame in Replay to avoid the "black supply" issue
    	// It has to happen on Frame 1 - Frame 0 does not work
    	if (frameId == 1 && IsReplay())
    	{
    		if (gGame.Hack_ClearBuggedBuildings())
    		{
    			gGame.Hack_SpawnFixedBuildings();
    			gGame.Hack_RefreshBuildingFogStates();
    		}
    	}

    Not particularly useful but perhaps gives a bit of a clue where to start looking and how to approach it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething is not working right, typically is user facingGenToolRelates to GenToolMinorSeverity: Minor < Major < Critical < Blocker

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions