mirror of
https://gitlab.com/GaryOderNichts/re3-wiiu.git
synced 2024-11-22 09:09:15 +01:00
added CMBlur
This commit is contained in:
parent
ee5088b3e1
commit
852d8298b1
@ -1,5 +1,31 @@
|
|||||||
|
#define WITHD3D
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "TimeCycle.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
DefinedState(void)
|
||||||
|
{
|
||||||
|
RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP);
|
||||||
|
RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)TRUE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD);
|
||||||
|
RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEALPHAPRIMITIVEBUFFER, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEBORDERCOLOR, (void*)RWRGBALONG(0, 0, 0, 255));
|
||||||
|
RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEFOGCOLOR,
|
||||||
|
(void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255));
|
||||||
|
RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR);
|
||||||
|
RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
|
||||||
|
|
||||||
|
// D3D stuff
|
||||||
|
RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
|
||||||
|
RwD3D8SetRenderState(D3DRS_ALPHAREF, 2);
|
||||||
|
}
|
||||||
|
|
||||||
RwObject*
|
RwObject*
|
||||||
GetFirstObjectCallback(RwObject *object, void *data)
|
GetFirstObjectCallback(RwObject *object, void *data)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
void DefinedState(void);
|
||||||
RwObject *GetFirstObject(RwFrame *frame);
|
RwObject *GetFirstObject(RwFrame *frame);
|
||||||
|
@ -107,5 +107,8 @@ public:
|
|||||||
static int GetFluffyCloudsBottomRed(void) { return m_nCurrentFluffyCloudsBottomRed; }
|
static int GetFluffyCloudsBottomRed(void) { return m_nCurrentFluffyCloudsBottomRed; }
|
||||||
static int GetFluffyCloudsBottomGreen(void) { return m_nCurrentFluffyCloudsBottomGreen; }
|
static int GetFluffyCloudsBottomGreen(void) { return m_nCurrentFluffyCloudsBottomGreen; }
|
||||||
static int GetFluffyCloudsBottomBlue(void) { return m_nCurrentFluffyCloudsBottomBlue; }
|
static int GetFluffyCloudsBottomBlue(void) { return m_nCurrentFluffyCloudsBottomBlue; }
|
||||||
|
static int GetFogRed(void) { return m_nCurrentFogColourRed; }
|
||||||
|
static int GetFogGreen(void) { return m_nCurrentFogColourGreen; }
|
||||||
|
static int GetFogBlue(void) { return m_nCurrentFogColourBlue; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,11 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
|
#ifdef WITHD3D
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <d3d8types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <rwcore.h>
|
#include <rwcore.h>
|
||||||
#include <rpworld.h>
|
#include <rpworld.h>
|
||||||
|
|
||||||
|
@ -25,17 +25,12 @@ void operator delete(void *ptr) noexcept { gtadelete(ptr); }
|
|||||||
unsigned __int64 myrand_seed = 1;
|
unsigned __int64 myrand_seed = 1;
|
||||||
|
|
||||||
int
|
int
|
||||||
myps2rand(void)
|
myrand(void)
|
||||||
{
|
{
|
||||||
myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1;
|
myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1;
|
||||||
return ((myrand_seed >> 32) & 0x7FFFFFFF);
|
return ((myrand_seed >> 32) & 0x7FFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
int myrand(void)
|
|
||||||
{
|
|
||||||
return myps2rand();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
mysrand(unsigned int seed)
|
mysrand(unsigned int seed)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,34 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "patcher.h"
|
||||||
#include "Draw.h"
|
#include "Draw.h"
|
||||||
|
|
||||||
float &CDraw::ms_fNearClipZ = *(float*)0x8E2DC4;
|
float &CDraw::ms_fNearClipZ = *(float*)0x8E2DC4;
|
||||||
float &CDraw::ms_fFarClipZ = *(float*)0x9434F0;
|
float &CDraw::ms_fFarClipZ = *(float*)0x9434F0;
|
||||||
float &CDraw::ms_fFOV = *(float*)0x5FBC6C;
|
float &CDraw::ms_fFOV = *(float*)0x5FBC6C;
|
||||||
|
|
||||||
|
static float hFov2vFov(float hfov)
|
||||||
|
{
|
||||||
|
float w = SCREENW;
|
||||||
|
float h = SCREENH;
|
||||||
|
|
||||||
|
// => tan(hFOV/2) = tan(vFOV/2)*aspectRatio
|
||||||
|
// => tan(vFOV/2) = tan(hFOV/2)/aspectRatio
|
||||||
|
float ar1 = 4.0/3.0;
|
||||||
|
float ar2 = w/h;
|
||||||
|
hfov = DEGTORAD(hfov);
|
||||||
|
float vfov = atan(tan(hfov/2) / ar1) *2;
|
||||||
|
hfov = atan(tan(vfov/2) * ar2) *2;
|
||||||
|
return RADTODEG(hfov);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CDraw::SetFOV(float fov)
|
||||||
|
{
|
||||||
|
// TODO: fix FOV here or somewhere else?
|
||||||
|
// ms_fFOV = hFov2vFov(fov);
|
||||||
|
ms_fFOV = fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTPATCHES
|
||||||
|
InjectHook(0x4FE7B0, CDraw::SetFOV, PATCH_JUMP);
|
||||||
|
ENDPATCHES
|
||||||
|
@ -11,6 +11,6 @@ public:
|
|||||||
static float GetNearClipZ(void) { return ms_fNearClipZ; }
|
static float GetNearClipZ(void) { return ms_fNearClipZ; }
|
||||||
static void SetFarClipZ(float farclip) { ms_fFarClipZ = farclip; }
|
static void SetFarClipZ(float farclip) { ms_fFarClipZ = farclip; }
|
||||||
static float GetFarClipZ(void) { return ms_fFarClipZ; }
|
static float GetFarClipZ(void) { return ms_fFarClipZ; }
|
||||||
static void SetFOV(float fov) { ms_fFOV = fov; }
|
static void SetFOV(float fov);
|
||||||
static float GetFOV(void) { return ms_fFOV; }
|
static float GetFOV(void) { return ms_fFOV; }
|
||||||
};
|
};
|
||||||
|
221
src/render/MBlur.cpp
Normal file
221
src/render/MBlur.cpp
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include "patcher.h"
|
||||||
|
#include "RwHelper.h"
|
||||||
|
#include "MBlur.h"
|
||||||
|
|
||||||
|
RwRaster *&CMBlur::pFrontBuffer = *(RwRaster**)0x8E2C48;
|
||||||
|
bool &CMBlur::ms_bJustInitialised = *(bool*)0x95CDAB;
|
||||||
|
bool &CMBlur::BlurOn = *(bool*)0x95CDAD;
|
||||||
|
|
||||||
|
static RwIm2DVertex Vertex[4];
|
||||||
|
//static RwIm2DVertex *Vertex = (RwIm2DVertex*)0x62F780;
|
||||||
|
static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 };
|
||||||
|
|
||||||
|
void
|
||||||
|
CMBlur::MotionBlurOpen(RwCamera *cam)
|
||||||
|
{
|
||||||
|
// TODO. this is simplified
|
||||||
|
|
||||||
|
RwRect rect = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
if(pFrontBuffer)
|
||||||
|
MotionBlurClose();
|
||||||
|
|
||||||
|
if(BlurOn){
|
||||||
|
for(rect.w = 1; rect.w < RwRasterGetWidth(RwCameraGetRaster(cam)); rect.w *= 2);
|
||||||
|
for(rect.h = 1; rect.h < RwRasterGetHeight(RwCameraGetRaster(cam)); rect.h *= 2);
|
||||||
|
pFrontBuffer = RwRasterCreate(rect.w, rect.h, RwRasterGetDepth(RwCameraGetRaster(cam)), rwRASTERTYPECAMERATEXTURE);
|
||||||
|
if(pFrontBuffer)
|
||||||
|
ms_bJustInitialised = true;
|
||||||
|
else{
|
||||||
|
debug("MBlurOpen can't create raster.");
|
||||||
|
BlurOn = false;
|
||||||
|
rect.w = RwRasterGetWidth(RwCameraGetRaster(cam));
|
||||||
|
rect.h = RwRasterGetHeight(RwCameraGetRaster(cam));
|
||||||
|
}
|
||||||
|
CreateImmediateModeData(cam, &rect);
|
||||||
|
}else{
|
||||||
|
rect.w = RwRasterGetWidth(RwCameraGetRaster(cam));
|
||||||
|
rect.h = RwRasterGetHeight(RwCameraGetRaster(cam));
|
||||||
|
CreateImmediateModeData(cam, &rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CMBlur::MotionBlurClose(void)
|
||||||
|
{
|
||||||
|
if(pFrontBuffer){
|
||||||
|
RwRasterDestroy(pFrontBuffer);
|
||||||
|
pFrontBuffer = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CMBlur::CreateImmediateModeData(RwCamera *cam, RwRect *rect)
|
||||||
|
{
|
||||||
|
float zero, xmax, ymax;
|
||||||
|
|
||||||
|
if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){
|
||||||
|
zero = 0.5f;
|
||||||
|
xmax = rect->w + 0.5f;
|
||||||
|
ymax = rect->h + 0.5f;
|
||||||
|
}else{
|
||||||
|
zero = -0.5f;
|
||||||
|
xmax = rect->w - 0.5f;
|
||||||
|
ymax = rect->h - 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
RwIm2DVertexSetScreenX(&Vertex[0], zero);
|
||||||
|
RwIm2DVertexSetScreenY(&Vertex[0], zero);
|
||||||
|
RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ());
|
||||||
|
RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255);
|
||||||
|
|
||||||
|
RwIm2DVertexSetScreenX(&Vertex[1], zero);
|
||||||
|
RwIm2DVertexSetScreenY(&Vertex[1], ymax);
|
||||||
|
RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ());
|
||||||
|
RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255);
|
||||||
|
|
||||||
|
RwIm2DVertexSetScreenX(&Vertex[2], xmax);
|
||||||
|
RwIm2DVertexSetScreenY(&Vertex[2], ymax);
|
||||||
|
RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ());
|
||||||
|
RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255);
|
||||||
|
|
||||||
|
RwIm2DVertexSetScreenX(&Vertex[3], xmax);
|
||||||
|
RwIm2DVertexSetScreenY(&Vertex[3], zero);
|
||||||
|
RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ());
|
||||||
|
RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam));
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CMBlur::MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 alpha, int32 type, uint32 bluralpha)
|
||||||
|
{
|
||||||
|
RwRGBA color = { red, green, blue, alpha };
|
||||||
|
if(BlurOn){
|
||||||
|
if(pFrontBuffer){
|
||||||
|
if(ms_bJustInitialised)
|
||||||
|
ms_bJustInitialised = false;
|
||||||
|
else
|
||||||
|
OverlayRender(cam, pFrontBuffer, color, type, bluralpha);
|
||||||
|
}
|
||||||
|
RwRasterPushContext(pFrontBuffer);
|
||||||
|
RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0);
|
||||||
|
RwRasterPopContext();
|
||||||
|
}else{
|
||||||
|
OverlayRender(cam, nil, color, type, bluralpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CMBlur::OverlayRender(RwCamera *cam, RwRaster *raster, RwRGBA color, int32 type, uint32 bluralpha)
|
||||||
|
{
|
||||||
|
int r, g, b, a;
|
||||||
|
|
||||||
|
r = color.red;
|
||||||
|
g = color.green;
|
||||||
|
b = color.blue;
|
||||||
|
a = color.alpha;
|
||||||
|
|
||||||
|
DefinedState();
|
||||||
|
|
||||||
|
switch(type){
|
||||||
|
case 3:
|
||||||
|
r = 0;
|
||||||
|
g = 255;
|
||||||
|
b = 0;
|
||||||
|
a = 128;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
r = 100;
|
||||||
|
g = 220;
|
||||||
|
b = 230;
|
||||||
|
a = 158;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
r = 80;
|
||||||
|
g = 255;
|
||||||
|
b = 230;
|
||||||
|
a = 138;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
r = 255;
|
||||||
|
g = 60;
|
||||||
|
b = 60;
|
||||||
|
a = 200;
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
r = 255;
|
||||||
|
g = 180;
|
||||||
|
b = 180;
|
||||||
|
a = 128;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!BlurOn){
|
||||||
|
r *= 0.6f;
|
||||||
|
g *= 0.6f;
|
||||||
|
b *= 0.6f;
|
||||||
|
if(type != 1)
|
||||||
|
a *= 0.6f;
|
||||||
|
// game clamps to 255 here, but why?
|
||||||
|
}
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a);
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a);
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a);
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a);
|
||||||
|
|
||||||
|
RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
|
||||||
|
|
||||||
|
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, BlurOn ? raster : nil);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
|
||||||
|
RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
|
||||||
|
|
||||||
|
a = bluralpha/2;
|
||||||
|
if(a < 30)
|
||||||
|
a = 30;
|
||||||
|
|
||||||
|
if(BlurOn && a != 0){ // the second condition should always be true
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, a);
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, a);
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, a);
|
||||||
|
RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, a);
|
||||||
|
RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
|
||||||
|
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
|
||||||
|
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTPATCHES
|
||||||
|
InjectHook(0x50AE40, CMBlur::MotionBlurOpen, PATCH_JUMP);
|
||||||
|
InjectHook(0x50B170, CMBlur::MotionBlurClose, PATCH_JUMP);
|
||||||
|
InjectHook(0x50A800, CMBlur::CreateImmediateModeData, PATCH_JUMP);
|
||||||
|
InjectHook(0x50AD70, CMBlur::MotionBlurRender, PATCH_JUMP);
|
||||||
|
InjectHook(0x50A9C0, CMBlur::OverlayRender, PATCH_JUMP);
|
||||||
|
ENDPATCHES
|
15
src/render/MBlur.h
Normal file
15
src/render/MBlur.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class CMBlur
|
||||||
|
{
|
||||||
|
static RwRaster *&pFrontBuffer;
|
||||||
|
static bool &ms_bJustInitialised;
|
||||||
|
static bool &BlurOn;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void MotionBlurOpen(RwCamera *cam);
|
||||||
|
static void MotionBlurClose(void);
|
||||||
|
static void CreateImmediateModeData(RwCamera *cam, RwRect *rect);
|
||||||
|
static void MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 alpha, int32 type, uint32 bluralpha);
|
||||||
|
static void OverlayRender(RwCamera *cam, RwRaster *raster, RwRGBA color, int32 type, uint32 bluralpha);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user