Raised This Month: $12 Target: $400
 3% 

[TUT] Bits, Bit-fields, and Bit-wise Operators


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 10-06-2010 , 21:25   [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #1

This tutorial covers the basics of bits, bit-fields, and bit-wise operators and is written on a very basic level that everyone should be able to understand. I know a similar tutorial already exists but in my opinion it isn't very clear if the reader does not have any experience with the topic. I've also included basic examples of how bit-fields can be used in plugins. Please let me know of any errors\typos or recommendations. Also, if you've read this and are still unsure of something, please tell me so I can explain further.

What is a bit-field?
A bit-field is a series of contiguous bits within a memory cell (non-array). The number of bits in a given bit-field is determined by the number of bytes allocated for the data-type being used. By default, all bits in a bit-field are 0. When no bits are 1 in a given bit-field, the resulting value will be 0. If one or more bits are 1 within a bit-field, the resulting number will be non-zero. AMX-X has only 1 data-type which is a 4-byte cell so it is limited to 32-bits in a single bit-field. If you need to manipulate bits above 32, see the bit-field array example below.

Example of bit-field sizes:
  • 1-bytes = 8 bits == 0000 0000
  • 2-bytes = 16 bits = 0000 0000 0000 0000
  • 4-bytes = 32 bits = 0000 0000 0000 0000 0000 0000 0000 0000
How are numbers stored on a computer?
All information on a computer is stored in memory (RAM, ROM, hard-disc, flash memory, etc). At the lowest level, memory consists of many many many on-off switches (known as bits). An integer value is a result of a combination of bit(s) that are set to 1 or 0. If you see me refer to a binary number, this means the bit-representation of an integer.

Each bit cooresponds to a power of 2. For this example I will use 8 bits just as a quick example of how bits form a number. Bit-ordering (I'm not getting into endianness) is as follows: 7654 3210

The below values correspond to each of those bits. As you can see, if a given bit is 1 in a bitfield, the integer value will increased by the corresponding value for the respective power of 2.

1 === 2 power of 0 -- Stored in bits: 0000 0001 (binary representation)
2 === 2 power of 1 -- Stored in bits: 0000 0010
4 === 2 power of 2 -- Stored in bits: 0000 0100
8 === 2 power of 3 -- Stored in bits: 0000 1000
16 == 2 power of 4 -- Stored in bits: 0001 0000
32 == 2 power of 5 -- Stored in bits: 0010 0000
64 == 2 power of 6 -- Stored in bits: 0100 0000
128 = 2 power of 7 -- Stored in bits: 1000 0000

Suppose we have a variable holding the number 97, our bit-field would appear as: 0110 0001

Notice bits 1, 6, and 7 are set. We can then add each corresponding number (or 2 power of bit#) to our resulting number.

1 == 2 power of 0 -- 1
2
3
4
5
6 == 2 power of 5 -- 32
7 == 2 power of 6 -- 64
8 =

Or 2^0 + 2^6 + 2^7 = 1 + 32 + 64 = 97

What are the bit-wise operators and how do I use them?
  • & - And
  • | - Or
  • ^ - Xor (Exclusive Or)
  • ~ - Not (Or Ones-complement)
  • << - Left bit-shift
  • >> - Right bit-shift

Operator: &
Function: And
Description:
This operator is used to check if and only if both of the corresponding bits in both operands are 1. The resulting bit-field will consist of bits (1's) that were present at the same location within both operands.

15 & 3 = 3

15 = 0000 1111
03 = 0000 0011
03 = 0000 0011
_________________

145 & 97 = 1

145 = 1001 0001
097 = 0110 0001
001 = 0000 0001
_________________

255 & 234 = 234

255 = 1111 1111
234 = 1110 1010
234 = 1110 1010

Operator: |
Function: Or
Description:
This operator is used to check if one or both bits are 1 in the corresponding bit positions of the two operands. The resulting bit-field will consist of bits (1's) that were present in either of the operands for each bit location.

15 | 3 = 15

15 = 0000 1111
03 = 0000 0011
15 = 0000 1111
_________________

145 | 97 = 241

145 = 1001 0001
097 = 0110 0001
241 = 1111 0001
_________________

255 | 234 = 255

255 = 1111 1111
234 = 1110 1010
255 = 1111 1111

Operator: ^
Function: Xor (Exclusive Or)
Description:
This operator is used to check where the corresponding bits in the two operands are different. The resulting bit-field will consist of bits (1's) that were opposite (0 & 1 or 1 & 0) at the same location within both operands.

15 ^ 3 = 12

15 = 0000 1111
03 = 0000 0011
12 = 0000 1100
_________________

145 ^ 97 = 240

145 = 1001 0001
097 = 0110 0001
240 = 1111 0000
_________________

255 ^ 234 = 21

255 = 1111 1111
234 = 1110 1010
021 = 0001 0101

Operator: ~
Function: Not
Description:
Not takes the binary representation of a number, and turns all zeros into ones, and ones into zeros. Unlike the previous 3 bitwise operators [AND, OR, XOR], NOT takes just one operand

0000 1111 = 15
1111 0000 = ~15 = 240
_________________

1010 1010 = 170
0101 0101 = ~170 = 85
_________________

Operator: <<
Function: Left bit-shift
Description:
Shift bits in operand to the left by specified bit positions.

X << Y

This will shift bits in the X operand Y bits to the left. Any bits to the left that fall out of the bit-range will be dropped. Any bits added to the right due to the shift are 0. This example assumes a 1-byte memory cell so any bits shifted left past the 8th bit are dropped; if you are using a larger memory cell (in AMXX, a cell is 4-bytes or 32-bits) these bits will not be dropped but moved past the 8th bit to bits 9-32).

15 << 4

015 = 0000 1111
030 = 0001 1110 [All bits shifted 1 position]
060 = 0011 1100 [All bits shifted 2 positions]
120 = 0111 1000 [All bits shifted 3 positions]
240 = 1111 0000 [All bits shifted 4 positions]
_________________

209 << 2

209 = 1101 0001
162 = 1010 0010 [All bits shifted 1 position]
068 = 0100 0100 [All bits shifted 2 positions]

Operator: >>
Function: Right bit-shift
Description:
Shift bits in operand to the right by specified bit positions.

X >> Y

This will shift bits in the X operand Y bits to the right. Any bits to the right that fall out of the bit-range will be dropped. Any bits added to the left due to the shift are 0.

15 >> 4

015 = 0000 1111
007 = 0000 0111 [All bits right-shifted 1 position]
003 = 0000 0011 [All bits right-shifted 2 positions]
001 = 0000 0001 [All bits right-shifted 3 positions]
000 = 0000 0000 [All bits right-shifted 4 positions]
_________________

209 >> 2

209 = 1101 0001
104 = 0110 1000 [All bits right-shifted 1 position]
052 = 0011 0100 [All bits right-shifted 2 positions]

Common bit-field operations

Setting bit(s) to 1\true in a bit-field

This is done using the bit-wise OR operator |. As explained above, this will set the bit to true if it exists in either operand.

Assume an empty variable which we want to set bit 4 to true.

Set a single bit
iVariable = iVariable | ( 1 << 4 );

0000 0000 = iVariable (freshly declared, no value set)
0001 0000 = 1 << 4
0001 0000 = iVariable | ( 1 << 4 )

Set multiple bits in the same expression
iVariable = iVariable | ( ( 1 << 4 ) | ( 1 << 5 ) )

0000 0000 = iVariable
0001 0000 = 1 << 4
0010 0000 = 1 << 5
0011 0000 = iVariable | ( ( 1 << 4 ) | ( 1 << 5 ) )

Setting bit(s) to 0\false in a bit-field

This is done using a combination of bit-wise AND & and the NOT ~ operator.

Assume a bit-field with bits 4 and 5 set.

iVariable = ( ( 1 << 4 ) | ( 1 << 5 ) )

Suppose we need to set bit 4 to 0\false.

iVariable = iVariable & ~( 1 << 4 )

0011 0000 = ( ( 1 << 4 ) | ( 1 << 5 ) )
1110 1111 = ~( 1 << 4 )
0010 0000 = ( ( 1 << 4 ) | ( 1 << 5 ) ) & ~( 1 << 4 )

The same can be done for setting multiple bits to 0\false in a single expression. You only need to bit-wise OR the bits you want set to 0.

iVariable = iVariable & ~( ( 1 << 4 ) | ( 1 << 5 ) )

Using bit-fields
In a bit-field you can set bit(s) to 1, set bit(s) to 0, check if a particular bit(s) is 0/1, and utilize the integer that results from the bit-field. This makes bit-fields very useful and efficient.

Example 1, using bit-fields as a boolean

A useful application is if you want to store if a player is admin when he connects to the server. Common practice is to create an array of cells, using one cell for each player's true\false value.

Boolean array method:
PHP Code:
new boolg_IsAdmin[33];

public 
client_putinserverid )
{
    
g_IsAdminid ] = boolis_user_adminid );
}

public 
client_disconnectid )
{
    
g_IsAdminid ] = false;

If player id's 2 and 6 are admins, elements g_IsAdmin[2] and g_IsAdmin[6] will be true.

A cell is 4-bytes so using an array for this purpose requires 132 bytes of memory (33-cells times 4-bytes).

If a bit-field is used instead, you will only declare a single cell and that can store the same info but using only 4 bytes!

Bit-field method
PHP Code:
new g_IsAdmin//Only need a single cell of memory

public client_putinserverid )
{
    if ( 
is_user_adminid ) ) 
            
