mirror of
https://github.com/holub/mame
synced 2025-04-23 00:39:36 +03:00
on-the-fly decryption support for chocovdr & nflclsfb (nw)
This commit is contained in:
parent
b6e1d3d986
commit
977221d7a5
@ -406,6 +406,10 @@ ADDRESS_MAP_END
|
||||
|
||||
WRITE16_MEMBER(namcos10_state::crypto_switch_w)
|
||||
{
|
||||
printf("crypto_switch_w: %04x\n", data);
|
||||
if (decrypter == 0)
|
||||
return;
|
||||
|
||||
if (BIT(data, 15) != 0)
|
||||
decrypter->activate(data & 0xf);
|
||||
else
|
||||
@ -462,6 +466,9 @@ READ16_MEMBER( namcos10_state::nand_data_r )
|
||||
/* printf( "data<-%08x (%08x)\n", data, nand_address ); */
|
||||
nand_address++;
|
||||
|
||||
if (decrypter == 0)
|
||||
return data;
|
||||
|
||||
if (decrypter->is_active())
|
||||
return decrypter->decrypt(data);
|
||||
else
|
||||
@ -490,6 +497,7 @@ READ16_MEMBER(namcos10_state::nand_block_r)
|
||||
|
||||
static ADDRESS_MAP_START( namcos10_memn_map, AS_PROGRAM, 32, namcos10_state )
|
||||
AM_RANGE(0x1f300000, 0x1f300003) AM_WRITE16(crypto_switch_w, 0x0000ffff)
|
||||
AM_RANGE(0x1f380000, 0x1f380003) AM_WRITE16(crypto_switch_w, 0x0000ffff)
|
||||
AM_RANGE(0x1f400000, 0x1f400003) AM_READ16(nand_status_r, 0x0000ffff)
|
||||
AM_RANGE(0x1f410000, 0x1f410003) AM_WRITE8(nand_address1_w, 0x000000ff)
|
||||
AM_RANGE(0x1f420000, 0x1f420003) AM_WRITE8(nand_address2_w, 0x000000ff)
|
||||
@ -580,7 +588,9 @@ DRIVER_INIT_MEMBER(namcos10_state,gunbalna)
|
||||
DRIVER_INIT_MEMBER(namcos10_state,chocovdr)
|
||||
{
|
||||
int regSize = machine().root_device().memregion("user2")->bytes();
|
||||
decrypt_bios(machine(), "user2", 0x8400, regSize, 0x5, 0x4, 0x6, 0x7, 0x1, 0x0, 0x2, 0x3, 0xc, 0xf, 0xe, 0xd, 0x8, 0xb, 0xa, 0x9);
|
||||
decrypt_bios(machine(), "user2", 0x0008400, 0x0029400, 0x5, 0x4, 0x6, 0x7, 0x1, 0x0, 0x2, 0x3, 0xc, 0xf, 0xe, 0xd, 0x8, 0xb, 0xa, 0x9);
|
||||
decrypt_bios(machine(), "user2", 0x01eae00, 0x105ae00, 0x5, 0x4, 0x6, 0x7, 0x1, 0x0, 0x2, 0x3, 0xc, 0xf, 0xe, 0xd, 0x8, 0xb, 0xa, 0x9);
|
||||
decrypt_bios(machine(), "user2", 0x1080000, regSize , 0x5, 0x4, 0x6, 0x7, 0x1, 0x0, 0x2, 0x3, 0xc, 0xf, 0xe, 0xd, 0x8, 0xb, 0xa, 0x9);
|
||||
memn_driver_init();
|
||||
}
|
||||
|
||||
@ -594,7 +604,9 @@ DRIVER_INIT_MEMBER(namcos10_state,panikuru)
|
||||
DRIVER_INIT_MEMBER(namcos10_state,nflclsfb)
|
||||
{
|
||||
int regSize = machine().root_device().memregion("user2")->bytes();
|
||||
decrypt_bios(machine(), "user2", 0x8400, regSize, 0x6, 0x5, 0x4, 0x7, 0x1, 0x3, 0x0, 0x2, 0xc, 0xd, 0xe, 0xf, 0x8, 0xb, 0xa, 0x9);
|
||||
decrypt_bios(machine(), "user2", 0x0008400, 0x0029400, 0x6, 0x5, 0x4, 0x7, 0x1, 0x3, 0x0, 0x2, 0xc, 0xd, 0xe, 0xf, 0x8, 0xb, 0xa, 0x9);
|
||||
decrypt_bios(machine(), "user2", 0x0214200, 0x105ae00, 0x6, 0x5, 0x4, 0x7, 0x1, 0x3, 0x0, 0x2, 0xc, 0xd, 0xe, 0xf, 0x8, 0xb, 0xa, 0x9);
|
||||
decrypt_bios(machine(), "user2", 0x1080000, regSize , 0x6, 0x5, 0x4, 0x7, 0x1, 0x3, 0x0, 0x2, 0xc, 0xd, 0xe, 0xf, 0x8, 0xb, 0xa, 0x9);
|
||||
memn_driver_init();
|
||||
}
|
||||
|
||||
@ -646,14 +658,9 @@ static MACHINE_CONFIG_START( namcos10_memn, namcos10_state )
|
||||
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED(ns10_konotako, namcos10_memn)
|
||||
static MACHINE_CONFIG_DERIVED(ns10_chocovdr, namcos10_memn)
|
||||
/* decrypter device (CPLD in hardware?) */
|
||||
MCFG_DEVICE_ADD("decrypter", KONOTAKO_DECRYPTER, 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED(ns10_startrgn, namcos10_memn)
|
||||
/* decrypter device (CPLD in hardware?) */
|
||||
MCFG_DEVICE_ADD("decrypter", STARTRGN_DECRYPTER, 0)
|
||||
MCFG_DEVICE_ADD("decrypter", CHOCOVDR_DECRYPTER, 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED(ns10_gamshara, namcos10_memn)
|
||||
@ -661,6 +668,21 @@ static MACHINE_CONFIG_DERIVED(ns10_gamshara, namcos10_memn)
|
||||
MCFG_DEVICE_ADD("decrypter", GAMSHARA_DECRYPTER, 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED(ns10_konotako, namcos10_memn)
|
||||
/* decrypter device (CPLD in hardware?) */
|
||||
MCFG_DEVICE_ADD("decrypter", KONOTAKO_DECRYPTER, 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED(ns10_nflclsfb, namcos10_memn)
|
||||
/* decrypter device (CPLD in hardware?) */
|
||||
MCFG_DEVICE_ADD("decrypter", NFLCLSFB_DECRYPTER, 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED(ns10_startrgn, namcos10_memn)
|
||||
/* decrypter device (CPLD in hardware?) */
|
||||
MCFG_DEVICE_ADD("decrypter", STARTRGN_DECRYPTER, 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static INPUT_PORTS_START( namcos10 )
|
||||
/* IN 0 */
|
||||
PORT_START("SYSTEM")
|
||||
@ -862,9 +884,9 @@ GAME( 2001, gjspace, 0, namcos10_memn, namcos10, namcos10_state, gjspac
|
||||
GAME( 2001, mrdrilrg, 0, namcos10_memn, namcos10, namcos10_state, mrdrilrg, ROT0, "Namco", "Mr. Driller G (Japan, DRG1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // PORT_4WAY joysticks
|
||||
GAME( 2001, mrdrilrga, mrdrilrg, namcos10_memn, namcos10, namcos10_state, mrdrilrg, ROT0, "Namco", "Mr. Driller G ALT (Japan, DRG1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // PORT_4WAY joysticks
|
||||
GAME( 2001, knpuzzle, 0, namcos10_memn, namcos10, namcos10_state, knpuzzle, ROT0, "Namco", "Kotoba no Puzzle Mojipittan (Japan, KPM1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2002, chocovdr, 0, namcos10_memn, namcos10, namcos10_state, chocovdr, ROT0, "Namco", "Uchuu Daisakusen: Chocovader Contactee (Japan, CVC1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2002, chocovdr, 0, ns10_chocovdr, namcos10, namcos10_state, chocovdr, ROT0, "Namco", "Uchuu Daisakusen: Chocovader Contactee (Japan, CVC1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2002, startrgn, 0, ns10_startrgn, namcos10, namcos10_state, startrgn, ROT0, "Namco", "Star Trigon (Japan, STT1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
|
||||
GAME( 2002, panikuru, 0, namcos10_memn, namcos10, namcos10_state, panikuru, ROT0, "Namco", "Panicuru Panekuru (Japan, PPA1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2003, nflclsfb, 0, namcos10_memn, namcos10, namcos10_state, nflclsfb, ROT0, "Namco", "NFL Classic Football (US, NCF3 Ver.A.)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2003, nflclsfb, 0, ns10_nflclsfb, namcos10, namcos10_state, nflclsfb, ROT0, "Namco", "NFL Classic Football (US, NCF3 Ver.A.)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2003, gamshara, 0, ns10_gamshara, namcos10, namcos10_state, gamshara, ROT0, "Mitchell", "Gamshara (10021 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
|
||||
GAME( 2003, konotako, 0, ns10_konotako, namcos10, namcos10_state, konotako, ROT0, "Mitchell", "Kono Tako (10021 Ver.A)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
|
||||
|
@ -84,8 +84,10 @@ really exist.
|
||||
#include "emu.h"
|
||||
#include "ns10crypt.h"
|
||||
|
||||
const device_type CHOCOVDR_DECRYPTER = &device_creator<chocovdr_decrypter_device>;
|
||||
const device_type GAMSHARA_DECRYPTER = &device_creator<gamshara_decrypter_device>;
|
||||
const device_type KONOTAKO_DECRYPTER = &device_creator<konotako_decrypter_device>;
|
||||
const device_type NFLCLSFB_DECRYPTER = &device_creator<nflclsfb_decrypter_device>;
|
||||
const device_type STARTRGN_DECRYPTER = &device_creator<startrgn_decrypter_device>;
|
||||
|
||||
// this could perfectly be part of the per-game logic; by now, only gamshara seems to use it, so we keep it global
|
||||
@ -127,16 +129,30 @@ UINT16 ns10_decrypter_device::decrypt(UINT16 cipherword)
|
||||
for (int j = 15; j >= 0; --j)
|
||||
{
|
||||
_mask <<= 1;
|
||||
_mask ^= gf2_reduce(_logic.eMask[j] & _previous_cipherwords);
|
||||
_mask ^= gf2_reduce(_logic.dMask[j] & _previous_plainwords);
|
||||
_mask ^= _reducer.gf2_reduce(_logic.eMask[j] & _previous_cipherwords);
|
||||
_mask ^= _reducer.gf2_reduce(_logic.dMask[j] & _previous_plainwords);
|
||||
}
|
||||
_mask ^= _logic.xMask;
|
||||
_mask ^= _logic.nonlinear_calculation(_previous_cipherwords, _previous_plainwords);
|
||||
_mask ^= _logic.nonlinear_calculation(_previous_cipherwords, _previous_plainwords, _reducer);
|
||||
|
||||
return plainword;
|
||||
}
|
||||
|
||||
void ns10_decrypter_device::device_start()
|
||||
{
|
||||
_active = false;
|
||||
}
|
||||
|
||||
void ns10_decrypter_device::init(int iv)
|
||||
{
|
||||
// by now, only gamshara requires non-trivial initialization code; data
|
||||
// should be moved to the per-game logic in case any other game do it differently
|
||||
_previous_cipherwords = BITSWAP16(initSbox[iv],3,16,16,2,1,16,16,0,16,16,16,16,16,16,16,16);
|
||||
_previous_plainwords = 0;
|
||||
_mask = 0;
|
||||
}
|
||||
|
||||
gf2_reducer::gf2_reducer()
|
||||
{
|
||||
int reduction;
|
||||
|
||||
@ -151,16 +167,7 @@ void ns10_decrypter_device::device_start()
|
||||
}
|
||||
}
|
||||
|
||||
void ns10_decrypter_device::init(int iv)
|
||||
{
|
||||
// by now, only gamshara requires non-trivial initialization code; data
|
||||
// should be moved to the per-game logic in case any other game do it differently
|
||||
_previous_cipherwords = BITSWAP16(initSbox[iv],3,16,16,2,1,16,16,0,16,16,16,16,16,16,16,16);
|
||||
_previous_plainwords = 0;
|
||||
_mask = 0;
|
||||
}
|
||||
|
||||
int ns10_decrypter_device::gf2_reduce(UINT64 num)
|
||||
int gf2_reducer::gf2_reduce(UINT64 num)const
|
||||
{
|
||||
return
|
||||
_gf2Reduction[num & 0xffff] ^
|
||||
@ -172,7 +179,29 @@ int ns10_decrypter_device::gf2_reduce(UINT64 num)
|
||||
|
||||
// game-specific logic
|
||||
|
||||
static UINT16 gamshara_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords)
|
||||
static UINT16 chocovdr_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords, const gf2_reducer& reducer)
|
||||
{
|
||||
UINT64 previous_masks = previous_cipherwords ^ previous_plainwords;
|
||||
return ((previous_masks >> 9) & (reducer.gf2_reduce(0x0000000010065810ull & previous_cipherwords) ^ reducer.gf2_reduce(0x0000000021005810ull & previous_plainwords)) & 1) << 10;
|
||||
}
|
||||
|
||||
static const ns10_decrypter_device::ns10_crypto_logic chocovdr_crypto_logic = {
|
||||
{
|
||||
0x00005239351ec1daull, 0x0000000000008090ull, 0x0000000048264808ull, 0x0000000000004820ull,
|
||||
0x0000000000000500ull, 0x0000000058ff5a54ull, 0x00000000d8220208ull, 0x00005239351e91d3ull,
|
||||
0x000000009a1dfaffull, 0x0000000090040001ull, 0x0000000000000100ull, 0x0000000000001408ull,
|
||||
0x0000000032efd3f1ull, 0x00000000000000d0ull, 0x0000000032efd2d7ull, 0x0000000000000840ull,
|
||||
}, {
|
||||
0x00002000410485daull, 0x0000000000008081ull, 0x0000000008044088ull, 0x0000000000004802ull,
|
||||
0x0000000000000500ull, 0x00000000430cda54ull, 0x0000000010000028ull, 0x00002000410491dbull,
|
||||
0x000000001100fafeull, 0x0000000018040001ull, 0x0000000000000010ull, 0x0000000000000508ull,
|
||||
0x000000006800d3f5ull, 0x0000000000000058ull, 0x000000006800d2d5ull, 0x0000000000001840ull,
|
||||
},
|
||||
0x5b22,
|
||||
chocovdr_nonlinear_calc
|
||||
};
|
||||
|
||||
static UINT16 gamshara_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords, const gf2_reducer&)
|
||||
{
|
||||
UINT64 previous_masks = previous_cipherwords ^ previous_plainwords;
|
||||
return ((previous_masks >> 7) & (previous_masks >> 13) & 1) << 2;
|
||||
@ -194,7 +223,7 @@ static const ns10_decrypter_device::ns10_crypto_logic gamshara_crypto_logic = {
|
||||
gamshara_nonlinear_calc
|
||||
};
|
||||
|
||||
static UINT16 konotako_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords)
|
||||
static UINT16 konotako_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords, const gf2_reducer&)
|
||||
{
|
||||
UINT64 previous_masks = previous_cipherwords ^ previous_plainwords;
|
||||
return ((previous_masks >> 7) & (previous_masks >> 15) & 1) << 15;
|
||||
@ -216,7 +245,29 @@ static const ns10_decrypter_device::ns10_crypto_logic konotako_crypto_logic = {
|
||||
konotako_nonlinear_calc
|
||||
};
|
||||
|
||||
static UINT16 startrgn_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords)
|
||||
static UINT16 nflclsfb_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords, const gf2_reducer& reducer)
|
||||
{
|
||||
UINT64 previous_masks = previous_cipherwords ^ previous_plainwords;
|
||||
return ((previous_masks >> 1) & (reducer.gf2_reduce(0x0000000040de8fb3ull & previous_cipherwords) ^ reducer.gf2_reduce(0x0000000088008fb3ull & previous_plainwords)) & 1) << 2;
|
||||
}
|
||||
|
||||
static const ns10_decrypter_device::ns10_crypto_logic nflclsfb_crypto_logic = {
|
||||
{
|
||||
0x000034886e281880ull, 0x0000000012c5e7baull, 0x0000000000000200ull, 0x000000002900002aull,
|
||||
0x00000000000004c0ull, 0x0000000012c5e6baull, 0x00000000e0df8bbbull, 0x000000002011532aull,
|
||||
0x0000000000009040ull, 0x0000000000006004ull, 0x000000000000a001ull, 0x000034886e2818e1ull,
|
||||
0x0000000000004404ull, 0x0000000000004200ull, 0x0000000000009100ull, 0x0000000020115712ull,
|
||||
}, {
|
||||
0x00000e00060819c0ull, 0x000000000e08e7baull, 0x0000000000000800ull, 0x000000000100002aull,
|
||||
0x00000000000010c0ull, 0x000000000e08cebaull, 0x0000000088018bbbull, 0x000000008c005302ull,
|
||||
0x000000000000c040ull, 0x0000000000006010ull, 0x0000000000000001ull, 0x00000e00060818e3ull,
|
||||
0x0000000000000404ull, 0x0000000000004201ull, 0x0000000000001100ull, 0x000000008c0057b2ull,
|
||||
},
|
||||
0xbe32,
|
||||
nflclsfb_nonlinear_calc
|
||||
};
|
||||
|
||||
static UINT16 startrgn_nonlinear_calc(UINT64 previous_cipherwords, UINT64 previous_plainwords, const gf2_reducer&)
|
||||
{
|
||||
UINT64 previous_masks = previous_cipherwords ^ previous_plainwords;
|
||||
return ((previous_masks >> 12) & (previous_masks >> 14) & 1) << 4;
|
||||
@ -241,6 +292,11 @@ static const ns10_decrypter_device::ns10_crypto_logic startrgn_crypto_logic = {
|
||||
|
||||
// game-specific devices
|
||||
|
||||
chocovdr_decrypter_device::chocovdr_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: ns10_decrypter_device(CHOCOVDR_DECRYPTER, chocovdr_crypto_logic, mconfig, tag, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
gamshara_decrypter_device::gamshara_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: ns10_decrypter_device(GAMSHARA_DECRYPTER, gamshara_crypto_logic, mconfig, tag, owner, clock)
|
||||
{
|
||||
@ -251,6 +307,11 @@ konotako_decrypter_device::konotako_decrypter_device(const machine_config &mconf
|
||||
{
|
||||
}
|
||||
|
||||
nflclsfb_decrypter_device::nflclsfb_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: ns10_decrypter_device(NFLCLSFB_DECRYPTER, nflclsfb_crypto_logic, mconfig, tag, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
startrgn_decrypter_device::startrgn_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: ns10_decrypter_device(STARTRGN_DECRYPTER, startrgn_crypto_logic, mconfig, tag, owner, clock)
|
||||
{
|
||||
|
@ -4,6 +4,15 @@
|
||||
#ifndef _NS10CRYPT_H_
|
||||
#define _NS10CRYPT_H_
|
||||
|
||||
class gf2_reducer // helper class
|
||||
{
|
||||
public:
|
||||
gf2_reducer();
|
||||
int gf2_reduce(UINT64 num)const;
|
||||
private:
|
||||
int _gf2Reduction[0x10000];
|
||||
};
|
||||
|
||||
class ns10_decrypter_device : public device_t
|
||||
{
|
||||
public:
|
||||
@ -14,7 +23,7 @@ public:
|
||||
UINT64 eMask[16];
|
||||
UINT64 dMask[16];
|
||||
UINT16 xMask;
|
||||
UINT16(*nonlinear_calculation)(UINT64, UINT64); // preliminary encoding; need research
|
||||
UINT16(*nonlinear_calculation)(UINT64, UINT64, const gf2_reducer&); // preliminary encoding; need research
|
||||
};
|
||||
|
||||
void activate(int iv);
|
||||
@ -34,16 +43,23 @@ private:
|
||||
UINT64 _previous_plainwords;
|
||||
bool _active;
|
||||
const ns10_crypto_logic& _logic;
|
||||
int _gf2Reduction[0x10000];
|
||||
static const int initSbox[16];
|
||||
const gf2_reducer _reducer;
|
||||
|
||||
void device_start();
|
||||
void init(int iv);
|
||||
int gf2_reduce(UINT64 num);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// game-specific devices
|
||||
|
||||
class chocovdr_decrypter_device : public ns10_decrypter_device
|
||||
{
|
||||
public:
|
||||
chocovdr_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
};
|
||||
|
||||
class gamshara_decrypter_device : public ns10_decrypter_device
|
||||
{
|
||||
public:
|
||||
@ -56,6 +72,12 @@ public:
|
||||
konotako_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
};
|
||||
|
||||
class nflclsfb_decrypter_device : public ns10_decrypter_device
|
||||
{
|
||||
public:
|
||||
nflclsfb_decrypter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
};
|
||||
|
||||
class startrgn_decrypter_device : public ns10_decrypter_device
|
||||
{
|
||||
public:
|
||||
@ -63,8 +85,10 @@ public:
|
||||
};
|
||||
|
||||
|
||||
extern const device_type CHOCOVDR_DECRYPTER;
|
||||
extern const device_type GAMSHARA_DECRYPTER;
|
||||
extern const device_type KONOTAKO_DECRYPTER;
|
||||
extern const device_type NFLCLSFB_DECRYPTER;
|
||||
extern const device_type STARTRGN_DECRYPTER;
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user