From 5501d495c72d0cb8f2d82d4424e64d67a6a6a8ea Mon Sep 17 00:00:00 2001
From: Jaime Ita Passos <jp6781615@gmail.com>
Date: Wed, 27 Jan 2021 17:48:57 -0300
Subject: [PATCH] OpenGL backend: Manage uploaded GPU textures with an internal
 list Indirectly fixes the game doing whatever after freeing a patch. This
 commit implements a FTextureInfo struct type, instead of it being a typedef
 to the GLMipmap_s struct type.

---
 src/f_finale.c                   | 44 ++++++++---------
 src/hardware/hw_cache.c          |  1 -
 src/hardware/hw_data.h           | 19 ++++----
 src/hardware/hw_defs.h           | 11 ++++-
 src/hardware/hw_drv.h            |  8 ++-
 src/hardware/r_opengl/r_opengl.c | 84 ++++++++++++++++++++++----------
 src/sdl/hwsym_sdl.c              |  1 -
 src/sdl/i_video.c                |  1 -
 src/w_wad.c                      | 18 +------
 src/win32/win_dll.c              |  2 -
 src/z_zone.h                     |  3 +-
 11 files changed, 106 insertions(+), 86 deletions(-)

diff --git a/src/f_finale.c b/src/f_finale.c
index 2232b669f6..fdcfad2795 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2546,28 +2546,28 @@ static void F_UnloadAlacroixGraphics(SINT8 oldttscale)
 	oldttscale--; // zero-based index
 	for (i = 0; i < TTMAX_ALACROIX; i++)
 	{
-		if(ttembl[oldttscale][i]) { Z_Free(ttembl[oldttscale][i]); ttembl[oldttscale][i] = 0; }
-		if(ttribb[oldttscale][i]) { Z_Free(ttribb[oldttscale][i]); ttribb[oldttscale][i] = 0; }
-		if(ttsont[oldttscale][i]) { Z_Free(ttsont[oldttscale][i]); ttsont[oldttscale][i] = 0; }
-		if(ttrobo[oldttscale][i]) { Z_Free(ttrobo[oldttscale][i]); ttrobo[oldttscale][i] = 0; }
-		if(tttwot[oldttscale][i]) { Z_Free(tttwot[oldttscale][i]); tttwot[oldttscale][i] = 0; }
-		if(ttrbtx[oldttscale][i]) { Z_Free(ttrbtx[oldttscale][i]); ttrbtx[oldttscale][i] = 0; }
-		if(ttsoib[oldttscale][i]) { Z_Free(ttsoib[oldttscale][i]); ttsoib[oldttscale][i] = 0; }
-		if(ttsoif[oldttscale][i]) { Z_Free(ttsoif[oldttscale][i]); ttsoif[oldttscale][i] = 0; }
-		if(ttsoba[oldttscale][i]) { Z_Free(ttsoba[oldttscale][i]); ttsoba[oldttscale][i] = 0; }
-		if(ttsobk[oldttscale][i]) { Z_Free(ttsobk[oldttscale][i]); ttsobk[oldttscale][i] = 0; }
-		if(ttsodh[oldttscale][i]) { Z_Free(ttsodh[oldttscale][i]); ttsodh[oldttscale][i] = 0; }
-		if(tttaib[oldttscale][i]) { Z_Free(tttaib[oldttscale][i]); tttaib[oldttscale][i] = 0; }
-		if(tttaif[oldttscale][i]) { Z_Free(tttaif[oldttscale][i]); tttaif[oldttscale][i] = 0; }
-		if(tttaba[oldttscale][i]) { Z_Free(tttaba[oldttscale][i]); tttaba[oldttscale][i] = 0; }
-		if(tttabk[oldttscale][i]) { Z_Free(tttabk[oldttscale][i]); tttabk[oldttscale][i] = 0; }
-		if(tttabt[oldttscale][i]) { Z_Free(tttabt[oldttscale][i]); tttabt[oldttscale][i] = 0; }
-		if(tttaft[oldttscale][i]) { Z_Free(tttaft[oldttscale][i]); tttaft[oldttscale][i] = 0; }
-		if(ttknib[oldttscale][i]) { Z_Free(ttknib[oldttscale][i]); ttknib[oldttscale][i] = 0; }
-		if(ttknif[oldttscale][i]) { Z_Free(ttknif[oldttscale][i]); ttknif[oldttscale][i] = 0; }
-		if(ttknba[oldttscale][i]) { Z_Free(ttknba[oldttscale][i]); ttknba[oldttscale][i] = 0; }
-		if(ttknbk[oldttscale][i]) { Z_Free(ttknbk[oldttscale][i]); ttknbk[oldttscale][i] = 0; }
-		if(ttkndh[oldttscale][i]) { Z_Free(ttkndh[oldttscale][i]); ttkndh[oldttscale][i] = 0; }
+		if(ttembl[oldttscale][i]) { Patch_Free(ttembl[oldttscale][i]); ttembl[oldttscale][i] = 0; }
+		if(ttribb[oldttscale][i]) { Patch_Free(ttribb[oldttscale][i]); ttribb[oldttscale][i] = 0; }
+		if(ttsont[oldttscale][i]) { Patch_Free(ttsont[oldttscale][i]); ttsont[oldttscale][i] = 0; }
+		if(ttrobo[oldttscale][i]) { Patch_Free(ttrobo[oldttscale][i]); ttrobo[oldttscale][i] = 0; }
+		if(tttwot[oldttscale][i]) { Patch_Free(tttwot[oldttscale][i]); tttwot[oldttscale][i] = 0; }
+		if(ttrbtx[oldttscale][i]) { Patch_Free(ttrbtx[oldttscale][i]); ttrbtx[oldttscale][i] = 0; }
+		if(ttsoib[oldttscale][i]) { Patch_Free(ttsoib[oldttscale][i]); ttsoib[oldttscale][i] = 0; }
+		if(ttsoif[oldttscale][i]) { Patch_Free(ttsoif[oldttscale][i]); ttsoif[oldttscale][i] = 0; }
+		if(ttsoba[oldttscale][i]) { Patch_Free(ttsoba[oldttscale][i]); ttsoba[oldttscale][i] = 0; }
+		if(ttsobk[oldttscale][i]) { Patch_Free(ttsobk[oldttscale][i]); ttsobk[oldttscale][i] = 0; }
+		if(ttsodh[oldttscale][i]) { Patch_Free(ttsodh[oldttscale][i]); ttsodh[oldttscale][i] = 0; }
+		if(tttaib[oldttscale][i]) { Patch_Free(tttaib[oldttscale][i]); tttaib[oldttscale][i] = 0; }
+		if(tttaif[oldttscale][i]) { Patch_Free(tttaif[oldttscale][i]); tttaif[oldttscale][i] = 0; }
+		if(tttaba[oldttscale][i]) { Patch_Free(tttaba[oldttscale][i]); tttaba[oldttscale][i] = 0; }
+		if(tttabk[oldttscale][i]) { Patch_Free(tttabk[oldttscale][i]); tttabk[oldttscale][i] = 0; }
+		if(tttabt[oldttscale][i]) { Patch_Free(tttabt[oldttscale][i]); tttabt[oldttscale][i] = 0; }
+		if(tttaft[oldttscale][i]) { Patch_Free(tttaft[oldttscale][i]); tttaft[oldttscale][i] = 0; }
+		if(ttknib[oldttscale][i]) { Patch_Free(ttknib[oldttscale][i]); ttknib[oldttscale][i] = 0; }
+		if(ttknif[oldttscale][i]) { Patch_Free(ttknif[oldttscale][i]); ttknif[oldttscale][i] = 0; }
+		if(ttknba[oldttscale][i]) { Patch_Free(ttknba[oldttscale][i]); ttknba[oldttscale][i] = 0; }
+		if(ttknbk[oldttscale][i]) { Patch_Free(ttknbk[oldttscale][i]); ttknbk[oldttscale][i] = 0; }
+		if(ttkndh[oldttscale][i]) { Patch_Free(ttkndh[oldttscale][i]); ttkndh[oldttscale][i] = 0; }
 	}
 	ttloaded[oldttscale] = false;
 }
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 37e9f690fe..83a4e2e03d 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -1091,7 +1091,6 @@ void HWR_UnlockCachedPatch(GLPatch_t *gpatch)
 		return;
 
 	Z_ChangeTag(gpatch->mipmap->data, PU_HWRCACHE_UNLOCKED);
