mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 06:08:35 +00:00
DREAMWEB: Drawing of background objects ported to C++
This commit is contained in:
parent
e36832bbf8
commit
3700e04e0f
@ -117,6 +117,12 @@ generator = cpp(context, "DreamGen", blacklist = [
|
||||
'movemap',
|
||||
'doorway',
|
||||
'widedoor',
|
||||
'showallobs',
|
||||
'getxad',
|
||||
'getyad',
|
||||
'getmapad',
|
||||
'calcfrframe',
|
||||
'finalframe',
|
||||
], skip_output = [
|
||||
# These functions are processed but not output
|
||||
'dreamweb',
|
||||
|
134
engines/dreamweb/backdrop.cpp
Normal file
134
engines/dreamweb/backdrop.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dreamweb/dreamweb.h"
|
||||
#include "engines/util.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace DreamGen {
|
||||
|
||||
uint8 DreamGenContext::getxad(const uint8 *setData, uint8 *result) {
|
||||
uint8 v0 = setData[0];
|
||||
uint8 v1 = setData[1];
|
||||
uint8 v2 = setData[2];
|
||||
if (v0 != 0)
|
||||
return 0;
|
||||
if (v1 < data.byte(kMapx))
|
||||
return 0;
|
||||
v1 -= data.byte(kMapx);
|
||||
if (v1 >= 11)
|
||||
return 0;
|
||||
*result = (v1 << 4) | v2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8 DreamGenContext::getyad(const uint8 *setData, uint8 *result) {
|
||||
uint8 v0 = setData[3];
|
||||
uint8 v1 = setData[4];
|
||||
if (v0 < data.byte(kMapy))
|
||||
return 0;
|
||||
v0 -= data.byte(kMapy);
|
||||
if (v0 >= 10)
|
||||
return 0;
|
||||
*result = (v0 << 4) | v1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DreamGenContext::getmapad() {
|
||||
ch = getmapad((const uint8 *)es.ptr(si, 5));
|
||||
}
|
||||
|
||||
uint8 DreamGenContext::getmapad(const uint8 *setData) {
|
||||
uint8 xad, yad;
|
||||
if (getxad(setData, &xad) == 0)
|
||||
return 0;
|
||||
data.word(kObjectx) = xad;
|
||||
if (getyad(setData, &yad) == 0)
|
||||
return 0;
|
||||
data.word(kObjecty) = yad;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DreamGenContext::calcfrframe() {
|
||||
const Frame *frame = (const Frame *)segRef(data.word(kFrsegment)).ptr(data.word(kCurrentframe) * sizeof(Frame), sizeof(Frame));
|
||||
data.word(kSavesource) = data.word(kFramesad) + frame->ptr();
|
||||
data.byte(kSavesize+0) = frame->width;
|
||||
data.byte(kSavesize+1) = frame->height;
|
||||
data.word(kOffsetx) = frame->x;
|
||||
data.word(kOffsety) = frame->y;
|
||||
}
|
||||
|
||||
void DreamGenContext::finalframe() {
|
||||
uint16 x, y;
|
||||
finalframe(&x, &y);
|
||||
di = x;
|
||||
bx = y;
|
||||
}
|
||||
|
||||
void DreamGenContext::finalframe(uint16 *x, uint16 *y) {
|
||||
data.byte(kSavex) = (data.word(kObjectx) + data.word(kOffsetx)) & 0xff;
|
||||
data.byte(kSavey) = (data.word(kObjecty) + data.word(kOffsety)) & 0xff;
|
||||
*x = data.word(kObjectx);
|
||||
*y = data.word(kObjecty);
|
||||
}
|
||||
|
||||
void DreamGenContext::showallobs() {
|
||||
data.word(kListpos) = kSetlist;
|
||||
memset(segRef(data.word(kBuffers)).ptr(kSetlist, 0), 0xff, 128 * 5);
|
||||
data.word(kFrsegment) = data.word(kSetframes);
|
||||
data.word(kDataad) = kFramedata;
|
||||
data.word(kFramesad) = kFrames;
|
||||
|
||||
const Frame *frames = (const Frame *)segRef(data.word(kFrsegment)).ptr(0, 0);
|
||||
ObjData *setEntries = (ObjData *)segRef(data.word(kSetdat)).ptr(0, 128 * sizeof(ObjData));
|
||||
for (size_t i = 0; i < 128; ++i) {
|
||||
ObjData *setEntry = setEntries + i;
|
||||
if (getmapad(setEntry->b58) == 0)
|
||||
continue;
|
||||
uint8 currentFrame = setEntry->b18[0];
|
||||
data.word(kCurrentframe) = currentFrame;
|
||||
if (currentFrame == 0xff)
|
||||
continue;
|
||||
calcfrframe();
|
||||
uint16 x, y;
|
||||
finalframe(&x, &y);
|
||||
setEntry->b17 = setEntry->b18[0];
|
||||
if ((setEntry->type == 0) && (setEntry->priority != 5) && (setEntry->priority != 6)) {
|
||||
x += data.word(kMapadx);
|
||||
y += data.word(kMapady);
|
||||
uint8 width, height;
|
||||
showframe(frames, x, y, data.word(kCurrentframe), 0, &width, &height);
|
||||
} else
|
||||
makebackob(setEntry);
|
||||
|
||||
ObjPos *objPos = (ObjPos *)segRef(data.word(kBuffers)).ptr(data.word(kListpos), sizeof(ObjPos));
|
||||
objPos->xMin = data.byte(kSavex);
|
||||
objPos->yMin = data.byte(kSavey);
|
||||
objPos->xMax = data.byte(kSavex) + data.byte(kSavesize+0);
|
||||
objPos->yMax = data.byte(kSavey) + data.byte(kSavesize+1);
|
||||
objPos->index = i;
|
||||
data.word(kListpos) += sizeof(ObjPos);
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace dreamgen */
|
||||
|
@ -6585,93 +6585,6 @@ _tmp28a:
|
||||
goto _tmp28;
|
||||
}
|
||||
|
||||
void DreamGenContext::showallobs() {
|
||||
STACK_CHECK;
|
||||
es = data.word(kBuffers);
|
||||
bx = (0+(228*13)+32+60+(32*32)+(11*10*3)+768+768+768+(32*32));
|
||||
data.word(kListpos) = bx;
|
||||
di = bx;
|
||||
cx = 128*5;
|
||||
al = 255;
|
||||
_stosb(cx, true);
|
||||
es = data.word(kSetframes);
|
||||
data.word(kFrsegment) = es;
|
||||
ax = (0);
|
||||
data.word(kDataad) = ax;
|
||||
ax = (0+2080);
|
||||
data.word(kFramesad) = ax;
|
||||
data.byte(kCurrentob) = 0;
|
||||
ds = data.word(kSetdat);
|
||||
si = 0;
|
||||
cx = 128;
|
||||
showobsloop:
|
||||
push(cx);
|
||||
push(si);
|
||||
push(si);
|
||||
_add(si, 58);
|
||||
es = data.word(kSetdat);
|
||||
getmapad();
|
||||
si = pop();
|
||||
_cmp(ch, 0);
|
||||
if (flags.z())
|
||||
goto blankframe;
|
||||
al = es.byte(si+18);
|
||||
ah = 0;
|
||||
data.word(kCurrentframe) = ax;
|
||||
_cmp(al, 255);
|
||||
if (flags.z())
|
||||
goto blankframe;
|
||||
push(es);
|
||||
push(si);
|
||||
calcfrframe();
|
||||
finalframe();
|
||||
si = pop();
|
||||
es = pop();
|
||||
al = es.byte(si+18);
|
||||
es.byte(si+17) = al;
|
||||
_cmp(es.byte(si+8), 0);
|
||||
if (!flags.z())
|
||||
goto animating;
|
||||
_cmp(es.byte(si+5), 5);
|
||||
if (flags.z())
|
||||
goto animating;
|
||||
_cmp(es.byte(si+5), 6);
|
||||
if (flags.z())
|
||||
goto animating;
|
||||
ax = data.word(kCurrentframe);
|
||||
ah = 0;
|
||||
_add(di, data.word(kMapadx));
|
||||
_add(bx, data.word(kMapady));
|
||||
showframe();
|
||||
goto drawnsetob;
|
||||
animating:
|
||||
makebackob();
|
||||
drawnsetob:
|
||||
si = data.word(kListpos);
|
||||
es = data.word(kBuffers);
|
||||
al = data.byte(kSavex);
|
||||
ah = data.byte(kSavey);
|
||||
es.word(si) = ax;
|
||||
cx = ax;
|
||||
ax = data.word(kSavesize);
|
||||
_add(al, cl);
|
||||
_add(ah, ch);
|
||||
es.word(si+2) = ax;
|
||||
al = data.byte(kCurrentob);
|
||||
es.byte(si+4) = al;
|
||||
_add(si, 5);
|
||||
data.word(kListpos) = si;
|
||||
blankframe:
|
||||
_inc(data.byte(kCurrentob));
|
||||
si = pop();
|
||||
cx = pop();
|
||||
_add(si, 64);
|
||||
_dec(cx);
|
||||
if (flags.z())
|
||||
return /* (finishedsetobs) */;
|
||||
goto showobsloop;
|
||||
}
|
||||
|
||||
void DreamGenContext::showallfree() {
|
||||
STACK_CHECK;
|
||||
es = data.word(kBuffers);
|
||||
@ -6841,52 +6754,6 @@ blankex:
|
||||
goto exloop;
|
||||
}
|
||||
|
||||
void DreamGenContext::calcfrframe() {
|
||||
STACK_CHECK;
|
||||
dx = data.word(kFrsegment);
|
||||
ax = data.word(kFramesad);
|
||||
push(ax);
|
||||
cx = data.word(kDataad);
|
||||
ax = data.word(kCurrentframe);
|
||||
ds = dx;
|
||||
bx = 6;
|
||||
_mul(bx);
|
||||
_add(ax, cx);
|
||||
bx = ax;
|
||||
cx = ds.word(bx);
|
||||
ax = ds.word(bx+2);
|
||||
dx = ds.word(bx+4);
|
||||
bx = pop();
|
||||
push(dx);
|
||||
_add(ax, bx);
|
||||
data.word(kSavesource) = ax;
|
||||
data.word(kSavesize) = cx;
|
||||
ax = pop();
|
||||
push(ax);
|
||||
ah = 0;
|
||||
data.word(kOffsetx) = ax;
|
||||
ax = pop();
|
||||
al = ah;
|
||||
ah = 0;
|
||||
data.word(kOffsety) = ax;
|
||||
return;
|
||||
ax = pop();
|
||||
cx = 0;
|
||||
data.word(kSavesize) = cx;
|
||||
}
|
||||
|
||||
void DreamGenContext::finalframe() {
|
||||
STACK_CHECK;
|
||||
ax = data.word(kObjecty);
|
||||
_add(ax, data.word(kOffsety));
|
||||
bx = data.word(kObjectx);
|
||||
_add(bx, data.word(kOffsetx));
|
||||
data.byte(kSavex) = bl;
|
||||
data.byte(kSavey) = al;
|
||||
di = data.word(kObjectx);
|
||||
bx = data.word(kObjecty);
|
||||
}
|
||||
|
||||
void DreamGenContext::adjustlen() {
|
||||
STACK_CHECK;
|
||||
ah = al;
|
||||
@ -6899,70 +6766,6 @@ void DreamGenContext::adjustlen() {
|
||||
ch = al;
|
||||
}
|
||||
|
||||
void DreamGenContext::getmapad() {
|
||||
STACK_CHECK;
|
||||
getxad();
|
||||
_cmp(ch, 0);
|
||||
if (flags.z())
|
||||
return /* (over146) */;
|
||||
data.word(kObjectx) = ax;
|
||||
getyad();
|
||||
_cmp(ch, 0);
|
||||
if (flags.z())
|
||||
return /* (over146) */;
|
||||
data.word(kObjecty) = ax;
|
||||
ch = 1;
|
||||
}
|
||||
|
||||
void DreamGenContext::getxad() {
|
||||
STACK_CHECK;
|
||||
cl = es.byte(si);
|
||||
_inc(si);
|
||||
al = es.byte(si);
|
||||
_inc(si);
|
||||
ah = es.byte(si);
|
||||
_inc(si);
|
||||
_cmp(cl, 0);
|
||||
if (!flags.z())
|
||||
goto over148;
|
||||
_sub(al, data.byte(kMapx));
|
||||
if (flags.c())
|
||||
goto over148;
|
||||
_cmp(al, 11);
|
||||
if (!flags.c())
|
||||
goto over148;
|
||||
cl = 4;
|
||||
_shl(al, cl);
|
||||
_or(al, ah);
|
||||
ah = 0;
|
||||
ch = 1;
|
||||
return;
|
||||
over148:
|
||||
ch = 0;
|
||||
}
|
||||
|
||||
void DreamGenContext::getyad() {
|
||||
STACK_CHECK;
|
||||
al = es.byte(si);
|
||||
_inc(si);
|
||||
ah = es.byte(si);
|
||||
_inc(si);
|
||||
_sub(al, data.byte(kMapy));
|
||||
if (flags.c())
|
||||
goto over147;
|
||||
_cmp(al, 10);
|
||||
if (!flags.c())
|
||||
goto over147;
|
||||
cl = 4;
|
||||
_shl(al, cl);
|
||||
_or(al, ah);
|
||||
ah = 0;
|
||||
ch = 1;
|
||||
return;
|
||||
over147:
|
||||
ch = 0;
|
||||
}
|
||||
|
||||
void DreamGenContext::autolook() {
|
||||
STACK_CHECK;
|
||||
ax = data.word(kMousex);
|
||||
@ -19169,15 +18972,9 @@ void DreamGenContext::__dispatch_call(uint16 addr) {
|
||||
case addr_addalong: addalong(); break;
|
||||
case addr_addlength: addlength(); break;
|
||||
case addr_drawflags: drawflags(); break;
|
||||
case addr_showallobs: showallobs(); break;
|
||||
case addr_showallfree: showallfree(); break;
|
||||
case addr_showallex: showallex(); break;
|
||||
case addr_calcfrframe: calcfrframe(); break;
|
||||
case addr_finalframe: finalframe(); break;
|
||||
case addr_adjustlen: adjustlen(); break;
|
||||
case addr_getmapad: getmapad(); break;
|
||||
case addr_getxad: getxad(); break;
|
||||
case addr_getyad: getyad(); break;
|
||||
case addr_autolook: autolook(); break;
|
||||
case addr_look: look(); break;
|
||||
case addr_dolook: dolook(); break;
|
||||
|
@ -477,15 +477,9 @@ public:
|
||||
static const uint16 addr_dolook = 0xc474;
|
||||
static const uint16 addr_look = 0xc470;
|
||||
static const uint16 addr_autolook = 0xc46c;
|
||||
static const uint16 addr_getyad = 0xc468;
|
||||
static const uint16 addr_getxad = 0xc464;
|
||||
static const uint16 addr_getmapad = 0xc460;
|
||||
static const uint16 addr_adjustlen = 0xc45c;
|
||||
static const uint16 addr_finalframe = 0xc458;
|
||||
static const uint16 addr_calcfrframe = 0xc454;
|
||||
static const uint16 addr_showallex = 0xc450;
|
||||
static const uint16 addr_showallfree = 0xc44c;
|
||||
static const uint16 addr_showallobs = 0xc444;
|
||||
static const uint16 addr_drawflags = 0xc43c;
|
||||
static const uint16 addr_addlength = 0xc438;
|
||||
static const uint16 addr_addalong = 0xc434;
|
||||
@ -1293,6 +1287,7 @@ public:
|
||||
void startdmablock();
|
||||
void useopenbox();
|
||||
void clearbuffers();
|
||||
//void getyad();
|
||||
void neterror();
|
||||
void storeit();
|
||||
//void lockeddoorway();
|
||||
@ -1405,7 +1400,7 @@ public:
|
||||
void watchcount();
|
||||
void fadedownmon();
|
||||
void loadcart();
|
||||
void splitintolines();
|
||||
//void calcfrframe();
|
||||
void bartender();
|
||||
void eden();
|
||||
void showdiary();
|
||||
@ -1485,7 +1480,7 @@ public:
|
||||
//void cls();
|
||||
//void printsprites();
|
||||
//void checkifperson();
|
||||
void showallobs();
|
||||
//void showallobs();
|
||||
//void getnumber();
|
||||
void adjustleft();
|
||||
void calledenslift();
|
||||
@ -1596,7 +1591,7 @@ public:
|
||||
void loadmenu();
|
||||
//void aboutturn();
|
||||
void checkifpathison();
|
||||
void smallcandle();
|
||||
//void finalframe();
|
||||
void receptionist();
|
||||
void selectslot();
|
||||
void edenscdplayer();
|
||||
@ -1604,7 +1599,6 @@ public:
|
||||
void fadeupmon();
|
||||
void paltoendpal();
|
||||
void fadetowhite();
|
||||
void textformonk();
|
||||
void loadsavebox();
|
||||
void soundend();
|
||||
void redes();
|
||||
@ -1623,7 +1617,7 @@ public:
|
||||
void getbacktoops();
|
||||
void rollendcredits();
|
||||
void intro1text();
|
||||
void getmapad();
|
||||
void transfertoex();
|
||||
void playchannel1();
|
||||
void playchannel0();
|
||||
void usemon();
|
||||
@ -1672,7 +1666,7 @@ public:
|
||||
void nothelderror();
|
||||
//void readheader();
|
||||
void getsetad();
|
||||
void getyad();
|
||||
void textformonk();
|
||||
void reconstruct();
|
||||
void soldier1();
|
||||
void getundercentre();
|
||||
@ -1717,7 +1711,7 @@ public:
|
||||
void fadescreendown();
|
||||
void findxyfrompath();
|
||||
void namestoold();
|
||||
void getxad();
|
||||
//void getxad();
|
||||
void openinv();
|
||||
void lookatplace();
|
||||
void useaxe();
|
||||
@ -1827,7 +1821,7 @@ public:
|
||||
void dolook();
|
||||
void opentvdoor();
|
||||
void triggermessage();
|
||||
void finalframe();
|
||||
void smallcandle();
|
||||
//void plotreel();
|
||||
void swapwithopen();
|
||||
//void makesprite();
|
||||
@ -1929,7 +1923,7 @@ public:
|
||||
void closefile();
|
||||
void delcurs();
|
||||
void randomaccess();
|
||||
void calcfrframe();
|
||||
void splitintolines();
|
||||
void intromagic3();
|
||||
void initialmoncols();
|
||||
void checkforshake();
|
||||
@ -1967,7 +1961,7 @@ public:
|
||||
void showfirstuse();
|
||||
void setupemm();
|
||||
void usefullcart();
|
||||
void transfertoex();
|
||||
//void getmapad();
|
||||
void getlocation();
|
||||
void geteitherad();
|
||||
void placesetobject();
|
||||
|
@ -1,6 +1,7 @@
|
||||
MODULE := engines/dreamweb
|
||||
|
||||
MODULE_OBJS := \
|
||||
backdrop.o \
|
||||
console.o \
|
||||
detection.o \
|
||||
dreamweb.o \
|
||||
|
@ -60,7 +60,7 @@ struct ObjData {
|
||||
uint8 b2;
|
||||
uint8 b3;
|
||||
uint8 b4;
|
||||
uint8 b5;
|
||||
uint8 priority;
|
||||
uint8 b6;
|
||||
uint8 delay;
|
||||
uint8 type;
|
||||
@ -73,7 +73,44 @@ struct ObjData {
|
||||
uint8 b15;
|
||||
uint8 b16;
|
||||
uint8 b17;
|
||||
uint8 b18[256]; // NB: Don't know the size yet
|
||||
uint8 b18[13]; // NB: Don't know the size yet
|
||||
uint8 b31;
|
||||
uint8 b32;
|
||||
uint8 b33;
|
||||
uint8 b34;
|
||||
uint8 b35;
|
||||
uint8 b36;
|
||||
uint8 b37;
|
||||
uint8 b38;
|
||||
uint8 b39;
|
||||
uint8 b40;
|
||||
uint8 b41;
|
||||
uint8 b42;
|
||||
uint8 b43;
|
||||
uint8 b44;
|
||||
uint8 b45;
|
||||
uint8 b46;
|
||||
uint8 b47;
|
||||
uint8 b48;
|
||||
uint8 b49;
|
||||
uint8 b50;
|
||||
uint8 b51;
|
||||
uint8 b52;
|
||||
uint8 b53;
|
||||
uint8 b54;
|
||||
uint8 b55;
|
||||
uint8 b56;
|
||||
uint8 b57;
|
||||
uint8 b58[5];
|
||||
uint8 b63;
|
||||
};
|
||||
|
||||
struct ObjPos {
|
||||
uint8 xMin;
|
||||
uint8 yMin;
|
||||
uint8 xMax;
|
||||
uint8 yMax;
|
||||
uint8 index;
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
|
@ -679,20 +679,17 @@ uint8 *DreamGenContext::getroomspathsCPP() {
|
||||
return (uint8 *)result;
|
||||
}
|
||||
|
||||
void DreamGenContext::makebackob() {
|
||||
void DreamGenContext::makebackob(ObjData *objData) {
|
||||
if (data.byte(kNewobs) == 0)
|
||||
return;
|
||||
uint8 priority = es.byte(si+5);
|
||||
uint8 type = es.byte(si+8);
|
||||
uint8 priority = objData->priority;
|
||||
uint8 type = objData->type;
|
||||
Sprite *sprite = makesprite(data.word(kObjectx), data.word(kObjecty), addr_backobject, data.word(kSetframes), 0);
|
||||
|
||||
// Recover es:bx from sprite
|
||||
es = data.word(kBuffers);
|
||||
bx = kSpritetable;
|
||||
Sprite *sprites = (Sprite *)es.ptr(bx, sizeof(Sprite) * 16);
|
||||
bx += sizeof(Sprite) * (sprite - sprites);
|
||||
//
|
||||
sprite->setObjData(si);
|
||||
uint16 objDataOffset = (uint8 *)objData - segRef(data.word(kSetdat)).ptr(0, 0);
|
||||
assert(objDataOffset % sizeof(ObjData) == 0);
|
||||
assert(objDataOffset < 128 * sizeof(ObjData));
|
||||
sprite->setObjData(objDataOffset);
|
||||
if (priority == 255)
|
||||
priority = 0;
|
||||
sprite->priority = priority;
|
||||
|
@ -105,7 +105,7 @@
|
||||
void turnpathoffCPP(uint8 param);
|
||||
void getroomspaths();
|
||||
uint8 *getroomspathsCPP();
|
||||
void makebackob();
|
||||
void makebackob(ObjData *objData);
|
||||
void modifychar();
|
||||
void lockmon();
|
||||
void cancelch0();
|
||||
@ -130,5 +130,13 @@
|
||||
void madmantext();
|
||||
void madmode();
|
||||
void movemap(uint8 param);
|
||||
void getmapad();
|
||||
uint8 getmapad(const uint8 *setData);
|
||||
uint8 getxad(const uint8 *setData, uint8 *result);
|
||||
uint8 getyad(const uint8 *setData, uint8 *result);
|
||||
void calcfrframe();
|
||||
void finalframe();
|
||||
void finalframe(uint16 *x, uint16 *y);
|
||||
void showallobs();
|
||||
bool isCD();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user