From af734b78148f192945cd5f4e1be801bf46afd2be Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Mon, 10 Mar 2025 01:46:33 -0400 Subject: [PATCH] [+] Send AllNet and obtain poweron url --- .../aqua/sega/chusan/ChusanDataBroker.kt | 53 +++++++++++++++++++ .../aqua/sega/util/AllNetBillingDecoder.kt | 12 +++++ 2 files changed, 65 insertions(+) create mode 100644 src/main/java/icu/samnyan/aqua/sega/chusan/ChusanDataBroker.kt diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanDataBroker.kt b/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanDataBroker.kt new file mode 100644 index 00000000..61dee2aa --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/ChusanDataBroker.kt @@ -0,0 +1,53 @@ +package icu.samnyan.aqua.sega.chusan + +import ext.header +import ext.post +import ext.request +import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external.Chu3DataExport +import icu.samnyan.aqua.sega.util.AllNetBillingDecoder + + +val keychipPattern = Regex("([A-Z\\d]{4}-[A-Z\\d]{11}|[A-Z\\d]{11})") + +class AllNetHost(val dns: String, val keychip: String, val game: String, val version: String, val card: String) { + init { + // Check if keychip is valid + // TODO : Use a more appropriate exception + if (!keychipPattern.matches(keychip)) throw Exception("Invalid keychip") + } + + val requestKeychip by lazy { + // A123-45678901337 -> A1234567890 + if (keychip.length == 11) keychip + else keychip.substring(0, 4) + keychip.substring(5, 11) + } + + // Send AllNet PowerOn request to obtain game URL + val gameUrl by lazy { + "$dns/sys/servlet/PowerOn".request() + .header("Content-Type" to "application/x-www-form-urlencoded") + .post(AllNetBillingDecoder.encodeAllNet(mapOf( + "game_id" to game, + "ver" to version, + "serial" to requestKeychip, + "ip" to "127.0.0.1", "firm_ver" to "60001", "boot_ver" to "0000", + "encode" to "UTF-8", "format_ver" to "3", "hops" to "1", "token" to "2864179931" + ))) + .body() + .split("&") + .map { it.split("=") } + .filter { it.size == 2 } + .associate { it[0] to it[1] }["uri"] + ?: throw Exception("PowerOn Failed: No game URL returned") + } +} +class ChusanDataBroker { + + fun pull(host: AllNetHost): Chu3DataExport { + // Send AllNet PowerOn request to obtain game URL + + return Chu3DataExport() + } + + +} diff --git a/src/main/java/icu/samnyan/aqua/sega/util/AllNetBillingDecoder.kt b/src/main/java/icu/samnyan/aqua/sega/util/AllNetBillingDecoder.kt index 765dcf9f..25297e32 100644 --- a/src/main/java/icu/samnyan/aqua/sega/util/AllNetBillingDecoder.kt +++ b/src/main/java/icu/samnyan/aqua/sega/util/AllNetBillingDecoder.kt @@ -21,8 +21,20 @@ object AllNetBillingDecoder { } } + fun encode(src: Map, base64: Boolean): ByteArray { + // Join the key-value pairs with '&' symbol + val output = src.map { "${it.key}=${it.value}" }.joinToString("&") + + // Compress the joined string + val bytes = ZLib.compress(output.toByteArray(UTF_8)) + + // Encode the compressed byte array to Base64 MIME encoding + return if (!base64) bytes else Base64.getMimeEncoder().encode(bytes) + } + @JvmStatic fun decodeAllNet(src: ByteArray) = decode(src, base64 = true, nowrap = false) + fun encodeAllNet(src: Map) = encode(src, base64 = true) @JvmStatic fun decodeBilling(src: ByteArray) = decode(src, base64 = false, nowrap = true)