Implemented all seven packet types.

This commit is contained in:
Joe Kearney 2025-02-09 17:42:21 -06:00
parent bc6da05747
commit c9704c4bf4
3 changed files with 240 additions and 71 deletions

View file

@ -1,7 +1,10 @@
package club.clubk.ktag.bletool package club.clubk.ktag.bletool
import androidx.compose.ui.graphics.Color
data class ByteCellData( data class ByteCellData(
val value: Byte, val value: Byte,
val isHeader: Boolean, val isHeader: Boolean,
val description: String val description: String,
val backgroundColor: Color? = null
) )

View file

@ -15,12 +15,15 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@ -49,12 +52,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
private const val TAG = "BLE Tool" private const val TAG = "BLE Tool"
@ -95,7 +94,7 @@ val advertisementPresets = listOf(
0x00.toByte(), 0x00.toByte(),
0x00.toByte(), // Event Data 0x00.toByte(), // Event Data
), ),
"KTag Instigate Game Packet" "KTag Event Packet"
), ),
AdvertisementPreset( AdvertisementPreset(
"03 Tag", "03 Tag",
@ -146,10 +145,64 @@ val advertisementPresets = listOf(
), ),
"KTag Status Packet" "KTag Status Packet"
), ),
AdvertisementPreset(
"06 Configuration",
byteArrayOf(
0x06.toByte(), // Packet Type: Configuration
0x00.toByte(), // Event Number
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(),
0xFF.toByte(), // Target Bluetooth Device Address
0x02.toByte(), // Subtype: Request Parameter Change
0x01.toByte(),
0x00.toByte(), // Key 1: Team ID
0x02.toByte(),
0x00.toByte(),
0x00.toByte(),
0x00.toByte(), // Value 1: 2
0xFF.toByte(),
0xFF.toByte(), // Key 2: Unused
0x00.toByte(),
0x00.toByte(),
0x00.toByte(),
0x00.toByte(), // Value 2: Unused
),
"KTag Configuration Packet"
),
AdvertisementPreset(
"07 Hello",
byteArrayOf(
0x07.toByte(), // Packet Type: Hello
0x00.toByte(), // Event Number
0x01.toByte(), // SystemK Major Version
0x00.toByte(), // SystemK Minor Version
0x02.toByte(), //
0x00.toByte(), // Device Type: Mobile App
0x02.toByte(), // Team ID
0x4B.toByte(), // 'K'
0x54.toByte(), // 'T'
0x61.toByte(), // 'a'
0x67.toByte(), // 'g'
0x20.toByte(), // ' '
0x42.toByte(), // 'B'
0x4C.toByte(), // 'L'
0x45.toByte(), // 'E'
0x20.toByte(), // ' '
0x54.toByte(), // 'T'
0x6F.toByte(), // 'o'
0x6F.toByte(), // 'o'
0x6C.toByte(), // 'l'
0x00.toByte(), // Device Name: "KTag BLE Tool"
),
"KTag Hello Packet"
),
AdvertisementPreset( AdvertisementPreset(
"Empty", "Empty",
ByteArray(23) { 0 }, ByteArray(23) { 0 },
"All zeros - clear current data" "All zeros (clear current data)"
) )
) )
@ -326,71 +379,13 @@ fun BleAdvertiserScreen() {
// Create list of byte cells with their properties // Create list of byte cells with their properties
val byteCells = remember(advertisementData) { val byteCells = remember(advertisementData) {
val packetType = advertisementData[8]
advertisementData.mapIndexed { index, byte -> advertisementData.mapIndexed { index, byte ->
ByteCellData( ByteCellData(
value = byte, isHeader = index < N_HEADER_BYTES, description = when (index) { value = byte,
0 -> "Length" // Always "Length" isHeader = index < N_HEADER_BYTES,
1 -> "Type" // Always "Type" description = PacketFieldUtils.getFieldDescription(packetType, index),
2 -> "Mfg ID" // Always "Mfg ID" backgroundColor = PacketFieldUtils.getFieldColor(packetType, index)
3 -> "Mfg ID" // Always "Mfg ID"
4 -> "'K'" // Always "'K'"
5 -> "'T'" // Always "'T'"
6 -> "'a'" // Always "'a'"
7 -> "'g'" // Always "'g'"
9 -> "Event Number"
else -> {
when (advertisementData[8]) {
0x01.toByte() -> when(index){
8 -> "Instigate Game" // Packet Type 01
else -> ""
}
0x02.toByte() -> when(index){
8 -> "Event" // Packet Type 02
else -> ""
}
0x03.toByte() -> when(index){
8 -> "Tag" // Packet Type 03
10 -> "Tx Pwr (dBm)"
11 -> "Protocol"
12 -> "Team ID"
13 -> "Player ID"
14 -> "Damage"
15 -> "Damage"
16 -> "Color"
17 -> "Color"
18 -> "Color"
19 -> "Color"
20 -> "Target Address"
21 -> "Target Address"
22 -> "Target Address"
23 -> "Target Address"
24 -> "Target Address"
25 -> "Target Address"
else -> ""
}
0x04.toByte() -> when(index){
8 -> "Console" // Packet Type 04
else -> ""
}
0x05.toByte() -> when(index){
8 -> "Status" // Packet Type 05
else -> ""
}
0x06.toByte() -> when(index){
8 -> "Configuration" // Packet Type 06
else -> ""
}
0x07.toByte() -> when(index){
8 -> "Hello" // Packet Type 07
else -> ""
}
else -> when(index){
8 -> "Packet Type"
else -> ""
}
}
}
}
) )
} }
} }
@ -556,6 +551,7 @@ fun ByteCell(
val backgroundColor = when { val backgroundColor = when {
data.isHeader -> MaterialTheme.colorScheme.secondaryContainer data.isHeader -> MaterialTheme.colorScheme.secondaryContainer
isSelected -> MaterialTheme.colorScheme.primary isSelected -> MaterialTheme.colorScheme.primary
data.backgroundColor != null -> data.backgroundColor
else -> MaterialTheme.colorScheme.primaryContainer else -> MaterialTheme.colorScheme.primaryContainer
} }

View file

@ -0,0 +1,170 @@
package club.clubk.ktag.bletool
import androidx.compose.ui.graphics.Color
object PacketFieldUtils {
fun getFieldDescription(packetType: Byte, index: Int): String {
return when (index) {
0 -> "Length" // Always "Length"
1 -> "Type" // Always "Type"
2 -> "Mfg ID" // Always "Mfg ID"
3 -> "Mfg ID" // Always "Mfg ID"
4 -> "'K'" // Always "'K'"
5 -> "'T'" // Always "'T'"
6 -> "'a'" // Always "'a'"
7 -> "'g'" // Always "'g'"
8 -> when (packetType) {
0x01.toByte() -> "Instigate Game"
0x02.toByte() -> "Event"
0x03.toByte() -> "Tag"
0x04.toByte() -> "Console"
0x05.toByte() -> "Status"
0x06.toByte() -> "Configuration"
0x07.toByte() -> "Hello"
else -> "Packet Type"
}
9 -> "Event Number"
else -> when (packetType) {
0x01.toByte() -> when (index) { // Instigate Game packet fields
in 10..13 -> "Game Length (ms)"
in 14..17 -> "Time till Countdown (ms)"
else -> ""
}
0x02.toByte() -> when (index) { // Event packet fields
in 10..15 -> "Target Address"
in 16..19 -> "Event ID"
in 20..23 -> "Event Data"
else -> ""
}
0x03.toByte() -> when (index) { // Tag packet fields
10 -> "Tx Pwr (dBm)"
11 -> "Protocol"
12 -> "Team ID"
13 -> "Player ID"
14, 15 -> "Damage"
in 16..19 -> "Color"
in 20..25 -> "Target Address"
else -> ""
}
0x04.toByte() -> when (index) { // Console packet fields
in 10..30 -> "Console String"
else -> ""
}
0x05.toByte() -> when (index) { // Status packet fields
10 -> "Tx Pwr (dBm)"
11 -> "Protocol"
12 -> "Team ID"
13 -> "Player ID"
14, 15 -> "Health"
16, 17 -> "Max Health"
in 18..21 -> "Primary Color"
in 22..25 -> "Secondary Color"
26 -> "SystemK State"
else -> ""
}
0x06.toByte() -> when (index) { // Configuration packet fields
in 10..15 -> "Target Address"
16 -> "Subtype"
17, 18 -> "Key 1"
in 19..22 -> "Value 1"
23, 24 -> "Key 2"
in 25..28 -> "Value 2"
else -> ""
}
0x07.toByte() -> when (index) { // Hello packet fields
10 -> "SystemK Major Version"
11 -> "SystemK Minor Version"
12, 13 -> "Device Type"
14 -> "Team ID"
in 15..30 -> "Device Name"
else -> ""
}
else -> ""
}
}
}
fun getFieldColor(packetType: Byte, index: Int): Color? {
// Header bytes always return null to use default header color
if (index < 8) return null
// Packet type byte is always a distinct color
if (index == 8) return Color(0xFFE6F3FF) // Light blue
// Event number is always the same color across all packet types
if (index == 9) return Color(0xFFE6FFE6) // Light green
return when (packetType) {
0x01.toByte() -> when (index) { // Instigate Game packet
in 10..13 -> Color(0xFFFFF3E6) // Game Length - Light orange
in 14..17 -> Color(0xFFE6FFF3) // Time until Countdown - Light cyan
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
0x02.toByte() -> when (index) { // Event packet
in 10..15 -> Color(0xFFE6F3FF) // Target Address - Light blue
in 16..19 -> Color(0xFFF3E6FF) // Event ID - Light purple
in 20..23 -> Color(0xFFE6FFE6) // Event Data - Light green
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
0x03.toByte() -> when (index) { // Tag packet
10 -> Color(0xFFFFF3E6) // Tx Power - Light orange
11 -> Color(0xFFE6FFF3) // Protocol - Light cyan
12 -> Color(0xFFFFE6F3) // Team ID - Light pink
13 -> Color(0xFFF3E6FF) // Player ID - Light purple
in 14..15 -> Color(0xFFFFE6E6) // Damage - Light red
in 16..19 -> Color(0xFFE6FFE6) // Color - Light green
in 20..25 -> Color(0xFFE6F3FF) // Target Address - Light blue
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
0x04.toByte() -> when (index) { // Console packet
in 10..30 -> Color(0xFFFFF3E6) // Console String - Light orange
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
0x05.toByte() -> when (index) { // Status packet
10 -> Color(0xFFFFF3E6) // Tx Power - Light orange
11 -> Color(0xFFE6FFF3) // Protocol - Light cyan
12 -> Color(0xFFFFE6F3) // Team ID - Light pink
13 -> Color(0xFFF3E6FF) // Player ID - Light purple
14, 15 -> Color(0xFFFFE6E6) // Health - Light red
16, 17 -> Color(0xFFE6FFE6) // Maximum Health - Light green
in 18..21 -> Color(0xFFE6F3FF) // Primary Color - Light blue
in 22..25 -> Color(0xFFF3E6FF) // Secondary Color - Light purple
26 -> Color(0xFFFFF3E6) // SystemK State - Light orange
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
0x06.toByte() -> when (index) { // Configuration packet
in 10..15 -> Color(0xFFE6F3FF) // Target Address - Light blue
16 -> Color(0xFFFFF3E6) // Subtype - Light orange
17, 18 -> Color(0xFFF3E6FF) // Key 1 - Light purple
in 19..22 -> Color(0xFFE6FFE6) // Value 1 - Light green
23, 24 -> Color(0xFFF3E6FF) // Key 2 - Light purple
in 25..28 -> Color(0xFFE6FFE6) // Value 2 - Light green
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
0x07.toByte() -> when (index) { // Hello packet
10 -> Color(0xFFFFF3E6) // SystemK Major Version - Light orange
11 -> Color(0xFFE6FFF3) // SystemK Minor Version - Light cyan
12, 13 -> Color(0xFFE6F3FF) // Device Type - Light blue
14 -> Color(0xFFFFE6F3) // Team ID - Light pink
in 15..30 -> Color(0xFFF3E6FF) // Device Name - Light purple
else -> Color(0xFFF5F5F5) // Light gray for undefined fields
}
else -> Color(0xFFF5F5F5) // Light gray for unknown packet types
}
}
}