g_IsAdmin |= ( << ( id 31 ) );
}

public 
client_disconnectid )
{
    
g_IsAdmin &= ~( << ( id 31 ) );

You may be wondering why 1 is being shifted left by ( id & 31 ). This is because a 4-byte data-type is limited to 32 bits which means the maximum shift left that can be done without losing data is 31. Since player-ids in Half-Life can range from 1-32, we can not do 1 << 32 so doing a ( id & 31 ) will give us a usable value. When ( # & 31 ) is used, the value will be equal to the player id except when id == 32, when id == 32, the resulting value is 0. In a nutshell, player-id's 1-31 are returned as normal but when the player id is 32, 0 is returned so we are essentially using 0-31 instead of 1-32 which is exactly what we need.

In the situation explained above where player id's 2 and 6 are online admins, our bit-field would look like the below. If you are wondering why the bits are not at the shift position based on player id, refer to the ( id & 31 ) explained above.

0000 0000 0000 0000 0000 0000 0100 0100

A bonus for using bit-fields for storing boolean values is you can quickly check if any bits are set, or in this case, if any admins are online. Remember, if no bits are 1 in a given bit-field, the resulting value is 0; like-wise, if there are 1 or more bits set to 1 in the bit-field, the resulting number is non-zero.

PHP Code:
if ( g_IsAdmin )
    
//there are 1 or more admins online
else
    
//there are no admins 
If an array was used, you would need to use a loop to iterate through all elements in the array to check if each value is 1. This is what makes bit-fields not only more memory efficient but also CPU efficient and easier (less code).

Example 2, boolean for weapon index(s)

For this example I will show how you can flag various weapons in a bit-field. This can be used in a weapon restriction plugin or any plugin that needs a true\false value for each weapon. This is possible because weapons in CS have indexes that range from 1-32. In this example we will be creating a bit-field to represent weapons CSW_SG550 [13], CSW_AWP [18], & CSW_G3SG1 [24]. The highest index for a weapon (primary\secondary) is 30 (P90) so you do not need to worry about using ( # & 31) or anything like that.

g_Weapons = ( 1 << CSW_SG550 ) | ( 1 << CSW_AWP ) | ( 1 << CSW_G3SG1 );

This is what will occur with the above operation

0000 0000 0000 0000 0000 0000 0000 0001 = 1 (How 1 is represented on the bit-level. We are going to shift this bit to each weapon index bit position)

0000 0000 0000 0000 0010 0000 0000 0000 = 1 << CSW_SG550 (1 left-shifted by CSW_SG550 [13])
0000 0000 0000 0100 0000 0000 0000 0000 = 1 << CSW_AWP (1 left-shifted by CSW_AWP [18])
0000 0001 0000 0000 0000 0000 0000 0000 = 1 << CSW_G3SG1 (1 left-shifted by CSW_G3SG1 [24])
0000 0001 0000 0100 0010 0000 0000 0000 = g_Weapons (The result)

Remember, we are using Or for the above operation which will set each bit-position to 1 if it exists in either operand. This is why g_Weapons now has each bit set in the appropriate bit-position.

Now that we have a bit-field with our desired weapons, we can then check the weapon bit with the following:

PHP Code:
if ( g_Weapons & ( << CSW_AWP ) )
    
//weapon is in bitfield 
What is occurring with this statement is we are taking 1 and pushing the bit to the left by CSW_AWP [18] bits. This will set the 18th bit to 1 so we will be able to check it against g_Weapons to see if both bits exist in the position. Remember that the & operator checks if the bit is 1 at both cooresponding bit positions in each operand.

0000 0000 0000 0000 0000 0000 0000 0001 = 1 (Number 1 represented in bits)

0000 0000 0000 0100 0000 0000 0000 0000 = 1 << CSW_AWP (Left bit-shift of 18 bits)
0000 0001 0000 0100 0010 0000 0000 0000 = g_Weapons
0000 0000 0000 0100 0000 0000 0000 0000 = Bits in the CSW_AWP [18] position do both exist so this would return true

Suppose we want to check if scout is in our bit-field. CSW_SCOUT = index 3

PHP Code:
if ( g_Weapons & ( << CSW_SCOUT ) )
    
//That weapon is NOT in bitfield 
0000 0000 0000 0000 0000 0000 0000 0001 = 1

0000 0000 0000 0000 0000 0000 0000 1000 = 1 << CSW_SCOUT (Left bit-shift of 3 bits)
0000 0001 0000 0100 0010 0000 0000 0000 = g_Weapons
0000 0000 0000 0000 0000 0000 0000 0000 = Bits in the 4th bit (1<<3) position do not both exist so this would return false

Example 3, using a bit-field for your plugins flags\options.

PHP Code:
#define MAX_PLAYERS    32

//Create a list of options that are supported by the plugin
enum ( <<=_Options
{
    
Speed=1// 1 << 0 or 1 
    
AutoAim,  // 1 << 1 or 1 << 1 (as value is calculated by enum)        
    
HighJump// 1 << 2 or 2 << 1
    
ExtraHP,   // 1 << 3 or 4 << 1
    
ExtraArmor // 1 << 4 or 8 << 1
}

//Define an array that will keep track of each option a player currently has
new g_OptionsMAX_PLAYERS+];

//Examples of how to apply option for player
g_Optionsid ] |= Speed;
g_Optionsid ] |= HighJump;
g_Optionsid ] |= ExtraArmor;

