Q2 optional brush face flags in BP, 220 mapformats
This commit is contained in:
parent
3129fd0bce
commit
b58408344c
|
|
@ -31,6 +31,8 @@ enum EBrushType
|
|||
{
|
||||
eBrushTypeQuake,
|
||||
eBrushTypeQuake2,
|
||||
eBrushTypeQuake2BP,
|
||||
eBrushTypeQuake2Valve220,
|
||||
eBrushTypeQuake3,
|
||||
eBrushTypeQuake3BP,
|
||||
eBrushTypeQuake3Valve220,
|
||||
|
|
|
|||
|
|
@ -448,12 +448,12 @@ public:
|
|||
if( !m_formatDetected ){
|
||||
EBrushType detectedFormat;
|
||||
if ( string_equal( primitive, "brushDef" ) ) {
|
||||
detectedFormat = eBrushTypeQuake3BP;
|
||||
globalWarningStream() << "detectedFormat = eBrushTypeQuake3BP\n";
|
||||
detectedFormat = eBrushTypeQuake2BP;
|
||||
globalWarningStream() << "detectedFormat = eBrushTypeQuake2BP\n";
|
||||
}
|
||||
else if ( string_equal( primitive, "(" ) && tokeniser.bufferContains( " [ " ) && tokeniser.bufferContains( " ] " ) ) {
|
||||
detectedFormat = eBrushTypeQuake3Valve220;
|
||||
globalWarningStream() << "detectedFormat = eBrushTypeQuake3Valve220\n";
|
||||
detectedFormat = eBrushTypeQuake2Valve220;
|
||||
globalWarningStream() << "detectedFormat = eBrushTypeQuake2Valve220\n";
|
||||
}
|
||||
else if ( string_equal( primitive, "(" ) ) {
|
||||
detectedFormat = eBrushTypeQuake2;
|
||||
|
|
@ -473,10 +473,10 @@ public:
|
|||
switch ( GlobalBrushCreator().getFormat() )
|
||||
{
|
||||
case eBrushTypeQuake2:
|
||||
case eBrushTypeQuake3Valve220:
|
||||
case eBrushTypeQuake2Valve220:
|
||||
tokeniser.ungetToken(); // (
|
||||
// fall through
|
||||
case eBrushTypeQuake3BP:
|
||||
case eBrushTypeQuake2BP:
|
||||
return GlobalBrushCreator().createBrush();
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -888,10 +888,12 @@ inline TexdefTypeId BrushType_getTexdefType( EBrushType type ){
|
|||
switch ( type )
|
||||
{
|
||||
case eBrushTypeQuake3BP:
|
||||
case eBrushTypeQuake2BP:
|
||||
case eBrushTypeDoom3:
|
||||
case eBrushTypeQuake4:
|
||||
return TEXDEFTYPEID_BRUSHPRIMITIVES;
|
||||
case eBrushTypeValve220:
|
||||
case eBrushTypeQuake2Valve220:
|
||||
case eBrushTypeQuake3Valve220:
|
||||
return TEXDEFTYPEID_VALVE;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -58,8 +58,10 @@ const char* BrushType_getName( EBrushType type ){
|
|||
case eBrushTypeQuake2:
|
||||
case eBrushTypeQuake3:
|
||||
return "Axial Projection";
|
||||
case eBrushTypeQuake2BP:
|
||||
case eBrushTypeQuake3BP:
|
||||
return "Brush Primitives";
|
||||
case eBrushTypeQuake2Valve220:
|
||||
case eBrushTypeQuake3Valve220:
|
||||
case eBrushTypeValve220:
|
||||
return "Valve 220";
|
||||
|
|
@ -348,8 +350,8 @@ public:
|
|||
BrushQuake2API(){
|
||||
g_multipleBrushTypes = true;
|
||||
g_brushTypes[0] = eBrushTypeQuake2;
|
||||
g_brushTypes[1] = eBrushTypeQuake3BP;
|
||||
g_brushTypes[2] = eBrushTypeQuake3Valve220;
|
||||
g_brushTypes[1] = eBrushTypeQuake2BP;
|
||||
g_brushTypes[2] = eBrushTypeQuake2Valve220;
|
||||
|
||||
Brush_Construct( eBrushTypeQuake2 );
|
||||
|
||||
|
|
|
|||
|
|
@ -196,36 +196,22 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class Quake2FaceTokenImporter
|
||||
class QuakeFaceTokenImporter
|
||||
{
|
||||
Face& m_face;
|
||||
public:
|
||||
Quake2FaceTokenImporter( Face& face ) : m_face( face ){
|
||||
QuakeFaceTokenImporter( Face& face ) : m_face( face ){
|
||||
}
|
||||
bool importTokens( Tokeniser& tokeniser ){
|
||||
RETURN_FALSE_IF_FAIL( FacePlane_importTokens( m_face.getPlane(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importTokens( m_face.getShader(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceTexdef_importTokens( m_face.getTexdef(), tokeniser ) );
|
||||
// optional face flags
|
||||
// Q1: normally not present, try to load to handle 3rd party maps with them or extended format with flags
|
||||
// Q2: presence determines whether flags are considered as specified (otherwise embedded .wal flags are used)
|
||||
// Q3: normally present, load optionally to handle 3rd party maps without them
|
||||
if ( Tokeniser_nextTokenIsDigit( tokeniser ) ) {
|
||||
m_face.getShader().m_flags.m_specified = true;
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importContentsFlagsValue( m_face.getShader(), tokeniser ) );
|
||||
}
|
||||
m_face.getTexdef().m_scaleApplied = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Quake3FaceTokenImporter
|
||||
{
|
||||
Face& m_face;
|
||||
public:
|
||||
Quake3FaceTokenImporter( Face& face ) : m_face( face ){
|
||||
}
|
||||
bool importTokens( Tokeniser& tokeniser ){
|
||||
RETURN_FALSE_IF_FAIL( FacePlane_importTokens( m_face.getPlane(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importTokens( m_face.getShader(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceTexdef_importTokens( m_face.getTexdef(), tokeniser ) );
|
||||
if ( Tokeniser_nextTokenIsDigit( tokeniser ) ) { ///optional for more flexibility
|
||||
m_face.getShader().m_flags.m_specified = true; // enable for Q2
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importContentsFlagsValue( m_face.getShader(), tokeniser ) );
|
||||
}
|
||||
m_face.getTexdef().m_scaleApplied = true;
|
||||
|
|
@ -244,6 +230,7 @@ public:
|
|||
RETURN_FALSE_IF_FAIL( FaceTexdef_BP_importTokens( m_face.getTexdef(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importTokens( m_face.getShader(), tokeniser ) );
|
||||
if ( Tokeniser_nextTokenIsDigit( tokeniser ) ) { ///optional for more flexibility
|
||||
m_face.getShader().m_flags.m_specified = true; // enable for Q2
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importContentsFlagsValue( m_face.getShader(), tokeniser ) );
|
||||
}
|
||||
|
||||
|
|
@ -254,53 +241,18 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class QuakeFaceTokenImporter
|
||||
{
|
||||
Face& m_face;
|
||||
public:
|
||||
QuakeFaceTokenImporter( Face& face ) : m_face( face ){
|
||||
}
|
||||
bool importTokens( Tokeniser& tokeniser ){
|
||||
RETURN_FALSE_IF_FAIL( FacePlane_importTokens( m_face.getPlane(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importTokens( m_face.getShader(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceTexdef_importTokens( m_face.getTexdef(), tokeniser ) );
|
||||
if ( Tokeniser_nextTokenIsDigit( tokeniser ) ) { ///try to load for more flexibility
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importContentsFlagsValue( m_face.getShader(), tokeniser ) );
|
||||
}
|
||||
m_face.getTexdef().m_scaleApplied = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Valve220FaceTokenImporter
|
||||
{
|
||||
Face& m_face;
|
||||
public:
|
||||
Valve220FaceTokenImporter( Face& face ) : m_face( face ){
|
||||
}
|
||||
bool importTokens( Tokeniser& tokeniser ){
|
||||
RETURN_FALSE_IF_FAIL( FacePlane_importTokens( m_face.getPlane(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importTokens( m_face.getShader(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceTexdef_Valve220_importTokens( m_face.getTexdef(), tokeniser ) );
|
||||
if ( Tokeniser_nextTokenIsDigit( tokeniser ) ) { ///try to load for more flexibility
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importContentsFlagsValue( m_face.getShader(), tokeniser ) );
|
||||
}
|
||||
m_face.getTexdef().m_scaleApplied = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Quake3Valve220FaceTokenImporter
|
||||
{
|
||||
Face& m_face;
|
||||
public:
|
||||
Quake3Valve220FaceTokenImporter( Face& face ) : m_face( face ){
|
||||
}
|
||||
bool importTokens( Tokeniser& tokeniser ){
|
||||
RETURN_FALSE_IF_FAIL( FacePlane_importTokens( m_face.getPlane(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importTokens( m_face.getShader(), tokeniser ) );
|
||||
RETURN_FALSE_IF_FAIL( FaceTexdef_Valve220_importTokens( m_face.getTexdef(), tokeniser ) );
|
||||
if ( Tokeniser_nextTokenIsDigit( tokeniser ) ) { ///optional for more flexibility
|
||||
m_face.getShader().m_flags.m_specified = true; // enable for Q2
|
||||
RETURN_FALSE_IF_FAIL( FaceShader_importContentsFlagsValue( m_face.getShader(), tokeniser ) );
|
||||
}
|
||||
m_face.getTexdef().m_scaleApplied = true;
|
||||
|
|
@ -444,53 +396,25 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class Quake2FaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
public:
|
||||
Quake2FaceTokenExporter( const Face& face ) : m_face( face ){
|
||||
enum class FaceExportFlags{
|
||||
yes,
|
||||
no,
|
||||
optional
|
||||
};
|
||||
|
||||
template<FaceExportFlags exportFlags>
|
||||
void FaceFlags_exportTokens( const Face& face, TokenWriter& writer ){
|
||||
if constexpr ( exportFlags == FaceExportFlags::yes ){
|
||||
FaceShader_ContentsFlagsValue_exportTokens( face.getShader(), writer );
|
||||
}
|
||||
void exportTokens( TokenWriter& writer ) const {
|
||||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceTexdef_exportTokens( m_face.getTexdef(), writer );
|
||||
if ( m_face.getShader().m_flags.m_specified || m_face.isDetail() ) {
|
||||
FaceShader_ContentsFlagsValue_exportTokens( m_face.getShader(), writer );
|
||||
else if constexpr ( exportFlags == FaceExportFlags::optional ){
|
||||
if ( face.getShader().m_flags.m_specified || face.isDetail() ) {
|
||||
FaceShader_ContentsFlagsValue_exportTokens( face.getShader(), writer );
|
||||
}
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
|
||||
class Quake3FaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
public:
|
||||
Quake3FaceTokenExporter( const Face& face ) : m_face( face ){
|
||||
}
|
||||
void exportTokens( TokenWriter& writer ) const {
|
||||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceTexdef_exportTokens( m_face.getTexdef(), writer );
|
||||
FaceShader_ContentsFlagsValue_exportTokens( m_face.getShader(), writer );
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
|
||||
class Quake3BPFaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
public:
|
||||
Quake3BPFaceTokenExporter( const Face& face ) : m_face( face ){
|
||||
}
|
||||
void exportTokens( TokenWriter& writer ) const {
|
||||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceTexdef_BP_exportTokens( m_face.getTexdef(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceShader_ContentsFlagsValue_exportTokens( m_face.getShader(), writer );
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<FaceExportFlags exportFlags>
|
||||
class QuakeFaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
|
|
@ -501,10 +425,28 @@ public:
|
|||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceTexdef_exportTokens( m_face.getTexdef(), writer );
|
||||
FaceFlags_exportTokens<exportFlags>( m_face, writer );
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
|
||||
template<FaceExportFlags exportFlags>
|
||||
class Quake3BPFaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
public:
|
||||
Quake3BPFaceTokenExporter( const Face& face ) : m_face( face ){
|
||||
}
|
||||
void exportTokens( TokenWriter& writer ) const {
|
||||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceTexdef_BP_exportTokens( m_face.getTexdef(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceFlags_exportTokens<exportFlags>( m_face, writer );
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
|
||||
template<FaceExportFlags exportFlags>
|
||||
class Valve220FaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
|
|
@ -515,21 +457,7 @@ public:
|
|||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceTexdef_Valve220_exportTokens( m_face.getTexdef(), writer );
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
|
||||
class Quake3Valve220FaceTokenExporter
|
||||
{
|
||||
const Face& m_face;
|
||||
public:
|
||||
Quake3Valve220FaceTokenExporter( const Face& face ) : m_face( face ){
|
||||
}
|
||||
void exportTokens( TokenWriter& writer ) const {
|
||||
FacePlane_exportTokens( m_face.getPlane(), writer );
|
||||
FaceShader_exportTokens( m_face.getShader(), writer );
|
||||
FaceTexdef_Valve220_exportTokens( m_face.getTexdef(), writer );
|
||||
FaceShader_ContentsFlagsValue_exportTokens( m_face.getShader(), writer );
|
||||
FaceFlags_exportTokens<exportFlags>( m_face, writer );
|
||||
writer.nextLine();
|
||||
}
|
||||
};
|
||||
|
|
@ -543,7 +471,7 @@ public:
|
|||
BrushTokenImporter( Brush& brush ) : m_brush( brush ){
|
||||
}
|
||||
bool importTokens( Tokeniser& tokeniser ){
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4 ) {
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeQuake2BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4 ) {
|
||||
tokeniser.nextLine();
|
||||
RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "{" ) );
|
||||
}
|
||||
|
|
@ -579,46 +507,33 @@ public:
|
|||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake:
|
||||
case eBrushTypeQuake2:
|
||||
{
|
||||
Quake2FaceTokenImporter importer( face );
|
||||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake3:
|
||||
{
|
||||
Quake3FaceTokenImporter importer( face );
|
||||
QuakeFaceTokenImporter importer( face );
|
||||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake2BP:
|
||||
case eBrushTypeQuake3BP:
|
||||
{
|
||||
Quake3BPFaceTokenImporter importer( face );
|
||||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake:
|
||||
{
|
||||
QuakeFaceTokenImporter importer( face );
|
||||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeValve220:
|
||||
{
|
||||
Valve220FaceTokenImporter importer( face );
|
||||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake2Valve220:
|
||||
case eBrushTypeQuake3Valve220:
|
||||
{
|
||||
Quake3Valve220FaceTokenImporter importer( face );
|
||||
Valve220FaceTokenImporter importer( face );
|
||||
RETURN_FALSE_IF_FAIL( importer.importTokens( tokeniser ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
face.planeChanged();
|
||||
}
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4 ) {
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeQuake2BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4 ) {
|
||||
tokeniser.nextLine();
|
||||
RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "}" ) );
|
||||
}
|
||||
|
|
@ -648,7 +563,7 @@ public:
|
|||
writer.writeToken( "{" );
|
||||
writer.nextLine();
|
||||
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP ) {
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeQuake2BP ) {
|
||||
writer.writeToken( "brushDef" );
|
||||
writer.nextLine();
|
||||
writer.writeToken( "{" );
|
||||
|
|
@ -680,39 +595,51 @@ public:
|
|||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake:
|
||||
{
|
||||
QuakeFaceTokenExporter<FaceExportFlags::no> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake2:
|
||||
{
|
||||
Quake2FaceTokenExporter exporter( face );
|
||||
QuakeFaceTokenExporter<FaceExportFlags::optional> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake3:
|
||||
{
|
||||
Quake3FaceTokenExporter exporter( face );
|
||||
QuakeFaceTokenExporter<FaceExportFlags::yes> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake2BP:
|
||||
{
|
||||
Quake3BPFaceTokenExporter<FaceExportFlags::optional> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake3BP:
|
||||
{
|
||||
Quake3BPFaceTokenExporter exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake:
|
||||
{
|
||||
QuakeFaceTokenExporter exporter( face );
|
||||
Quake3BPFaceTokenExporter<FaceExportFlags::yes> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeValve220:
|
||||
{
|
||||
Valve220FaceTokenExporter exporter( face );
|
||||
Valve220FaceTokenExporter<FaceExportFlags::no> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake2Valve220:
|
||||
{
|
||||
Valve220FaceTokenExporter<FaceExportFlags::optional> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
case eBrushTypeQuake3Valve220:
|
||||
{
|
||||
Quake3Valve220FaceTokenExporter exporter( face );
|
||||
Valve220FaceTokenExporter<FaceExportFlags::yes> exporter( face );
|
||||
exporter.exportTokens( writer );
|
||||
}
|
||||
break;
|
||||
|
|
@ -720,7 +647,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4 ) {
|
||||
if ( Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeQuake2BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4 ) {
|
||||
writer.writeToken( "}" );
|
||||
writer.nextLine();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ public:
|
|||
FaceXMLExporter( const Face& face ) : m_face( face ){
|
||||
}
|
||||
void exportXML( XMLImporter& importer ){
|
||||
bool bAlternateTexdef = ( Face::m_type == eBrushTypeQuake3BP || Face::m_type == eBrushTypeDoom3 || Face::m_type == eBrushTypeQuake4 );
|
||||
bool bAlternateTexdef = ( Face::m_type == eBrushTypeQuake3BP || Face::m_type == eBrushTypeQuake2BP || Face::m_type == eBrushTypeDoom3 || Face::m_type == eBrushTypeQuake4 );
|
||||
|
||||
// write shader
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user