Skip to content

Commit 39ff94a

Browse files
authored
Merge branch 'master' into fix/improve-aclrequest-detection
2 parents ca1e0ef + e0ae3d9 commit 39ff94a

File tree

4 files changed

+2303
-2039
lines changed

4 files changed

+2303
-2039
lines changed

Client/core/DXHook/CProxyDirect3DVertexBuffer.cpp

Lines changed: 225 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
#include "CAdditionalVertexStreamManager.h"
1515
#include "CVertexStreamBoundingBoxManager.h"
1616

17+
#include <algorithm>
18+
#include <array>
19+
#include <cstring>
20+
#include <new>
21+
1722
/////////////////////////////////////////////////////////////
1823
//
1924
// CProxyDirect3DVertexBuffer::CProxyDirect3DVertexBuffer
@@ -109,34 +114,50 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
109114
CVertexStreamBoundingBoxManager::GetSingleton()->OnVertexBufferRangeInvalidated(m_pOriginal, OffsetToLock, SizeToLock);
110115
}
111116

117+
ClearFallbackLock();
118+
112119
*ppbData = NULL;
113-
HRESULT hr = DoLock(OffsetToLock, SizeToLock, ppbData, Flags);
114120

115-
// Report problems
116-
if (FAILED(hr) || *ppbData == NULL)
121+
const UINT clampedSize = ClampLockSize(OffsetToLock, SizeToLock);
122+
123+
HRESULT hr = DoLock(OffsetToLock, clampedSize, ppbData, Flags);
124+
if (SUCCEEDED(hr) && *ppbData != NULL)
125+
return hr;
126+
127+
HRESULT recoveryHr = TryRecoverLock(OffsetToLock, clampedSize, ppbData, Flags, hr);
128+
if (SUCCEEDED(recoveryHr) && *ppbData != NULL)
129+
return recoveryHr;
130+
131+
HRESULT finalHr = FAILED(recoveryHr) ? recoveryHr : hr;
132+
HRESULT fallbackHr = ActivateFallbackLock(OffsetToLock, clampedSize, ppbData, Flags, finalHr);
133+
if (SUCCEEDED(fallbackHr) && *ppbData != NULL)
134+
return fallbackHr;
135+
136+
if (FAILED(fallbackHr))
137+
finalHr = fallbackHr;
138+
139+
struct
117140
{
118-
struct
119-
{
120-
const char* szText;
121-
uint uiReportId;
122-
uint uiLogEventId;
123-
} info;
124-
if (hr == D3D_OK)
125-
info = {"result NULL", 8621, 621};
126-
else if (hr == STATUS_ARRAY_BOUNDS_EXCEEDED)
127-
info = {"offset out of range", 8622, 622};
128-
else if (hr == STATUS_ACCESS_VIOLATION)
129-
info = {"access violation", 8623, 623};
130-
else
131-
info = {"fail", 8620, 620};
141+
const char* szText;
142+
uint uiReportId;
143+
uint uiLogEventId;
144+
} info;
145+
if (finalHr == D3D_OK)
146+
info = {"result NULL", 8621, 621};
147+
else if (finalHr == STATUS_ARRAY_BOUNDS_EXCEEDED)
148+
info = {"offset out of range", 8622, 622};
149+
else if (finalHr == STATUS_ACCESS_VIOLATION)
150+
info = {"access violation", 8623, 623};
151+
else
152+
info = {"fail", 8620, 620};
132153

133-
SString strMessage("Lock VertexBuffer [%s] hr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x", info.szText, hr, m_iMemUsed,
134-
m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock, Flags);
135-
WriteDebugEvent(strMessage);
136-
AddReportLog(info.uiReportId, strMessage);
137-
CCore::GetSingleton().LogEvent(info.uiLogEventId, "Lock VertexBuffer", "", strMessage);
138-
}
139-
return hr;
154+
SString strMessage("Lock VertexBuffer [%s] hr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x", info.szText, finalHr, m_iMemUsed,
155+
m_dwUsage, m_dwFVF, m_pool, OffsetToLock, clampedSize, Flags);
156+
WriteDebugEvent(strMessage);
157+
AddReportLog(info.uiReportId, strMessage);
158+
CCore::GetSingleton().LogEvent(info.uiLogEventId, "Lock VertexBuffer", "", strMessage);
159+
160+
return finalHr;
140161
}
141162