//Example how to check if a player has an option
if ( g_Optionsid ] & Speed )
    
//player has speed 
If you were to do the above with an array of booleans you would need to create a 2-dimension array as demonstrated below:

PHP Code:
#define MAX_PLAYERS    32

//Create options as normal enum (no bitshift)
enum _:Options
{
    
Speed,   //0
    
AutoAim,   //1
    
HighJump,  //2
    
ExtraHP,   //3
    
ExtraArmor //4
}

new 
g_bOptionsMAX_PLAYERS+][ Options ];

//Examples of how to apply option for player
g_Optionsid ][ Speed ] = true;
g_Optionsid ][ HighJump ] = true;
g_Optionsid ][ ExtraArmor ]  true;

//Example how to check if a player has an option
if ( g_Optionsid ][ Speed ] )
    
//player has speed 
Useful Macros
To easily manipulate bits in your plugin to work with players (or any 1-32 indexes) without having to re-write the bit operations each time, here are some macros. Remember, the max bit you can manipulate is 32 in AMX-X since the only data-type available is a 4-byte cell [32 bits].

PHP Code:
//If you are storing a player index or anything that has an index up to 32.
//Example: SetPlayerBit( BitFieldVar , # )
#define SetPlayerBit(%1,%2)      (%1 |= (1<<(%2&31)))
#define ClearPlayerBit(%1,%2)    (%1 &= ~(1 <<(%2&31)))
#define CheckPlayerBit(%1,%2)    (%1 & (1<<(%2&31)))

