Continued Clean-Up More Efficient
more of the same stuff… organizing and cleaning up stuff to make it run better. CPU load now reduced by half!
This commit is contained in:
parent
f23780901b
commit
43c70bbdb1
171
ipsc.py
171
ipsc.py
@ -48,12 +48,12 @@ except ImportError:
|
|||||||
sys.exit('IPSC mask values file not found or invalid')
|
sys.exit('IPSC mask values file not found or invalid')
|
||||||
|
|
||||||
|
|
||||||
ids = []
|
ids = {}
|
||||||
try:
|
try:
|
||||||
with open('./radioids.csv', 'r') as radioids_csv:
|
with open('./radioids.csv', 'r') as radioids_csv:
|
||||||
radio_ids = csv.reader(radioids_csv, dialect='excel', delimiter=',')
|
radio_ids = csv.reader(radioids_csv, dialect='excel', delimiter=',')
|
||||||
for row in radio_ids:
|
for row in radio_ids:
|
||||||
ids.append(row)
|
ids[int(row[1])] = (row[0])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sys.exit('No Radio ID CSV file found')
|
sys.exit('No Radio ID CSV file found')
|
||||||
|
|
||||||
@ -167,42 +167,22 @@ def call_ctl_3(_network, _data):
|
|||||||
def xcmp_xnl(_network, _data):
|
def xcmp_xnl(_network, _data):
|
||||||
print('({}) XCMP/XNL Packet Received From: {}' .format(_network, _src_sub))
|
print('({}) XCMP/XNL Packet Received From: {}' .format(_network, _src_sub))
|
||||||
|
|
||||||
def group_voice(_network, _src_sub, _dst_group, _call, _peerid, _data):
|
def group_voice(_network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||||
# _log = logger.debug
|
# _log = logger.debug
|
||||||
|
if ((_network, _ts) not in ACTIVE_CALLS) or _end:
|
||||||
if _call == '\x00':
|
|
||||||
if (_network, 'Slot 1') not in ACTIVE_CALLS:
|
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
|
||||||
_dst_group = get_info(int(binascii.b2a_hex(_dst_group), 16))
|
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
|
||||||
ACTIVE_CALLS.append((_network, 'Slot 1'))
|
|
||||||
print('{} ({}) CALL START Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t1' .format(_time, _network, _peerid, _src_sub, _dst_group))
|
|
||||||
|
|
||||||
if _call == '\x20':
|
|
||||||
if (_network, 'Slot 2') not in ACTIVE_CALLS:
|
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
|
||||||
_dst_group = get_info(int(binascii.b2a_hex(_dst_group), 16))
|
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
|
||||||
ACTIVE_CALLS.append((_network, 'Slot 2'))
|
|
||||||
print('{} ({}) CALL START Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t2' .format(_time, _network, _peerid, _src_sub, _dst_group))
|
|
||||||
|
|
||||||
if _call == '\x40':
|
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
||||||
_dst_group = get_info(int(binascii.b2a_hex(_dst_group), 16))
|
_dst_sub = get_info(int_id(_dst_sub))
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
_peerid = get_info(int_id(_peerid))
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
_src_sub = get_info(int_id(_src_sub))
|
||||||
ACTIVE_CALLS.remove((_network, 'Slot 1'))
|
if not _end: ACTIVE_CALLS.append((_network, _ts))
|
||||||
print('{} ({}) CALL END Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t1 \a' .format(_time, _network, _peerid, _src_sub, _dst_group))
|
if _end: ACTIVE_CALLS.remove((_network, _ts))
|
||||||
|
|
||||||
if _call == '\x60':
|
if _ts: _ts = 2
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
else: _ts = 1
|
||||||
_dst_group = get_info(int(binascii.b2a_hex(_dst_group), 16))
|
if _end: _end = 'END'
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
else: _end = 'START'
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
|
||||||
ACTIVE_CALLS.remove((_network, 'Slot 2'))
|
print('{} ({}) Call {} Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t{}' .format(_time, _network, _end, _peerid, _src_sub, _dst_sub, _ts))
|
||||||
print('{} ({}) CALL END Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t2 \a' .format(_time, _network, _peerid, _src_sub, _dst_group))
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
for source in NETWORK[_network]['RULES']['GROUP_VOICE']:
|
for source in NETWORK[_network]['RULES']['GROUP_VOICE']:
|
||||||
@ -221,63 +201,41 @@ def group_voice(_network, _src_sub, _dst_group, _call, _peerid, _data):
|
|||||||
send_to_ipsc(_target, _data)
|
send_to_ipsc(_target, _data)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def private_voice(_network, _src_sub, _dst_sub, _call, _peerid, _data):
|
def private_voice(_network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||||
# _log = logger.debug
|
# _log = logger.debug
|
||||||
|
if ((_network, _ts) not in ACTIVE_CALLS) or _end:
|
||||||
if _call == '\x00':
|
|
||||||
if (_network, 'Slot 1') not in ACTIVE_CALLS:
|
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
|
||||||
_dst_sub = get_info(int(binascii.b2a_hex(_dst_sub), 16))
|
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
|
||||||
ACTIVE_CALLS.append((_network, 'Slot 1'))
|
|
||||||
print('{} ({}) CALL START Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t1' .format(_time, _network, _peerid, _src_sub, _dst_sub))
|
|
||||||
|
|
||||||
if _call == '\x20':
|
|
||||||
if (_network, 'Slot 2') not in ACTIVE_CALLS:
|
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
|
||||||
_dst_sub = get_info(int(binascii.b2a_hex(_dst_sub), 16))
|
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
|
||||||
ACTIVE_CALLS.append((_network, 'Slot 2'))
|
|
||||||
print('({} {}) CALL START Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t2' .format(_time, _network, _peerid, _src_sub, _dst_sub))
|
|
||||||
|
|
||||||
if _call == '\x40':
|
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
||||||
_dst_sub = get_info(int(binascii.b2a_hex(_dst_sub), 16))
|
_dst_sub = get_info(int_id(_dst_sub))
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
_peerid = get_info(int_id(_peerid))
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
_src_sub = get_info(int_id(_src_sub))
|
||||||
ACTIVE_CALLS.remove((_network, 'Slot 1'))
|
if not _end: ACTIVE_CALLS.append((_network, _ts))
|
||||||
print('{} ({}) CALL END Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t1 \a' .format(_time, _network, _peerid, _src_sub, _dst_sub))
|
if _end: ACTIVE_CALLS.remove((_network, _ts))
|
||||||
|
|
||||||
if _call == '\x60':
|
if _ts: _ts = 2
|
||||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
else: _ts = 1
|
||||||
_dst_sub = get_info(int(binascii.b2a_hex(_dst_sub), 16))
|
if _end: _end = 'END'
|
||||||
_peerid = get_info(int(binascii.b2a_hex(_peerid), 16))
|
else: _end = 'START'
|
||||||
_src_sub = get_info(int(binascii.b2a_hex(_src_sub), 16))
|
|
||||||
ACTIVE_CALLS.remove((_network, 'Slot 2'))
|
|
||||||
print('{} ({}) CALL END Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t2 \a' .format(_time, _network, _peerid, _src_sub, _dst_sub))
|
|
||||||
|
|
||||||
def group_data(_network, _src_sub, _dst_sub, _call, _peerid, _data):
|
print('{} ({}) Call {} Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t{}' .format(_time, _network, _end, _peerid, _src_sub, _dst_sub, _ts))
|
||||||
|
|
||||||
_dst_sub = get_info(_dst_sub)
|
def group_data(_network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||||
_peerid = get_info(_peerid)
|
_dst_sub = get_info(int_id(_dst_sub))
|
||||||
_src_sub = get_info(_src_sub)
|
_peerid = get_info(int_id(_peerid))
|
||||||
|
_src_sub = get_info(int_id(_src_sub))
|
||||||
print('({}) Group Data Packet Received From: {}' .format(_network, _src_sub))
|
print('({}) Group Data Packet Received From: {}' .format(_network, _src_sub))
|
||||||
|
|
||||||
def private_data(__network, _src_sub, _dst_sub, _call, _peerid, _data):
|
def private_data(_network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||||
|
_dst_sub = get_info(int_id(_dst_sub))
|
||||||
_dst_sub = get_info(_dst_sub)
|
_peerid = get_info(int_id(_peerid))
|
||||||
_peerid = get_info(_peerid)
|
_src_sub = get_info(int_id(_src_sub))
|
||||||
_src_sub = get_info(_src_sub)
|
|
||||||
print('({}) Private Data Packet Received From: {} To: {}' .format(_network, _src_sub, _dst_sub))
|
print('({}) Private Data Packet Received From: {} To: {}' .format(_network, _src_sub, _dst_sub))
|
||||||
|
|
||||||
def unknown_message(_network, _packettype, _data):
|
def unknown_message(_network, _packettype, _peerid, _data):
|
||||||
_peerid = binascii.b2a_hex(_data[1:5])
|
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
||||||
_packettype = binascii.b2a_hex(_packettype)
|
_packettype = binascii.b2a_hex(_packettype)
|
||||||
_peerid = get_info(_peerid)
|
_peerid = get_info(int_id(_peerid))
|
||||||
print("({}) Unknown message type encountered, Packet Type: {} From: {} " .format(_network, _packettype, _peerid))
|
print('{} ({}) Unknown message type encountered\n\tPacket Type: {}\n\tFrom: {}' .format(_time, _network, _packettype, _peerid))
|
||||||
print(binascii.b2a_hex(_data))
|
print('\t', binascii.b2a_hex(_data))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -285,6 +243,11 @@ def unknown_message(_network, _packettype, _data):
|
|||||||
# UTILITY FUNCTIONS FOR INTERNAL USE
|
# UTILITY FUNCTIONS FOR INTERNAL USE
|
||||||
#************************************************
|
#************************************************
|
||||||
|
|
||||||
|
# Convert a hex string to an int (radio ID, etc.)
|
||||||
|
#
|
||||||
|
def int_id(_hex_string):
|
||||||
|
return int(binascii.b2a_hex(_hex_string), 16)
|
||||||
|
|
||||||
# Re-Write Source Radio-ID (DMR NAT)
|
# Re-Write Source Radio-ID (DMR NAT)
|
||||||
#
|
#
|
||||||
def dmr_nat(_data, _nat_id):
|
def dmr_nat(_data, _nat_id):
|
||||||
@ -297,9 +260,8 @@ def dmr_nat(_data, _nat_id):
|
|||||||
# Lookup text data for numeric IDs
|
# Lookup text data for numeric IDs
|
||||||
#
|
#
|
||||||
def get_info(_id):
|
def get_info(_id):
|
||||||
for id in ids:
|
if _id in ids:
|
||||||
if int(id[1]) == _id:
|
return ids[_id]
|
||||||
return id[0]
|
|
||||||
return _id
|
return _id
|
||||||
|
|
||||||
# Remove the hash from a packet and return the payload
|
# Remove the hash from a packet and return the payload
|
||||||
@ -654,7 +616,7 @@ class IPSC(DatagramProtocol):
|
|||||||
The dict will typically contain a peer_id so the origin of the event is known.
|
The dict will typically contain a peer_id so the origin of the event is known.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
return
|
|
||||||
|
|
||||||
#************************************************
|
#************************************************
|
||||||
# RECEIVED DATAGRAM - ACT IMMEDIATELY!!!
|
# RECEIVED DATAGRAM - ACT IMMEDIATELY!!!
|
||||||
@ -670,11 +632,10 @@ class IPSC(DatagramProtocol):
|
|||||||
def datagramReceived(self, data, (host, port)):
|
def datagramReceived(self, data, (host, port)):
|
||||||
_packettype = data[0:1]
|
_packettype = data[0:1]
|
||||||
_peerid = data[1:5]
|
_peerid = data[1:5]
|
||||||
_dec_peerid = int(binascii.b2a_hex(_peerid), 16)
|
|
||||||
|
|
||||||
# Authenticate the packet
|
# Authenticate the packet
|
||||||
if self.validate_auth(self._local['AUTH_KEY'], data) == False:
|
if self.validate_auth(self._local['AUTH_KEY'], data) == False:
|
||||||
logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, binascii.b2a_hex(_packettype), _dec_peerid)
|
logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, binascii.b2a_hex(_packettype), int(binascii.b2a_hex(_peerid), 16))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Strip the hash, we won't need it anymore
|
# Strip the hash, we won't need it anymore
|
||||||
@ -683,7 +644,7 @@ class IPSC(DatagramProtocol):
|
|||||||
# Packets types that must be originated from a peer (including master peer)
|
# Packets types that must be originated from a peer (including master peer)
|
||||||
if (_packettype in ANY_PEER_REQUIRED):
|
if (_packettype in ANY_PEER_REQUIRED):
|
||||||
if not(valid_master(self._network, _peerid) == False or valid_peer(self._peer_list, _peerid) == False):
|
if not(valid_master(self._network, _peerid) == False or valid_peer(self._peer_list, _peerid) == False):
|
||||||
logger.warning('(%s) PeerError: Peer not in peer-list: %s', self._network, _dec_peerid)
|
logger.warning('(%s) PeerError: Peer not in peer-list: %s', self._network, int(binascii.b2a_hex(_peerid), 16))
|
||||||
return
|
return
|
||||||
|
|
||||||
# User, as in "subscriber" generated packets - a.k.a someone trasmitted
|
# User, as in "subscriber" generated packets - a.k.a someone trasmitted
|
||||||
@ -691,27 +652,29 @@ class IPSC(DatagramProtocol):
|
|||||||
# Extract commonly used items from the packet header
|
# Extract commonly used items from the packet header
|
||||||
_src_sub = data[6:9]
|
_src_sub = data[6:9]
|
||||||
_dst_sub = data[9:12]
|
_dst_sub = data[9:12]
|
||||||
_call = data[17:18]
|
_call = int_id(data[17:18])
|
||||||
|
_ts = bool(_call & TS_CALL_MSK)
|
||||||
|
_end = bool(_call & END_MSK)
|
||||||
|
|
||||||
# User Voice and Data Call Types:
|
# User Voice and Data Call Types:
|
||||||
if (_packettype == GROUP_VOICE):
|
if (_packettype == GROUP_VOICE):
|
||||||
self._notify_event(self._network, 'group_voice', {'peer_id': _dec_peerid})
|
self._notify_event(self._network, 'group_voice', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
|
||||||
group_voice(self._network, _src_sub, _dst_sub, _call, _peerid, data)
|
group_voice(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif (_packettype == PVT_VOICE):
|
elif (_packettype == PVT_VOICE):
|
||||||
self._notify_event(self._network, 'private_voice', {'peer_id': _dec_peerid})
|
self._notify_event(self._network, 'private_voice', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
|
||||||
private_voice(self._network, _src_sub, _dst_sub, _call, _peerid, data)
|
private_voice(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif (_packettype == GROUP_DATA):
|
elif (_packettype == GROUP_DATA):
|
||||||
self._notify_event(self._network, 'group_data', {'peer_id': _dec_peerid})
|
self._notify_event(self._network, 'group_data', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
|
||||||
group_data(self._network, _src_sub, _dst_sub, _call, _peerid, data)
|
group_data(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif (_packettype == PVT_DATA):
|
elif (_packettype == PVT_DATA):
|
||||||
self._notify_event(self._network, 'private_voice', {'peer_id': _dec_peerid})
|
self._notify_event(self._network, 'private_voice', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
|
||||||
private_data(self._network, _src_sub, _dst_sub, _call, _peerid, data)
|
private_data(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||||
return
|
return
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -751,7 +714,7 @@ class IPSC(DatagramProtocol):
|
|||||||
# Packets types that must be originated from a peer
|
# Packets types that must be originated from a peer
|
||||||
if (_packettype in PEER_REQUIRED):
|
if (_packettype in PEER_REQUIRED):
|
||||||
if valid_peer(self._peer_list, _peerid) == False:
|
if valid_peer(self._peer_list, _peerid) == False:
|
||||||
logger.warning('(%s) PeerError: Peer %s not in peer-list: %s', self._network, _dec_peerid, self._peer_list)
|
logger.warning('(%s) PeerError: Peer %s not in peer-list: %s', self._network, int(binascii.b2a_hex(_peerid), 16), self._peer_list)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Packets we send...
|
# Packets we send...
|
||||||
@ -766,7 +729,7 @@ class IPSC(DatagramProtocol):
|
|||||||
self.transport.write(peer_reg_reply_packet, (host, port))
|
self.transport.write(peer_reg_reply_packet, (host, port))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Packets we recieve...
|
# Packets we receive...
|
||||||
elif (_packettype == PEER_ALIVE_REPLY):
|
elif (_packettype == PEER_ALIVE_REPLY):
|
||||||
for peer in self._config['PEERS']:
|
for peer in self._config['PEERS']:
|
||||||
if peer['RADIO_ID'] == _peerid:
|
if peer['RADIO_ID'] == _peerid:
|
||||||
@ -782,9 +745,10 @@ class IPSC(DatagramProtocol):
|
|||||||
|
|
||||||
|
|
||||||
# Packets types that must be originated from a Master
|
# Packets types that must be originated from a Master
|
||||||
|
# Packets we receive...
|
||||||
if (_packettype in MASTER_REQUIRED):
|
if (_packettype in MASTER_REQUIRED):
|
||||||
if valid_master(self._network, _peerid) == False:
|
if valid_master(self._network, _peerid) == False:
|
||||||
logger.warning('(%s) PeerError: Master %s is invalid: %s', self._network, _dec_peerid, self._peer_list)
|
logger.warning('(%s) PeerError: Master %s is invalid: %s', self._network, int(binascii.b2a_hex(_peerid), 16), self._peer_list)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (_packettype == MASTER_ALIVE_REPLY):
|
if (_packettype == MASTER_ALIVE_REPLY):
|
||||||
@ -815,9 +779,10 @@ class IPSC(DatagramProtocol):
|
|||||||
|
|
||||||
# If there's a packet type we don't know aobut, it should be logged so we can figure it out and take an appropriate action!
|
# If there's a packet type we don't know aobut, it should be logged so we can figure it out and take an appropriate action!
|
||||||
else:
|
else:
|
||||||
unknown_message(self._network, _packettype, data)
|
unknown_message(self._network, _packettype, _peerid, data)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
#************************************************
|
#************************************************
|
||||||
# Derived Class
|
# Derived Class
|
||||||
# used in the rare event of an
|
# used in the rare event of an
|
||||||
|
@ -49,3 +49,13 @@ PKT_AUTH_MSK = 0b00010000
|
|||||||
DATA_CALL_MSK = 0b00001000
|
DATA_CALL_MSK = 0b00001000
|
||||||
VOICE_CALL_MSK = 0b00000100
|
VOICE_CALL_MSK = 0b00000100
|
||||||
MSTR_PEER_MSK = 0b00000001
|
MSTR_PEER_MSK = 0b00000001
|
||||||
|
|
||||||
|
# TIMESLOT CALL & STATUS BYTE
|
||||||
|
|
||||||
|
# Byte 17 of Group and Private Voice/Data Packets
|
||||||
|
# ..x.. ....TS Value (0=TS1, 1=TS2)
|
||||||
|
# .x... ....TS In Progress/End (0=In Progress, 1=End)
|
||||||
|
# Possible values: 0x00=TS1, 0x20=TS2, 0x40=TS1 End, 0x60=TS2 End
|
||||||
|
# MASK VALUE:
|
||||||
|
END_MSK = 0b01000000
|
||||||
|
TS_CALL_MSK = 0b00100000
|
Loading…
x
Reference in New Issue
Block a user