package pl.radical.open.gg.utils;
import pl.radical.open.gg.GGException;
import pl.radical.open.gg.dicts.Encoding;
import pl.radical.open.gg.packet.dicts.GGHashType;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.collections.primitives.ArrayCharList;
import org.apache.commons.collections.primitives.CharList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author <a href="mailto:mati@sz.home.pl">Mateusz Szczap</a>
* @author <a href="mailto:lukasz.rzanek@radical.com.pl>Łukasz Rżanek</a>
*/
public final class GGUtils {
private static final Logger LOG = LoggerFactory.getLogger(GGUtils.class);
private GGUtils() {
}
public static String prettyBytesToString(final byte[] bytes) {
final StringBuffer received = new StringBuffer();
received.append('{');
final char[] dump = Hex.encodeHex(bytes);
int i = 0;
for (final char c : dump) {
received.append(c);
i++;
if (i % 2 == 0) {
received.append(' ');
}
}
received.append('}');
return received.toString();
}
public static int byteToInt(final byte[] buf) {
return byteToInt(buf, 0);
}
public static int byteToInt(final byte[] buf, final int start) {
int i = 0;
int pos = start;
int tmp;
tmp = unsignedByteToInt(buf[pos++]) << 0;
i += tmp;
tmp = unsignedByteToInt(buf[pos++]) << 8;
i += tmp;
tmp = unsignedByteToInt(buf[pos++]) << 16;
i += tmp;
tmp = unsignedByteToInt(buf[pos++]) << 24;
i += tmp;
return i;
}
public static int byteToShort(final byte[] buf, final int start) {
int i = 0;
int pos = start;
int tmp;
tmp = unsignedByteToInt(buf[pos++]) << 0;
i += tmp;
tmp = unsignedByteToInt(buf[pos++]) << 8;
i += tmp;
return i;
}
public static byte[] intToByteArray(final int value) {
final byte[] result = new byte[4];
result[0] = (byte) (value & 0xFF);
result[1] = (byte) (value >> 8 & 0xFF);
result[2] = (byte) (value >> 16 & 0xFF);
result[3] = (byte) (value >> 24 & 0xFF);
return result;
}
public static byte[] shortToByteArray(final short value) {
final byte[] result = new byte[2];
result[0] = (byte) (value & 0xFF);
result[1] = (byte) (value >> 8 & 0xFF);
return result;
}
public static long secondsToMillis(final int seconds) {
return seconds * 1000L;
}
public static int millisToSeconds(final long millis) {
return (int) (millis / 1000L);
}
public static int unsignedByteToInt(final byte value) {
int result = value;
if (value < 0) {
result = (value & 0x7F) + 0x80;
}
return result;
}
public static byte[] intToByte(final int buf) {
final byte[] toSend = new byte[4];
toSend[3] = (byte) (buf >> 24 & 0xFF);
toSend[2] = (byte) (buf >> 16 & 0xFF);
toSend[1] = (byte) (buf >> 8 & 0xFF);
toSend[0] = (byte) (buf & 0xFF);
return toSend;
}
public static long unsignedIntToLong(int value) {
long plus = 0;
plus -= value & 0x80000000;
value &= 0x7FFFFFFF;
return value + plus;
}
public static String byteToString(final byte[] data, final int startIndex) {
int counter = 0;
while (counter + startIndex < data.length && data[counter + startIndex] != 0) {
counter++;
}
final byte[] desc = new byte[counter];
System.arraycopy(data, startIndex, desc, 0, counter);
String returnString = null;
try {
returnString = new String(desc, Encoding.WINDOWS1250.getValue());
} catch (final UnsupportedEncodingException ex) {
LOG.warn("Unable to convert", ex);
// FIXME Co to jest u diabła???
returnString = new String(desc);
}
return returnString;
}
public static String byteToString(final byte[] data, final int startIndex, final Encoding encoding) {
int counter = 0;
while (counter + startIndex < data.length && data[counter + startIndex] != 0) {
counter++;
}
final byte[] desc = new byte[counter];
System.arraycopy(data, startIndex, desc, 0, counter);
String returnString = null;
try {
returnString = new String(desc, encoding.getValue());
} catch (final UnsupportedEncodingException ex) {
LOG.warn("Unable to convert", ex);
returnString = new String(desc);
}
return returnString;
}
public static byte[] convertIntToByteArray(final int value) {
final byte[] bytes = new byte[4];
bytes[0] = (byte) (value & 0xFF);
bytes[1] = (byte) (value >> 8 & 0xFF);
bytes[2] = (byte) (value >> 16 & 0xFF);
bytes[3] = (byte) (value >> 24 & 0xFF);
return bytes;
}
public static int getLoginHash(final char[] password, final int seed) {
long x, y, z;
y = seed;
int i;
for (x = 0, i = 0; i < password.length; i++) {
x = x & 0xffffff00 | password[i];
y ^= x;
int k = (int) y;
k += x;
y = GGUtils.unsignedIntToLong(k);
k = (int) x;
k <<= 8;
x = GGUtils.unsignedIntToLong(k);
y ^= x;
k = (int) x;
k <<= 8;
x = GGUtils.unsignedIntToLong(k);
k = (int) y;
k -= x;
y = GGUtils.unsignedIntToLong(k);
k = (int) x;
k <<= 8;
x = GGUtils.unsignedIntToLong(k);
y ^= x;
z = y & 0x1f;
y = GGUtils.unsignedIntToLong((int) (y << z | y >> 32 - z));
}
return (int) y;
}
public static char[] byteToCharArray(final byte[] bytes) {
final CharList charList = new ArrayCharList();
for (final byte b : bytes) {
charList.add((char) b);
}
return charList.toArray();
}
private static byte[] getLoginHashSHA(final char[] password, final int seed) throws GGException {
try {
final MessageDigest hash = MessageDigest.getInstance("SHA1");
hash.update(new String(password).getBytes());
hash.update(GGUtils.intToByte(seed));
return hash.digest();
} catch (final NoSuchAlgorithmException e) {
LOG.error("SHA1 algorithm not usable", e);
throw new GGException("SHA1 algorithm not usable!", e);
}
}
public static byte[] getLoginHash(final char[] password, final int seed, final GGHashType hashType) throws GGException {
byte[] result;
switch (hashType) {
case GG_LOGIN_HASH_GG32:
result = intToByte(getLoginHash(password, seed));
break;
case GG_LOGIN_HASH_SHA1:
result = getLoginHashSHA(password, seed);
break;
default:
throw new GGException("Hash algorithm to be used during login was not specified");
}
return result;
}
public static int copy(final InputStream input, final OutputStream output) throws IOException {
final byte[] buffer = new byte[1024];
int count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
}