//Safe for using values 0-31.
//Example: SetBit( BitFieldVar , # )
#define SetBit(%1,%2)      (%1 |= (1<<%2))
#define ClearBit(%1,%2)    (%1 &= ~(1<<%2))
#define CheckBit(%1,%2)    (%1 & (1<<%2)) 
The benefits of using bit-fields
A major benefit is conserving memory and CPU but IMO they are, in most cases, easier than using an array of bools. As you saw towards the beginning of this tutorial, you save 131 bytes of memory by using a bit-field over an array for storing player boolean (true\false) values. It also can result in less code because you can quickly check if bits exist or not with a simple if-statement; using an array you would need some type of loop to check each element.

Manipulating bits outside of the bit-range of a memory cell
This can be done through the usage of a bit array. This will simply use the first array element for bits 1-32, the second element for bits 33-64, and so on. There is not max-bit limitation on a bit-array as there is with a normal bit-field.

PHP Code:
g_BitField[0//bits 1-32
g_BitField[1//bits 33-64
g_BitField[2//bits 65-96 
Here's an example on how to setup your plugin to work with unlimited bits:

PHP Code:
#define SetBit(%1,%2)      (%1[%2>>5] |= (1<<(%2 & 31)))
#define ClearBit(%1,%2)    (%1[%2>>5] &= ~(1<<(%2 & 31)))
#define CheckBit(%1,%2)    (%1[%2>>5] & (1<<(%2 & 31))) 
PHP Code:
//Set the highest bit that we need to access
#define MAX_BIT    515

//Define an array large enough to contain all of the needed bits (1 -> MAX_BIT)
new iBitfieldArray[ ( MAX_BIT 32 ) + ];
//or you can do
new iBitfieldArray[ ( MAX_BIT >> ) + ];

//Set some random bits
SetBitiBitfieldArray 256 );
SetBitiBitfieldArray 500 );

//Check if the above worked successfully
if ( CheckBitiBitfieldArray 256 ) )
    