-	Z_ChangeTag(gpatch, PU_HWRPATCHINFO_UNLOCKED);
 }
 
 static const INT32 picmode2GR[] =
diff --git a/src/hardware/hw_data.h b/src/hardware/hw_data.h
index 11e41b18ac..ce65276665 100644
--- a/src/hardware/hw_data.h
+++ b/src/hardware/hw_data.h
@@ -48,18 +48,19 @@ struct GLColormap_s
 typedef struct GLColormap_s GLColormap_t;
 
 
-// data holds the address of the graphics data cached in heap memory
-//                NULL if the texture is not in Doom heap cache.
+// Texture information (misleadingly named "mipmap" all over the code.)
+// The *data pointer holds the address of the graphics data cached in heap memory.
+// NULL if the texture is not in SRB2's heap cache.
 struct GLMipmap_s
 {
-	// for TexDownloadMipMap
+	// for UpdateTexture
 	GLTextureFormat_t     format;
 	void                 *data;
 
 	UINT32                flags;
 	UINT16                height;
 	UINT16                width;
-	UINT32                downloaded;     // The GPU has this texture.
+	UINT32                downloaded; // The GPU has this texture.
 
 	struct GLMipmap_s    *nextcolormap;
 	struct GLColormap_s  *colormap;
@@ -70,22 +71,22 @@ typedef struct GLMipmap_s GLMipmap_t;
 
 
 //
-// Doom texture info, as cached for hardware rendering
+// Level textures, as cached for hardware rendering.
 //
 struct GLMapTexture_s
 {
 	GLMipmap_t  mipmap;
-	float       scaleX;             //used for scaling textures on walls
+	float       scaleX; // Used for scaling textures on walls
 	float       scaleY;
 };
 typedef struct GLMapTexture_s GLMapTexture_t;
 
 
-// a cached patch as converted to hardware format
+// Patch information for the hardware renderer.
 struct GLPatch_s
 {
-	float               max_s,max_t;
-	GLMipmap_t          *mipmap;
+	GLMipmap_t *mipmap; // Texture data. Allocated whenever the patch is.
+	float       max_s, max_t;
 };
 typedef struct GLPatch_s GLPatch_t;
 
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index a782762a38..bd6afc74fa 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -255,7 +255,16 @@ enum ETextureFlags
 	TF_TRANSPARENT = 0x00000040,        // texture with some alpha == 0
 };
 
