Skip to content

Commit 15a53d5

Browse files
authored
[GEN][ZH] Fix crash when taking a screenshot while the Game Window clips over the screen edge (#892)
1 parent 228f7fe commit 15a53d5

File tree

4 files changed

+170
-92
lines changed
  • GeneralsMD/Code
  • Generals/Code

4 files changed

+170
-92
lines changed

Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,32 +2872,37 @@ void W3DDisplay::takeScreenShot(void)
28722872
done = true;
28732873
}
28742874

2875-
// Lock front buffer and copy
2875+
// TheSuperHackers @bugfix xezon 21/05/2025 Get the back buffer and create a copy of the surface.
2876+
// Originally this code took the front buffer and tried to lock it. This does not work when the
2877+
// render view clips outside the desktop boundaries. It crashed the game.
2878+
SurfaceClass* surface = DX8Wrapper::_Get_DX8_Back_Buffer();
28762879

2877-
IDirect3DSurface8 *fb;
2878-
fb=DX8Wrapper::_Get_DX8_Front_Buffer();
2879-
D3DSURFACE_DESC desc;
2880-
fb->GetDesc(&desc);
2880+
SurfaceClass::SurfaceDescription surfaceDesc;
2881+
surface->Get_Description(surfaceDesc);
28812882

2882-
RECT bounds;
2883-
POINT point;
2883+
SurfaceClass* surfaceCopy = NEW_REF(SurfaceClass, (DX8Wrapper::_Create_DX8_Surface(surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format)));
2884+
DX8Wrapper::_Copy_DX8_Rects(surface->Peek_D3D_Surface(), NULL, 0, surfaceCopy->Peek_D3D_Surface(), NULL);
28842885

2885-
GetClientRect(ApplicationHWnd,&bounds);
2886-
point.x=bounds.left; point.y=bounds.top;
2887-
ClientToScreen(ApplicationHWnd, &point);
2888-
bounds.left=point.x; bounds.top=point.y;
2889-
point.x=bounds.right; point.y=bounds.bottom;
2890-
ClientToScreen(ApplicationHWnd, &point);
2891-
bounds.right=point.x; bounds.bottom=point.y;
2892-
2893-
D3DLOCKED_RECT lrect;
2886+
surface->Release_Ref();
2887+
surface = NULL;
28942888

2895-
DX8_ErrorCode(fb->LockRect(&lrect,&bounds,D3DLOCK_READONLY));
2889+
struct Rect
2890+
{
2891+
int Pitch;
2892+
void* pBits;
2893+
} lrect;
2894+
2895+
lrect.pBits = surfaceCopy->Lock(&lrect.Pitch);
2896+
if (lrect.pBits == NULL)
2897+
{
2898+
surfaceCopy->Release_Ref();
2899+
return;
2900+
}
28962901

28972902
unsigned int x,y,index,index2,width,height;
28982903

2899-
width=bounds.right-bounds.left;
2900-
height=bounds.bottom-bounds.top;
2904+
width = surfaceDesc.Width;
2905+
height = surfaceDesc.Height;
29012906

29022907
char *image=NEW char[3*width*height];
29032908
#ifdef CAPTURE_TO_TARGA
@@ -2917,7 +2922,9 @@ void W3DDisplay::takeScreenShot(void)
29172922
}
29182923
}
29192924

2920-
fb->Release();
2925+
surfaceCopy->Unlock();
2926+
surfaceCopy->Release_Ref();
2927+
surfaceCopy = NULL;
29212928

29222929
Targa targ;
29232930
memset(&targ.Header,0,sizeof(targ.Header));
@@ -2946,7 +2953,9 @@ void W3DDisplay::takeScreenShot(void)
29462953
}
29472954
}
29482955

2949-
fb->Release();
2956+
surfaceCopy->Unlock();
2957+
surfaceCopy->Release_Ref();
2958+
surfaceCopy = NULL;
29502959

29512960
//Flip the image
29522961
char *ptr,*ptr1;
@@ -2980,7 +2989,7 @@ void W3DDisplay::takeScreenShot(void)
29802989
TheInGameUI->message(TheGameText->fetch("GUI:ScreenCapture"), ufileName.str());
29812990
}
29822991