ClearBitiBitfieldArray 256 );

if ( 
CheckBitiBitfieldArray 20 ) )
//false

ClearBitiBitfieldArray 256 ); 
If you want to practice your knowledge, you can do bit-operations with calculators that exist on the net, here's one, many others exist. Also, the calculator that ships with Windows can do bit operations and even convert an integer value to its binary representation. To access it, click View->Scientific. Enter a number, and towards the top left, under the white number display box, click the Bin option and it will display the bits representing that integer number. You can also do all of the explained bit operators with the exception of bit-shifting (can be done mathematically, though).

Experimenting with binary numbers
Pawn has a constant (0b) that allows you to work with values in binary directly. To use it, prefix a set of bits with 0b; the number 10 in bits is represented by 1010 so to store this value in a variable, you would do iVar = 0b1010. You can practice your knowledge using this if you are still uncomfortable after reading this tut. This functions identically to the hexadecimal prefix 0x that you may have seen used elsewhere.

A few more examples:
PHP Code:
i1 0b1;
i2 0b10;
i10 0b1010;
i15 0b1111;
i255 0b11111111
Operation example:
PHP Code:
new iNum1 0b1011//11
new iNum2 0b1111//15

iResult iNum1 iNum2;

0b1011 iNum1//11
0b1111 iNum2//15
0b1011 iResult//15 
Interesting reading once you have an understanding of bits: Link
__________________

Last edited by Bugsy; 06-22-2015 at 21:08. Reason: Fixed link at bottom of thread
Bugsy is offline
Exolent[jNr]
Veteran Member
Join Date: Feb 2007
Location: Tennessee
Old 10-06-2010 , 21:43   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #2

