[O] Reduce code

This commit is contained in:
Azalea
2024-02-22 21:22:08 -05:00
parent 8f250e755e
commit a9a947203d
10 changed files with 172 additions and 224 deletions

View File

@@ -1 +0,0 @@
package icu.samnyan.aqua.sega.aimedb

View File

@@ -1,6 +1,5 @@
package icu.samnyan.aqua.sega.aimedb
import icu.samnyan.aqua.sega.aimedb.util.Encryption
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
@@ -25,24 +24,23 @@ class AimeDbDecoder : ByteToMessageDecoder() {
* @param input ByteBuf in
* @param out List<Object>
*/
@Throws(Exception::class)
override fun decode(ctx: ChannelHandlerContext, input: ByteBuf, out: MutableList<Any>) {
if (input.readableBytes() < 16) return
if (length == 0) {
length = getLength(input)
logger.info("Aime Server Request Length: $length")
logger.info("AimeDB Request Length: $length")
}
if (input.readableBytes() < length) return
// Create a byte array to store the encrypted data
val result = Encryption.decrypt(input.readBytes(length))
val result = AimeDbEncryption.decrypt(input.readBytes(length))
val resultMap: MutableMap<String, Any> = HashMap()
resultMap["type"] = result.getShortLE(0x04).toInt()
resultMap["data"] = result
logger.debug("Aime Server Request Type: " + resultMap["type"])
logger.debug("AimeDB Request Type: " + resultMap["type"])
out.add(resultMap)
}
@@ -55,12 +53,11 @@ class AimeDbDecoder : ByteToMessageDecoder() {
*/
private fun getLength(input: ByteBuf): Int {
val currentPos = input.readerIndex()
val result = Encryption.decrypt(input)
val result = AimeDbEncryption.decrypt(input)
// Check the header
if (result.getByte(0).toInt() != 0x3e) {
throw InvalidRequestException()
}
val header = result.getByte(0).toInt()
assert(header == 0x3e) { "AimeDB: Invalid header $header" }
// Read the length from offset 6
return result.getShortLE(currentPos + 6).toInt()

View File

@@ -1,6 +1,5 @@
package icu.samnyan.aqua.sega.aimedb
import icu.samnyan.aqua.sega.aimedb.util.Encryption
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.MessageToByteEncoder
@@ -13,16 +12,14 @@ import org.springframework.stereotype.Component
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
class AimeDbEncoder : MessageToByteEncoder<Any>() {
override fun encode(ctx: ChannelHandlerContext, msg: Any, out: ByteBuf) {
if (msg is ByteBuf) {
msg.writerIndex(0)
msg.writeShortLE(0xa13e)
msg.writeShortLE(0x3087)
msg.setShortLE(0x0006, msg.capacity())
val encryptedResp = Encryption.encrypt(msg)
ctx.writeAndFlush(encryptedResp)
}
class AimeDbEncoder : MessageToByteEncoder<ByteBuf>() {
override fun encode(ctx: ChannelHandlerContext, msg: ByteBuf, out: ByteBuf) {
msg.writerIndex(0)
msg.writeShortLE(0xa13e)
msg.writeShortLE(0x3087)
msg.setShortLE(0x0006, msg.capacity())
val encryptedResp = AimeDbEncryption.encrypt(msg)
ctx.writeAndFlush(encryptedResp)
}
}

View File

@@ -0,0 +1,20 @@
package icu.samnyan.aqua.sega.aimedb
import icu.samnyan.aqua.sega.util.ByteBufUtil
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled.copiedBuffer
import java.nio.charset.StandardCharsets
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
/**
* @author samnyan (privateamusement@protonmail.com)
*/
object AimeDbEncryption {
val KEY = SecretKeySpec("Copyright(C)SEGA".toByteArray(StandardCharsets.UTF_8), "AES")
val cipher = Cipher.getInstance("AES/ECB/NoPadding").apply { init(Cipher.ENCRYPT_MODE, KEY) }
fun decrypt(src: ByteBuf) = copiedBuffer(cipher.doFinal(ByteBufUtil.toBytes(src)))
fun encrypt(src: ByteBuf) = copiedBuffer(cipher.doFinal(ByteBufUtil.toAllBytes(src)))
}

View File

@@ -27,22 +27,22 @@ class AimeDbRequestHandler(
data class AimeBaseInfo(val gameId: String, val keychipId: String)
fun getBaseInfo(input: ByteBuf) = AimeBaseInfo(
gameId = input.toString(0x000a, 0x000e - 0x000a, StandardCharsets.US_ASCII),
keychipId = input.toString(0x0014, 0x001f - 0x0014, StandardCharsets.US_ASCII)
gameId = input.toString(0x0a, 0x0e - 0x0a, StandardCharsets.US_ASCII),
keychipId = input.toString(0x14, 0x1f - 0x14, StandardCharsets.US_ASCII)
)
final val handlers = mapOf<Int, (ByteBuf) -> ByteBuf?>(
0x0001 to ::doFelicaLookup,
0x0004 to ::doLookup,
0x0005 to ::doRegister,
0x0009 to ::doLog,
0x000b to ::doCampaign,
0x000d to ::doTouch,
0x000f to ::doLookup2,
0x0011 to ::doFelicaLookup2,
0x0013 to ::doUnknown19,
0x0064 to ::doHello,
0x0066 to ::doGoodbye
0x01 to ::doFelicaLookup,
0x04 to ::doLookup,
0x05 to ::doRegister,
0x09 to ::doLog,
0x0b to ::doCampaign,
0x0d to ::doTouch,
0x0f to ::doLookup2,
0x11 to ::doFelicaLookup2,
0x13 to ::doUnknown19,
0x64 to ::doHello,
0x66 to ::doGoodbye
)
/**
@@ -82,18 +82,18 @@ class AimeDbRequestHandler(
* Felica Lookup v1: Return the Felica IDm as-is
*/
fun doFelicaLookup(msg: ByteBuf): ByteBuf {
val idm = msg.slice(0x0020, 0x0028 - 0x0020).getLong(0)
val pmm = msg.slice(0x0028, 0x0030 - 0x0028).getLong(0)
val idm = msg.slice(0x20, 0x28 - 0x20).getLong(0)
val pmm = msg.slice(0x28, 0x30 - 0x28).getLong(0)
logger.info("> Felica Lookup v1 ($idm, $pmm)")
// Get the decimal represent of the hex value, same from minime
val accessCode = idm.toString().replace("-", "").padStart(20, '0')
logger.info("> Response: $accessCode")
return Unpooled.copiedBuffer(ByteArray(0x0030)).apply {
setShortLE(0x0004, 0x0003)
setShortLE(0x0008, 1)
setBytes(0x0024, ByteBufUtil.decodeHexDump(accessCode))
return Unpooled.copiedBuffer(ByteArray(0x30)).apply {
setShortLE(0x04, 0x03)
setShortLE(0x08, 1)
setBytes(0x24, ByteBufUtil.decodeHexDump(accessCode))
}
}
@@ -101,8 +101,8 @@ class AimeDbRequestHandler(
* Felica Lookup v2: Look up the card in the card repository, return the External ID
*/
fun doFelicaLookup2(msg: ByteBuf): ByteBuf {
val idm = msg.slice(0x0020, 0x0028 - 0x0020).getLong(0)
val pmm = msg.slice(0x0028, 0x0030 - 0x0028).getLong(0)
val idm = msg.slice(0x20, 0x28 - 0x20).getLong(0)
val pmm = msg.slice(0x28, 0x30 - 0x28).getLong(0)
logger.info("> Felica Lookup v2 ($idm, $pmm)")
// Get the decimal represent of the hex value, same from minime
@@ -111,13 +111,13 @@ class AimeDbRequestHandler(
logger.info("Response: $accessCode, $aimeId")
return Unpooled.copiedBuffer(ByteArray(0x0140)).apply {
setShortLE(0x0004, 0x0012)
setShortLE(0x0008, 1)
setLongLE(0x0020, aimeId)
setIntLE(0x0024, -0x1) // 0xFFFFFFFF
setIntLE(0x0028, -0x1) // 0xFFFFFFFF
setBytes(0x002c, ByteBufUtil.decodeHexDump(accessCode))
setShortLE(0x0037, 0x0001)
setShortLE(0x04, 0x12)
setShortLE(0x08, 1)
setLongLE(0x20, aimeId)
setIntLE(0x24, -0x1) // 0xFFFFFFFF
setIntLE(0x28, -0x1) // 0xFFFFFFFF
setBytes(0x2c, ByteBufUtil.decodeHexDump(accessCode))
setShortLE(0x37, 0x01)
}
}
@@ -125,32 +125,32 @@ class AimeDbRequestHandler(
* Lookup v1: Find the LUID in the database and return the External ID
*/
fun doLookup(msg: ByteBuf): ByteBuf {
val luid = ByteBufUtil.hexDump(msg.slice(0x0020, 0x002a - 0x0020))
val luid = ByteBufUtil.hexDump(msg.slice(0x20, 0x2a - 0x20))
logger.info("> Lookup v1 ($luid)")
val aimeId = cardService.getCardByAccessCode(luid).getOrNull()?.extId ?: -1
logger.info("> Response: $aimeId")
return Unpooled.copiedBuffer(ByteArray(0x0130)).apply {
setShortLE(0x0004, 0x0006)
setShortLE(0x0008, 1)
setLongLE(0x0020, aimeId)
setByte(0x0024, 0)
setShortLE(0x04, 0x06)
setShortLE(0x08, 1)
setLongLE(0x20, aimeId)
setByte(0x24, 0)
}
}
fun doLookup2(msg: ByteBuf): ByteBuf {
val luid = ByteBufUtil.hexDump(msg.slice(0x0020, 0x002a - 0x0020))
val luid = ByteBufUtil.hexDump(msg.slice(0x20, 0x2a - 0x20))
logger.info("> Lookup v2 ($luid)")
val aimeId = cardService.getCardByAccessCode(luid).getOrNull()?.extId ?: -1
logger.info("Response: $aimeId")
return Unpooled.copiedBuffer(ByteArray(0x0130)).apply {
setShortLE(0x0004, 0x0010)
setShortLE(0x0008, 1)
setLongLE(0x0020, aimeId)
setByte(0x0024, 0)
setShortLE(0x04, 0x10)
setShortLE(0x08, 1)
setLongLE(0x20, aimeId)
setByte(0x24, 0)
}
}
@@ -158,7 +158,7 @@ class AimeDbRequestHandler(
* Register: Register a new card by access code
*/
fun doRegister(msg: ByteBuf): ByteBuf {
val luid = ByteBufUtil.hexDump(msg.slice(0x0020, 0x002a - 0x0020))
val luid = ByteBufUtil.hexDump(msg.slice(0x20, 0x2a - 0x20))
logger.info("> Register ($luid)")
var status = 0
@@ -173,58 +173,58 @@ class AimeDbRequestHandler(
else logger.warn("> Duplicated Aime Card Register detected, access code: $luid")
logger.info("> Response: $status, $aimeId")
return Unpooled.copiedBuffer(ByteArray(0x0030)).apply {
setShortLE(0x0004, 0x0006)
setShortLE(0x0008, status)
setLongLE(0x0020, aimeId)
return Unpooled.copiedBuffer(ByteArray(0x30)).apply {
setShortLE(0x04, 0x06)
setShortLE(0x08, status)
setLongLE(0x20, aimeId)
}
}
/**
* Log: Just log the request and return a status 1
*/
fun doLog(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x0020)).apply {
setShortLE(0x0004, 0x000a)
setShortLE(0x0008, 1)
fun doLog(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x20)).apply {
setShortLE(0x04, 0x0a)
setShortLE(0x08, 1)
}
/**
* Campaign: Just return a status 1
*/
fun doCampaign(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x0200)).apply {
setShortLE(0x0004, 0x000c)
setShortLE(0x0008, 1)
setShortLE(0x04, 0x0c)
setShortLE(0x08, 1)
}
/**
* Touch: Just return a status 1
*/
fun doTouch(msg: ByteBuf): ByteBuf {
val aimeId = msg.getUnsignedIntLE(0x0020)
val aimeId = msg.getUnsignedIntLE(0x20)
logger.info("> Touch ($aimeId)")
return Unpooled.copiedBuffer(ByteArray(0x0050)).apply {
setShortLE(0x0004, 0x000e)
setShortLE(0x0008, 1)
setShortLE(0x0020, 0x006f)
setShortLE(0x0024, 0x0001)
return Unpooled.copiedBuffer(ByteArray(0x50)).apply {
setShortLE(0x04, 0x0e)
setShortLE(0x08, 1)
setShortLE(0x20, 0x6f)
setShortLE(0x24, 0x01)
}
}
/**
* We don't know what this is, just return a status 1
*/
fun doUnknown19(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x0040)).apply {
setShortLE(0x0004, 0x0014)
setShortLE(0x0008, 1)
fun doUnknown19(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x40)).apply {
setShortLE(0x04, 0x14)
setShortLE(0x08, 1)
}
/**
* Ping: Just return a status 1
*/
fun doHello(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x0020)).apply {
setShortLE(0x0004, 0x0065)
setShortLE(0x0008, 1)
fun doHello(msg: ByteBuf) = Unpooled.copiedBuffer(ByteArray(0x20)).apply {
setShortLE(0x04, 0x65)
setShortLE(0x08, 1)
}
fun doGoodbye(msg: ByteBuf) = null

View File

@@ -1,74 +0,0 @@
package icu.samnyan.aqua.sega.aimedb;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Component
public class AimeDbServer {
private static final Logger logger = LoggerFactory.getLogger(AimeDbServer.class);
private final AimeDbServerInitializer aimeDbServerInitializer;
private final String address;
private final int port;
private final boolean enableAimeDb;
public AimeDbServer(AimeDbServerInitializer aimeDbServerInitializer,
@Value("${aimedb.server.address}") String address,
@Value("${aimedb.server.port}") int port,
@Value("${aimedb.server.enable}") boolean enableAimeDb) {
this.aimeDbServerInitializer = aimeDbServerInitializer;
this.address = address;
this.port = port;
this.enableAimeDb = enableAimeDb;
}
public void start() throws Exception {
if (!enableAimeDb) {
logger.info("Aime DB is disabled.");
return;
}
ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
bootstrap.group(boss, work)
.handler(new LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel.class)
.childHandler(aimeDbServerInitializer)
.option(ChannelOption.SO_BACKLOG, 128);
InetSocketAddress socket;
if(StringUtils.isNotBlank(this.address)) {
try {
socket = new InetSocketAddress(InetAddress.getByName(this.address), this.port);
} catch (UnknownHostException e) {
logger.error("UnknownHostException, please check you have set a correct aimedb.server.address.");
socket = new InetSocketAddress(this.port);
}
} else {
socket = new InetSocketAddress(this.port);
}
ChannelFuture f = bootstrap.bind(socket).sync();
logger.info("Aime DB start up on " + socket);
f.channel().closeFuture();
}
}

View File

@@ -0,0 +1,78 @@
package icu.samnyan.aqua.sega.aimedb
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.handler.logging.LogLevel
import io.netty.handler.logging.LoggingHandler
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.UnknownHostException
@Configuration
@ConfigurationProperties(prefix = "aimedb.server")
class AimeDbProps {
var enable = true
var address = "0.0.0.0"
var port = 22345
}
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Component
class AimeDbServer(
val initializer: AimeDbServerInitializer,
val props: AimeDbProps
) {
val logger: Logger = LoggerFactory.getLogger(AimeDbServer::class.java)
fun start() {
if (!props.enable) return logger.info("Aime DB is disabled.")
val bootstrap = ServerBootstrap()
.group(NioEventLoopGroup(), NioEventLoopGroup())
.handler(LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel::class.java)
.childHandler(initializer)
.option(ChannelOption.SO_BACKLOG, 128)
if (props.address.isBlank()) props.address = "0.0.0.0"
try {
val socket = InetSocketAddress(InetAddress.getByName(props.address), props.port)
val f = bootstrap.bind(socket).sync()
logger.info("Aime DB start up on $socket")
f.channel().closeFuture()
}
catch (e: UnknownHostException) {
logger.error("UnknownHostException, please check you have set a correct aimedb.server.address.")
throw e
}
}
}
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
class AimeDbServerInitializer(
val aimeDbRequestHandler: AimeDbRequestHandler
) : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
ch.pipeline().apply {
addLast("encoder", AimeDbEncoder())
addLast("decoder", AimeDbDecoder())
addLast("handler", aimeDbRequestHandler)
}
}
}

View File

@@ -1,24 +0,0 @@
package icu.samnyan.aqua.sega.aimedb
import io.netty.channel.ChannelInitializer
import io.netty.channel.socket.SocketChannel
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
class AimeDbServerInitializer(
val aimeDbRequestHandler: AimeDbRequestHandler
) : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
ch.pipeline().apply {
addLast("encoder", AimeDbEncoder())
addLast("decoder", AimeDbDecoder())
addLast("handler", aimeDbRequestHandler)
}
}
}

View File

@@ -1,7 +0,0 @@
package icu.samnyan.aqua.sega.aimedb;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class InvalidRequestException extends Exception {
}

View File

@@ -1,38 +0,0 @@
package icu.samnyan.aqua.sega.aimedb.util;
import icu.samnyan.aqua.sega.util.ByteBufUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public class Encryption {
private static final SecretKeySpec KEY = new SecretKeySpec("Copyright(C)SEGA".getBytes(StandardCharsets.UTF_8), "AES");
public static ByteBuf decrypt(ByteBuf src) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, KEY);
return Unpooled.copiedBuffer(cipher.doFinal(ByteBufUtil.toBytes(src)));
}
public static ByteBuf encrypt(ByteBuf src) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, KEY);
byte[] bytes = cipher.doFinal(ByteBufUtil.toAllBytes(src));
return Unpooled.copiedBuffer(bytes);
}
}