2983-
/** Start/Stop campturing an AVI movie*/
2992+
/** Start/Stop capturing an AVI movie*/
29842993
void W3DDisplay::toggleMovieCapture(void)
29852994
{
29862995
WW3D::Toggle_Movie_Capture("Movie",30);

Generals/Code/Libraries/Source/WWVegas/WW3D2/ww3d.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,24 +1347,37 @@ void WW3D::Make_Screen_Shot( const char * filename_base , const float gamma, con
13471347
gamma_lut[i] = (unsigned char) (256.0f * powf(i / 256.0f, recip));
13481348
}
13491349

1350-
// Lock front buffer and copy
1350+
// TheSuperHackers @bugfix xezon 21/05/2025 Get the back buffer and create a copy of the surface.
1351+
// Originally this code took the front buffer and tried to lock it. This does not work when the
1352+
// render view clips outside the desktop boundaries. It crashed the game.
1353+
SurfaceClass* surface = DX8Wrapper::_Get_DX8_Back_Buffer();
13511354

1352-
IDirect3DSurface8 *fb;
1353-
fb=DX8Wrapper::_Get_DX8_Front_Buffer();
1354-
D3DSURFACE_DESC desc;
1355-
fb->GetDesc(&desc);
1355+
SurfaceClass::SurfaceDescription surfaceDesc;
1356+
surface->Get_Description(surfaceDesc);
13561357

1357-
RECT bounds;
1358-
GetWindowRect(_Hwnd,&bounds);
1358+
SurfaceClass* surfaceCopy = NEW_REF(SurfaceClass, (DX8Wrapper::_Create_DX8_Surface(surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format)));
1359+
DX8Wrapper::_Copy_DX8_Rects(surface->Peek_D3D_Surface(), NULL, 0, surfaceCopy->Peek_D3D_Surface(), NULL);
1360+
1361+
surface->Release_Ref();
1362+
surface = NULL;
13591363

1360-
D3DLOCKED_RECT lrect;
1364+
struct Rect
1365+
{
1366+
int Pitch;
1367+
void* pBits;
1368+
} lrect;
13611369

1362-
DX8_ErrorCode(fb->LockRect(&lrect,&bounds,D3DLOCK_READONLY));
1370+
lrect.pBits = surfaceCopy->Lock(&lrect.Pitch);
1371+
if (lrect.pBits == NULL)
1372+
{
1373+
surfaceCopy->Release_Ref();
1374+
return;
1375+
}
13631376

13641377
unsigned int x,y,index,index2,width,height;
13651378

1366-
width=bounds.right-bounds.left;
1367-
height=bounds.bottom-bounds.top;
1379+
width = surfaceDesc.Width;
1380+
height = surfaceDesc.Height;
13681381

13691382
unsigned char *image=W3DNEWARRAY unsigned char[3*width*height];
13701383

@@ -1383,7 +1396,9 @@ void WW3D::Make_Screen_Shot( const char * filename_base , const float gamma, con
13831396
}
13841397
}
13851398

1386-
fb->Release();
1399+
surfaceCopy->Unlock();
1400+
surfaceCopy->Release_Ref();
1401+
surfaceCopy = NULL;
13871402

13881403
switch (format) {
13891404
case TGA:
@@ -1682,24 +1697,37 @@ void WW3D::Update_Movie_Capture( void )
16821697
WWPROFILE("WW3D::Update_Movie_Capture");
16831698
WWDEBUG_SAY(( "Updating\n"));
16841699

1685-
// Lock front buffer and copy
1700+
// TheSuperHackers @bugfix xezon 21/05/2025 Get the back buffer and create a copy of the surface.
1701+
// Originally this code took the front buffer and tried to lock it. This does not work when the
1702+
// render view clips outside the desktop boundaries. It crashed the game.
1703+
SurfaceClass* surface = DX8Wrapper::_Get_DX8_Back_Buffer();
16861704

1687-
IDirect3DSurface8 *fb;
1688-
fb=DX8Wrapper::_Get_DX8_Front_Buffer();
1689-
D3DSURFACE_DESC desc;
1690-
fb->GetDesc(&desc);
1705+
SurfaceClass::SurfaceDescription surfaceDesc;
1706+
surface->Get_Description(surfaceDesc);
16911707

1692-
RECT bounds;
1693-
GetWindowRect(_Hwnd,&bounds);
1708+
SurfaceClass* surfaceCopy = NEW_REF(SurfaceClass, (DX8Wrapper::_Create_DX8_Surface(surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format)));
1709+
DX8Wrapper::_Copy_DX8_Rects(surface->Peek_D3D_Surface(), NULL, 0, surfaceCopy->Peek_D3D_Surface(), NULL);
1710+
1711+
surface->Release_Ref();
1712+
surface = NULL;
16941713

1695-
D3DLOCKED_RECT lrect;
1714+
struct Rect
1715+
{
1716+
int Pitch;
1717+
void* pBits;
1718+
} lrect;
16961719

1697-
DX8_ErrorCode(fb->LockRect(&lrect,&bounds,D3DLOCK_READONLY));
1720+
lrect.pBits = surfaceCopy->Lock(&lrect.Pitch);
1721+
if (lrect.pBits == NULL)
1722+
{
1723+
surfaceCopy->Release_Ref();
1724+
return;
1725+
}
16981726

16991727
unsigned int x,y,index,index2,width,height;
17001728

1701-
width=bounds.right-bounds.left;
1702-
height=bounds.bottom-bounds.top;
1729+
width = surfaceDesc.Width;
1730+
height = surfaceDesc.Height;
17031731

17041732
char *image=(char *)Movie->GetBuffer();
17051733

@@ -1718,7 +1746,9 @@ void WW3D::Update_Movie_Capture( void )
17181746
}
17191747
}
17201748

