08 Feb 2018
This commit is contained in:
parent
811947e623
commit
89c67d9ce5
|
@ -19,15 +19,7 @@ GNU General Public License for more details.
|
|||
#include "lightstyle.h"
|
||||
#include "dlight.h"
|
||||
|
||||
// changes for version 28
|
||||
// replace decal_t from software declaration to hardware (matched to normal HL)
|
||||
// mextrasurf_t->increased limit of reserved fields (up from 7 to 32)
|
||||
// replace R_StoreEfrags with him extended version
|
||||
// formed group for BSP decal manipulating
|
||||
// move misc functions at end of the interface
|
||||
// added new export for clearing studio decals
|
||||
|
||||
#define CL_RENDER_INTERFACE_VERSION 36
|
||||
#define CL_RENDER_INTERFACE_VERSION 37 // Xash3D 1.0
|
||||
#define MAX_STUDIO_DECALS 4096 // + unused space of BSP decals
|
||||
|
||||
// render info parms
|
||||
|
@ -69,9 +61,9 @@ GNU General Public License for more details.
|
|||
#define PARM_STENCIL_ACTIVE 36
|
||||
#define PARM_WATER_ALPHA 37
|
||||
|
||||
// skybox ordering
|
||||
enum
|
||||
{
|
||||
// skybox ordering
|
||||
SKYBOX_RIGHT = 0,
|
||||
SKYBOX_BACK,
|
||||
SKYBOX_LEFT,
|
||||
|
@ -86,8 +78,8 @@ typedef enum
|
|||
TF_KEEP_SOURCE = (1<<1), // some images keep source
|
||||
TF_NOFLIP_TGA = (1<<2), // Steam background completely ignore tga attribute 0x20
|
||||
TF_EXPAND_SOURCE = (1<<3), // Don't keep source as 8-bit expand to RGBA
|
||||
// reserved
|
||||
// reserved
|
||||
TF_TEXTURE_2D_ARRAY = (1<<4), // this is 2D texture array (multi-layers)
|
||||
TF_TEXTURE_RECTANGLE= (1<<5), // this is GL_TEXTURE_RECTANGLE
|
||||
TF_CUBEMAP = (1<<6), // it's cubemap texture
|
||||
TF_DEPTHMAP = (1<<7), // custom texture filter used
|
||||
TF_QUAKEPAL = (1<<8), // image has an quake1 palette
|
||||
|
@ -104,9 +96,9 @@ typedef enum
|
|||
TF_BORDER = (1<<19), // zero clamp for projected textures
|
||||
TF_TEXTURE_3D = (1<<20), // this is GL_TEXTURE_3D
|
||||
TF_ATLAS_PAGE = (1<<21), // bit who indicate lightmap page or deluxemap page
|
||||
TF_TEXTURE_RECTANGLE= (1<<22), // this is GL_TEXTURE_RECTANGLE
|
||||
// reserved
|
||||
TF_TEXTURE_2D_ARRAY = (1<<24), // this is 2D texture array (multi-layers)
|
||||
// reserved
|
||||
// reserved
|
||||
TF_IMG_UPLOADED = (1<<25), // this is set for first time when called glTexImage, otherwise it will be call glTexSubImage
|
||||
TF_ARB_FLOAT = (1<<26), // float textures
|
||||
TF_NOCOMPARE = (1<<27), // disable comparing for depth textures
|
||||
|
@ -126,15 +118,17 @@ typedef enum
|
|||
GLES_WRAPPER_NANOGL, // used on GLES platforms
|
||||
} gles_wrapper_t;
|
||||
|
||||
// 12 bytes here
|
||||
// 30 bytes here
|
||||
typedef struct modelstate_s
|
||||
{
|
||||
short sequence;
|
||||
short frame; // 10 bits multiple by 4, should be enough
|
||||
byte blending[2];
|
||||
byte controller[4];
|
||||
byte poseparam[16];
|
||||
byte body;
|
||||
byte skin;
|
||||
short scale; // model scale (multiplied by 16)
|
||||
} modelstate_t;
|
||||
|
||||
typedef struct decallist_s
|
||||
|
@ -195,6 +189,9 @@ typedef struct render_api_s
|
|||
void (*AVI_UploadRawFrame)( int texture, int cols, int rows, int width, int height, const byte *data );
|
||||
void (*AVI_FreeVideo)( void *Avi );
|
||||
int (*AVI_IsActive)( void *Avi );
|
||||
void (*AVI_Reserved0)( void ); // for potential interface expansion without broken compatibility
|
||||
void (*AVI_Reserved1)( void );
|
||||
void (*AVI_Reserved2)( void );
|
||||
|
||||
// glState related calls (must use this instead of normal gl-calls to prevent de-synchornize local states between engine and the client)
|
||||
void (*GL_Bind)( int tmu, unsigned int texnum );
|
||||
|
@ -213,22 +210,29 @@ typedef struct render_api_s
|
|||
// Misc renderer functions
|
||||
void (*GL_DrawParticles)( const struct ref_viewpass_s *rvp, qboolean trans_pass, float frametime );
|
||||
void (*EnvShot)( const float *vieworg, const char *name, qboolean skyshot, int shotsize ); // creates a cubemap or skybox into gfx\env folder
|
||||
int (*COM_CompareFileTime)( const char *filename1, const char *filename2, int *iCompare );
|
||||
void (*Host_Error)( const char *error, ... ); // cause Host Error
|
||||
int (*SPR_LoadExt)( const char *szPicName, unsigned int texFlags ); // extended version of SPR_Load
|
||||
colorVec (*LightVec)( const float *start, const float *end, float *lightspot );
|
||||
struct mstudiotex_s *( *StudioGetTexture )( struct cl_entity_s *e );
|
||||
const struct ref_overview_s *( *GetOverviewParms )( void );
|
||||
void (*S_FadeMusicVolume)( float fadePercent ); // fade background track (0-100 percents)
|
||||
void (*SetRandomSeed)( long lSeed ); // set custom seed for RANDOM_FLOAT\RANDOM_LONG for predictable random
|
||||
void (*R_Reserved0)( void ); // for potential interface expansion without broken compatibility
|
||||
void (*R_Reserved1)( void );
|
||||
void (*R_Reserved2)( void );
|
||||
|
||||
// static allocations
|
||||
void *(*pfnMemAlloc)( size_t cb, const char *filename, const int fileline );
|
||||
void (*pfnMemFree)( void *mem, const char *filename, const int fileline );
|
||||
// find in files
|
||||
|
||||
// engine utils (not related with render API but placed here)
|
||||
char **(*pfnGetFilesList)( const char *pattern, int *numFiles, int gamedironly );
|
||||
unsigned long (*pfnFileBufferCRC32)( const void *buffer, const int length );
|
||||
int (*COM_CompareFileTime)( const char *filename1, const char *filename2, int *iCompare );
|
||||
void (*Host_Error)( const char *error, ... ); // cause Host Error
|
||||
void* ( *pfnGetModel )( int modelindex );
|
||||
float (*pfnTime)( void );
|
||||
// ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 35
|
||||
float (*pfnTime)( void ); // Sys_DoubleTime
|
||||
void (*Cvar_Set)( char *name, char *value );
|
||||
void (*S_FadeMusicVolume)( float fadePercent ); // fade background track (0-100 percents)
|
||||
void (*SetRandomSeed)( long lSeed ); // set custom seed for RANDOM_FLOAT\RANDOM_LONG for predictable random
|
||||
// ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 37
|
||||
} render_api_t;
|
||||
|
||||
// render callbacks
|
||||
|
|
|
@ -57,7 +57,6 @@ typedef struct triangleapi_s
|
|||
void (*LightAtPoint)( float *pos, float *value );
|
||||
void (*Color4fRendermode)( float r, float g, float b, float a, int rendermode );
|
||||
void (*FogParams)( float flDensity, int iFogSkybox );
|
||||
colorVec (*LightVec)( const float *start, const float *end, float *lightspot );
|
||||
} triangleapi_t;
|
||||
|
||||
#endif//TRIANGLEAPI_H
|
|
@ -80,7 +80,6 @@ typedef struct
|
|||
float size;
|
||||
} daliashdr_t;
|
||||
|
||||
// TODO: could be shorts
|
||||
typedef struct
|
||||
{
|
||||
int onseam;
|
||||
|
|
|
@ -1298,7 +1298,6 @@ qboolean CL_GetEntitySpatialization( channel_t *ch )
|
|||
VectorAdd( ch->origin, ent->curstate.origin, ch->origin );
|
||||
|
||||
// setup mins\maxs
|
||||
// FIXME: should to expand them for rotating bmodels?
|
||||
VectorAdd( ent->curstate.mins, ent->curstate.origin, ch->absmin );
|
||||
VectorAdd( ent->curstate.maxs, ent->curstate.origin, ch->absmax );
|
||||
|
||||
|
|
|
@ -1922,25 +1922,6 @@ static float pfnGetClientMaxspeed( void )
|
|||
return cl.local.maxspeed;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnCheckParm
|
||||
|
||||
=============
|
||||
*/
|
||||
static int pfnCheckParm( char *parm, char **ppnext )
|
||||
{
|
||||
static char str[64];
|
||||
|
||||
if( Sys_GetParmFromCmdLine( parm, str ))
|
||||
{
|
||||
// get the pointer on cmdline param
|
||||
if( ppnext ) *ppnext = str;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnGetMousePosition
|
||||
|
@ -3426,7 +3407,6 @@ triangleapi_t gTriApi =
|
|||
TriLightAtPoint,
|
||||
TriColor4fRendermode,
|
||||
TriFogParams,
|
||||
R_LightVec, // Xash3D added
|
||||
};
|
||||
|
||||
static efx_api_t gEfxApi =
|
||||
|
@ -3622,7 +3602,7 @@ static cl_enginefunc_t gEngfuncs =
|
|||
pfnPhysInfo_ValueForKey,
|
||||
pfnServerInfo_ValueForKey,
|
||||
pfnGetClientMaxspeed,
|
||||
pfnCheckParm,
|
||||
COM_CheckParm,
|
||||
Key_Event,
|
||||
pfnGetMousePosition,
|
||||
pfnIsNoClipping,
|
||||
|
|
|
@ -1390,7 +1390,7 @@ void CL_ParseStudioDecal( sizebuf_t *msg )
|
|||
vec3_t start, pos;
|
||||
int decalIndex, entityIndex;
|
||||
int modelIndex = 0;
|
||||
int flags;
|
||||
int i, flags;
|
||||
|
||||
MSG_ReadVec3Coord( msg, pos );
|
||||
MSG_ReadVec3Coord( msg, start );
|
||||
|
@ -1402,13 +1402,14 @@ void CL_ParseStudioDecal( sizebuf_t *msg )
|
|||
state.frame = MSG_ReadShort( msg );
|
||||
state.blending[0] = MSG_ReadByte( msg );
|
||||
state.blending[1] = MSG_ReadByte( msg );
|
||||
state.controller[0] = MSG_ReadByte( msg );
|
||||
state.controller[1] = MSG_ReadByte( msg );
|
||||
state.controller[2] = MSG_ReadByte( msg );
|
||||
state.controller[3] = MSG_ReadByte( msg );
|
||||
for( i = 0; i < 4; i++ )
|
||||
state.controller[i] = MSG_ReadByte( msg );
|
||||
for( i = 0; i < 16; i++ )
|
||||
state.poseparam[i] = MSG_ReadByte( msg );
|
||||
modelIndex = MSG_ReadWord( msg );
|
||||
state.body = MSG_ReadByte( msg );
|
||||
state.skin = MSG_ReadByte( msg );
|
||||
state.scale = MSG_ReadWord( msg );
|
||||
|
||||
if( clgame.drawFuncs.R_StudioDecalShoot != NULL )
|
||||
{
|
||||
|
|
|
@ -549,7 +549,7 @@ qboolean SCR_LoadVariableWidthFont( const char *fontname )
|
|||
================
|
||||
SCR_LoadCreditsFont
|
||||
|
||||
FIXME: INTRESOURCE
|
||||
INTERNAL RESOURCE
|
||||
================
|
||||
*/
|
||||
void SCR_LoadCreditsFont( void )
|
||||
|
@ -565,7 +565,7 @@ void SCR_LoadCreditsFont( void )
|
|||
================
|
||||
SCR_InstallParticlePalette
|
||||
|
||||
FIXME: INTRESOURCE
|
||||
INTERNAL RESOURCE
|
||||
================
|
||||
*/
|
||||
void SCR_InstallParticlePalette( void )
|
||||
|
@ -606,7 +606,7 @@ void SCR_InstallParticlePalette( void )
|
|||
================
|
||||
SCR_RegisterTextures
|
||||
|
||||
FIXME: INTRESOURCE
|
||||
INTERNAL RESOURCE
|
||||
================
|
||||
*/
|
||||
void SCR_RegisterTextures( void )
|
||||
|
|
|
@ -50,7 +50,7 @@ model_t *cl_sprite_glow = NULL;
|
|||
================
|
||||
CL_LoadClientSprites
|
||||
|
||||
FIXME: INTRESOURCE
|
||||
INTERNAL RESOURCE
|
||||
================
|
||||
*/
|
||||
void CL_LoadClientSprites( void )
|
||||
|
|
|
@ -204,8 +204,6 @@ void R_BeamSetAttributes( BEAM *pbeam, float r, float g, float b, float framerat
|
|||
{
|
||||
pbeam->frame = (float)startFrame;
|
||||
pbeam->frameRate = framerate;
|
||||
|
||||
// FIXME: pass through lightgammatable ?
|
||||
pbeam->r = r;
|
||||
pbeam->g = g;
|
||||
pbeam->b = b;
|
||||
|
|
|
@ -1426,6 +1426,9 @@ static render_api_t gRenderAPI =
|
|||
R_UploadStretchRaw,
|
||||
AVI_FreeVideo,
|
||||
AVI_IsActive,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
GL_Bind,
|
||||
GL_SelectTexture,
|
||||
GL_LoadTexMatrixExt,
|
||||
|
@ -1440,19 +1443,24 @@ static render_api_t gRenderAPI =
|
|||
NULL,
|
||||
CL_DrawParticlesExternal,
|
||||
R_EnvShot,
|
||||
COM_CompareFileTime,
|
||||
Host_Error,
|
||||
pfnSPR_LoadExt,
|
||||
R_LightVec,
|
||||
R_StudioGetTexture,
|
||||
GL_GetOverviewParms,
|
||||
S_FadeMusicVolume,
|
||||
COM_SetRandomSeed,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
R_Mem_Alloc,
|
||||
R_Mem_Free,
|
||||
pfnGetFilesList,
|
||||
pfnFileBufferCRC32,
|
||||
COM_CompareFileTime,
|
||||
Host_Error,
|
||||
Mod_Handle,
|
||||
pfnTime,
|
||||
Cvar_Set,
|
||||
S_FadeMusicVolume,
|
||||
COM_SetRandomSeed,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -406,7 +406,6 @@ void CL_DrawParticles( double frametime )
|
|||
|
||||
p->color = bound( 0, p->color, 255 );
|
||||
pColor = &clgame.palette[p->color];
|
||||
// FIXME: should we pass color through lightgamma table?
|
||||
|
||||
alpha = 255 * (p->die - cl.time) * 16.0f;
|
||||
if( alpha > 255 || p->type == pt_static )
|
||||
|
|
|
@ -752,6 +752,25 @@ int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCom
|
|||
return bRet;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
COM_CheckParm
|
||||
|
||||
=============
|
||||
*/
|
||||
int COM_CheckParm( char *parm, char **ppnext )
|
||||
{
|
||||
static char str[64];
|
||||
|
||||
if( Sys_GetParmFromCmdLine( parm, str ))
|
||||
{
|
||||
// get the pointer on cmdline param
|
||||
if( ppnext ) *ppnext = str;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnTime
|
||||
|
|
|
@ -20,6 +20,20 @@ GNU General Public License for more details.
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
===================================================================================================================================
|
||||
Legend:
|
||||
|
||||
INTERNAL RESOURCE - function contain hardcoded path to resource that engine required (optional in most cases)
|
||||
TODO - some functionality not impemented but planned
|
||||
FIXME - code doesn't working properly in some rare cases
|
||||
HACKHACK - unexpected behavior on some input params (or something like)
|
||||
BUGBUG - code doesn't working properly in most cases!
|
||||
TESTTEST - this code may be unstable and needs to be more tested
|
||||
g-cont: - notes from engine author
|
||||
===================================================================================================================================
|
||||
*/
|
||||
|
||||
// disable some warnings
|
||||
#pragma warning(disable : 4244) // MIPS
|
||||
#pragma warning(disable : 4018) // signed/unsigned mismatch
|
||||
|
@ -697,6 +711,7 @@ void COM_TrimSpace( const char *source, char *dest );
|
|||
edict_t* pfnPEntityOfEntIndex( int iEntIndex );
|
||||
void pfnGetModelBounds( model_t *mod, float *mins, float *maxs );
|
||||
void pfnCVarDirectSet( cvar_t *var, const char *szValue );
|
||||
int COM_CheckParm( char *parm, char **ppnext );
|
||||
void pfnGetGameDir( char *szGetGameDir );
|
||||
int pfnDecalIndex( const char *m );
|
||||
int pfnGetModelType( model_t *mod );
|
||||
|
|
|
@ -630,7 +630,7 @@ static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font
|
|||
================
|
||||
Con_LoadConsoleFont
|
||||
|
||||
FIXME: INTRESOURCE
|
||||
INTERNAL RESOURCE
|
||||
================
|
||||
*/
|
||||
static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
|
||||
|
@ -2224,8 +2224,7 @@ void Con_CharEvent( int key )
|
|||
=========
|
||||
Con_VidInit
|
||||
|
||||
reload backgrounds
|
||||
FIXME: INTRESOURCE
|
||||
INTERNAL RESOURCE
|
||||
=========
|
||||
*/
|
||||
void Con_VidInit( void )
|
||||
|
|
|
@ -97,13 +97,10 @@ void Image_DXTGetPixelFormat( dds_t *hdr )
|
|||
{
|
||||
uint bits = hdr->dsPixelFormat.dwRGBBitCount;
|
||||
|
||||
// all volume textures I've seem so far didn't have the DDS_COMPLEX flag set,
|
||||
// even though this is normally required. But because noone does set it,
|
||||
// also read images without it (TODO: check file size for 3d texture?)
|
||||
if( !( hdr->dsCaps.dwCaps2 & DDS_VOLUME ))
|
||||
if( !FBitSet( hdr->dsCaps.dwCaps2, DDS_VOLUME ))
|
||||
hdr->dwDepth = 1;
|
||||
|
||||
if( hdr->dsPixelFormat.dwFlags & DDS_FOURCC )
|
||||
if( FBitSet( hdr->dsPixelFormat.dwFlags, DDS_FOURCC ))
|
||||
{
|
||||
switch( hdr->dsPixelFormat.dwFourCC )
|
||||
{
|
||||
|
@ -311,7 +308,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, size_t filesize )
|
|||
else if( image.type == PF_DXT5 && Image_CheckDXT5Alpha( &header, fin ))
|
||||
SetBits( image.flags, IMAGE_HAS_ALPHA );
|
||||
if( !FBitSet( header.dsPixelFormat.dwFlags, DDS_LUMINANCE ))
|
||||
SetBits( image.flags, IMAGE_HAS_COLOR ); // FIXME: analyze colors
|
||||
SetBits( image.flags, IMAGE_HAS_COLOR );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -694,7 +694,6 @@ void QuaternionSlerpNoAlign( const vec4_t p, const vec4_t q, float t, vec4_t qt
|
|||
}
|
||||
else
|
||||
{
|
||||
// TODO: add short circuit for cosom == 1.0f?
|
||||
sclp = 1.0f - t;
|
||||
sclq = t;
|
||||
}
|
||||
|
|
|
@ -962,7 +962,6 @@ Delta_CompareField
|
|||
|
||||
compare fields by offsets
|
||||
assume from and to is valid
|
||||
TESTTEST: clamp all fields and multiply by specified value before comparing
|
||||
=====================
|
||||
*/
|
||||
qboolean Delta_CompareField( delta_t *pField, void *from, void *to, float timebase )
|
||||
|
|
|
@ -106,6 +106,7 @@ typedef struct server_physics_api_s
|
|||
const byte *(*pfnLoadImagePixels)( const char *filename, int *width, int *height );
|
||||
|
||||
const char* (*pfnGetModelName)( int modelindex );
|
||||
int (*pfnCheckParm)( char *parm, char **ppnext );
|
||||
} server_physics_api_t;
|
||||
|
||||
// physic callbacks
|
||||
|
|
|
@ -445,6 +445,8 @@ NOTE: static decals only accepted when game is loading
|
|||
*/
|
||||
void SV_CreateStudioDecal( sizebuf_t *msg, const float *origin, const float *start, int decalIndex, int entityIndex, int modelIndex, int flags, modelstate_t *state )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( msg == &sv.signon && sv.state != ss_loading )
|
||||
return;
|
||||
|
||||
|
@ -456,7 +458,7 @@ void SV_CreateStudioDecal( sizebuf_t *msg, const float *origin, const float *sta
|
|||
ASSERT( start );
|
||||
|
||||
// this can happens if serialized map contain 4096 static decals...
|
||||
if( MSG_GetNumBytesLeft( msg ) < 32 )
|
||||
if( MSG_GetNumBytesLeft( msg ) < 50 )
|
||||
return;
|
||||
|
||||
// static decals are posters, it's always reliable
|
||||
|
@ -472,13 +474,14 @@ void SV_CreateStudioDecal( sizebuf_t *msg, const float *origin, const float *sta
|
|||
MSG_WriteShort( msg, state->frame );
|
||||
MSG_WriteByte( msg, state->blending[0] );
|
||||
MSG_WriteByte( msg, state->blending[1] );
|
||||
MSG_WriteByte( msg, state->controller[0] );
|
||||
MSG_WriteByte( msg, state->controller[1] );
|
||||
MSG_WriteByte( msg, state->controller[2] );
|
||||
MSG_WriteByte( msg, state->controller[3] );
|
||||
for( i = 0; i < 4; i++ )
|
||||
MSG_WriteByte( msg, state->controller[i] );
|
||||
for( i = 0; i < 16; i++ )
|
||||
MSG_WriteByte( msg, state->poseparam[i] );
|
||||
MSG_WriteWord( msg, modelIndex );
|
||||
MSG_WriteByte( msg, state->body );
|
||||
MSG_WriteByte( msg, state->skin );
|
||||
MSG_WriteWord( msg, state->scale );
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2042,6 +2042,7 @@ static server_physics_api_t gPhysicsAPI =
|
|||
COM_SaveFile,
|
||||
pfnLoadImagePixels,
|
||||
pfnGetModelName,
|
||||
COM_CheckParm,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -240,7 +240,7 @@ hull_t *SV_HullForBsp( edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_
|
|||
// author: The FiEctro
|
||||
hull = &model->hulls[COM_RandomLong( 0, 0 )];
|
||||
#endif
|
||||
// FIXME: find a better method to detect quake-maps?
|
||||
// g-cont: find a better method to detect quake-maps?
|
||||
if( FBitSet( world.flags, FWORLD_SKYSPHERE ))
|
||||
{
|
||||
// alternate hull select for quake maps
|
||||
|
|
Reference in New Issue