android: CRITICAL fix - commands were byte-reversed (ByteBuffer big-endian); official app sends little-endian, palindrome 66b masked it for 16 rounds
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
package com.mag160c.thermal.usb
|
package com.mag160c.thermal.usb
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vendor command/response protocol — VERBATIM from the official MAG-Cx app's
|
* Vendor command/response protocol — VERBATIM from the official MAG-Cx app's
|
||||||
* Java source (sdk/UsbCommunication.java + P2DCmd.java/D2PCmd.java, jadx).
|
* Java source (sdk/UsbCommunication.java + P2DCmd.java/D2PCmd.java, jadx).
|
||||||
@@ -33,17 +31,25 @@ object MagProtocol {
|
|||||||
const val RSP_SEND_CALI_FILE = 0x5BB5B55D
|
const val RSP_SEND_CALI_FILE = 0x5BB5B55D
|
||||||
const val RSP_SEND_CALI_INFO = 0x5BB5B55E
|
const val RSP_SEND_CALI_INFO = 0x5BB5B55E
|
||||||
|
|
||||||
fun cmd4(magic: Int): ByteArray {
|
/**
|
||||||
val b = ByteBuffer.allocate(4)
|
* 4-byte LITTLE-ENDIAN command — byte-identical to the official app's
|
||||||
b.putInt(magic)
|
* GlobalFunc.intToByteArray (a&255 first). CRITICAL: ByteBuffer.putInt
|
||||||
return b.array()
|
* defaults to BIG_ENDIAN and once reversed every non-palindrome magic
|
||||||
}
|
* (66c/66f/670/672/673/674) — the camera only understood the palindrome
|
||||||
|
* 0x6BB6B66B, which masked this for 16 rounds.
|
||||||
|
*/
|
||||||
|
fun cmd4(magic: Int): ByteArray = byteArrayOf(
|
||||||
|
(magic and 0xFF).toByte(),
|
||||||
|
((magic shr 8) and 0xFF).toByte(),
|
||||||
|
((magic shr 16) and 0xFF).toByte(),
|
||||||
|
((magic shr 24) and 0xFF).toByte(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 8-byte {u32 magic LE, u32 param LE} (official SetShutterState packet). */
|
||||||
fun cmd8(magic: Int, param: Int): ByteArray {
|
fun cmd8(magic: Int, param: Int): ByteArray {
|
||||||
val b = ByteBuffer.allocate(8)
|
val lo = cmd4(magic)
|
||||||
b.putInt(magic)
|
val hi = cmd4(param)
|
||||||
b.putInt(param)
|
return byteArrayOf(lo[0], lo[1], lo[2], lo[3], hi[0], hi[1], hi[2], hi[3])
|
||||||
return b.array()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Camera info block (0x5BB5B55B, 0x38 bytes): +0x00 pid, +0x08 serial,
|
/** Camera info block (0x5BB5B55B, 0x38 bytes): +0x00 pid, +0x08 serial,
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.mag160c.thermal.usb
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wire-format regression lock: commands MUST be little-endian, byte-identical
|
||||||
|
* to the official MAG-Cx app (GlobalFunc.intToByteArray: a&255 goes first).
|
||||||
|
* ByteBuffer.putInt defaults to BIG_ENDIAN and once reversed every
|
||||||
|
* non-palindrome command magic — the camera answered only 0x6BB6B66B (a
|
||||||
|
* palindrome) which hid the bug for 16 rounds of black-screen debugging.
|
||||||
|
*/
|
||||||
|
class MagProtocolTest {
|
||||||
|
@Test
|
||||||
|
fun cmd4IsLittleEndian() {
|
||||||
|
// official: intToByteArray(1807136364) == {6C, B6, B6, 6B}
|
||||||
|
val b = MagProtocol.cmd4(0x6BB6B66C)
|
||||||
|
assertEquals(0x6C.toByte(), b[0])
|
||||||
|
assertEquals(0xB6.toByte(), b[1])
|
||||||
|
assertEquals(0xB6.toByte(), b[2])
|
||||||
|
assertEquals(0x6B.toByte(), b[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cmd8MatchesOfficialSetShutterStatePacket() {
|
||||||
|
// official setShutterState_(on): {72, B6, B6, 6B, on, on>>8, on>>16, on>>24}
|
||||||
|
val b = MagProtocol.cmd8(0x6BB6B672, 1)
|
||||||
|
assertEquals(0x72.toByte(), b[0])
|
||||||
|
assertEquals(0xB6.toByte(), b[1])
|
||||||
|
assertEquals(0xB6.toByte(), b[2])
|
||||||
|
assertEquals(0x6B.toByte(), b[3])
|
||||||
|
assertEquals(0x01.toByte(), b[4])
|
||||||
|
assertEquals(0x00.toByte(), b[5])
|
||||||
|
assertEquals(0x00.toByte(), b[6])
|
||||||
|
assertEquals(0x00.toByte(), b[7])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startTransferImgPacketMatchesOfficial() {
|
||||||
|
val b = MagProtocol.cmd4(0x6BB6B673)
|
||||||
|
assertEquals(listOf<Byte>(0x73, 0xB6.toByte(), 0xB6.toByte(), 0x6B), b.toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user