mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 12:12:50 +01:00
Implemented mem_func_ptr_t.
Improved cellFsAioRead. Fixed cellAudioOut* return error codes.
This commit is contained in:
parent
1d61e7b4a4
commit
76695a97b9
@ -418,12 +418,27 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
u32 GetAddr() const { return m_addr; }
|
||||
__forceinline u32 GetAddr() const { return m_addr; }
|
||||
|
||||
bool IsGood() const
|
||||
__forceinline bool IsGood() const
|
||||
{
|
||||
return Memory.IsGoodAddr(m_addr, sizeof(T));
|
||||
}
|
||||
|
||||
__forceinline operator bool() const
|
||||
{
|
||||
return m_addr != 0;
|
||||
}
|
||||
|
||||
__forceinline bool operator != (nullptr_t) const
|
||||
{
|
||||
return m_addr != 0;
|
||||
}
|
||||
|
||||
__forceinline bool operator == (nullptr_t) const
|
||||
{
|
||||
return m_addr == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -434,6 +449,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
template<typename NT> operator mem_ptr_t<NT>&() { return (mem_ptr_t<NT>&)*this; }
|
||||
template<typename NT> operator const mem_ptr_t<NT>&() const { return (const mem_ptr_t<NT>&)*this; }
|
||||
|
||||
T* operator -> ()
|
||||
{
|
||||
return (T*)&Memory[this->m_addr];
|
||||
@ -512,8 +530,6 @@ public:
|
||||
return (const T&)Memory[this->m_addr + sizeof(T) * index];
|
||||
}
|
||||
|
||||
operator bool() const { return this->m_addr == 0; }
|
||||
|
||||
bool operator == (mem_ptr_t right) const { return this->m_addr == right.m_addr; }
|
||||
bool operator != (mem_ptr_t right) const { return this->m_addr != right.m_addr; }
|
||||
bool operator > (mem_ptr_t right) const { return this->m_addr > right.m_addr; }
|
||||
@ -529,6 +545,32 @@ public:
|
||||
bool operator <= (T* right) const { return (T*)&Memory[this->m_addr] <= right; }
|
||||
};
|
||||
|
||||
template<>
|
||||
class mem_ptr_t<void> : public mem_base_t<u8>
|
||||
{
|
||||
public:
|
||||
mem_ptr_t(u32 addr) : mem_base_t<u8>(addr)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename NT> operator mem_ptr_t<NT>&() { return (mem_ptr_t<NT>&)*this; }
|
||||
template<typename NT> operator const mem_ptr_t<NT>&() const { return (const mem_ptr_t<NT>&)*this; }
|
||||
|
||||
bool operator == (mem_ptr_t right) const { return this->m_addr == right.m_addr; }
|
||||
bool operator != (mem_ptr_t right) const { return this->m_addr != right.m_addr; }
|
||||
bool operator > (mem_ptr_t right) const { return this->m_addr > right.m_addr; }
|
||||
bool operator < (mem_ptr_t right) const { return this->m_addr < right.m_addr; }
|
||||
bool operator >= (mem_ptr_t right) const { return this->m_addr >= right.m_addr; }
|
||||
bool operator <= (mem_ptr_t right) const { return this->m_addr <= right.m_addr; }
|
||||
|
||||
bool operator == (void* right) const { return (void*)&Memory[this->m_addr] == right; }
|
||||
bool operator != (void* right) const { return (void*)&Memory[this->m_addr] != right; }
|
||||
bool operator > (void* right) const { return (void*)&Memory[this->m_addr] > right; }
|
||||
bool operator < (void* right) const { return (void*)&Memory[this->m_addr] < right; }
|
||||
bool operator >= (void* right) const { return (void*)&Memory[this->m_addr] >= right; }
|
||||
bool operator <= (void* right) const { return (void*)&Memory[this->m_addr] <= right; }
|
||||
};
|
||||
|
||||
template<typename T> static bool operator == (T* left, mem_ptr_t<T> right) { return left == (T*)&Memory[right.GetAddr()]; }
|
||||
template<typename T> static bool operator != (T* left, mem_ptr_t<T> right) { return left != (T*)&Memory[right.GetAddr()]; }
|
||||
template<typename T> static bool operator > (T* left, mem_ptr_t<T> right) { return left > (T*)&Memory[right.GetAddr()]; }
|
||||
@ -638,6 +680,123 @@ public:
|
||||
void SetAddr(const u64 addr) { m_addr = addr; }
|
||||
};
|
||||
|
||||
template<typename T> class mem_func_ptr_t;
|
||||
|
||||
template<typename RT>
|
||||
class mem_func_ptr_t<RT (*)()> : public mem_base_t<u64>
|
||||
{
|
||||
__forceinline void call_func(bool is_async)
|
||||
{
|
||||
Callback cb;
|
||||
cb.SetAddr(m_addr);
|
||||
cb.Branch(!is_async);
|
||||
}
|
||||
|
||||
public:
|
||||
__forceinline void operator()()
|
||||
{
|
||||
call_func(false);
|
||||
}
|
||||
|
||||
__forceinline void async()
|
||||
{
|
||||
call_func(true);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename T1>
|
||||
class mem_func_ptr_t<RT (*)(T1)> : public mem_base_t<u64>
|
||||
{
|
||||
__forceinline void call_func(bool is_async, T1 a1)
|
||||
{
|
||||
Callback cb;
|
||||
cb.SetAddr(m_addr);
|
||||
cb.Handle(a1);
|
||||
cb.Branch(!is_async);
|
||||
}
|
||||
|
||||
public:
|
||||
__forceinline void operator()(T1 a1)
|
||||
{
|
||||
call_func(false, a1);
|
||||
}
|
||||
|
||||
__forceinline void async(T1 a1)
|
||||
{
|
||||
call_func(true, a1);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename T1, typename T2>
|
||||
class mem_func_ptr_t<RT (*)(T1, T2)> : public mem_base_t<u64>
|
||||
{
|
||||
__forceinline void call_func(bool is_async, T1 a1, T2 a2)
|
||||
{
|
||||
Callback cb;
|
||||
cb.SetAddr(m_addr);
|
||||
cb.Handle(a1, a2);
|
||||
cb.Branch(!is_async);
|
||||
}
|
||||
|
||||
public:
|
||||
__forceinline void operator()(T1 a1, T2 a2)
|
||||
{
|
||||
call_func(false, a1, a2);
|
||||
}
|
||||
|
||||
__forceinline void async(T1 a1, T2 a2)
|
||||
{
|
||||
call_func(true, a1, a2);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename T1, typename T2, typename T3>
|
||||
class mem_func_ptr_t<RT (*)(T1, T2, T3)> : public mem_base_t<u64>
|
||||
{
|
||||
__forceinline void call_func(bool is_async, T1 a1, T2 a2, T3 a3)
|
||||
{
|
||||
Callback cb;
|
||||
cb.SetAddr(m_addr);
|
||||
cb.Handle(a1, a2, a3);
|
||||
cb.Branch(!is_async);
|
||||
}
|
||||
|
||||
public:
|
||||
__forceinline void operator()(T1 a1, T2 a2, T3 a3)
|
||||
{
|
||||
call_func(false, a1, a2, a3);
|
||||
}
|
||||
|
||||
__forceinline void async(T1 a1, T2 a2, T3 a3)
|
||||
{
|
||||
call_func(true, a1, a2, a3);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RT, typename T1, typename T2, typename T3, typename T4>
|
||||
class mem_func_ptr_t<RT (*)(T1, T2, T3, T4)> : public mem_base_t<u64>
|
||||
{
|
||||
__forceinline void call_func(bool is_async, T1 a1, T2 a2, T3 a3, T4 a4)
|
||||
{
|
||||
Callback cb;
|
||||
cb.SetAddr(m_addr);
|
||||
cb.Handle(a1, a2, a3, a4);
|
||||
cb.Branch(!is_async);
|
||||
}
|
||||
|
||||
public:
|
||||
__forceinline void operator()(T1 a1, T2 a2, T3 a3, T4 a4)
|
||||
{
|
||||
call_func(false, a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
__forceinline void async(T1 a1, T2 a2, T3 a3, T4 a4)
|
||||
{
|
||||
call_func(true, a1, a2, a3, a4);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class MemoryAllocator
|
||||
{
|
||||
|
@ -106,9 +106,6 @@ enum
|
||||
CELL_SYSUTIL_PAD_RUMBLE_ON = 1,
|
||||
};
|
||||
|
||||
void cellSysutil_init();
|
||||
Module cellSysutil(0x0015, cellSysutil_init);
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
@ -132,6 +129,11 @@ enum CellMsgDialogType
|
||||
CELL_MSGDIALOG_DEFAULT_CURSOR_NO = 0x00000100,
|
||||
};
|
||||
|
||||
typedef void (*CellMsgDialogCallback)(int buttonType, mem_ptr_t<void> userData);
|
||||
|
||||
void cellSysutil_init();
|
||||
Module cellSysutil(0x0015, cellSysutil_init);
|
||||
|
||||
int cellSysutilGetSystemParamInt(int id, mem32_t value)
|
||||
{
|
||||
cellSysutil.Log("cellSysutilGetSystemParamInt(id=0x%x, value_addr=0x%x)", id, value.GetAddr());
|
||||
@ -477,7 +479,7 @@ int cellSysutilUnregisterCallback(int slot)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
int cellMsgDialogOpen2(u32 type, char* msgString, u32 callback_addr, u32 userData, u32 extParam)
|
||||
int cellMsgDialogOpen2(u32 type, char* msgString, mem_func_ptr_t<CellMsgDialogCallback> callback, mem_ptr_t<void> userData, u32 extParam)
|
||||
{
|
||||
long style = 0;
|
||||
|
||||
@ -520,17 +522,16 @@ int cellMsgDialogOpen2(u32 type, char* msgString, u32 callback_addr, u32 userDat
|
||||
break;
|
||||
}
|
||||
|
||||
Callback2 callback(0, callback_addr, userData);
|
||||
callback.Handle(status);
|
||||
callback.Branch(true);
|
||||
if(callback)
|
||||
callback(status, userData);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
int cellMsgDialogOpenErrorCode(u32 errorCode, u32 callback_addr, u32 userData, u32 extParam)
|
||||
int cellMsgDialogOpenErrorCode(u32 errorCode, mem_func_ptr_t<CellMsgDialogCallback> callback, mem_ptr_t<void> userData, u32 extParam)
|
||||
{
|
||||
cellSysutil.Warning("cellMsgDialogOpenErrorCode(errorCode=0x%x, callback_addr=0x%x, userData=%d, extParam=%d)",
|
||||
errorCode, callback_addr, userData, extParam);
|
||||
errorCode, callback.GetAddr(), userData, extParam);
|
||||
|
||||
std::string errorMessage;
|
||||
switch(errorCode)
|
||||
@ -622,10 +623,8 @@ int cellMsgDialogOpenErrorCode(u32 errorCode, u32 callback_addr, u32 userData, u
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: The following lines produce an infinite loop of cellMsgDialogOpenErrorCode's
|
||||
/*Callback2 callback(0, callback_addr, userData);
|
||||
callback.Handle(status);
|
||||
callback.Branch(true);*/
|
||||
if(callback)
|
||||
callback(status, userData);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -649,7 +648,7 @@ int cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32 option)
|
||||
case CELL_AUDIO_OUT_FS_192KHZ:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
default: return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
@ -659,7 +658,7 @@ int cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32 option)
|
||||
case CELL_AUDIO_OUT_CODING_TYPE_DTS:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
default: return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
}
|
||||
|
||||
switch(audioOut)
|
||||
@ -668,8 +667,7 @@ int cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32 option)
|
||||
case CELL_AUDIO_OUT_SECONDARY: return 0;
|
||||
}
|
||||
|
||||
CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION;
|
||||
|
||||
return CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION;
|
||||
}
|
||||
|
||||
int cellAudioOutGetSoundAvailability2(u32 audioOut, u32 type, u32 fs, u32 ch, u32 option)
|
||||
@ -690,7 +688,7 @@ int cellAudioOutGetSoundAvailability2(u32 audioOut, u32 type, u32 fs, u32 ch, u3
|
||||
case CELL_AUDIO_OUT_FS_192KHZ:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
default: return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
}
|
||||
|
||||
switch(ch)
|
||||
@ -700,7 +698,7 @@ int cellAudioOutGetSoundAvailability2(u32 audioOut, u32 type, u32 fs, u32 ch, u3
|
||||
case 8:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
default: return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
@ -710,7 +708,7 @@ int cellAudioOutGetSoundAvailability2(u32 audioOut, u32 type, u32 fs, u32 ch, u3
|
||||
case CELL_AUDIO_OUT_CODING_TYPE_DTS:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
default: return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_SOUND_MODE;
|
||||
}
|
||||
|
||||
switch(audioOut)
|
||||
@ -719,8 +717,7 @@ int cellAudioOutGetSoundAvailability2(u32 audioOut, u32 type, u32 fs, u32 ch, u3
|
||||
case CELL_AUDIO_OUT_SECONDARY: return 0;
|
||||
}
|
||||
|
||||
CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION;
|
||||
|
||||
return CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION;
|
||||
}
|
||||
|
||||
int cellAudioOutGetState(u32 audioOut, u32 deviceIndex, u32 state_addr)
|
||||
@ -831,7 +828,7 @@ int cellAudioOutGetNumberOfDevice(u32 audioOut)
|
||||
case CELL_AUDIO_OUT_SECONDARY: return 0;
|
||||
}
|
||||
|
||||
CELL_AUDIO_OUT_ERROR_UNSUPPORTED_AUDIO_OUT;
|
||||
return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_AUDIO_OUT;
|
||||
}
|
||||
|
||||
int cellAudioOutGetDeviceInfo(u32 audioOut, u32 deviceIndex, mem_ptr_t<CellAudioOutDeviceInfo> info)
|
||||
@ -863,7 +860,7 @@ int cellAudioOutSetCopyControl(u32 audioOut, u32 control)
|
||||
case CELL_AUDIO_OUT_SECONDARY:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_UNSUPPORTED_AUDIO_OUT;
|
||||
default: return CELL_AUDIO_OUT_ERROR_UNSUPPORTED_AUDIO_OUT;
|
||||
}
|
||||
|
||||
switch(control)
|
||||
@ -873,7 +870,7 @@ int cellAudioOutSetCopyControl(u32 audioOut, u32 control)
|
||||
case CELL_AUDIO_OUT_COPY_CONTROL_COPY_NEVER:
|
||||
break;
|
||||
|
||||
default: CELL_AUDIO_OUT_ERROR_ILLEGAL_PARAMETER;
|
||||
default: return CELL_AUDIO_OUT_ERROR_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
return CELL_AUDIO_OUT_SUCCEEDED;
|
||||
|
@ -135,46 +135,26 @@ int cellFsSdataOpen(u32 path_addr, int flags, mem32_t fd, mem32_t arg, u64 size)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
/*struct CellFsAio
|
||||
{
|
||||
be_t<u32> fd;
|
||||
be_t<u64> offset;
|
||||
be_t<u32> buf_addr;
|
||||
be_t<u64> size;
|
||||
be_t<u64> user_data;
|
||||
};
|
||||
|
||||
int cellFsAioRead(mem_ptr_t<CellFsAio> aio, mem32_t id, mem32_t func_addr)
|
||||
{
|
||||
if(!aio.IsGood() || !id.IsGood() || !func_addr.IsGood())
|
||||
return CELL_EFAULT;
|
||||
|
||||
//CellFsAio *xaio, CellFsErrno error, int xid, uint64_t size;
|
||||
Callback callback;
|
||||
callback.SetAddr(func_addr);
|
||||
callback.SetName("cellFsAioReadCallback");
|
||||
MemoryAllocator<be_t<u64>> nread;
|
||||
int error = cellFsRead(aio->fd, id.GetAddr(), aio->size, nread);
|
||||
callback.Handle(aio.GetAddr(), error, id, *nread);
|
||||
callback.Branch(true);
|
||||
|
||||
return CELL_OK;
|
||||
}*/
|
||||
|
||||
std::atomic<u32> g_FsAioReadID = 0;
|
||||
|
||||
int cellFsAioRead(mem_ptr_t<CellFsAio> aio, mem32_t aio_id, u32 func_addr)
|
||||
int cellFsAioRead(mem_ptr_t<CellFsAio> aio, mem32_t aio_id, mem_func_ptr_t<void (*)(mem_ptr_t<CellFsAio> xaio, u32 error, int xid, u64 size)> func)
|
||||
{
|
||||
ID id;
|
||||
u32 fd = (u32)aio->fd;
|
||||
if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH;
|
||||
vfsFileBase& orig_file = *(vfsFileBase*)id.m_data;
|
||||
//open the file again (to prevent access conflicts roughly)
|
||||
vfsStream& file = vfsLocalFile(orig_file.GetPath().AfterFirst('/'), vfsRead);
|
||||
|
||||
u64 nbytes = (u64)aio->size;
|
||||
const u32 buf_addr = (u32)aio->buf_addr;
|
||||
if(Memory.IsGoodAddr(buf_addr) && !Memory.IsGoodAddr(buf_addr, nbytes))
|
||||
|
||||
u64 res;
|
||||
u32 error;
|
||||
|
||||
if(Memory.IsGoodAddr(buf_addr))
|
||||
{
|
||||
vfsFile file(orig_file.GetPath().AfterFirst('/'), vfsRead);
|
||||
if(!Memory.IsGoodAddr(buf_addr, nbytes))
|
||||
{
|
||||
MemoryBlock& block = Memory.GetMemByAddr(buf_addr);
|
||||
nbytes = block.GetSize() - (buf_addr - block.GetStartAddr());
|
||||
@ -182,28 +162,24 @@ int cellFsAioRead(mem_ptr_t<CellFsAio> aio, mem32_t aio_id, u32 func_addr)
|
||||
|
||||
//read data immediately (actually it should be read in special thread)
|
||||
file.Seek((u64)aio->offset);
|
||||
const u64 res = nbytes ? file.Read(Memory.GetMemFromAddr(buf_addr), nbytes) : 0;
|
||||
file.Close();
|
||||
res = nbytes ? file.Read(Memory.GetMemFromAddr(buf_addr), nbytes) : 0;
|
||||
error = CELL_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = 0;
|
||||
error = CELL_EFAULT;
|
||||
}
|
||||
|
||||
//get a unique id for the callback
|
||||
const u32 xid = g_FsAioReadID++;
|
||||
aio_id = xid;
|
||||
|
||||
sys_fs.Warning("cellFsAioRead(aio_addr: 0x%x[%s], id_addr: 0x%x, func_addr: 0x%x[res=%d, addr=0x%x])", aio.GetAddr(),
|
||||
orig_file.GetPath().c_str(), aio_id.GetAddr(), func_addr, res, (u32)aio->buf_addr);
|
||||
orig_file.GetPath().c_str(), aio_id.GetAddr(), func.GetAddr(), res, (u32)aio->buf_addr);
|
||||
|
||||
//TODO: init the callback
|
||||
CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU);
|
||||
new_thread.SetEntry(func_addr);
|
||||
new_thread.SetPrio(1001);
|
||||
new_thread.SetStackSize(0x10000);
|
||||
new_thread.SetName("FsAioReadCallback");
|
||||
new_thread.SetArg(0, aio.GetAddr()); //xaio
|
||||
new_thread.SetArg(1, CELL_OK); //error code
|
||||
new_thread.SetArg(2, xid); //xid (unique id)
|
||||
new_thread.SetArg(3, res); //size (bytes read)
|
||||
new_thread.Run();
|
||||
new_thread.Exec();
|
||||
if(func)
|
||||
func(aio, error, xid, res);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
@ -302,6 +302,8 @@ public:
|
||||
virtual void operator()() { declCPU(); m_call(ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11), ARG(12)); }
|
||||
};
|
||||
|
||||
#undef ARG
|
||||
|
||||
template<typename TR>
|
||||
func_caller* bind_func(TR (*call)())
|
||||
{
|
||||
@ -379,5 +381,3 @@ func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
|
||||
{
|
||||
return new binder_func_12<TR, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(call);
|
||||
}
|
||||
|
||||
#undef ARG(n)
|
||||
|
@ -225,7 +225,6 @@ extern int cellFsLseek(u32 fd, s64 offset, u32 whence, mem64_t pos);
|
||||
extern int cellFsFtruncate(u32 fd, u64 size);
|
||||
extern int cellFsTruncate(u32 path_addr, u64 size);
|
||||
extern int cellFsFGetBlockSize(u32 fd, mem64_t sector_size, mem64_t block_size);
|
||||
extern int cellFsAioRead(mem_ptr_t<CellFsAio> aio, mem32_t id, u32 func_addr);
|
||||
|
||||
//cellVideo
|
||||
extern int cellVideoOutGetState(u32 videoOut, u32 deviceIndex, u32 state_addr);
|
||||
|
Loading…
Reference in New Issue
Block a user