-typedef struct GLMipmap_s FTextureInfo;
+struct FTextureInfo
+{
+	UINT32 width, height;
+	UINT32 downloaded;
+	UINT32 format;
+
+	struct GLMipmap_s *texture;
+	struct FTextureInfo *prev, *next;
+};
+typedef struct FTextureInfo FTextureInfo;
 
 // jimita 14032019
 struct FLightInfo
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index 5a2e0e44ea..da4ee86143 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -40,13 +40,12 @@ EXPORT void HWRAPI(DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutV
 EXPORT void HWRAPI(RenderSkyDome) (gl_sky_t *sky);
 EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags);
 EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor);
-EXPORT void HWRAPI(SetTexture) (FTextureInfo *TexInfo);
-EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *TexInfo);
-EXPORT void HWRAPI(DeleteTexture) (FTextureInfo *TexInfo);
+EXPORT void HWRAPI(SetTexture) (GLMipmap_t *TexInfo);
+EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *TexInfo);
+EXPORT void HWRAPI(DeleteTexture) (GLMipmap_t *TexInfo);
 EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data);
 EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip);
 EXPORT void HWRAPI(ClearMipMapCache) (void);
-EXPORT void HWRAPI(ClearCacheList) (void);
 
 //Hurdler: added for backward compatibility
 EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value);
@@ -101,7 +100,6 @@ struct hwdriver_s
 	ReadRect            pfnReadRect;
 	GClipRect           pfnGClipRect;
 	ClearMipMapCache    pfnClearMipMapCache;
-	ClearCacheList      pfnClearCacheList;
 	SetSpecialState     pfnSetSpecialState;//Hurdler: added for backward compatibility
 	DrawModel           pfnDrawModel;
 	CreateModelVBOs     pfnCreateModelVBOs;
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 8cd948eead..2568a7d08c 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -58,8 +58,9 @@ static  GLuint      tex_downloaded  = 0;
 static  GLfloat     fov             = 90.0f;
 static  FBITFIELD   CurrentPolyFlags;
 
-static  FTextureInfo *gl_cachetail = NULL;
-static  FTextureInfo *gl_cachehead = NULL;
+// Linked list of all textures.
+static FTextureInfo *TexCacheTail = NULL;
+static FTextureInfo *TexCacheHead = NULL;
 
 RGBA_t  myPaletteData[256];
 GLint   screen_width    = 0;               // used by Draw2DLine()
