, value: E): T? = map.entries.firstOrNull {
+ ObjectsCompat.equals(value, it.value)
+ }?.key
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/HardwareUtils.java b/src/main/java/in/dragonbra/javasteam/util/HardwareUtils.java
deleted file mode 100644
index 0c71d162..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/HardwareUtils.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package in.dragonbra.javasteam.util;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.SystemUtils;
-
-import java.io.*;
-import java.lang.reflect.Method;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Scanner;
-
-/**
- * @author lngtr
- * @since 2018-02-24
- */
-public class HardwareUtils {
-
- // Everything taken from here
- // https://stackoverflow.com/questions/1986732/how-to-get-a-unique-computer-identifier-in-java-like-disk-id-or-motherboard-id
- private static String SERIAL_NUMBER;
- private static String MACHINE_NAME;
-
- public static byte[] getMachineID() {
- // the aug 25th 2015 CM update made well-formed machine MessageObjects required for logon
- // this was flipped off shortly after the update rolled out, likely due to linux steamclients running on distros without a way to build a machineid
- // so while a valid MO isn't currently (as of aug 25th) required, they could be in the future and we'll abide by The Valve Law now
-
- if (SERIAL_NUMBER != null) {
- return SERIAL_NUMBER.getBytes();
- }
-
- if (SystemUtils.IS_OS_WINDOWS) {
- SERIAL_NUMBER = getSerialNumberWin();
- }
- if (SystemUtils.IS_OS_MAC) {
- SERIAL_NUMBER = getSerialNumberMac();
- }
- if (SystemUtils.IS_OS_LINUX) {
- SERIAL_NUMBER = getSerialNumberUnix();
- }
-
- // if SERIAL_NUMBER still was null
- if (SERIAL_NUMBER == null) {
- SERIAL_NUMBER = "JavaSteam-SerialNumber";
- }
-
- return SERIAL_NUMBER.getBytes();
- }
-
- private static String getSerialNumberWin() {
- String sn = null;
-
- Runtime runtime = Runtime.getRuntime();
- Process process;
-
- try {
- process = runtime.exec(new String[]{"wmic", "bios", "get", "serialnumber"});
- } catch (IOException e) {
- return null;
- }
-
- var os = process.getOutputStream();
-
- try {
- os.close();
- } catch (IOException ignored) {
- }
-
- try (var sc = new Scanner(process.getInputStream())) {
- while (sc.hasNext()) {
- String next = sc.next();
- if ("SerialNumber".equals(next)) {
- sn = sc.next().trim();
- break;
- }
- }
- }
-
- return sn;
- }
-
- private static String getSerialNumberMac() {
- String sn = null;
-
- Runtime runtime = Runtime.getRuntime();
- Process process;
-
- try {
- process = runtime.exec(new String[]{"/usr/sbin/system_profiler", "SPHardwareDataType"});
- } catch (IOException e) {
- return null;
- }
-
- var os = process.getOutputStream();
-
- try {
- os.close();
- } catch (IOException ignored) {
- }
-
- String line;
- String marker = "Serial Number";
- try (var br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
- while ((line = br.readLine()) != null) {
- if (line.contains(marker)) {
- sn = line.split(":")[1].trim();
- break;
- }
- }
- } catch (IOException e) {
- return null;
- }
-
- return sn;
- }
-
- private static String getSerialNumberUnix() {
- String sn = readDmidecode();
-
- if (sn == null) {
- sn = readLshal();
- }
-
- return sn;
- }
-
- private static BufferedReader read(String command) {
-
- Runtime runtime = Runtime.getRuntime();
- Process process;
- try {
- process = runtime.exec(command.split(" "));
- } catch (IOException e) {
- return null;
- }
-
- var os = process.getOutputStream();
-
- try {
- os.close();
- } catch (IOException ignored) {
- }
-
- return new BufferedReader(new InputStreamReader(process.getInputStream()));
- }
-
- private static String readDmidecode() {
-
- String sn = null;
-
- String line;
- String marker = "Serial Number:";
-
- try (var br = read("dmidecode -t system")) {
- if (br == null) {
- return null;
- }
-
- while ((line = br.readLine()) != null) {
- if (line.contains(marker)) {
- sn = line.split(marker)[1].trim();
- break;
- }
- }
- } catch (IOException e) {
- return null;
- }
-
- return sn;
- }
-
- private static String readLshal() {
- String sn = null;
-
- String line;
- String marker = "system.hardware.serial =";
-
- try (var br = read("lshal")) {
- if (br == null) {
- return null;
- }
- while ((line = br.readLine()) != null) {
- if (line.contains(marker)) {
- //noinspection RegExpRedundantEscape
- sn = line.split(marker)[1].replaceAll("\\(string\\)|(\\')", "").trim();
- break;
- }
- }
- } catch (IOException e) {
- return null;
- }
-
- return sn;
- }
-
- public static String getMachineName() {
- return getMachineName(false);
- }
-
- public static String getMachineName(boolean addTag) {
- if (MACHINE_NAME != null) {
- return MACHINE_NAME;
- }
-
-
- if (SystemUtils.IS_OS_ANDROID) {
- MACHINE_NAME = getAndroidDeviceName();
- } else {
- MACHINE_NAME = getDeviceName();
- }
-
- if (StringUtils.isBlank(MACHINE_NAME)) {
- MACHINE_NAME = "Unknown";
- }
-
- if (addTag || MACHINE_NAME.contains("Unknown")) {
- return MACHINE_NAME + " (JavaSteam)";
- } else {
- return MACHINE_NAME;
- }
- }
-
- private static String getDeviceName() {
- var hostname = SystemUtils.getHostName();
- if (StringUtils.isBlank(hostname)) {
- try {
- // Last fallback.
- hostname = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- hostname = null;
- }
- }
-
- return hostname;
- }
-
- private static String getAndroidDeviceName() {
- String manufacturer = getAndroidSystemProperty("ro.product.manufacturer");
- String model = getAndroidSystemProperty("ro.product.model");
-
- if (manufacturer == null || model == null) {
- return "Android Device";
- }
-
- if (model.startsWith(manufacturer)) {
- return model;
- }
- return manufacturer + " " + model;
- }
-
- private static String getAndroidSystemProperty(String key) {
- try {
- Class> systemProperties = Class.forName("android.os.SystemProperties");
- Method get = systemProperties.getMethod("get", String.class);
- return (String) get.invoke(null, key);
- } catch (Exception e) {
- return null;
- }
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/HardwareUtils.kt b/src/main/java/in/dragonbra/javasteam/util/HardwareUtils.kt
new file mode 100644
index 00000000..71cfd3dd
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/HardwareUtils.kt
@@ -0,0 +1,118 @@
+package `in`.dragonbra.javasteam.util
+
+import org.apache.commons.lang3.SystemUtils
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import java.io.StringReader
+import java.net.InetAddress
+import java.util.Scanner
+
+/**
+ * @author lngtr
+ * @since 2018-02-24
+ */
+// https://stackoverflow.com/questions/1986732/how-to-get-a-unique-computer-identifier-in-java-like-disk-id-or-motherboard-id
+object HardwareUtils {
+
+ private val serialNumber: String by lazy {
+ when {
+ SystemUtils.IS_OS_WINDOWS -> getSerialNumberWin()
+ SystemUtils.IS_OS_MAC -> getSerialNumberMac()
+ SystemUtils.IS_OS_LINUX -> getSerialNumberUnix()
+ else -> null
+ } ?: "JavaSteam-SerialNumber"
+ }
+
+ private val resolvedMachineName: String by lazy {
+ val name = if (SystemUtils.IS_OS_ANDROID) getAndroidDeviceName() else getDeviceName()
+ name.takeUnless { it.isNullOrBlank() } ?: "Unknown"
+ }
+
+ // the aug 25th 2015 CM update made well-formed machine MessageObjects required for logon
+ // this was flipped off shortly after the update rolled out, likely due to linux steamclients running on distros without a way to build a machineid
+ // so while a valid MO isn't currently (as of aug 25th) required, they could be in the future and we'll abide by The Valve Law now
+ @JvmStatic
+ fun getMachineID(): ByteArray = serialNumber.toByteArray()
+
+ @JvmStatic
+ @JvmOverloads
+ fun getMachineName(addTag: Boolean = false): String =
+ if (addTag || resolvedMachineName.contains("Unknown")) "$resolvedMachineName (JavaSteam)" else resolvedMachineName
+
+ private fun getSerialNumberWin(): String? = runCatching {
+ val process = Runtime.getRuntime().exec(arrayOf("wmic", "bios", "get", "serialnumber"))
+ runCatching { process.outputStream.close() }
+ Scanner(process.inputStream).use { sc ->
+ while (sc.hasNext()) {
+ if (sc.next() == "SerialNumber") return@runCatching sc.next().trim()
+ }
+ null
+ }
+ }.getOrNull()
+
+ private fun getSerialNumberMac(): String? = runCatching {
+ val process = Runtime.getRuntime().exec(arrayOf("/usr/sbin/system_profiler", "SPHardwareDataType"))
+ runCatching { process.outputStream.close() }
+ BufferedReader(InputStreamReader(process.inputStream)).use { br ->
+ br.lineSequence()
+ .firstOrNull { it.contains("Serial Number") }
+ ?.substringAfter(":")
+ ?.trim()
+ }
+ }.getOrNull()
+
+ private fun getSerialNumberUnix(): String? = readDmidecode() ?: readLshal()
+
+ private fun read(command: String): BufferedReader? {
+ val process = runCatching {
+ Runtime.getRuntime().exec(command.split(" ").toTypedArray())
+ }.getOrNull() ?: return null
+
+ runCatching { process.outputStream.close() }
+
+ return runCatching {
+ BufferedReader(InputStreamReader(process.inputStream)).use { br ->
+ BufferedReader(StringReader(br.readText()))
+ }
+ }.also {
+ process.destroy()
+ }.getOrNull()
+ }
+
+ private fun readDmidecode(): String? =
+ read("dmidecode -t system")?.use { br ->
+ br.lineSequence()
+ .firstOrNull { it.contains("Serial Number:") }
+ ?.substringAfter("Serial Number:")
+ ?.trim()
+ }
+
+ private fun readLshal(): String? =
+ read("lshal")?.use { br ->
+ br.lineSequence()
+ .firstOrNull { it.contains("system.hardware.serial =") }
+ ?.substringAfter("system.hardware.serial =")
+ ?.replace("(string)", "")
+ ?.replace("'", "")
+ ?.trim()
+ }
+
+ private fun getDeviceName(): String? {
+ val hostname = SystemUtils.getHostName()
+ if (!hostname.isNullOrBlank()) return hostname
+ return runCatching { InetAddress.getLocalHost().hostName }.getOrNull()
+ }
+
+ private fun getAndroidDeviceName(): String? {
+ val manufacturer = getAndroidSystemProperty("ro.product.manufacturer")
+ val model = getAndroidSystemProperty("ro.product.model")
+ if (manufacturer == null || model == null) return "Android Device"
+ return if (model.startsWith(manufacturer)) model else "$manufacturer $model"
+ }
+
+ private fun getAndroidSystemProperty(key: String): String? = runCatching {
+ val systemProperties = Class.forName("android.os.SystemProperties")
+ val get = systemProperties.getMethod("get", String::class.java)
+ get.invoke(null, key) as? String
+ }.getOrNull()
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.java b/src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.kt
similarity index 70%
rename from src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.java
rename to src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.kt
index a2ad8876..256d36dd 100644
--- a/src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.java
+++ b/src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.kt
@@ -1,27 +1,23 @@
-package in.dragonbra.javasteam.util;
+package `in`.dragonbra.javasteam.util
-import in.dragonbra.javasteam.enums.EMsg;
+import `in`.dragonbra.javasteam.enums.EMsg
/**
* This is a debug utility, do not use it to implement your business logic.
- *
* This interface is used for logging network messages sent to and received from the Steam server that the client is connected to.
*/
-public interface IDebugNetworkListener {
-
+interface IDebugNetworkListener {
/**
* Called when a packet is received from the Steam server.
- *
* @param msgType Network message type of this packet message.
* @param data Raw packet data that was received.
*/
- void onIncomingNetworkMessage(EMsg msgType, byte[] data);
+ fun onIncomingNetworkMessage(msgType: EMsg, data: ByteArray)
/**
* Called when a packet is about to be sent to the Steam server.
- *
* @param msgType Network message type of this packet message.
* @param data Raw packet data that was received.
*/
- void onOutgoingNetworkMessage(EMsg msgType, byte[] data);
+ fun onOutgoingNetworkMessage(msgType: EMsg, data: ByteArray)
}
diff --git a/src/main/java/in/dragonbra/javasteam/util/KeyDictionary.java b/src/main/java/in/dragonbra/javasteam/util/KeyDictionary.java
deleted file mode 100644
index 7c10695d..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/KeyDictionary.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package in.dragonbra.javasteam.util;
-
-import in.dragonbra.javasteam.enums.EUniverse;
-
-import java.util.Map;
-
-/**
- * Contains the public keys that Steam uses for each of the {@link EUniverse}
- */
-public class KeyDictionary {
-
- private static final Map KEYS;
-
- static {
- KEYS = Map.of(
- EUniverse.Public, new byte[]{
- (byte) 0x30, (byte) 0x81, (byte) 0x9D, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8B, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xDF, (byte) 0xEC, (byte) 0x1A,
- (byte) 0xD6, (byte) 0x2C, (byte) 0x10, (byte) 0x66, (byte) 0x2C, (byte) 0x17, (byte) 0x35, (byte) 0x3A, (byte) 0x14, (byte) 0xB0, (byte) 0x7C, (byte) 0x59, (byte) 0x11, (byte) 0x7F, (byte) 0x9D, (byte) 0xD3,
- (byte) 0xD8, (byte) 0x2B, (byte) 0x7A, (byte) 0xE3, (byte) 0xE0, (byte) 0x15, (byte) 0xCD, (byte) 0x19, (byte) 0x1E, (byte) 0x46, (byte) 0xE8, (byte) 0x7B, (byte) 0x87, (byte) 0x74, (byte) 0xA2, (byte) 0x18,
- (byte) 0x46, (byte) 0x31, (byte) 0xA9, (byte) 0x03, (byte) 0x14, (byte) 0x79, (byte) 0x82, (byte) 0x8E, (byte) 0xE9, (byte) 0x45, (byte) 0xA2, (byte) 0x49, (byte) 0x12, (byte) 0xA9, (byte) 0x23, (byte) 0x68,
- (byte) 0x73, (byte) 0x89, (byte) 0xCF, (byte) 0x69, (byte) 0xA1, (byte) 0xB1, (byte) 0x61, (byte) 0x46, (byte) 0xBD, (byte) 0xC1, (byte) 0xBE, (byte) 0xBF, (byte) 0xD6, (byte) 0x01, (byte) 0x1B, (byte) 0xD8,
- (byte) 0x81, (byte) 0xD4, (byte) 0xDC, (byte) 0x90, (byte) 0xFB, (byte) 0xFE, (byte) 0x4F, (byte) 0x52, (byte) 0x73, (byte) 0x66, (byte) 0xCB, (byte) 0x95, (byte) 0x70, (byte) 0xD7, (byte) 0xC5, (byte) 0x8E,
- (byte) 0xBA, (byte) 0x1C, (byte) 0x7A, (byte) 0x33, (byte) 0x75, (byte) 0xA1, (byte) 0x62, (byte) 0x34, (byte) 0x46, (byte) 0xBB, (byte) 0x60, (byte) 0xB7, (byte) 0x80, (byte) 0x68, (byte) 0xFA, (byte) 0x13,
- (byte) 0xA7, (byte) 0x7A, (byte) 0x8A, (byte) 0x37, (byte) 0x4B, (byte) 0x9E, (byte) 0xC6, (byte) 0xF4, (byte) 0x5D, (byte) 0x5F, (byte) 0x3A, (byte) 0x99, (byte) 0xF9, (byte) 0x9E, (byte) 0xC4, (byte) 0x3A,
- (byte) 0xE9, (byte) 0x63, (byte) 0xA2, (byte) 0xBB, (byte) 0x88, (byte) 0x19, (byte) 0x28, (byte) 0xE0, (byte) 0xE7, (byte) 0x14, (byte) 0xC0, (byte) 0x42, (byte) 0x89, (byte) 0x02, (byte) 0x01, (byte) 0x11
- },
- EUniverse.Beta, new byte[]{
- (byte) 0x30, (byte) 0x81, (byte) 0x9D, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8B, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xAE, (byte) 0xD1, (byte) 0x4B,
- (byte) 0xC0, (byte) 0xA3, (byte) 0x36, (byte) 0x8B, (byte) 0xA0, (byte) 0x39, (byte) 0x0B, (byte) 0x43, (byte) 0xDC, (byte) 0xED, (byte) 0x6A, (byte) 0xC8, (byte) 0xF2, (byte) 0xA3, (byte) 0xE4, (byte) 0x7E,
- (byte) 0x09, (byte) 0x8C, (byte) 0x55, (byte) 0x2E, (byte) 0xE7, (byte) 0xE9, (byte) 0x3C, (byte) 0xBB, (byte) 0xE5, (byte) 0x5E, (byte) 0x0F, (byte) 0x18, (byte) 0x74, (byte) 0x54, (byte) 0x8F, (byte) 0xF3,
- (byte) 0xBD, (byte) 0x56, (byte) 0x69, (byte) 0x5B, (byte) 0x13, (byte) 0x09, (byte) 0xAF, (byte) 0xC8, (byte) 0xBE, (byte) 0xB3, (byte) 0xA1, (byte) 0x48, (byte) 0x69, (byte) 0xE9, (byte) 0x83, (byte) 0x49,
- (byte) 0x65, (byte) 0x8D, (byte) 0xD2, (byte) 0x93, (byte) 0x21, (byte) 0x2F, (byte) 0xB9, (byte) 0x1E, (byte) 0xFA, (byte) 0x74, (byte) 0x3B, (byte) 0x55, (byte) 0x22, (byte) 0x79, (byte) 0xBF, (byte) 0x85,
- (byte) 0x18, (byte) 0xCB, (byte) 0x6D, (byte) 0x52, (byte) 0x44, (byte) 0x4E, (byte) 0x05, (byte) 0x92, (byte) 0x89, (byte) 0x6A, (byte) 0xA8, (byte) 0x99, (byte) 0xED, (byte) 0x44, (byte) 0xAE, (byte) 0xE2,
- (byte) 0x66, (byte) 0x46, (byte) 0x42, (byte) 0x0C, (byte) 0xFB, (byte) 0x6E, (byte) 0x4C, (byte) 0x30, (byte) 0xC6, (byte) 0x6C, (byte) 0x5C, (byte) 0x16, (byte) 0xFF, (byte) 0xBA, (byte) 0x9C, (byte) 0xB9,
- (byte) 0x78, (byte) 0x3F, (byte) 0x17, (byte) 0x4B, (byte) 0xCB, (byte) 0xC9, (byte) 0x01, (byte) 0x5D, (byte) 0x3E, (byte) 0x37, (byte) 0x70, (byte) 0xEC, (byte) 0x67, (byte) 0x5A, (byte) 0x33, (byte) 0x48,
- (byte) 0xF7, (byte) 0x46, (byte) 0xCE, (byte) 0x58, (byte) 0xAA, (byte) 0xEC, (byte) 0xD9, (byte) 0xFF, (byte) 0x4A, (byte) 0x78, (byte) 0x6C, (byte) 0x83, (byte) 0x4B, (byte) 0x02, (byte) 0x01, (byte) 0x11
- },
- EUniverse.Internal, new byte[]{
- (byte) 0x30, (byte) 0x81, (byte) 0x9D, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8B, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xA8, (byte) 0xFE, (byte) 0x01,
- (byte) 0x3B, (byte) 0xB6, (byte) 0xD7, (byte) 0x21, (byte) 0x4B, (byte) 0x53, (byte) 0x23, (byte) 0x6F, (byte) 0xA1, (byte) 0xAB, (byte) 0x4E, (byte) 0xF1, (byte) 0x07, (byte) 0x30, (byte) 0xA7, (byte) 0xC6,
- (byte) 0x7E, (byte) 0x6A, (byte) 0x2C, (byte) 0xC2, (byte) 0x5D, (byte) 0x3A, (byte) 0xB8, (byte) 0x40, (byte) 0xCA, (byte) 0x59, (byte) 0x4D, (byte) 0x16, (byte) 0x2D, (byte) 0x74, (byte) 0xEB, (byte) 0x0E,
- (byte) 0x72, (byte) 0x46, (byte) 0x29, (byte) 0xF9, (byte) 0xDE, (byte) 0x9B, (byte) 0xCE, (byte) 0x4B, (byte) 0x8C, (byte) 0xD0, (byte) 0xCA, (byte) 0xF4, (byte) 0x08, (byte) 0x94, (byte) 0x46, (byte) 0xA5,
- (byte) 0x11, (byte) 0xAF, (byte) 0x3A, (byte) 0xCB, (byte) 0xB8, (byte) 0x4E, (byte) 0xDE, (byte) 0xC6, (byte) 0xD8, (byte) 0x85, (byte) 0x0A, (byte) 0x7D, (byte) 0xAA, (byte) 0x96, (byte) 0x0A, (byte) 0xEA,
- (byte) 0x7B, (byte) 0x51, (byte) 0xD6, (byte) 0x22, (byte) 0x62, (byte) 0x5C, (byte) 0x1E, (byte) 0x58, (byte) 0xD7, (byte) 0x46, (byte) 0x1E, (byte) 0x09, (byte) 0xAE, (byte) 0x43, (byte) 0xA7, (byte) 0xC4,
- (byte) 0x34, (byte) 0x69, (byte) 0xA2, (byte) 0xA5, (byte) 0xE8, (byte) 0x44, (byte) 0x76, (byte) 0x18, (byte) 0xE2, (byte) 0x3D, (byte) 0xB7, (byte) 0xC5, (byte) 0xA8, (byte) 0x96, (byte) 0xFD, (byte) 0xE5,
- (byte) 0xB4, (byte) 0x4B, (byte) 0xF8, (byte) 0x40, (byte) 0x12, (byte) 0xA6, (byte) 0x17, (byte) 0x4E, (byte) 0xC4, (byte) 0xC1, (byte) 0x60, (byte) 0x0E, (byte) 0xB0, (byte) 0xC2, (byte) 0xB8, (byte) 0x40,
- (byte) 0x4D, (byte) 0x9E, (byte) 0x76, (byte) 0x4C, (byte) 0x44, (byte) 0xF4, (byte) 0xFC, (byte) 0x6F, (byte) 0x14, (byte) 0x89, (byte) 0x73, (byte) 0xB4, (byte) 0x13, (byte) 0x02, (byte) 0x01, (byte) 0x11
- },
- EUniverse.Dev, new byte[]{
- (byte) 0x30, (byte) 0x81, (byte) 0x9D, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8B, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xD0, (byte) 0x05, (byte) 0x2C,
- (byte) 0xE9, (byte) 0x80, (byte) 0x95, (byte) 0xCD, (byte) 0x30, (byte) 0x83, (byte) 0xA8, (byte) 0xE9, (byte) 0x25, (byte) 0x96, (byte) 0x63, (byte) 0xCE, (byte) 0xCC, (byte) 0x48, (byte) 0x5D, (byte) 0x5C,
- (byte) 0x52, (byte) 0x00, (byte) 0xDB, (byte) 0x1E, (byte) 0x78, (byte) 0xD7, (byte) 0x6A, (byte) 0x4C, (byte) 0x2C, (byte) 0xC8, (byte) 0x41, (byte) 0x8C, (byte) 0xCC, (byte) 0x87, (byte) 0x46, (byte) 0xFB,
- (byte) 0x1B, (byte) 0xC9, (byte) 0xE8, (byte) 0x6E, (byte) 0x4F, (byte) 0x7A, (byte) 0x6B, (byte) 0xC3, (byte) 0xE7, (byte) 0x0F, (byte) 0xD5, (byte) 0xA9, (byte) 0x5D, (byte) 0x6C, (byte) 0xD4, (byte) 0xEE,
- (byte) 0xA2, (byte) 0xCC, (byte) 0x80, (byte) 0x5A, (byte) 0xD3, (byte) 0xCE, (byte) 0x53, (byte) 0x59, (byte) 0xE6, (byte) 0x80, (byte) 0x91, (byte) 0xC4, (byte) 0xC0, (byte) 0xD5, (byte) 0xF0, (byte) 0x63,
- (byte) 0x23, (byte) 0x91, (byte) 0x69, (byte) 0x70, (byte) 0xC5, (byte) 0xBB, (byte) 0xBD, (byte) 0x05, (byte) 0xE2, (byte) 0x4F, (byte) 0x7D, (byte) 0x90, (byte) 0x12, (byte) 0xED, (byte) 0xAC, (byte) 0x4F,
- (byte) 0x86, (byte) 0x96, (byte) 0x3C, (byte) 0x89, (byte) 0xCC, (byte) 0x92, (byte) 0x15, (byte) 0x63, (byte) 0xCB, (byte) 0x57, (byte) 0x70, (byte) 0xB9, (byte) 0xC3, (byte) 0xAE, (byte) 0x08, (byte) 0x4F,
- (byte) 0xC8, (byte) 0x56, (byte) 0x16, (byte) 0xB0, (byte) 0x0C, (byte) 0xC6, (byte) 0xC8, (byte) 0x8A, (byte) 0x80, (byte) 0xD2, (byte) 0x37, (byte) 0xF7, (byte) 0x7F, (byte) 0xAB, (byte) 0x93, (byte) 0xBB,
- (byte) 0xE6, (byte) 0xDE, (byte) 0x95, (byte) 0x78, (byte) 0xB8, (byte) 0x11, (byte) 0xC9, (byte) 0xE5, (byte) 0x62, (byte) 0xAD, (byte) 0xBC, (byte) 0x0C, (byte) 0x87, (byte) 0x02, (byte) 0x01, (byte) 0x11
- }
- );
- }
-
- /**
- * Gets the public key for the given universe.
- *
- * @param universe The universe.
- * @return The public key.
- */
- public static byte[] getPublicKey(EUniverse universe) {
- return KEYS.get(universe);
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/KeyDictionary.kt b/src/main/java/in/dragonbra/javasteam/util/KeyDictionary.kt
new file mode 100644
index 00000000..a65a84f7
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/KeyDictionary.kt
@@ -0,0 +1,135 @@
+package `in`.dragonbra.javasteam.util
+
+import `in`.dragonbra.javasteam.enums.EUniverse
+
+/**
+ * Contains the public keys that Steam uses for each of the [EUniverse]
+ */
+object KeyDictionary {
+ private val KEYS: Map = mapOf(
+ EUniverse.Public to byteArrayOf(
+ 0x30.toByte(), 0x81.toByte(), 0x9D.toByte(), 0x30.toByte(), 0x0D.toByte(), 0x06.toByte(),
+ 0x09.toByte(), 0x2A.toByte(), 0x86.toByte(), 0x48.toByte(), 0x86.toByte(), 0xF7.toByte(),
+ 0x0D.toByte(), 0x01.toByte(), 0x01.toByte(), 0x01.toByte(), 0x05.toByte(), 0x00.toByte(),
+ 0x03.toByte(), 0x81.toByte(), 0x8B.toByte(), 0x00.toByte(), 0x30.toByte(), 0x81.toByte(),
+ 0x87.toByte(), 0x02.toByte(), 0x81.toByte(), 0x81.toByte(), 0x00.toByte(), 0xDF.toByte(),
+ 0xEC.toByte(), 0x1A.toByte(), 0xD6.toByte(), 0x2C.toByte(), 0x10.toByte(), 0x66.toByte(),
+ 0x2C.toByte(), 0x17.toByte(), 0x35.toByte(), 0x3A.toByte(), 0x14.toByte(), 0xB0.toByte(),
+ 0x7C.toByte(), 0x59.toByte(), 0x11.toByte(), 0x7F.toByte(), 0x9D.toByte(), 0xD3.toByte(),
+ 0xD8.toByte(), 0x2B.toByte(), 0x7A.toByte(), 0xE3.toByte(), 0xE0.toByte(), 0x15.toByte(),
+ 0xCD.toByte(), 0x19.toByte(), 0x1E.toByte(), 0x46.toByte(), 0xE8.toByte(), 0x7B.toByte(),
+ 0x87.toByte(), 0x74.toByte(), 0xA2.toByte(), 0x18.toByte(), 0x46.toByte(), 0x31.toByte(),
+ 0xA9.toByte(), 0x03.toByte(), 0x14.toByte(), 0x79.toByte(), 0x82.toByte(), 0x8E.toByte(),
+ 0xE9.toByte(), 0x45.toByte(), 0xA2.toByte(), 0x49.toByte(), 0x12.toByte(), 0xA9.toByte(),
+ 0x23.toByte(), 0x68.toByte(), 0x73.toByte(), 0x89.toByte(), 0xCF.toByte(), 0x69.toByte(),
+ 0xA1.toByte(), 0xB1.toByte(), 0x61.toByte(), 0x46.toByte(), 0xBD.toByte(), 0xC1.toByte(),
+ 0xBE.toByte(), 0xBF.toByte(), 0xD6.toByte(), 0x01.toByte(), 0x1B.toByte(), 0xD8.toByte(),
+ 0x81.toByte(), 0xD4.toByte(), 0xDC.toByte(), 0x90.toByte(), 0xFB.toByte(), 0xFE.toByte(),
+ 0x4F.toByte(), 0x52.toByte(), 0x73.toByte(), 0x66.toByte(), 0xCB.toByte(), 0x95.toByte(),
+ 0x70.toByte(), 0xD7.toByte(), 0xC5.toByte(), 0x8E.toByte(), 0xBA.toByte(), 0x1C.toByte(),
+ 0x7A.toByte(), 0x33.toByte(), 0x75.toByte(), 0xA1.toByte(), 0x62.toByte(), 0x34.toByte(),
+ 0x46.toByte(), 0xBB.toByte(), 0x60.toByte(), 0xB7.toByte(), 0x80.toByte(), 0x68.toByte(),
+ 0xFA.toByte(), 0x13.toByte(), 0xA7.toByte(), 0x7A.toByte(), 0x8A.toByte(), 0x37.toByte(),
+ 0x4B.toByte(), 0x9E.toByte(), 0xC6.toByte(), 0xF4.toByte(), 0x5D.toByte(), 0x5F.toByte(),
+ 0x3A.toByte(), 0x99.toByte(), 0xF9.toByte(), 0x9E.toByte(), 0xC4.toByte(), 0x3A.toByte(),
+ 0xE9.toByte(), 0x63.toByte(), 0xA2.toByte(), 0xBB.toByte(), 0x88.toByte(), 0x19.toByte(),
+ 0x28.toByte(), 0xE0.toByte(), 0xE7.toByte(), 0x14.toByte(), 0xC0.toByte(), 0x42.toByte(),
+ 0x89.toByte(), 0x02.toByte(), 0x01.toByte(), 0x11.toByte()
+ ),
+ EUniverse.Beta to byteArrayOf(
+ 0x30.toByte(), 0x81.toByte(), 0x9D.toByte(), 0x30.toByte(), 0x0D.toByte(), 0x06.toByte(),
+ 0x09.toByte(), 0x2A.toByte(), 0x86.toByte(), 0x48.toByte(), 0x86.toByte(), 0xF7.toByte(),
+ 0x0D.toByte(), 0x01.toByte(), 0x01.toByte(), 0x01.toByte(), 0x05.toByte(), 0x00.toByte(),
+ 0x03.toByte(), 0x81.toByte(), 0x8B.toByte(), 0x00.toByte(), 0x30.toByte(), 0x81.toByte(),
+ 0x87.toByte(), 0x02.toByte(), 0x81.toByte(), 0x81.toByte(), 0x00.toByte(), 0xAE.toByte(),
+ 0xD1.toByte(), 0x4B.toByte(), 0xC0.toByte(), 0xA3.toByte(), 0x36.toByte(), 0x8B.toByte(),
+ 0xA0.toByte(), 0x39.toByte(), 0x0B.toByte(), 0x43.toByte(), 0xDC.toByte(), 0xED.toByte(),
+ 0x6A.toByte(), 0xC8.toByte(), 0xF2.toByte(), 0xA3.toByte(), 0xE4.toByte(), 0x7E.toByte(),
+ 0x09.toByte(), 0x8C.toByte(), 0x55.toByte(), 0x2E.toByte(), 0xE7.toByte(), 0xE9.toByte(),
+ 0x3C.toByte(), 0xBB.toByte(), 0xE5.toByte(), 0x5E.toByte(), 0x0F.toByte(), 0x18.toByte(),
+ 0x74.toByte(), 0x54.toByte(), 0x8F.toByte(), 0xF3.toByte(), 0xBD.toByte(), 0x56.toByte(),
+ 0x69.toByte(), 0x5B.toByte(), 0x13.toByte(), 0x09.toByte(), 0xAF.toByte(), 0xC8.toByte(),
+ 0xBE.toByte(), 0xB3.toByte(), 0xA1.toByte(), 0x48.toByte(), 0x69.toByte(), 0xE9.toByte(),
+ 0x83.toByte(), 0x49.toByte(), 0x65.toByte(), 0x8D.toByte(), 0xD2.toByte(), 0x93.toByte(),
+ 0x21.toByte(), 0x2F.toByte(), 0xB9.toByte(), 0x1E.toByte(), 0xFA.toByte(), 0x74.toByte(),
+ 0x3B.toByte(), 0x55.toByte(), 0x22.toByte(), 0x79.toByte(), 0xBF.toByte(), 0x85.toByte(),
+ 0x18.toByte(), 0xCB.toByte(), 0x6D.toByte(), 0x52.toByte(), 0x44.toByte(), 0x4E.toByte(),
+ 0x05.toByte(), 0x92.toByte(), 0x89.toByte(), 0x6A.toByte(), 0xA8.toByte(), 0x99.toByte(),
+ 0xED.toByte(), 0x44.toByte(), 0xAE.toByte(), 0xE2.toByte(), 0x66.toByte(), 0x46.toByte(),
+ 0x42.toByte(), 0x0C.toByte(), 0xFB.toByte(), 0x6E.toByte(), 0x4C.toByte(), 0x30.toByte(),
+ 0xC6.toByte(), 0x6C.toByte(), 0x5C.toByte(), 0x16.toByte(), 0xFF.toByte(), 0xBA.toByte(),
+ 0x9C.toByte(), 0xB9.toByte(), 0x78.toByte(), 0x3F.toByte(), 0x17.toByte(), 0x4B.toByte(),
+ 0xCB.toByte(), 0xC9.toByte(), 0x01.toByte(), 0x5D.toByte(), 0x3E.toByte(), 0x37.toByte(),
+ 0x70.toByte(), 0xEC.toByte(), 0x67.toByte(), 0x5A.toByte(), 0x33.toByte(), 0x48.toByte(),
+ 0xF7.toByte(), 0x46.toByte(), 0xCE.toByte(), 0x58.toByte(), 0xAA.toByte(), 0xEC.toByte(),
+ 0xD9.toByte(), 0xFF.toByte(), 0x4A.toByte(), 0x78.toByte(), 0x6C.toByte(), 0x83.toByte(),
+ 0x4B.toByte(), 0x02.toByte(), 0x01.toByte(), 0x11.toByte()
+ ),
+ EUniverse.Internal to byteArrayOf(
+ 0x30.toByte(), 0x81.toByte(), 0x9D.toByte(), 0x30.toByte(), 0x0D.toByte(), 0x06.toByte(),
+ 0x09.toByte(), 0x2A.toByte(), 0x86.toByte(), 0x48.toByte(), 0x86.toByte(), 0xF7.toByte(),
+ 0x0D.toByte(), 0x01.toByte(), 0x01.toByte(), 0x01.toByte(), 0x05.toByte(), 0x00.toByte(),
+ 0x03.toByte(), 0x81.toByte(), 0x8B.toByte(), 0x00.toByte(), 0x30.toByte(), 0x81.toByte(),
+ 0x87.toByte(), 0x02.toByte(), 0x81.toByte(), 0x81.toByte(), 0x00.toByte(), 0xA8.toByte(),
+ 0xFE.toByte(), 0x01.toByte(), 0x3B.toByte(), 0xB6.toByte(), 0xD7.toByte(), 0x21.toByte(),
+ 0x4B.toByte(), 0x53.toByte(), 0x23.toByte(), 0x6F.toByte(), 0xA1.toByte(), 0xAB.toByte(),
+ 0x4E.toByte(), 0xF1.toByte(), 0x07.toByte(), 0x30.toByte(), 0xA7.toByte(), 0xC6.toByte(),
+ 0x7E.toByte(), 0x6A.toByte(), 0x2C.toByte(), 0xC2.toByte(), 0x5D.toByte(), 0x3A.toByte(),
+ 0xB8.toByte(), 0x40.toByte(), 0xCA.toByte(), 0x59.toByte(), 0x4D.toByte(), 0x16.toByte(),
+ 0x2D.toByte(), 0x74.toByte(), 0xEB.toByte(), 0x0E.toByte(), 0x72.toByte(), 0x46.toByte(),
+ 0x29.toByte(), 0xF9.toByte(), 0xDE.toByte(), 0x9B.toByte(), 0xCE.toByte(), 0x4B.toByte(),
+ 0x8C.toByte(), 0xD0.toByte(), 0xCA.toByte(), 0xF4.toByte(), 0x08.toByte(), 0x94.toByte(),
+ 0x46.toByte(), 0xA5.toByte(), 0x11.toByte(), 0xAF.toByte(), 0x3A.toByte(), 0xCB.toByte(),
+ 0xB8.toByte(), 0x4E.toByte(), 0xDE.toByte(), 0xC6.toByte(), 0xD8.toByte(), 0x85.toByte(),
+ 0x0A.toByte(), 0x7D.toByte(), 0xAA.toByte(), 0x96.toByte(), 0x0A.toByte(), 0xEA.toByte(),
+ 0x7B.toByte(), 0x51.toByte(), 0xD6.toByte(), 0x22.toByte(), 0x62.toByte(), 0x5C.toByte(),
+ 0x1E.toByte(), 0x58.toByte(), 0xD7.toByte(), 0x46.toByte(), 0x1E.toByte(), 0x09.toByte(),
+ 0xAE.toByte(), 0x43.toByte(), 0xA7.toByte(), 0xC4.toByte(), 0x34.toByte(), 0x69.toByte(),
+ 0xA2.toByte(), 0xA5.toByte(), 0xE8.toByte(), 0x44.toByte(), 0x76.toByte(), 0x18.toByte(),
+ 0xE2.toByte(), 0x3D.toByte(), 0xB7.toByte(), 0xC5.toByte(), 0xA8.toByte(), 0x96.toByte(),
+ 0xFD.toByte(), 0xE5.toByte(), 0xB4.toByte(), 0x4B.toByte(), 0xF8.toByte(), 0x40.toByte(),
+ 0x12.toByte(), 0xA6.toByte(), 0x17.toByte(), 0x4E.toByte(), 0xC4.toByte(), 0xC1.toByte(),
+ 0x60.toByte(), 0x0E.toByte(), 0xB0.toByte(), 0xC2.toByte(), 0xB8.toByte(), 0x40.toByte(),
+ 0x4D.toByte(), 0x9E.toByte(), 0x76.toByte(), 0x4C.toByte(), 0x44.toByte(), 0xF4.toByte(),
+ 0xFC.toByte(), 0x6F.toByte(), 0x14.toByte(), 0x89.toByte(), 0x73.toByte(), 0xB4.toByte(),
+ 0x13.toByte(), 0x02.toByte(), 0x01.toByte(), 0x11.toByte()
+ ),
+ EUniverse.Dev to byteArrayOf(
+ 0x30.toByte(), 0x81.toByte(), 0x9D.toByte(), 0x30.toByte(), 0x0D.toByte(), 0x06.toByte(),
+ 0x09.toByte(), 0x2A.toByte(), 0x86.toByte(), 0x48.toByte(), 0x86.toByte(), 0xF7.toByte(),
+ 0x0D.toByte(), 0x01.toByte(), 0x01.toByte(), 0x01.toByte(), 0x05.toByte(), 0x00.toByte(),
+ 0x03.toByte(), 0x81.toByte(), 0x8B.toByte(), 0x00.toByte(), 0x30.toByte(), 0x81.toByte(),
+ 0x87.toByte(), 0x02.toByte(), 0x81.toByte(), 0x81.toByte(), 0x00.toByte(), 0xD0.toByte(),
+ 0x05.toByte(), 0x2C.toByte(), 0xE9.toByte(), 0x80.toByte(), 0x95.toByte(), 0xCD.toByte(),
+ 0x30.toByte(), 0x83.toByte(), 0xA8.toByte(), 0xE9.toByte(), 0x25.toByte(), 0x96.toByte(),
+ 0x63.toByte(), 0xCE.toByte(), 0xCC.toByte(), 0x48.toByte(), 0x5D.toByte(), 0x5C.toByte(),
+ 0x52.toByte(), 0x00.toByte(), 0xDB.toByte(), 0x1E.toByte(), 0x78.toByte(), 0xD7.toByte(),
+ 0x6A.toByte(), 0x4C.toByte(), 0x2C.toByte(), 0xC8.toByte(), 0x41.toByte(), 0x8C.toByte(),
+ 0xCC.toByte(), 0x87.toByte(), 0x46.toByte(), 0xFB.toByte(), 0x1B.toByte(), 0xC9.toByte(),
+ 0xE8.toByte(), 0x6E.toByte(), 0x4F.toByte(), 0x7A.toByte(), 0x6B.toByte(), 0xC3.toByte(),
+ 0xE7.toByte(), 0x0F.toByte(), 0xD5.toByte(), 0xA9.toByte(), 0x5D.toByte(), 0x6C.toByte(),
+ 0xD4.toByte(), 0xEE.toByte(), 0xA2.toByte(), 0xCC.toByte(), 0x80.toByte(), 0x5A.toByte(),
+ 0xD3.toByte(), 0xCE.toByte(), 0x53.toByte(), 0x59.toByte(), 0xE6.toByte(), 0x80.toByte(),
+ 0x91.toByte(), 0xC4.toByte(), 0xC0.toByte(), 0xD5.toByte(), 0xF0.toByte(), 0x63.toByte(),
+ 0x23.toByte(), 0x91.toByte(), 0x69.toByte(), 0x70.toByte(), 0xC5.toByte(), 0xBB.toByte(),
+ 0xBD.toByte(), 0x05.toByte(), 0xE2.toByte(), 0x4F.toByte(), 0x7D.toByte(), 0x90.toByte(),
+ 0x12.toByte(), 0xED.toByte(), 0xAC.toByte(), 0x4F.toByte(), 0x86.toByte(), 0x96.toByte(),
+ 0x3C.toByte(), 0x89.toByte(), 0xCC.toByte(), 0x92.toByte(), 0x15.toByte(), 0x63.toByte(),
+ 0xCB.toByte(), 0x57.toByte(), 0x70.toByte(), 0xB9.toByte(), 0xC3.toByte(), 0xAE.toByte(),
+ 0x08.toByte(), 0x4F.toByte(), 0xC8.toByte(), 0x56.toByte(), 0x16.toByte(), 0xB0.toByte(),
+ 0x0C.toByte(), 0xC6.toByte(), 0xC8.toByte(), 0x8A.toByte(), 0x80.toByte(), 0xD2.toByte(),
+ 0x37.toByte(), 0xF7.toByte(), 0x7F.toByte(), 0xAB.toByte(), 0x93.toByte(), 0xBB.toByte(),
+ 0xE6.toByte(), 0xDE.toByte(), 0x95.toByte(), 0x78.toByte(), 0xB8.toByte(), 0x11.toByte(),
+ 0xC9.toByte(), 0xE5.toByte(), 0x62.toByte(), 0xAD.toByte(), 0xBC.toByte(), 0x0C.toByte(),
+ 0x87.toByte(), 0x02.toByte(), 0x01.toByte(), 0x11.toByte()
+ )
+ )
+
+ /**
+ * Gets the public key for the given universe.
+ * @param universe The universe.
+ * @return The public key.
+ */
+ @JvmStatic
+ fun getPublicKey(universe: EUniverse?): ByteArray? = KEYS[universe]
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/MsgUtil.java b/src/main/java/in/dragonbra/javasteam/util/MsgUtil.kt
similarity index 50%
rename from src/main/java/in/dragonbra/javasteam/util/MsgUtil.java
rename to src/main/java/in/dragonbra/javasteam/util/MsgUtil.kt
index 9e7e5087..7f7cb55f 100644
--- a/src/main/java/in/dragonbra/javasteam/util/MsgUtil.java
+++ b/src/main/java/in/dragonbra/javasteam/util/MsgUtil.kt
@@ -1,72 +1,65 @@
-package in.dragonbra.javasteam.util;
+package `in`.dragonbra.javasteam.util
-import in.dragonbra.javasteam.enums.EMsg;
+import `in`.dragonbra.javasteam.enums.EMsg
/**
* @author lngtr
* @since 2018-02-21
*/
-public class MsgUtil {
-
- private static final int PROTO_MASK = 0x80000000;
- private static final int EMSG_MASK = ~PROTO_MASK;
+object MsgUtil {
+ private const val PROTO_MASK = -0x80000000
+ private const val EMSG_MASK = PROTO_MASK.inv()
/**
* Strips off the protobuf message flag and returns an EMsg.
- *
* @param msg The message number.
* @return The underlying EMsg.
*/
- public static EMsg getMsg(int msg) {
- return EMsg.from(msg & EMSG_MASK);
- }
+ @JvmStatic
+ fun getMsg(msg: Int): EMsg? = EMsg.from(msg and EMSG_MASK)
/**
* Strips off the protobuf message flag and returns an EMsg.
- *
* @param msg The message number.
* @return The underlying EMsg.
*/
- public static int getGCMsg(int msg) {
- return msg & EMSG_MASK;
- }
+ @JvmStatic
+ fun getGCMsg(msg: Int): Int = msg and EMSG_MASK
/**
* Crafts an EMsg, flagging it if required.
- *
* @param msg The EMsg to flag.
* @param protobuf if set to true, the message is protobuf flagged.
* @return A crafted EMsg, flagged if requested.
*/
- public static int makeMsg(int msg, boolean protobuf) {
+ @JvmStatic
+ fun makeMsg(msg: Int, protobuf: Boolean): Int {
if (protobuf) {
- return msg | PROTO_MASK;
+ return msg or PROTO_MASK
}
- return msg;
+ return msg
}
/**
* Crafts an EMsg, flagging it if required.
- *
* @param msg The EMsg to flag.
- * @param protobuf if set to true, the message is protobuf flagged.
+ * @param protobuf if set to **true**, the message is protobuf flagged.
* @return A crafted EMsg, flagged if requested
*/
- public static int makeGCMsg(int msg, boolean protobuf) {
+ @JvmStatic
+ fun makeGCMsg(msg: Int, protobuf: Boolean): Int {
if (protobuf) {
- return msg | PROTO_MASK;
+ return msg or PROTO_MASK
}
- return msg;
+ return msg
}
/**
* Determines whether message is protobuf flagged.
- *
* @param msg The message.
- * @return true if this message is protobuf flagged; otherwise, false.
+ * @return **true** if this message is protobuf flagged; otherwise, **false**.
*/
- public static boolean isProtoBuf(int msg) {
- return (msg & 0xffffffffL & (long) PROTO_MASK) > 0;
- }
+ @JvmStatic
+ fun isProtoBuf(msg: Int): Boolean = (msg.toLong() and 0xffffffffL and PROTO_MASK.toLong()) > 0
}
diff --git a/src/main/java/in/dragonbra/javasteam/util/NetHelpers.kt b/src/main/java/in/dragonbra/javasteam/util/NetHelpers.kt
index caa8894b..26bdc840 100644
--- a/src/main/java/in/dragonbra/javasteam/util/NetHelpers.kt
+++ b/src/main/java/in/dragonbra/javasteam/util/NetHelpers.kt
@@ -81,7 +81,6 @@ object NetHelpers {
val localIp = msgIpAddress.toBuilder()
if (localIp.hasV6()) {
- // TODO v6 validation
val v6Bytes = msgIpAddress.v6.toByteArray()
for (i in 0..15 step 4) {
v6Bytes[i] = (v6Bytes[i].toInt() xor 0x0D).toByte()
diff --git a/src/main/java/in/dragonbra/javasteam/util/NetHookNetworkListener.java b/src/main/java/in/dragonbra/javasteam/util/NetHookNetworkListener.java
deleted file mode 100644
index 93a16eb4..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/NetHookNetworkListener.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package in.dragonbra.javasteam.util;
-
-import in.dragonbra.javasteam.enums.EMsg;
-import in.dragonbra.javasteam.util.log.LogManager;
-import in.dragonbra.javasteam.util.log.Logger;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * Dump any network messages sent to and received from the Steam server that the client is connected to.
- * These messages are dumped to file, and can be analyzed further with NetHookAnalyzer, a hex editor, or your own purpose-built tools.
- *
- * Be careful with this, sensitive data may be written to the disk (such as your Steam password).
- */
-public class NetHookNetworkListener implements IDebugNetworkListener {
- private static final Logger logger = LogManager.getLogger(NetHookNetworkListener.class);
-
- private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy_MM_dd_H_m_s_S");
-
- private final AtomicLong messageNumber = new AtomicLong(0L);
-
- private final File logDirectory;
-
- public NetHookNetworkListener() {
- this("netlogs");
- }
-
- public NetHookNetworkListener(String path) {
-
- File dir = new File(path);
- dir.mkdir();
-
- logDirectory = new File(dir, FORMAT.format(new Date()));
- logDirectory.mkdir();
- }
-
- @Override
- public void onIncomingNetworkMessage(EMsg msgType, byte[] data) {
- logger.debug(String.format("<- Recv'd EMsg: %s (%d)", msgType, msgType.code()));
-
- try {
- Files.write(Paths.get(new File(logDirectory, getFile("in", msgType)).getAbsolutePath()), data);
- } catch (IOException e) {
- logger.debug(e);
- }
- }
-
- @Override
- public void onOutgoingNetworkMessage(EMsg msgType, byte[] data) {
- logger.debug(String.format("Sent -> EMsg: %s", msgType));
-
- try {
- Files.write(Paths.get(new File(logDirectory, getFile("out", msgType)).getAbsolutePath()), data);
- } catch (IOException e) {
- logger.debug(e);
- }
- }
-
- private String getFile(String direction, EMsg msgType) {
- return String.format("%d_%s_%d_k_EMsg%s.bin", messageNumber.getAndIncrement(), direction, msgType.code(), msgType);
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/NetHookNetworkListener.kt b/src/main/java/in/dragonbra/javasteam/util/NetHookNetworkListener.kt
new file mode 100644
index 00000000..999dd8c3
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/NetHookNetworkListener.kt
@@ -0,0 +1,64 @@
+package `in`.dragonbra.javasteam.util
+
+import `in`.dragonbra.javasteam.enums.EMsg
+import `in`.dragonbra.javasteam.util.log.LogManager
+import java.io.File
+import java.io.IOException
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.concurrent.atomic.AtomicLong
+
+/**
+ * Dump any network messages sent to and received from the Steam server that the client is connected to.
+ * These messages are dumped to file, and can be analyzed further with NetHookAnalyzer, a hex editor, or your own purpose-built tools.
+ * Be careful with this, sensitive data may be written to the disk (such as your Steam password).
+ */
+class NetHookNetworkListener @JvmOverloads constructor(path: String = "netlogs") : IDebugNetworkListener {
+
+ companion object {
+ private val logger = LogManager.getLogger()
+
+ private val FORMAT = SimpleDateFormat("yyyy_MM_dd_H_m_s_S")
+ }
+
+ private val messageNumber = AtomicLong(0L)
+
+ private val logDirectory: File
+
+ init {
+ val dir = File(path)
+ dir.mkdir()
+
+ logDirectory = File(dir, FORMAT.format(Date()))
+ logDirectory.mkdir()
+ }
+
+ override fun onIncomingNetworkMessage(msgType: EMsg, data: ByteArray) {
+ logger.debug("<- Recv'd EMsg: $msgType (${msgType.code()})")
+
+ try {
+ val file = File(logDirectory, getFile("in", msgType))
+ val path = Paths.get(file.absolutePath)
+ Files.write(path, data)
+ } catch (e: IOException) {
+ logger.debug(e)
+ }
+ }
+
+ override fun onOutgoingNetworkMessage(msgType: EMsg, data: ByteArray) {
+ logger.debug("Sent -> EMsg: $msgType")
+
+ try {
+ val file = File(logDirectory, getFile("out", msgType))
+ val path = Paths.get(file.absolutePath)
+ Files.write(path, data)
+ } catch (e: IOException) {
+ logger.debug(e)
+ }
+ }
+
+ private fun getFile(direction: String, msgType: EMsg): String =
+ "${messageNumber.getAndIncrement()}_${direction}_${msgType.code()}_k_EMsg$msgType.bin"
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/Strings.java b/src/main/java/in/dragonbra/javasteam/util/Strings.java
deleted file mode 100644
index 68763e5f..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/Strings.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package in.dragonbra.javasteam.util;
-
-import java.math.BigInteger;
-
-/**
- * @author lngtr
- * @since 2018-02-19
- */
-public class Strings {
-
- /**
- * the constant 2^64
- */
- private static final BigInteger TWO_64 = BigInteger.ONE.shiftLeft(64);
-
- public static boolean isNullOrEmpty(String str) {
- return str == null || str.isEmpty();
- }
-
- public String asUnsignedDecimalString(long l) {
- BigInteger b = BigInteger.valueOf(l);
- if (b.signum() < 0) {
- b = b.add(TWO_64);
- }
- return b.toString();
- }
-
- private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
- public static String toHex(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = HEX_ARRAY[v >>> 4];
- hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- public static byte[] decodeHex(String s) {
- int len = s.length();
- byte[] data = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
- + Character.digit(s.charAt(i + 1), 16));
- }
- return data;
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/Strings.kt b/src/main/java/in/dragonbra/javasteam/util/Strings.kt
new file mode 100644
index 00000000..2603cb0e
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/Strings.kt
@@ -0,0 +1,50 @@
+package `in`.dragonbra.javasteam.util
+
+/**
+ * Provides helper functions for null/empty string checks and hex string conversion.
+ * @author lngtr
+ * @since 2018-02-19
+ */
+object Strings {
+
+ private val HEX_ARRAY = "0123456789ABCDEF".toCharArray()
+
+ /**
+ * Checks whether the given string is `null` or empty.
+ * @param str the string to check.
+ * @return `true` if [str] is `null` or empty, `false` otherwise.
+ */
+ @JvmStatic
+ fun isNullOrEmpty(str: String?): Boolean = str == null || str.isEmpty()
+
+ /**
+ * Converts a byte array into its uppercase hexadecimal string representation.
+ * @param bytes the bytes to encode.
+ * @return a string twice the length of [bytes], containing its hex encoding.
+ */
+ @JvmStatic
+ fun toHex(bytes: ByteArray): String {
+ val hexChars = CharArray(bytes.size * 2)
+ for (j in bytes.indices) {
+ val v = bytes[j].toInt() and 0xFF
+ hexChars[j * 2] = HEX_ARRAY[v ushr 4]
+ hexChars[j * 2 + 1] = HEX_ARRAY[v and 0x0F]
+ }
+ return String(hexChars)
+ }
+
+ /**
+ * Decodes a hexadecimal string into a byte array.
+ * @param s the hex string to decode.
+ * @return the decoded bytes.
+ * @exception StringIndexOutOfBoundsException if [s] has an odd length.
+ */
+ @JvmStatic
+ fun decodeHex(s: String): ByteArray {
+ val data = ByteArray(s.length / 2)
+ for (i in s.indices step 2) {
+ data[i / 2] = ((Character.digit(s[i], 16) shl 4) + Character.digit(s[i + 1], 16)).toByte()
+ }
+ return data
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/Utils.java b/src/main/java/in/dragonbra/javasteam/util/Utils.java
deleted file mode 100644
index c027c605..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/Utils.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package in.dragonbra.javasteam.util;
-
-import in.dragonbra.javasteam.enums.EOSType;
-import in.dragonbra.javasteam.types.ChunkData;
-import org.apache.commons.lang3.SystemUtils;
-
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.ClosedChannelException;
-import java.util.Map;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.zip.CRC32;
-
-/**
- * @author lngtr
- * @since 2018-02-23
- */
-public class Utils {
-
- private static final String JAVA_RUNTIME = getSystemProperty("java.runtime.name");
-
- private static final Map WIN_OS_MAP = new LinkedHashMap<>();
-
- private static final Map OSX_OS_MAP = new LinkedHashMap<>();
-
- private static final Map LINUX_OS_MAP = new LinkedHashMap<>();
-
- private static final Map GENERIC_LINUX_OS_MAP = new LinkedHashMap<>();
-
- static {
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_95, EOSType.Win95);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_98, EOSType.Win98);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_ME, EOSType.WinME);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_NT, EOSType.WinNT);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_2000, EOSType.Win2000);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_XP, EOSType.WinXP);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_VISTA, EOSType.WinVista);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_7, EOSType.Windows7);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_8, EOSType.Windows8);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_10, EOSType.Windows10);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_11, EOSType.Win11);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_2003, EOSType.Win2003);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_2008, EOSType.Win2008);
- WIN_OS_MAP.put(SystemUtils.IS_OS_WINDOWS_2012, EOSType.Win2012);
- WIN_OS_MAP.put(checkOS("Windows Server 2016", "10.0"), EOSType.Win2016);
- WIN_OS_MAP.put(checkOS("Windows Server 2019", "10.0"), EOSType.Win2019);
- WIN_OS_MAP.put(checkOS("Windows Server 2022", "10.0"), EOSType.Win2022);
-
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_TIGER, EOSType.MacOS104);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_LEOPARD, EOSType.MacOS105);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_SNOW_LEOPARD, EOSType.MacOS106);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_LION, EOSType.MacOS107);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_MOUNTAIN_LION, EOSType.MacOS108);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_MAVERICKS, EOSType.MacOS109);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_YOSEMITE, EOSType.MacOS1010);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_EL_CAPITAN, EOSType.MacOS1011);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_SIERRA, EOSType.MacOS1012);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_HIGH_SIERRA, EOSType.Macos1013);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_MOJAVE, EOSType.Macos1014);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_CATALINA, EOSType.Macos1015);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_BIG_SUR, EOSType.MacOS11);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_MONTEREY, EOSType.MacOS12);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_VENTURA, EOSType.MacOS13);
- OSX_OS_MAP.put(SystemUtils.IS_OS_MAC_OSX_SONOMA, EOSType.MacOS14);
- OSX_OS_MAP.put(checkOS("Mac OS X", "15"), EOSType.MacOS15);
-
- LINUX_OS_MAP.put("2.2", EOSType.Linux22);
- LINUX_OS_MAP.put("2.4", EOSType.Linux24);
- LINUX_OS_MAP.put("2.6", EOSType.Linux26);
- LINUX_OS_MAP.put("3.2", EOSType.Linux32);
- LINUX_OS_MAP.put("3.5", EOSType.Linux35);
- LINUX_OS_MAP.put("3.6", EOSType.Linux36);
- LINUX_OS_MAP.put("3.10", EOSType.Linux310);
- LINUX_OS_MAP.put("3.16", EOSType.Linux316);
- LINUX_OS_MAP.put("3.18", EOSType.Linux318);
- LINUX_OS_MAP.put("4.1", EOSType.Linux41);
- LINUX_OS_MAP.put("4.4", EOSType.Linux44);
- LINUX_OS_MAP.put("4.9", EOSType.Linux49);
- LINUX_OS_MAP.put("4.14", EOSType.Linux414);
- LINUX_OS_MAP.put("4.19", EOSType.Linux419);
- LINUX_OS_MAP.put("5.4", EOSType.Linux54);
- LINUX_OS_MAP.put("5.10", EOSType.Linux510);
-
- GENERIC_LINUX_OS_MAP.put("3x", EOSType.Linux3x);
- GENERIC_LINUX_OS_MAP.put("4x", EOSType.Linux4x);
- GENERIC_LINUX_OS_MAP.put("5x", EOSType.Linux5x);
- GENERIC_LINUX_OS_MAP.put("6x", EOSType.Linux6x);
- GENERIC_LINUX_OS_MAP.put("7x", EOSType.Linux7x);
- }
-
- // Sorted in history order by each OS release.
- public static EOSType getOSType() {
- // Windows
- if (SystemUtils.IS_OS_WINDOWS) {
- for (Map.Entry winEntry : WIN_OS_MAP.entrySet()) {
- if (winEntry.getKey()) {
- return winEntry.getValue();
- }
- }
-
- return EOSType.WinUnknown;
- }
-
- // Mac OS
- if (SystemUtils.IS_OS_MAC) {
- for (Map.Entry osxEntry : OSX_OS_MAP.entrySet()) {
- if (osxEntry.getKey()) {
- return osxEntry.getValue();
- }
- }
-
- return EOSType.MacOSUnknown;
- }
-
- // Android
- if (JAVA_RUNTIME != null && JAVA_RUNTIME.startsWith("Android")) {
- return EOSType.AndroidUnknown;
- }
-
- // Linux
- if (SystemUtils.IS_OS_LINUX) {
- String linuxOsVersion = getSystemProperty("os.version");
-
- if (linuxOsVersion == null) {
- return EOSType.LinuxUnknown;
- }
-
- String[] osVersion = linuxOsVersion.split("\\.");
-
- if (osVersion.length < 2) {
- return EOSType.LinuxUnknown;
- }
-
- String version = osVersion[0] + "." + osVersion[1];
-
- EOSType linuxVersion = LINUX_OS_MAP.get(version);
- if (linuxVersion != null) {
- // Found Major/Minor version
- return linuxVersion;
- }
-
- String majorVersion = osVersion[0] + "x";
- for (Map.Entry linuxEntry : GENERIC_LINUX_OS_MAP.entrySet()) {
- if (linuxEntry.getKey().equals(majorVersion)) {
- // Found generic Linux version
- return linuxEntry.getValue();
- }
- }
-
- return EOSType.LinuxUnknown;
- }
-
- // Unknown OS
- return EOSType.Unknown;
- }
-
- @SuppressWarnings("SameParameterValue")
- private static boolean checkOS(String namePrefix, String versionPrefix) {
- return SystemUtils.OS_NAME.startsWith(namePrefix) && SystemUtils.OS_VERSION.startsWith(versionPrefix);
- }
-
- private static String getSystemProperty(final String property) {
- try {
- return System.getProperty(property);
- } catch (final SecurityException ex) {
- // we are not allowed to look at this property
- return null;
- }
- }
-
- /**
- * Convenience method for calculating the CRC32 checksum of a string.
- *
- * @param s the string
- * @return long value of the CRC32
- */
- public static long crc32(String s) {
- return crc32(s.getBytes());
- }
-
- /**
- * Convenience method for calculating the CRC32 checksum of a byte array.
- *
- * @param bytes the byte array
- * @return long value of the CRC32
- */
- public static long crc32(byte[] bytes) {
- return crc32(bytes, 0, bytes.length);
- }
-
- /**
- * Convenience method for calculating the CRC32 checksum of a byte array with offset and length.
- *
- * @param bytes the byte array
- * @param offset the offset to start from
- * @param length the number of bytes to checksum
- * @return long value of the CRC32
- */
- public static long crc32(byte[] bytes, int offset, int length) {
- var checksum = new CRC32();
- checksum.update(bytes, offset, length);
- return checksum.getValue();
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/Utils.kt b/src/main/java/in/dragonbra/javasteam/util/Utils.kt
new file mode 100644
index 00000000..2dac4e3d
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/Utils.kt
@@ -0,0 +1,177 @@
+package `in`.dragonbra.javasteam.util
+
+import `in`.dragonbra.javasteam.enums.EOSType
+import org.apache.commons.lang3.SystemUtils
+import java.util.zip.CRC32
+
+/**
+ * @author lngtr
+ * @since 2018-02-23
+ */
+object Utils {
+
+ private val javaRuntime: String? = getSystemProperty("java.runtime.name")
+
+ private val winOsMap: Map = linkedMapOf(
+ SystemUtils.IS_OS_WINDOWS_95 to EOSType.Win95,
+ SystemUtils.IS_OS_WINDOWS_98 to EOSType.Win98,
+ SystemUtils.IS_OS_WINDOWS_ME to EOSType.WinME,
+ SystemUtils.IS_OS_WINDOWS_NT to EOSType.WinNT,
+ SystemUtils.IS_OS_WINDOWS_2000 to EOSType.Win2000,
+ SystemUtils.IS_OS_WINDOWS_XP to EOSType.WinXP,
+ SystemUtils.IS_OS_WINDOWS_VISTA to EOSType.WinVista,
+ SystemUtils.IS_OS_WINDOWS_7 to EOSType.Windows7,
+ SystemUtils.IS_OS_WINDOWS_8 to EOSType.Windows8,
+ SystemUtils.IS_OS_WINDOWS_10 to EOSType.Windows10,
+ SystemUtils.IS_OS_WINDOWS_11 to EOSType.Win11,
+ SystemUtils.IS_OS_WINDOWS_2003 to EOSType.Win2003,
+ SystemUtils.IS_OS_WINDOWS_2008 to EOSType.Win2008,
+ SystemUtils.IS_OS_WINDOWS_2012 to EOSType.Win2012,
+ checkOS("Windows Server 2016", "10.0") to EOSType.Win2016,
+ checkOS("Windows Server 2019", "10.0") to EOSType.Win2019,
+ checkOS("Windows Server 2022", "10.0") to EOSType.Win2022,
+ )
+
+ private val osxOsMap: Map = linkedMapOf(
+ SystemUtils.IS_OS_MAC_OSX_TIGER to EOSType.MacOS104,
+ SystemUtils.IS_OS_MAC_OSX_LEOPARD to EOSType.MacOS105,
+ SystemUtils.IS_OS_MAC_OSX_SNOW_LEOPARD to EOSType.MacOS106,
+ SystemUtils.IS_OS_MAC_OSX_LION to EOSType.MacOS107,
+ SystemUtils.IS_OS_MAC_OSX_MOUNTAIN_LION to EOSType.MacOS108,
+ SystemUtils.IS_OS_MAC_OSX_MAVERICKS to EOSType.MacOS109,
+ SystemUtils.IS_OS_MAC_OSX_YOSEMITE to EOSType.MacOS1010,
+ SystemUtils.IS_OS_MAC_OSX_EL_CAPITAN to EOSType.MacOS1011,
+ SystemUtils.IS_OS_MAC_OSX_SIERRA to EOSType.MacOS1012,
+ SystemUtils.IS_OS_MAC_OSX_HIGH_SIERRA to EOSType.Macos1013,
+ SystemUtils.IS_OS_MAC_OSX_MOJAVE to EOSType.Macos1014,
+ SystemUtils.IS_OS_MAC_OSX_CATALINA to EOSType.Macos1015,
+ SystemUtils.IS_OS_MAC_OSX_BIG_SUR to EOSType.MacOS11,
+ SystemUtils.IS_OS_MAC_OSX_MONTEREY to EOSType.MacOS12,
+ SystemUtils.IS_OS_MAC_OSX_VENTURA to EOSType.MacOS13,
+ SystemUtils.IS_OS_MAC_OSX_SONOMA to EOSType.MacOS14,
+ SystemUtils.IS_OS_MAC_OSX_SEQUOIA to EOSType.MacOS15,
+ )
+
+ private val linuxOsMap: Map = linkedMapOf(
+ "2.2" to EOSType.Linux22,
+ "2.4" to EOSType.Linux24,
+ "2.6" to EOSType.Linux26,
+ "3.2" to EOSType.Linux32,
+ "3.5" to EOSType.Linux35,
+ "3.6" to EOSType.Linux36,
+ "3.10" to EOSType.Linux310,
+ "3.16" to EOSType.Linux316,
+ "3.18" to EOSType.Linux318,
+ "4.1" to EOSType.Linux41,
+ "4.4" to EOSType.Linux44,
+ "4.9" to EOSType.Linux49,
+ "4.14" to EOSType.Linux414,
+ "4.19" to EOSType.Linux419,
+ "5.4" to EOSType.Linux54,
+ "5.10" to EOSType.Linux510,
+ )
+
+ private val genericLinuxOsMap: Map = linkedMapOf(
+ "3x" to EOSType.Linux3x,
+ "4x" to EOSType.Linux4x,
+ "5x" to EOSType.Linux5x,
+ "6x" to EOSType.Linux6x,
+ "7x" to EOSType.Linux7x,
+ )
+
+ // Sorted in history order by each OS release.
+ @JvmStatic
+ fun getOSType(): EOSType {
+ // Windows
+ if (SystemUtils.IS_OS_WINDOWS) {
+ for ((matched, type) in winOsMap) {
+ if (matched) {
+ return type
+ }
+ }
+
+ return EOSType.WinUnknown
+ }
+
+ // Mac OS
+ if (SystemUtils.IS_OS_MAC) {
+ for ((matched, type) in osxOsMap) {
+ if (matched) {
+ return type
+ }
+ }
+
+ return EOSType.MacOSUnknown
+ }
+
+ // Android
+ if (javaRuntime?.startsWith("Android") == true) {
+ return EOSType.AndroidUnknown
+ }
+
+ // Linux
+ if (SystemUtils.IS_OS_LINUX) {
+ val linuxOsVersion = getSystemProperty("os.version") ?: return EOSType.LinuxUnknown
+
+ val osVersion = linuxOsVersion.split(".")
+ if (osVersion.size < 2) {
+ return EOSType.LinuxUnknown
+ }
+
+ val version = "${osVersion[0]}.${osVersion[1]}"
+
+ // Found Major/Minor version
+ linuxOsMap[version]?.let { return it }
+
+ // Found generic Linux version
+ val majorVersion = "${osVersion[0]}x"
+ genericLinuxOsMap[majorVersion]?.let { return it }
+
+ return EOSType.LinuxUnknown
+ }
+
+ // Unknown OS
+ return EOSType.Unknown
+ }
+
+ @Suppress("SameParameterValue")
+ private fun checkOS(namePrefix: String, versionPrefix: String): Boolean =
+ SystemUtils.OS_NAME.startsWith(namePrefix) && SystemUtils.OS_VERSION.startsWith(versionPrefix)
+
+ private fun getSystemProperty(property: String): String? = try {
+ System.getProperty(property)
+ } catch (_: SecurityException) {
+ // we are not allowed to look at this property
+ null
+ }
+
+ /**
+ * Convenience method for calculating the CRC32 checksum of a string.
+ * @param s the string
+ * @return long value of the CRC32
+ */
+ @JvmStatic
+ fun crc32(s: String): Long = crc32(s.toByteArray())
+
+ /**
+ * Convenience method for calculating the CRC32 checksum of a byte array.
+ * @param bytes the byte array
+ * @return long value of the CRC32
+ */
+ @JvmStatic
+ fun crc32(bytes: ByteArray): Long = crc32(bytes, 0, bytes.size)
+
+ /**
+ * Convenience method for calculating the CRC32 checksum of a byte array with offset and length.
+ * @param bytes the byte array
+ * @param offset the offset to start from
+ * @param length the number of bytes to checksum
+ * @return long value of the CRC32
+ */
+ @JvmStatic
+ fun crc32(bytes: ByteArray, offset: Int, length: Int): Long {
+ val checksum = CRC32()
+ checksum.update(bytes, offset, length)
+ return checksum.value
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/WebHelpers.java b/src/main/java/in/dragonbra/javasteam/util/WebHelpers.java
deleted file mode 100644
index 6b1e907b..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/WebHelpers.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package in.dragonbra.javasteam.util;
-
-import java.nio.charset.StandardCharsets;
-
-/**
- * @author lngtr
- * @since 2018-04-16
- */
-public class WebHelpers {
-
- private static boolean isUrlSafeChar(char ch) {
- return ch >= 'a' && ch <= 'z' ||
- ch >= 'A' && ch <= 'Z' ||
- ch >= '0' && ch <= '9' ||
- ch == '-' ||
- ch == '.' ||
- ch == '_';
- }
-
- public static String urlEncode(String input) {
- return urlEncode(input.getBytes(StandardCharsets.UTF_8));
- }
-
- public static String urlEncode(byte[] input) {
- StringBuilder encoded = new StringBuilder(input.length * 2);
-
- for (byte i : input) {
- char inch = (char) i;
-
- if (isUrlSafeChar(inch)) {
- encoded.append(inch);
- } else if (inch == ' ') {
- encoded.append('+');
- } else {
- encoded.append(String.format("%%%02X", i));
- }
- }
-
- return encoded.toString();
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/WebHelpers.kt b/src/main/java/in/dragonbra/javasteam/util/WebHelpers.kt
new file mode 100644
index 00000000..1b386016
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/WebHelpers.kt
@@ -0,0 +1,44 @@
+package `in`.dragonbra.javasteam.util
+
+import java.nio.charset.StandardCharsets
+
+/**
+ * Provides helper functions for URL encoding.
+ * @author lngtr
+ * @since 2018-04-16
+ */
+object WebHelpers {
+
+ private fun isUrlSafeChar(ch: Char): Boolean =
+ ch in 'a'..'z' || ch in 'A'..'Z' || ch in '0'..'9' || ch == '-' || ch == '.' || ch == '_'
+
+ /**
+ * URL-encodes the given string, using UTF-8 to convert it to bytes first.
+ * @param input the string to encode.
+ * @return the URL-encoded string.
+ */
+ @JvmStatic
+ fun urlEncode(input: String): String = urlEncode(input.toByteArray(StandardCharsets.UTF_8))
+
+ /**
+ * URL-encodes the given bytes.
+ * @param input the bytes to encode.
+ * @return the URL-encoded string.
+ */
+ @JvmStatic
+ fun urlEncode(input: ByteArray): String {
+ val encoded = StringBuilder(input.size * 2)
+
+ for (i in input) {
+ val inch = i.toInt().toChar()
+
+ when {
+ isUrlSafeChar(inch) -> encoded.append(inch)
+ inch == ' ' -> encoded.append('+')
+ else -> encoded.append("%%%02X".format(i))
+ }
+ }
+
+ return encoded.toString()
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/compat/Consumer.java b/src/main/java/in/dragonbra/javasteam/util/compat/Consumer.java
deleted file mode 100644
index 1756895e..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/compat/Consumer.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package in.dragonbra.javasteam.util.compat;
-
-public interface Consumer {
- void accept(T t);
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/compat/Consumer.kt b/src/main/java/in/dragonbra/javasteam/util/compat/Consumer.kt
new file mode 100644
index 00000000..d5043bcd
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/compat/Consumer.kt
@@ -0,0 +1,5 @@
+package `in`.dragonbra.javasteam.util.compat
+
+fun interface Consumer {
+ fun accept(t: T)
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.java b/src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.java
deleted file mode 100644
index 591a32b0..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package in.dragonbra.javasteam.util.compat;
-
-import java.util.Objects;
-
-/**
- * @author steev
- * @since 2018-03-21
- */
-public class ObjectsCompat {
- public static boolean equals(Object a, Object b) {
- return Objects.equals(a, b);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.kt b/src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.kt
new file mode 100644
index 00000000..25daec52
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.kt
@@ -0,0 +1,11 @@
+package `in`.dragonbra.javasteam.util.compat
+
+/**
+ * Compatibility for [java.util.Objects] for Android, which requires API 19+.
+ * @author steev
+ * @since 2018-03-21
+ */
+object ObjectsCompat {
+ @JvmStatic
+ fun equals(a: Any?, b: Any?): Boolean = a == b
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.java b/src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.java
deleted file mode 100644
index 0b4dd03b..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package in.dragonbra.javasteam.util.crypto;
-
-@SuppressWarnings("unused")
-public final class BerDecodeException extends Exception {
-
- private final int _position;
-
- public BerDecodeException() {
- _position = 0;
- }
-
- public BerDecodeException(String message) {
- super(message);
- _position = 0;
- }
-
- public BerDecodeException(String message, Exception ex) {
- super(message, ex);
- _position = 0;
- }
-
- public BerDecodeException(String message, int position) {
- super(message);
- _position = position;
- }
-
- public BerDecodeException(String message, int position, Exception ex) {
- super(message, ex);
- _position = position;
- }
-
- public int get_position() {
- return _position;
- }
-
- @Override
- public String getMessage() {
- return super.getMessage() + String.format(" (Position %d)%s", _position, System.lineSeparator());
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.kt b/src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.kt
new file mode 100644
index 00000000..10bf986c
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.kt
@@ -0,0 +1,10 @@
+package `in`.dragonbra.javasteam.util.crypto
+
+class BerDecodeException @JvmOverloads constructor(
+ msg: String? = null,
+ val position: Int = 0,
+ cause: Exception? = null,
+) : Exception(msg, cause) {
+ override val message: String
+ get() = super.message + " (Position $position)${System.lineSeparator()}"
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.java b/src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.java
deleted file mode 100644
index 4d5f624e..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package in.dragonbra.javasteam.util.crypto;
-
-/**
- * @author lngtr
- * @since 2018-03-02
- */
-public class CryptoException extends Exception {
- public CryptoException() {
- }
-
- public CryptoException(String message) {
- super(message);
- }
-
- public CryptoException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public CryptoException(Throwable cause) {
- super(cause);
- }
-
- public CryptoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.kt b/src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.kt
new file mode 100644
index 00000000..3d09328e
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.kt
@@ -0,0 +1,10 @@
+package `in`.dragonbra.javasteam.util.crypto
+
+/**
+ * @author lngtr
+ * @since 2018-03-02
+ */
+class CryptoException @JvmOverloads constructor(
+ message: String? = null,
+ cause: Throwable? = null,
+) : Exception(message, cause)
diff --git a/src/main/java/in/dragonbra/javasteam/util/crypto/RSACrypto.java b/src/main/java/in/dragonbra/javasteam/util/crypto/RSACrypto.java
deleted file mode 100644
index 60ce0018..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/crypto/RSACrypto.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package in.dragonbra.javasteam.util.crypto;
-
-import in.dragonbra.javasteam.util.log.LogManager;
-import in.dragonbra.javasteam.util.log.Logger;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Handles encrypting and decrypting using the RSA public key encryption algorithm.
- */
-public class RSACrypto {
-
- private static final Logger logger = LogManager.getLogger(RSACrypto.class);
-
- private Cipher cipher;
-
- public RSACrypto(byte[] key) {
- if (key == null) {
- throw new IllegalArgumentException("key is null");
- }
-
- try {
- final List list = new ArrayList<>();
- for (final byte b : key) {
- list.add(b);
- }
- final AsnKeyParser keyParser = new AsnKeyParser(list);
- final BigInteger[] keys = keyParser.parseRSAPublicKey();
- init(keys[0], keys[1]);
- } catch (final BerDecodeException e) {
- logger.error(e);
- }
- }
-
- private void init(BigInteger mod, BigInteger exp) {
- try {
- final RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(mod, exp);
-
- final KeyFactory factory = KeyFactory.getInstance("RSA");
- RSAPublicKey rsaKey = (RSAPublicKey) factory.generatePublic(publicKeySpec);
-
- cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", CryptoHelper.SEC_PROV);
- cipher.init(Cipher.ENCRYPT_MODE, rsaKey);
- } catch (final NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidKeySpecException
- | NoSuchProviderException e) {
- logger.debug(e);
- }
- }
-
- public byte[] encrypt(byte[] input) {
- try {
- return cipher.doFinal(input);
- } catch (final IllegalBlockSizeException | BadPaddingException e) {
- logger.debug(e);
- }
-
- return null;
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/crypto/RSACrypto.kt b/src/main/java/in/dragonbra/javasteam/util/crypto/RSACrypto.kt
new file mode 100644
index 00000000..bd99db2c
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/crypto/RSACrypto.kt
@@ -0,0 +1,41 @@
+package `in`.dragonbra.javasteam.util.crypto
+
+import `in`.dragonbra.javasteam.util.log.LogManager.getLogger
+import java.security.GeneralSecurityException
+import java.security.KeyFactory
+import java.security.spec.RSAPublicKeySpec
+import javax.crypto.Cipher
+
+/**
+ * Handles encrypting and decrypting using the RSA public key encryption algorithm.
+ */
+class RSACrypto(key: ByteArray?) {
+
+ companion object {
+ private val logger = getLogger(RSACrypto::class.java)
+ }
+
+ private val cipher: Cipher? = try {
+ requireNotNull(key) { "key is null" }
+ val keys = AsnKeyParser(key.asList()).parseRSAPublicKey()
+ val publicKeySpec = RSAPublicKeySpec(keys[0], keys[1])
+ val factory = KeyFactory.getInstance("RSA")
+ val rsaKey = factory.generatePublic(publicKeySpec)
+ Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", CryptoHelper.SEC_PROV).also {
+ it.init(Cipher.ENCRYPT_MODE, rsaKey)
+ }
+ } catch (e: BerDecodeException) {
+ logger.error(e)
+ null
+ } catch (e: GeneralSecurityException) {
+ logger.debug(e)
+ null
+ }
+
+ fun encrypt(input: ByteArray): ByteArray? = try {
+ cipher?.doFinal(input)
+ } catch (e: GeneralSecurityException) {
+ logger.debug(e)
+ null
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/Event.java b/src/main/java/in/dragonbra/javasteam/util/event/Event.java
deleted file mode 100644
index e13cc882..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/event/Event.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package in.dragonbra.javasteam.util.event;
-
-import java.util.HashSet;
-
-public class Event {
-
- protected final HashSet> handlers = new HashSet<>();
-
- public void addEventHandler(EventHandler handler) {
- synchronized (handlers) {
- handlers.add(handler);
- }
- }
-
- public void removeEventHandler(EventHandler handler) {
- synchronized (handlers) {
- handlers.remove(handler);
- }
- }
-
- public void handleEvent(Object sender, T e) {
- synchronized (handlers) {
- for (final EventHandler handler : handlers) {
- handler.handleEvent(sender, e);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/Event.kt b/src/main/java/in/dragonbra/javasteam/util/event/Event.kt
new file mode 100644
index 00000000..c11e4f2e
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/event/Event.kt
@@ -0,0 +1,19 @@
+package `in`.dragonbra.javasteam.util.event
+
+import java.util.concurrent.CopyOnWriteArrayList
+
+class Event {
+ private val handlers = CopyOnWriteArrayList>()
+
+ fun addEventHandler(handler: EventHandler) {
+ handlers.add(handler)
+ }
+
+ fun removeEventHandler(handler: EventHandler) {
+ handlers.remove(handler)
+ }
+
+ fun handleEvent(sender: Any, e: T) {
+ handlers.forEach { it.handleEvent(sender, e) }
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/EventArgs.java b/src/main/java/in/dragonbra/javasteam/util/event/EventArgs.java
deleted file mode 100644
index 062b8d25..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/event/EventArgs.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package in.dragonbra.javasteam.util.event;
-
-public class EventArgs {
-
- public static final EventArgs EMPTY = new EventArgs();
-
- public EventArgs() {
- }
-}
\ No newline at end of file
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/EventArgs.kt b/src/main/java/in/dragonbra/javasteam/util/event/EventArgs.kt
new file mode 100644
index 00000000..828e0918
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/event/EventArgs.kt
@@ -0,0 +1,8 @@
+package `in`.dragonbra.javasteam.util.event
+
+open class EventArgs {
+ companion object {
+ @JvmField
+ val EMPTY = EventArgs()
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/EventHandler.java b/src/main/java/in/dragonbra/javasteam/util/event/EventHandler.java
deleted file mode 100644
index dad86e7c..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/event/EventHandler.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package in.dragonbra.javasteam.util.event;
-
-public interface EventHandler {
- void handleEvent(Object sender, T e);
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/EventHandler.kt b/src/main/java/in/dragonbra/javasteam/util/event/EventHandler.kt
new file mode 100644
index 00000000..70bbb851
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/event/EventHandler.kt
@@ -0,0 +1,5 @@
+package `in`.dragonbra.javasteam.util.event
+
+fun interface EventHandler {
+ fun handleEvent(sender: Any, e: T)
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.java b/src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.java
deleted file mode 100644
index 66dcb852..00000000
--- a/src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package in.dragonbra.javasteam.util.event;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * @author lngtr
- * @since 2018-02-20
- */
-public class ScheduledFunction {
-
- private long delay;
-
- private final Runnable func;
-
- private Timer timer;
-
- private boolean bStarted = false;
-
- public ScheduledFunction(Runnable func, long delay) {
- this.delay = delay;
- this.func = func;
- }
-
- public void start() {
- if (!bStarted) {
- timer = new Timer();
- timer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- if (func != null) {
- func.run();
- }
- }
- }, 0, delay);
- bStarted = true;
- }
- }
-
- public void stop() {
- if (bStarted) {
- timer.cancel();
- timer = null;
- bStarted = false;
- }
- }
-
- public long getDelay() {
- return delay;
- }
-
- public void setDelay(long delay) {
- this.delay = delay;
- }
-}
diff --git a/src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.kt b/src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.kt
new file mode 100644
index 00000000..b860d0e2
--- /dev/null
+++ b/src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.kt
@@ -0,0 +1,32 @@
+package `in`.dragonbra.javasteam.util.event
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlin.time.Duration.Companion.milliseconds
+
+class ScheduledFunction(private val func: Runnable, var delay: Long) {
+ private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
+ private var job: Job? = null
+
+ @Synchronized
+ fun start() {
+ if (job?.isActive == true) return
+ job = scope.launch {
+ while (isActive) {
+ func.run()
+ delay(delay.milliseconds)
+ }
+ }
+ }
+
+ @Synchronized
+ fun stop() {
+ job?.cancel()
+ job = null
+ }
+}
diff --git a/src/main/java/in/dragonbra/javasteam/util/stream/BinaryReader.java b/src/main/java/in/dragonbra/javasteam/util/stream/BinaryReader.java
index 87ed2039..3e36eca7 100644
--- a/src/main/java/in/dragonbra/javasteam/util/stream/BinaryReader.java
+++ b/src/main/java/in/dragonbra/javasteam/util/stream/BinaryReader.java
@@ -155,15 +155,14 @@ private String readNullTermUtf8String() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
- while ((b = in.read()) != 0) {
- if (b <= 0) {
- break;
- }
+ while ((b = in.read()) > 0) {
baos.write(b);
position++;
}
- position++; // Increment for the null terminator
+ if (b == 0) {
+ position++; // Increment for the null terminator
+ }
return ByteArrayOutputStreamCompat.toString(baos);
}
diff --git a/src/main/java/in/dragonbra/javasteam/util/stream/MemoryStream.java b/src/main/java/in/dragonbra/javasteam/util/stream/MemoryStream.java
index 59f67fa4..fe927732 100644
--- a/src/main/java/in/dragonbra/javasteam/util/stream/MemoryStream.java
+++ b/src/main/java/in/dragonbra/javasteam/util/stream/MemoryStream.java
@@ -286,6 +286,9 @@ public long getPosition() {
* @param value The new position within the stream.
*/
public void setPosition(long value) {
+ if (value < 0 || value > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("value out of range: " + value);
+ }
position = origin + (int) value;
}
@@ -314,6 +317,9 @@ public long seek(long offset, SeekOrigin loc) {
throw new IllegalArgumentException("loc");
}
+ if (offset < Integer.MIN_VALUE || offset > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("offset out of range: " + offset);
+ }
position = reference + (int) offset;
return position;
}
diff --git a/src/test/java/in/dragonbra/javasteam/util/HardwareUtilsTest.java b/src/test/java/in/dragonbra/javasteam/util/HardwareUtilsTest.java
index 299da293..c02ccf19 100644
--- a/src/test/java/in/dragonbra/javasteam/util/HardwareUtilsTest.java
+++ b/src/test/java/in/dragonbra/javasteam/util/HardwareUtilsTest.java
@@ -1,21 +1,10 @@
package in.dragonbra.javasteam.util;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import java.lang.reflect.Field;
-
public class HardwareUtilsTest {
- @BeforeEach
- public void setUp() throws NoSuchFieldException, IllegalAccessException {
- // Ehh.... This resets the 'MACHINE_NAME' field for every test.
- Field field = HardwareUtils.class.getDeclaredField("MACHINE_NAME");
- field.setAccessible(true);
- field.set(null, null);
- }
-
@Test
public void machineNameWithTag() {
var name = HardwareUtils.getMachineName(true);
diff --git a/src/test/java/in/dragonbra/javasteam/util/NetHelpersTest.java b/src/test/java/in/dragonbra/javasteam/util/NetHelpersTest.java
index 00b4a5af..ea8950df 100644
--- a/src/test/java/in/dragonbra/javasteam/util/NetHelpersTest.java
+++ b/src/test/java/in/dragonbra/javasteam/util/NetHelpersTest.java
@@ -76,6 +76,26 @@ public void obfuscatePrivateIP() {
Assertions.assertArrayEquals(ipv6Bytes, NetHelpers.obfuscatePrivateIP(v6Addr).getV6().toByteArray());
}
+ @Test
+ public void obfuscatePrivateIPv6_isOwnInverse() throws UnknownHostException {
+ var v6Addr = NetHelpers.getMsgIPAddress(InetAddress.getByName("2001:db8::1"));
+ var obfuscated = NetHelpers.obfuscatePrivateIP(v6Addr);
+ var restored = NetHelpers.obfuscatePrivateIP(obfuscated);
+ Assertions.assertArrayEquals(v6Addr.getV6().toByteArray(), restored.getV6().toByteArray());
+ }
+
+ @Test
+ public void obfuscatePrivateIPv6_allZerosProducesMask() throws UnknownHostException {
+ var v6Addr = NetHelpers.getMsgIPAddress(InetAddress.getByName("::"));
+ byte[] expected = {
+ (byte) 0x0D, (byte) 0xF0, (byte) 0xAD, (byte) 0xBA,
+ (byte) 0x0D, (byte) 0xF0, (byte) 0xAD, (byte) 0xBA,
+ (byte) 0x0D, (byte) 0xF0, (byte) 0xAD, (byte) 0xBA,
+ (byte) 0x0D, (byte) 0xF0, (byte) 0xAD, (byte) 0xBA
+ };
+ Assertions.assertArrayEquals(expected, NetHelpers.obfuscatePrivateIP(v6Addr).getV6().toByteArray());
+ }
+
@Test
public void tryParseIPEndPoint() throws UnknownHostException {
var v4Addr = NetHelpers.tryParseIPEndPoint("127.0.0.1:1337");
diff --git a/src/test/java/in/dragonbra/javasteam/util/UtilsTest.java b/src/test/java/in/dragonbra/javasteam/util/UtilsTest.java
index 3fdf40cc..1f8200b4 100644
--- a/src/test/java/in/dragonbra/javasteam/util/UtilsTest.java
+++ b/src/test/java/in/dragonbra/javasteam/util/UtilsTest.java
@@ -18,4 +18,18 @@ public void crc32() {
long result = Utils.crc32("test_string");
Assertions.assertEquals(0x0967B587, result);
}
+
+ @Test
+ public void crc32WithByteArray() {
+ byte[] input = "test_string".getBytes();
+ long result = Utils.crc32(input);
+ Assertions.assertEquals(0x0967B587, result);
+ }
+
+ @Test
+ public void crc32WithOffsetAndLength() {
+ byte[] input = "xxtest_stringxx".getBytes();
+ long result = Utils.crc32(input, 2, "test_string".length());
+ Assertions.assertEquals(0x0967B587, result);
+ }
}
diff --git a/src/test/java/in/dragonbra/javasteam/util/crypto/AsnParserTest.java b/src/test/java/in/dragonbra/javasteam/util/crypto/AsnParserTest.java
new file mode 100644
index 00000000..f8f04919
--- /dev/null
+++ b/src/test/java/in/dragonbra/javasteam/util/crypto/AsnParserTest.java
@@ -0,0 +1,188 @@
+package in.dragonbra.javasteam.util.crypto;
+
+import in.dragonbra.javasteam.TestBase;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class AsnParserTest extends TestBase {
+
+ private static List bytes(int... values) {
+ Byte[] result = new Byte[values.length];
+ for (int i = 0; i < values.length; i++) {
+ result[i] = (byte) values[i];
+ }
+ return Arrays.asList(result);
+ }
+
+ @Test
+ public void positionTracking() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x01, 0x02, 0x03));
+ assertEquals(0, parser.currentPosition());
+ assertEquals(3, parser.remainingBytes());
+
+ parser.getNextOctet();
+ assertEquals(1, parser.currentPosition());
+ assertEquals(2, parser.remainingBytes());
+ }
+
+ @Test
+ public void getNextOctet() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x42));
+ assertEquals(0x42, parser.getNextOctet());
+ assertEquals(0, parser.remainingBytes());
+ }
+
+ @Test
+ public void getNextOctetEmptyThrows() {
+ var parser = new AsnParser(Collections.emptyList());
+ assertThrows(BerDecodeException.class, parser::getNextOctet);
+ }
+
+ @Test
+ public void getLengthShortForm() throws BerDecodeException {
+ assertEquals(127, new AsnParser(bytes(0x7f)).getLength());
+ assertEquals(0, new AsnParser(bytes(0x00)).getLength());
+ }
+
+ @Test
+ public void getLengthLongForm() throws BerDecodeException {
+ // 0x81 = long form, 1 byte follows: 0x42 = 66
+ assertEquals(0x42, new AsnParser(bytes(0x81, 0x42)).getLength());
+ }
+
+ @Test
+ public void getLengthInvalidEncodingThrows() {
+ // 0x85 = long form with 5 length bytes — exceeds max of 4
+ var parser = new AsnParser(bytes(0x85, 0x00, 0x00, 0x00, 0x00, 0x00));
+ assertThrows(BerDecodeException.class, parser::getLength);
+ }
+
+ @Test
+ public void next() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x02, 0x02, 0xAA, 0xBB));
+ assertArrayEquals(new byte[]{(byte) 0xAA, (byte) 0xBB}, parser.next());
+ }
+
+ @Test
+ public void nextNull() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x05, 0x00));
+ assertEquals(0, parser.nextNull());
+ assertEquals(0, parser.remainingBytes());
+ }
+
+ @Test
+ public void nextNullWrongTagThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x02, 0x00)).nextNull());
+ }
+
+ @Test
+ public void nextNullNonZeroSizeThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x05, 0x01)).nextNull());
+ }
+
+ @Test
+ public void isNextNull() {
+ assertTrue(new AsnParser(bytes(0x05, 0x00)).isNextNull());
+ assertFalse(new AsnParser(bytes(0x02, 0x00)).isNextNull());
+ }
+
+ @Test
+ public void nextSequence() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x30, 0x03, 0x01, 0x02, 0x03));
+ assertEquals(3, parser.nextSequence());
+ assertEquals(3, parser.remainingBytes());
+ }
+
+ @Test
+ public void nextSequenceWrongTagThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x02, 0x03, 0x01, 0x02, 0x03)).nextSequence());
+ }
+
+ @Test
+ public void nextSequenceOverflowThrows() {
+ // length claims 5 but only 1 byte remains
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x30, 0x05, 0x01)).nextSequence());
+ }
+
+ @Test
+ public void isNextSequence() {
+ assertTrue(new AsnParser(bytes(0x30, 0x00)).isNextSequence());
+ assertFalse(new AsnParser(bytes(0x02, 0x00)).isNextSequence());
+ }
+
+ @Test
+ public void nextOctetString() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x04, 0x02, 0xAA, 0xBB));
+ assertEquals(2, parser.nextOctetString());
+ assertEquals(2, parser.remainingBytes());
+ }
+
+ @Test
+ public void nextOctetStringWrongTagThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x02, 0x02, 0xAA, 0xBB)).nextOctetString());
+ }
+
+ @Test
+ public void isNextOctetString() {
+ assertTrue(new AsnParser(bytes(0x04, 0x00)).isNextOctetString());
+ assertFalse(new AsnParser(bytes(0x02, 0x00)).isNextOctetString());
+ }
+
+ @Test
+ public void nextBitString() throws BerDecodeException {
+ // tag 0x03, length 4, unused-bits byte 0x00, then 3 bytes content
+ var parser = new AsnParser(bytes(0x03, 0x04, 0x00, 0xDE, 0xAD, 0xBE));
+ assertEquals(3, parser.nextBitString());
+ assertEquals(3, parser.remainingBytes());
+ }
+
+ @Test
+ public void nextBitStringNonZeroUnusedThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x03, 0x02, 0x01, 0xDE)).nextBitString());
+ }
+
+ @Test
+ public void isNextBitString() {
+ assertTrue(new AsnParser(bytes(0x03, 0x00)).isNextBitString());
+ assertFalse(new AsnParser(bytes(0x02, 0x00)).isNextBitString());
+ }
+
+ @Test
+ public void nextInteger() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x02, 0x03, 0x01, 0x02, 0x03));
+ assertArrayEquals(new byte[]{0x01, 0x02, 0x03}, parser.nextInteger());
+ }
+
+ @Test
+ public void nextIntegerWrongTagThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x05, 0x03, 0x01, 0x02, 0x03)).nextInteger());
+ }
+
+ @Test
+ public void nextIntegerOverflowThrows() {
+ // length claims 5 but only 1 byte remains
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x02, 0x05, 0x01)).nextInteger());
+ }
+
+ @Test
+ public void isNextInteger() {
+ assertTrue(new AsnParser(bytes(0x02, 0x01, 0x00)).isNextInteger());
+ assertFalse(new AsnParser(bytes(0x05, 0x00)).isNextInteger());
+ }
+
+ @Test
+ public void nextOID() throws BerDecodeException {
+ var parser = new AsnParser(bytes(0x06, 0x03, 0x55, 0x04, 0x03));
+ assertArrayEquals(new byte[]{0x55, 0x04, 0x03}, parser.nextOID());
+ }
+
+ @Test
+ public void nextOIDWrongTagThrows() {
+ assertThrows(BerDecodeException.class, () -> new AsnParser(bytes(0x02, 0x03, 0x55, 0x04, 0x03)).nextOID());
+ }
+}
diff --git a/src/test/java/in/dragonbra/javasteam/util/crypto/RSACryptoTest.java b/src/test/java/in/dragonbra/javasteam/util/crypto/RSACryptoTest.java
index a226fd17..3aadc944 100644
--- a/src/test/java/in/dragonbra/javasteam/util/crypto/RSACryptoTest.java
+++ b/src/test/java/in/dragonbra/javasteam/util/crypto/RSACryptoTest.java
@@ -27,6 +27,7 @@ public void encrypt() {
var encrypted = rsaCrypto.encrypt(input);
Assertions.assertNotNull(encrypted);
+ Assertions.assertEquals(128, encrypted.length); // 1024-bit
}
@Test
@@ -34,4 +35,9 @@ public void cipherInstance() throws NoSuchAlgorithmException, NoSuchPaddingExcep
var cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", CryptoHelper.SEC_PROV);
Assertions.assertNotNull(cipher);
}
+
+ @Test
+ public void encryptNullKeyThrows() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new RSACrypto(null));
+ }
}
diff --git a/src/test/java/in/dragonbra/javasteam/util/event/ScheduledFunctionTest.java b/src/test/java/in/dragonbra/javasteam/util/event/ScheduledFunctionTest.java
new file mode 100644
index 00000000..31bdb484
--- /dev/null
+++ b/src/test/java/in/dragonbra/javasteam/util/event/ScheduledFunctionTest.java
@@ -0,0 +1,65 @@
+package in.dragonbra.javasteam.util.event;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class ScheduledFunctionTest {
+
+ @Test
+ void startInvokesFunction() throws InterruptedException {
+ var latch = new CountDownLatch(3);
+ var func = new ScheduledFunction(latch::countDown, 50L);
+
+ func.start();
+ boolean completed = latch.await(2, TimeUnit.SECONDS);
+ func.stop();
+
+ Assertions.assertTrue(completed, "Function was not invoked 3 times within timeout");
+ }
+
+ @Test
+ void stopPreventsInvocations() throws InterruptedException {
+ var count = new AtomicInteger();
+ var func = new ScheduledFunction(count::incrementAndGet, 50L);
+
+ func.start();
+ Thread.sleep(150);
+ func.stop();
+
+ int countAfterStop = count.get();
+ Thread.sleep(150);
+
+ Assertions.assertEquals(countAfterStop, count.get(), "Function was invoked after stop()");
+ }
+
+ @Test
+ void startIsIdempotent() throws InterruptedException {
+ var latch = new CountDownLatch(3);
+ var func = new ScheduledFunction(latch::countDown, 50L);
+
+ func.start();
+ func.start();
+ boolean completed = latch.await(2, TimeUnit.SECONDS);
+ func.stop();
+
+ Assertions.assertTrue(completed);
+ }
+
+ @Test
+ void stopWhenNotStartedDoesNotThrow() {
+ var func = new ScheduledFunction(() -> {}, 100L);
+ Assertions.assertDoesNotThrow(func::stop);
+ }
+
+ @Test
+ void delayGetterAndSetter() {
+ var func = new ScheduledFunction(() -> {}, 100L);
+ Assertions.assertEquals(100L, func.getDelay());
+ func.setDelay(200L);
+ Assertions.assertEquals(200L, func.getDelay());
+ }
+}
\ No newline at end of file