First. tl;dr. Will read when I get home.
__________________
No private work or selling mods.
Quote:
Originally Posted by xPaw View Post
I love you exolent!
Exolent[jNr] is offline
wrecked_
Veteran Member
Join Date: Jan 2010
Location: New York (GMT-5)
Old 10-06-2010 , 22:23   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #3

Fan-fucking-tastic job, I learned a lot. Your work is appreciated, sir.
__________________
[ Paid Requests ]
DO NOT PM ME ABOUT BLOCKMAKER
NO PRIVATE SUPPORT
wrecked_ is offline
fysiks
Veteran Member
Join Date: Sep 2007
Location: Flatland, USA
Old 10-06-2010 , 22:44   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #4

Great tutorial!

Concerning bits+array:

Code:
#define SetBit(%1,%2)      (%1[%2>>5] |= (1<<(%2 & 31)))
Wouldn't SetBit(g_BitField, 32) actually be the first bit in the second cell? If yes, the first cell would only use 1 - 31 (the 0th bit remains unused).

EDIT: oh wait, You can use SetBit(g_BitField, 0). Everything is based on 0 here like arrays. Was it confusing for anyone else? Or was I just confusing myself thinking too hard?
__________________

Last edited by fysiks; 10-06-2010 at 22:51.
fysiks is offline
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 10-06-2010 , 22:46   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #5

SetBit( g_BitField , 32 ) would set the 0 ( 1 << 0 ) bit.

32 & 31 = 0
__________________
Bugsy is offline
fysiks
Veteran Member
Join Date: Sep 2007
Location: Flatland, USA
Old 10-06-2010 , 22:52   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #6

But, what I was saying is that 32>>5 is 1 right? Which puts it in the the second cell of the array. There are still 32 bits used in each cell I realize now (see my edit) since you can set SetBit(array, 0).
__________________

Last edited by fysiks; 10-06-2010 at 22:54.
fysiks is offline
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 10-06-2010 , 22:54   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #7

Quote:
Originally Posted by fysiks View Post
But, what I was saying is that 32>>5 is 1 right? Which puts it in the the second cell of the array.
Yes you're right
__________________
Bugsy is offline
fysiks
Veteran Member
Join Date: Sep 2007
Location: Flatland, USA
Old 10-06-2010 , 22:56   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #8

Quote:
Originally Posted by Bugsy View Post
Yes you're right
I was just making it more confusing for myself.

Just using it is easier than trying to understand it lol .
__________________
fysiks is offline
Exolent[jNr]
Veteran Member
Join Date: Feb 2007
Location: Tennessee
Old 10-07-2010 , 00:19   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #9

Okay, so I've read it and it's very detailed and explained well.

I have one thought though.
Shouldn't this:
Code:
new iBitfieldArray[ ( MAX_BIT / 32 ) + 1 ];
be this:
Code:
new iBitfieldArray[ ( MAX_BIT >> 5 ) + 1 ];
Not that the first is wrong, but 1. we are talking about bits, and 2. it was proved in the other thread that the latter was quicker than the former.
__________________
No private work or selling mods.
Quote:
Originally Posted by xPaw View Post
I love you exolent!
Exolent[jNr] is offline
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 10-07-2010 , 01:11   Re: [TUT] Bits, Bit-fields, and Bit-wise Operators
Reply With Quote #10

Quote:
Originally Posted by Exolent[jNr] View Post
Okay, so I've read it and it's very detailed and explained well.

I have one thought though.
Shouldn't this:
Code:
new iBitfieldArray[ ( MAX_BIT / 32 ) + 1 ];
be this:
Code:
new iBitfieldArray[ ( MAX_BIT >> 5 ) + 1 ];
Not that the first is wrong, but 1. we are talking about bits, and 2. it was proved in the other thread that the latter was quicker than the former.
Either will do the same but I think it may just confuse readers more than help them having MAX_BIT >> 5. IMO, the latter method serves a better purpose in the macros because they will be used much more frequently whereas in the variable declaration it is probably only called once per map change (assuming a global var).
__________________
Bugsy is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 13:43.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode