Skip to content

Commit

Permalink
Merge pull request #16 from matejdro/apps
Browse files Browse the repository at this point in the history
App install
  • Loading branch information
crc-32 authored Mar 20, 2021
2 parents 0e9eb76 + 39bbc54 commit 3d82510
Show file tree
Hide file tree
Showing 15 changed files with 716 additions and 3 deletions.
140 changes: 140 additions & 0 deletions src/commonMain/kotlin/io/rebble/libpebblecommon/disk/PbwBinHeader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package io.rebble.libpebblecommon.disk

import io.rebble.libpebblecommon.packets.blobdb.AppMetadata
import io.rebble.libpebblecommon.structmapper.*
import io.rebble.libpebblecommon.util.DataBuffer

/**
* Header of the
*/
class PbwBinHeader() : StructMappable() {
/**
* Major header version.
*/
val headerVersionMajor: SUByte = SUByte(m)

/**
* Minor header version.
*/
val headerVersionMinor: SUByte = SUByte(m)

/**
* Major sdk version.
*/
val sdkVersionMajor: SUByte = SUByte(m)

/**
* Minor sdk version.
*/
val sdkVersionMinor: SUByte = SUByte(m)

/**
* Major app version.
*/
val appVersionMajor: SUByte = SUByte(m)

/**
* Minor app version.
*/
val appVersionMinor: SUByte = SUByte(m)

/**
* Size of the app payload in bytes
*/
val appSize: SUShort = SUShort(m)

/**
* ??? (Presumably offset where app payload starts?)
*/
val appOffset: SUInt = SUInt(m)

/**
* CRC checksum of the app payload
*/
val crc: SUInt = SUInt(m)

/**
* Name of the app
*/
val appName: SFixedString = SFixedString(m, 32)

/**
* Name of the company that made the app
*/
val companyName: SFixedString = SFixedString(m, 32)

/**
* Resource ID of the primary icon.
*/
val icon: SUInt = SUInt(m)

/**
* ???
*/
val symbolTableAddress: SUInt = SUInt(m)

/**
* List of app install flags. Should be forwarded to the watch when inserting into BlobDB.
*/
val flags: SUInt = SUInt(m)

/**
* ???
*/
val numRelocationListEntries: SUInt = SUInt(m)

/**
* UUID of the app
*/
val uuid: SUUID = SUUID(m)

fun toBlobDbApp(): AppMetadata {
return AppMetadata().also {
it.uuid.set(uuid.get())
it.flags.set(flags.get())
it.icon.set(icon.get())
it.appVersionMajor.set(appVersionMajor.get())
it.appVersionMinor.set(appVersionMinor.get())
it.sdkVersionMajor.set(sdkVersionMajor.get())
it.sdkVersionMinor.set(sdkVersionMinor.get())
it.appName.set(appName.get())
}
}

companion object {
const val SIZE: Int = 8 + 2 + 2 + 2 + 2 + 4 + 4 + 32 + 32 + 4 + 4 + 4 + 4 + 16

/**
* Parse existing Pbw binary payload header. You should read [SIZE] bytes from the binary
* payload and pass it into this method.
*
* @throws IllegalArgumentException if header is not valid pebble app header
*/
fun parseFileHeader(data: UByteArray): PbwBinHeader {
if (data.size != SIZE) {
throw IllegalArgumentException(
"Read data from the file should be exactly $SIZE bytes"
)
}

val buffer = DataBuffer(data)

val sentinel = buffer.getBytes(8)
if (!sentinel.contentEquals(EXPECTED_SENTINEL)) {
throw IllegalArgumentException("Sentinel does not match")
}

return PbwBinHeader().also {
it.fromBytes(buffer)
}
}

/**
* First 8 bytes of the header, spelling the word "PBLAPP" in ASCII,
* followed by two zeros.
*/
private val EXPECTED_SENTINEL = ubyteArrayOf(
0x50u, 0x42u, 0x4Cu, 0x41u, 0x50u, 0x50u, 0x00u, 0x00u
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.rebble.libpebblecommon.metadata

enum class WatchHardwarePlatform(val protocolNumber: UByte, val watchType: WatchType) {
PEBBLE_ONE_EV_1(1u, WatchType.APLITE),
PEBBLE_ONE_EV_2(2u, WatchType.APLITE),
PEBBLE_ONE_EV_2_3(3u, WatchType.APLITE),
PEBBLE_ONE_EV_2_4(4u, WatchType.APLITE),
PEBBLE_ONE_POINT_FIVE(5u, WatchType.APLITE),
PEBBLE_ONE_POINT_ZERO(6u, WatchType.APLITE),
PEBBLE_SNOWY_EVT_2(7u, WatchType.BASALT),
PEBBLE_SNOWY_DVT(8u, WatchType.BASALT),
PEBBLE_BOBBY_SMILES(10u, WatchType.BASALT),
PEBBLE_ONE_BIGBOARD_2(254u, WatchType.APLITE),
PEBBLE_ONE_BIGBOARD(255u, WatchType.APLITE),
PEBBLE_SNOWY_BIGBOARD(253u, WatchType.BASALT),
PEBBLE_SNOWY_BIGBOARD_2(252u, WatchType.BASALT),
PEBBLE_SPALDING_EVT(9u, WatchType.CHALK),
PEBBLE_SPALDING_PVT(11u, WatchType.CHALK),
PEBBLE_SPALDING_BIGBOARD(251u, WatchType.CHALK),
PEBBLE_SILK_EVT(12u, WatchType.DIORITE),
PEBBLE_SILK(14u, WatchType.DIORITE),
PEBBLE_SILK_BIGBOARD(250u, WatchType.DIORITE),
PEBBLE_SILK_BIGBOARD_2_PLUS(248u, WatchType.DIORITE),
PEBBLE_ROBERT_EVT(13u, WatchType.EMERY),
PEBBLE_ROBERT_BIGBOARD(249u, WatchType.EMERY),
PEBBLE_ROBERT_BIGBOARD_2(247u, WatchType.EMERY);

companion object {
fun fromProtocolNumber(number: UByte): WatchHardwarePlatform? {
return values().firstOrNull { it.protocolNumber == number }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.rebble.libpebblecommon.metadata

enum class WatchType(val codename: String) {
APLITE("aplite"),
BASALT("basalt"),
CHALK("chalk"),
DIORITE("diorite"),
EMERY("emery")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.rebble.libpebblecommon.packets

import io.rebble.libpebblecommon.protocolhelpers.PacketRegistry
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint
import io.rebble.libpebblecommon.structmapper.SUByte
import io.rebble.libpebblecommon.structmapper.SUInt
import io.rebble.libpebblecommon.structmapper.SUUID

sealed class AppFetchIncomingPacket() : PebblePacket(ProtocolEndpoint.APP_FETCH) {
/**
* Request command. See [AppFetchRequestCommand].
*/
val command = SUByte(m)

}

sealed class AppFetchOutgoingPacket(command: AppFetchRequestCommand) :
PebblePacket(ProtocolEndpoint.APP_FETCH) {
/**
* Request command. See [AppFetchRequestCommand].
*/
val command = SUByte(m, command.value)

}


/**
* Packet sent from the watch when user opens an app that is not in the watch storage.
*/
class AppFetchRequest : AppFetchIncomingPacket() {

/**
* UUID of the app to request
*/
val uuid = SUUID(m)

/**
* ID of the app bank. Use in the [PutBytesAppInit] packet to identify this app install.
*/
val appId = SUInt(m, endianness = '<')
}

/**
* Packet sent from the watch when user opens an app that is not in the watch storage.
*/
class AppFetchResponse(
status: AppFetchResponseStatus
) : AppFetchOutgoingPacket(AppFetchRequestCommand.FETCH_APP) {
/**
* Response status
*/
val status = SUByte(m, status.value)

}

enum class AppFetchRequestCommand(val value: UByte) {
FETCH_APP(0x01u)
}

enum class AppFetchResponseStatus(val value: UByte) {
/**
* Sent right before starting to send PutBytes data
*/
START(0x01u),

/**
* Sent when phone PutBytes is already busy sending something else
*/
BUSY(0x02u),

/**
* Sent when UUID that watch sent is not in the locker
*/
INVALID_UUID(0x03u),

/**
* Sent when there is generic data sending error (such as failure to read the local pbw file)
*/
NO_DATA(0x01u),
}


fun appFetchIncomingPacketsRegister() {
PacketRegistry.register(
ProtocolEndpoint.APP_FETCH,
AppFetchRequestCommand.FETCH_APP.value
) { AppFetchRequest() }
}
Loading

0 comments on commit 3d82510

Please sign in to comment.