1721-
fb->Release();
1749+
surfaceCopy->Unlock();
1750+
surfaceCopy->Release_Ref();
1751+
surfaceCopy = NULL;
17221752

17231753
Movie->Grab(image);
17241754
#endif

GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3012,32 +3012,37 @@ void W3DDisplay::takeScreenShot(void)
30123012
done = true;
30133013
}
30143014

3015-
// Lock front buffer and copy
3015+
// TheSuperHackers @bugfix xezon 21/05/2025 Get the back buffer and create a copy of the surface.
3016+
// Originally this code took the front buffer and tried to lock it. This does not work when the
3017+
// render view clips outside the desktop boundaries. It crashed the game.
3018+
SurfaceClass* surface = DX8Wrapper::_Get_DX8_Back_Buffer();
30163019

3017-
IDirect3DSurface8 *fb;
3018-
fb=DX8Wrapper::_Get_DX8_Front_Buffer();
3019-
D3DSURFACE_DESC desc;
3020-
fb->GetDesc(&desc);
3020+
SurfaceClass::SurfaceDescription surfaceDesc;
3021+
surface->Get_Description(surfaceDesc);
30213022

3022-
RECT bounds;
3023-
POINT point;
3023+
SurfaceClass* surfaceCopy = NEW_REF(SurfaceClass, (DX8Wrapper::_Create_DX8_Surface(surfaceDesc.Width, surfaceDesc.Height, surfaceDesc.Format)));
3024+
DX8Wrapper::_Copy_DX8_Rects(surface->Peek_D3D_Surface(), NULL, 0, surfaceCopy->Peek_D3D_Surface(), NULL);
30243025

3025-
GetClientRect(ApplicationHWnd,&bounds);
3026-
point.x=bounds.left; point.y=bounds.top;
3027-
ClientToScreen(ApplicationHWnd, &point);
3028-
bounds.left=point.x; bounds.top=point.y;
3029-
point.x=bounds.right; point.y=bounds.bottom;
3030-
ClientToScreen(ApplicationHWnd, &point);
3031-
bounds.right=point.x; bounds.bottom=point.y;
3032-
3033-
D3DLOCKED_RECT lrect;
3026+
surface->Release_Ref();
3027+
surface = NULL;
30343028

3035-
DX8_ErrorCode(fb->LockRect(&lrect,&bounds,D3DLOCK_READONLY));
3029+
struct Rect
3030+
{
3031+
int Pitch;
3032+
void* pBits;
3033+
} lrect;
3034+
3035+
lrect.pBits = surfaceCopy->Lock(&lrect.Pitch);
3036+
if (lrect.pBits == NULL)
3037+
{
3038+
surfaceCopy->Release_Ref();
3039+
return;
3040+
}
30363041

30373042
unsigned int x,y,index,index2,width,height;
30383043

3039-
width=bounds.right-bounds.left;
3040-
height=bounds.bottom-bounds.top;
3044+
width = surfaceDesc.Width;
3045+
height = surfaceDesc.Height;
30413046

30423047
char *image=NEW char[3*width*height];
30433048
#ifdef CAPTURE_TO_TARGA
@@ -3057,7 +3062,9 @@ void W3DDisplay::takeScreenShot(void)
30573062
}
30583063
}
30593064

3060-
fb->Release();
3065+
surfaceCopy->Unlock();
3066+
surfaceCopy->Release_Ref();
3067+
surfaceCopy = NULL;
30613068

30623069
Targa targ;
30633070
memset(&targ.Header,0,sizeof(targ.Header));
@@ -3086,7 +3093,9 @@ void W3DDisplay::takeScreenShot(void)
30863093
}
30873094
}
30883095

3089-
fb->Release();
3096+
surfaceCopy->Unlock();
3097+
surfaceCopy->Release_Ref();
3098+
surfaceCopy = NULL;
30903099

30913100
//Flip the image
30923101
char *ptr,*ptr1;
@@ -3120,7 +3129,7 @@ void W3DDisplay::takeScreenShot(void)
31203129
TheInGameUI->message(TheGameText->fetch("GUI:ScreenCapture"), ufileName.str());
31213130
}
31223131

3123-
/** Start/Stop campturing an AVI movie*/
3132+
/** Start/Stop capturing an AVI movie*/
31243133
void W3DDisplay::toggleMovieCapture(void)
31253134
{
31263135
WW3D::Toggle_Movie_Capture("Movie",30);

0 commit comments

Comments
 (0)