142163
/////////////////////////////////////////////////////////////
@@ -148,22 +169,197 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
148169
/////////////////////////////////////////////////////////////
149170
HRESULT CProxyDirect3DVertexBuffer::DoLock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags)
150171
{
151-
// Validate sizes because gta can give invalid values (reason unknown)
152-
if (OffsetToLock + SizeToLock > m_iMemUsed)
172+
if (OffsetToLock >= m_iMemUsed)
173+
return STATUS_ARRAY_BOUNDS_EXCEEDED;
174+
175+
UINT adjustedSize = ClampLockSize(OffsetToLock, SizeToLock);
176+
if (adjustedSize == 0)
177+
return STATUS_ARRAY_BOUNDS_EXCEEDED;
178+
179+
return LockInternal(OffsetToLock, adjustedSize, ppbData, Flags);
180+
}
181+
182+
HRESULT CProxyDirect3DVertexBuffer::Unlock()
183+
{
184+
if (!m_fallbackLock.active)
185+
return m_pOriginal->Unlock();
186+
187+
HRESULT copyHr = D3D_OK;
188+
189+
if ((m_fallbackLock.flags & D3DLOCK_READONLY) == 0 && m_fallbackLock.size > 0 && !m_fallbackLock.buffer.empty())
153190
{
154-
if (OffsetToLock > m_iMemUsed)
191+
void* pDestination = NULL;
192+
copyHr = LockInternal(m_fallbackLock.offset, m_fallbackLock.size, &pDestination, 0);
193+
if (SUCCEEDED(copyHr) && pDestination != NULL)
155194
{
156-
return STATUS_ARRAY_BOUNDS_EXCEEDED;
195+
std::memcpy(pDestination, m_fallbackLock.buffer.data(), m_fallbackLock.size);
196+
HRESULT unlockHr = m_pOriginal->Unlock();
197+
if (FAILED(unlockHr))
198+
{
199+
copyHr = unlockHr;
200+
SString strMessage("Unlock VertexBuffer [fallback unlock failed] hr:%x Length:%x Usage:%x FVF:%x Pool:%x Offset:%x Size:%x", unlockHr, m_iMemUsed,
201+
m_dwUsage, m_dwFVF, m_pool, m_fallbackLock.offset, m_fallbackLock.size);
202+
WriteDebugEvent(strMessage);
203+
AddReportLog(8627, strMessage);
204+
CCore::GetSingleton().LogEvent(627, "Unlock VertexBuffer", "", strMessage);
205+
}
206+
}
207+
else
208+
{
209+
if (SUCCEEDED(copyHr) && pDestination == NULL)
210+
copyHr = D3DERR_INVALIDCALL;
211+
212+
SString strMessage("Unlock VertexBuffer [fallback copy failed] hr:%x Length:%x Usage:%x FVF:%x Pool:%x Offset:%x Size:%x", copyHr, m_iMemUsed, m_dwUsage,
213+
m_dwFVF, m_pool, m_fallbackLock.offset, m_fallbackLock.size);
214+
WriteDebugEvent(strMessage);
215+
AddReportLog(8626, strMessage);
216+
CCore::GetSingleton().LogEvent(626, "Unlock VertexBuffer", "", strMessage);
157217
}
158-
SizeToLock = m_iMemUsed - OffsetToLock;
159218
}
160219

220+
ClearFallbackLock();
221+
return D3D_OK;
222+
}
223+
224+
UINT CProxyDirect3DVertexBuffer::ClampLockSize(UINT OffsetToLock, UINT SizeToLock) const
225+
{
226+
if (OffsetToLock >= m_iMemUsed)
227+
return 0;
228+
229+
const UINT available = static_cast<UINT>(m_iMemUsed - OffsetToLock);
230+
if (available == 0)
231+
return 0;
232+
233+
if (SizeToLock == 0)
234+
return available;
235+
236+
return std::min(SizeToLock, available);
237+
}
238+
239+
HRESULT CProxyDirect3DVertexBuffer::LockInternal(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags)
240+
{
241+
if (ppbData == NULL)
242+
return E_INVALIDARG;
243+
244+
*ppbData = NULL;
245+
161246
__try
162247
{
163248
return m_pOriginal->Lock(OffsetToLock, SizeToLock, ppbData, Flags);
164249
}
165250
__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
166251
{
252+
*ppbData = NULL;
167253
return STATUS_ACCESS_VIOLATION;
168254
}
169255
}
256+
257+
HRESULT CProxyDirect3DVertexBuffer::TryRecoverLock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags, HRESULT originalHr)
258+
{
259+
HRESULT lastHr = originalHr;
260+
261+
struct SLockAttempt
262+
{
263+
DWORD flags;
264+
UINT offset;
265+
UINT size;
266+
bool adjustPointer;
267+
};
268+
269+
std::array<SLockAttempt, 7> attempts = {
270+
SLockAttempt{Flags & ~D3DLOCK_DONOTWAIT, OffsetToLock, SizeToLock, false},
271+
SLockAttempt{Flags & ~D3DLOCK_NOOVERWRITE, OffsetToLock, SizeToLock, false},
272+
SLockAttempt{Flags & ~D3DLOCK_DISCARD, OffsetToLock, SizeToLock, false},
273+
SLockAttempt{Flags & ~(D3DLOCK_DONOTWAIT | D3DLOCK_NOOVERWRITE), OffsetToLock, SizeToLock, false},
274+
SLockAttempt{Flags & ~(D3DLOCK_DONOTWAIT | D3DLOCK_NOOVERWRITE | D3DLOCK_NO_DIRTY_UPDATE), OffsetToLock, SizeToLock, false},
275+
SLockAttempt{0u, OffsetToLock, SizeToLock, false},
276+
SLockAttempt{0u, 0u, static_cast<UINT>(m_iMemUsed), true},
277+
};
278+
279+
for (const SLockAttempt& attempt : attempts)
280+
{
281+
if (attempt.flags == Flags && attempt.offset == OffsetToLock && attempt.size == SizeToLock)
282+
continue;
283+
284+
if (attempt.size > m_iMemUsed)
285+
continue;
286+
287+
UINT clampedSize = ClampLockSize(attempt.offset, attempt.size);
288+
if (clampedSize == 0)
289+
continue;
290+
291+
void* pRecovered = NULL;
292+
HRESULT hr = LockInternal(attempt.offset, clampedSize, &pRecovered, attempt.flags);
293+
if (SUCCEEDED(hr) && pRecovered != NULL)
294+
{
295+
if (attempt.adjustPointer)
296+
{
297+
UINT pointerOffset = 0;
298+
if (OffsetToLock > attempt.offset)
299+
pointerOffset = OffsetToLock - attempt.offset;
300+
if (pointerOffset >= clampedSize)
301+
{
302+
lastHr = hr;
303+
continue;
304+
}
305+
pRecovered = static_cast<void*>(static_cast<std::uint8_t*>(pRecovered) + pointerOffset);
306+
}
307+
308+
*ppbData = pRecovered;
309+
310+
SString strMessage("Lock VertexBuffer [retry] hr:%x->%x Length:%x Usage:%x FVF:%x Pool:%x Offset:%x Size:%x Flags:%x->%x", originalHr, hr, m_iMemUsed,
311+
m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock, Flags, attempt.flags);
312+
WriteDebugEvent(strMessage);
313+
AddReportLog(8625, strMessage);
314+
CCore::GetSingleton().LogEvent(625, "Lock VertexBuffer", "", strMessage);
315+
316+
return hr;
317+
}
318+
319+
lastHr = hr;
320+
}
321+
322+
return lastHr;
323+
}
324+
325+
HRESULT CProxyDirect3DVertexBuffer::ActivateFallbackLock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags, HRESULT originalHr)
326+
{
327+
if (ppbData == NULL)
328+
return originalHr;
329+
330+
UINT clampedSize = ClampLockSize(OffsetToLock, SizeToLock);
331+
if (clampedSize == 0)
332+
return originalHr;
333+
334+
try
335+
{
336+
m_fallbackLock.buffer.resize(clampedSize);
337+
}
338+
catch (const std::bad_alloc&)
339+
{
340+
return E_OUTOFMEMORY;
341+
}
342+
343+
m_fallbackLock.active = true;
344+
m_fallbackLock.offset = OffsetToLock;
345+
m_fallbackLock.size = clampedSize;
346+
m_fallbackLock.flags = Flags;
347+
348+
*ppbData = m_fallbackLock.buffer.data();
349+
350+
SString strMessage("Lock VertexBuffer [fallback] hr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x", originalHr, m_iMemUsed,
351+
m_dwUsage, m_dwFVF, m_pool, OffsetToLock, clampedSize, Flags);
352+
WriteDebugEvent(strMessage);
353+
AddReportLog(8624, strMessage);
354+
CCore::GetSingleton().LogEvent(624, "Lock VertexBuffer", "", strMessage);
355+
356+
return D3D_OK;
357+
}
358+
359+
void CProxyDirect3DVertexBuffer::ClearFallbackLock()
360+
{
361+
m_fallbackLock.active = false;
362+
m_fallbackLock.offset = 0;
363+
m_fallbackLock.size = 0;
364+
m_fallbackLock.flags = 0;
365+
}