@@ -1287,10 +1288,30 @@ void SetStates(void)
 // -----------------+
 // DeleteTexture    : Deletes a texture from the GPU and frees its data
 // -----------------+
-EXPORT void HWRAPI(DeleteTexture) (FTextureInfo *pTexInfo)
+EXPORT void HWRAPI(DeleteTexture) (GLMipmap_t *pTexInfo)
 {
-	if (pTexInfo->downloaded)
+	FTextureInfo *head = TexCacheHead;
+
+	if (!pTexInfo)
+		return;
+	else if (pTexInfo->downloaded)
 		pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
+
+	while (head)
+	{
+		if (head->downloaded == pTexInfo->downloaded)
+		{
+			if (head->next)
+				head->next->prev = head->prev;
+			if (head->prev)
+				head->prev->next = head->next;
+			free(head);
+			break;
+		}
+
+		head = head->next;
+	}
+
 	pTexInfo->downloaded = 0;
 }
 
@@ -1303,23 +1324,26 @@ void Flush(void)
 {
 	//GL_DBG_Printf ("HWR_Flush()\n");
 
-	while (gl_cachehead)
+	while (TexCacheHead)
 	{
-		DeleteTexture(gl_cachehead);
-		gl_cachehead = gl_cachehead->nextmipmap;
-	}
+		FTextureInfo *pTexInfo = TexCacheHead;
+		GLMipmap_t *texture = pTexInfo->texture;
 
-	ClearCacheList(); //Hurdler: well, gl_cachehead is already NULL
-	tex_downloaded = 0;
-}
+		if (pTexInfo->downloaded)
+		{
+			pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
+			pTexInfo->downloaded = 0;
+		}
 
+		if (texture)
+			texture->downloaded = 0;
 
-// -----------------+
-// ClearCacheList   : Clears the texture cache tail and head
-// -----------------+
-EXPORT void HWRAPI(ClearCacheList) (void)
-{
-	gl_cachetail = gl_cachehead = NULL;
+		TexCacheHead = pTexInfo->next;
+		free(pTexInfo);
+	}
+
+	TexCacheTail = TexCacheHead = NULL; //Hurdler: well, TexCacheHead is already NULL
+	tex_downloaded = 0;
 }
 
 
@@ -1718,7 +1742,7 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags)
 // -----------------+
 // UpdateTexture    : Updates the texture data.
 // -----------------+
-EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *pTexInfo)
+EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo)
 {
 	// Download a mipmap
 	boolean updatemipmap = true;
@@ -1920,7 +1944,7 @@ EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *pTexInfo)
 // -----------------+
 // SetTexture       : The mipmap becomes the current texture source
 // -----------------+
-EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
+EXPORT void HWRAPI(SetTexture) (GLMipmap_t *pTexInfo)
 {
 	if (!pTexInfo)
 	{
@@ -1937,17 +1961,25 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
 	}
 	else
 	{
+		FTextureInfo *newTex = calloc(1, sizeof (*newTex));
+
 		UpdateTexture(pTexInfo);
-		pTexInfo->nextmipmap = NULL;
+
+		newTex->texture = pTexInfo;
+		newTex->downloaded = (UINT32)pTexInfo->downloaded;
+		newTex->width = (UINT32)pTexInfo->width;
+		newTex->height = (UINT32)pTexInfo->height;
+		newTex->format = (UINT32)pTexInfo->format;
 
 		// insertion at the tail
-		if (gl_cachetail)
+		if (TexCacheTail)
 		{
-			gl_cachetail->nextmipmap = pTexInfo;
-			gl_cachetail = pTexInfo;
+			newTex->prev = TexCacheTail;
+			TexCacheTail->next = newTex;
+			TexCacheTail = newTex;
 		}
 		else // initialization of the linked list
-			gl_cachetail = gl_cachehead = pTexInfo;
+			TexCacheTail = TexCacheHead = newTex;
 	}
 }
 
@@ -3011,7 +3043,7 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
 
 EXPORT INT32  HWRAPI(GetTextureUsed) (void)
 {
-	FTextureInfo *tmp = gl_cachehead;
+	FTextureInfo *tmp = TexCacheHead;
 	INT32 res = 0;
 
 	while (tmp)
@@ -3028,7 +3060,7 @@ EXPORT INT32  HWRAPI(GetTextureUsed) (void)
 
 		// Add it up!
 		res += tmp->height*tmp->width*bpp;
-		tmp = tmp->nextmipmap;
+		tmp = tmp->next;
 	}
 
 	return res;
diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c
index 3985086626..96e3d7d692 100644
--- a/src/sdl/hwsym_sdl.c
+++ b/src/sdl/hwsym_sdl.c
@@ -90,7 +90,6 @@ void *hwSym(const char *funcName,void *handle)
 	GETFUNC(ReadRect);
 	GETFUNC(GClipRect);
 	GETFUNC(ClearMipMapCache);
-	GETFUNC(ClearCacheList);
 	GETFUNC(SetSpecialState);
 	GETFUNC(GetTextureUsed);
 	GETFUNC(DrawModel);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 5ebff87001..0ed10463fc 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1862,7 +1862,6 @@ void VID_StartupOpenGL(void)
 		HWD.pfnReadRect         = hwSym("ReadRect",NULL);
 		HWD.pfnGClipRect        = hwSym("GClipRect",NULL);
 		HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
-		HWD.pfnClearCacheList   = hwSym("ClearCacheList",NULL);
 		HWD.pfnSetSpecialState  = hwSym("SetSpecialState",NULL);
 		HWD.pfnSetPalette       = hwSym("SetPalette",NULL);
 		HWD.pfnGetTextureUsed   = hwSym("GetTextureUsed",NULL);
diff --git a/src/w_wad.c b/src/w_wad.c
index 6566800c03..2cbcdecb54 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1682,26 +1682,12 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 
 		// read the lump in full
 		W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0);
+		ptr = lumpdata;
 
 #ifndef NO_PNG_LUMPS
-		// lump is a png so convert it
 		if (Picture_IsLumpPNG((UINT8 *)lumpdata, len))
-		{
-			size_t newlen;
-			void *converted = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &newlen, 0);
-			ptr = Z_Malloc(newlen, PU_STATIC, NULL);
-			M_Memcpy(ptr, converted, newlen);
-			Z_Free(converted);
-			len = newlen;
-		}
-		else // just copy it into the patch cache
+			ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0);
 #endif
-		{
-			ptr = Z_Malloc(len, PU_STATIC, NULL);
-			M_Memcpy(ptr, lumpdata, len);
-		}
-
-		Z_Free(lumpdata);
 
 		dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]);
 		Patch_Create(ptr, len, dest);
diff --git a/src/win32/win_dll.c b/src/win32/win_dll.c
index d942d8cd40..4743cec34b 100644
--- a/src/win32/win_dll.c
+++ b/src/win32/win_dll.c
@@ -111,7 +111,6 @@ static loadfunc_t hwdFuncTable[] = {
 	{"ReadRect@24",         &hwdriver.pfnReadRect},
 	{"GClipRect@20",        &hwdriver.pfnGClipRect},
 	{"ClearMipMapCache@0",  &hwdriver.pfnClearMipMapCache},
-	{"ClearCacheList@0",    &hwdriver.pfnClearCacheList},
 	{"SetSpecialState@8",   &hwdriver.pfnSetSpecialState},
 	{"DrawModel@16",        &hwdriver.pfnDrawModel},
 	{"SetTransform@4",      &hwdriver.pfnSetTransform},
@@ -145,7 +144,6 @@ static loadfunc_t hwdFuncTable[] = {
 	{"ReadRect",            &hwdriver.pfnReadRect},
 	{"GClipRect",           &hwdriver.pfnGClipRect},
 	{"ClearMipMapCache",    &hwdriver.pfnClearMipMapCache},
-	{"ClearCacheList",      &hwdriver.pfnClearCacheList},
 	{"SetSpecialState",     &hwdriver.pfnSetSpecialState},
 	{"DrawModel",           &hwdriver.pfnDrawModel},
 	{"SetTransform",        &hwdriver.pfnSetTransform},
diff --git a/src/z_zone.h b/src/z_zone.h
index e80a45e7fb..7b58be8f3f 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -68,8 +68,7 @@ enum
 	PU_HWRCACHE_UNLOCKED     = 102, // 'unlocked' PU_HWRCACHE memory:
 									// 'second-level' cache for graphics
                                     // stored in hardware format and downloaded as needed
-	PU_HWRPATCHINFO_UNLOCKED    = 103, // 'unlocked' PU_HWRPATCHINFO memory
-	PU_HWRMODELTEXTURE_UNLOCKED = 104, // 'unlocked' PU_HWRMODELTEXTURE memory
+	PU_HWRMODELTEXTURE_UNLOCKED = 103, // 'unlocked' PU_HWRMODELTEXTURE memory
 };
 
 //
-- 
GitLab