Client/core/DXHook/CProxyDirect3DVertexBuffer.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#pragma once
1313

1414
#include <d3d9.h>
15+
#include <vector>
16+
#include <cstdint>
1517
#include "CProxyDirect3DDevice9.h" // Include full definition for SResourceMemory
1618

1719
DEFINE_GUID(CProxyDirect3DVertexBuffer_GUID, 0x128A025E, 0x0100, 0x04F1, 0x40, 0x60, 0x53, 0x19, 0x44, 0x56, 0x59, 0x42);
@@ -39,7 +41,7 @@ class CProxyDirect3DVertexBuffer : public IDirect3DVertexBuffer9
3941

4042
/*** IDirect3DVertexBuffer9 methods ***/
4143
HRESULT __stdcall Lock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags);
42-
HRESULT __stdcall Unlock() { return m_pOriginal->Unlock(); }
44+
HRESULT __stdcall Unlock();
4345
HRESULT __stdcall GetDesc(D3DVERTEXBUFFER_DESC* pDesc) { return m_pOriginal->GetDesc(pDesc); }
4446

4547
// CProxyDirect3DVertexBuffer
@@ -49,11 +51,28 @@ class CProxyDirect3DVertexBuffer : public IDirect3DVertexBuffer9
4951
IDirect3DVertexBuffer9* GetOriginal() { return m_pOriginal; }
5052
HRESULT DoLock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags);
5153

54+
private:
55+
struct SFallbackLock
56+
{
57+
bool active = false;
58+
UINT offset = 0;
59+
UINT size = 0;
60+
DWORD flags = 0;
61+
std::vector<std::uint8_t> buffer;
62+
};
63+
64+
UINT ClampLockSize(UINT OffsetToLock, UINT SizeToLock) const;
65+
HRESULT LockInternal(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags);
66+
HRESULT TryRecoverLock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags, HRESULT originalHr);
67+
HRESULT ActivateFallbackLock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags, HRESULT originalHr);
68+
void ClearFallbackLock();
69+
5270
protected:
5371
IDirect3DVertexBuffer9* m_pOriginal;
5472
uint m_iMemUsed;
5573
DWORD m_dwUsage;
5674
DWORD m_dwFVF;
5775
D3DPOOL m_pool;
5876
CProxyDirect3DDevice9::SResourceMemory& m_stats;
77+
SFallbackLock m_fallbackLock;
5978
};

0 commit comments

Comments
 (0)