diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt index 270306d1..d034d838 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt @@ -381,37 +381,48 @@ class CompanionServer : Feature("CompanionServer") { } } } - // Strategy E: europe-west1 format — field3→3 → snap descriptors at field 1 + // Strategy E: europe-west1 repeated-f3 format + // outer: repeated field3 (story group) → field3 (story list) → repeated field3 (snap items) if (parsed == 0) { var countE = 0 - ProtoReader(buffer).followPath(3)?.followPath(3)?.eachBuffer(1) { - val userId = getString(4, 1) ?: getString(8, 1) ?: getString(1) ?: "public_unknown" - val url = getString(2, 2)?.substringBefore("?") ?: return@eachBuffer - val postedAt = getVarInt(3) ?: return@eachBuffer - val createdAt = getVarInt(27) ?: -1L - val keyB64 = getString(2, 5) ?: return@eachBuffer - val ivB64 = getString(2, 4) ?: return@eachBuffer - if (storeStory(userId, url, postedAt, createdAt, keyB64, ivB64)) countE++ + ProtoReader(buffer).eachBuffer(3) { // story group + followPath(3) { // story list + eachBuffer(3) { // snap item + val url = getString(2, 2)?.substringBefore("?") ?: return@eachBuffer + val postedAt = getVarInt(3) ?: return@eachBuffer + val createdAt = getVarInt(27) ?: -1L + val keyB64 = getString(2, 5) ?: return@eachBuffer + val ivB64 = getString(2, 4) ?: return@eachBuffer + val userId = getString(4, 1) ?: getString(8, 1) ?: "public_unknown" + if (storeStory(userId, url, postedAt, createdAt, keyB64, ivB64)) countE++ + } + } } if (countE > 0) parsed = countE - pushStoryDebug("probe", mapOf("endpoint" to endpoint, "strategy" to "E_f3f3f1", "found" to countE)) + pushStoryDebug("probe", mapOf("endpoint" to endpoint, "strategy" to "E_f3→f3→f3", "found" to countE)) } - // Strategy F: europe-west1 format — field3→3 → snap items at field 3 + // Strategy F: snap item is one level deeper — f3→f3→f3→f1 if (parsed == 0) { var countF = 0 - ProtoReader(buffer).followPath(3)?.followPath(3)?.eachBuffer(3) { - val userId = getString(4, 1) ?: getString(8, 1) ?: "public_unknown" - val url = getString(2, 2)?.substringBefore("?") ?: return@eachBuffer - val postedAt = getVarInt(3) ?: return@eachBuffer - val createdAt = getVarInt(27) ?: -1L - val keyB64 = getString(2, 5) ?: return@eachBuffer - val ivB64 = getString(2, 4) ?: return@eachBuffer - if (storeStory(userId, url, postedAt, createdAt, keyB64, ivB64)) countF++ + ProtoReader(buffer).eachBuffer(3) { + followPath(3) { + eachBuffer(3) { + eachBuffer(1) { + val url = getString(2, 2)?.substringBefore("?") ?: return@eachBuffer + val postedAt = getVarInt(3) ?: return@eachBuffer + val createdAt = getVarInt(27) ?: -1L + val keyB64 = getString(2, 5) ?: return@eachBuffer + val ivB64 = getString(2, 4) ?: return@eachBuffer + val userId = getString(4, 1) ?: getString(8, 1) ?: "public_unknown" + if (storeStory(userId, url, postedAt, createdAt, keyB64, ivB64)) countF++ + } + } + } } if (countF > 0) parsed = countF - pushStoryDebug("probe", mapOf("endpoint" to endpoint, "strategy" to "F_f3f3f3", "found" to countF)) + pushStoryDebug("probe", mapOf("endpoint" to endpoint, "strategy" to "F_f3→f3→f3→f1", "found" to countF)) } - // If still 0, dump hex + first snap descriptor bytes for structure analysis + // If still 0, dump the first snap item's raw bytes to see its actual field layout if (parsed == 0 && buffer.size > 1000) { val hexPreview = buffer.take(128).joinToString(" ") { "%02x".format(it) } val debugMap = mutableMapOf( @@ -420,14 +431,15 @@ class CompanionServer : Feature("CompanionServer") { "bytes" to buffer.size ) runCatching { - ProtoReader(buffer).followPath(3, 3)?.let { storyList -> - storyList.getByteArray(1)?.let { bytes -> - debugMap["f3f3f1_size"] = bytes.size - debugMap["f3f3f1_hex"] = bytes.take(256).joinToString(" ") { "%02x".format(it) } - } - storyList.getByteArray(3)?.let { bytes -> - debugMap["f3f3f3_size"] = bytes.size - debugMap["f3f3f3_hex"] = bytes.take(256).joinToString(" ") { "%02x".format(it) } + // Drill into first group → story list → first snap item and dump its bytes + ProtoReader(buffer).getByteArray(3)?.let { groupBytes -> + ProtoReader(groupBytes).getByteArray(3)?.let { listBytes -> + debugMap["story_list_size"] = listBytes.size + debugMap["story_list_hex"] = listBytes.take(256).joinToString(" ") { "%02x".format(it) } + ProtoReader(listBytes).getByteArray(3)?.let { snapBytes -> + debugMap["snap_item_size"] = snapBytes.size + debugMap["snap_item_hex"] = snapBytes.take(256).joinToString(" ") { "%02x".format(it) } + } } } } @@ -1072,7 +1084,7 @@ function addStoryDebugRow(e){ else if(t==='null_buffer')extra=' endpoint='+e.endpoint+' (null buffer!)'; else if(t==='parse_error')extra=' endpoint='+e.endpoint+' ERR: '+e.error; else if(t==='probe')extra=' ['+e.endpoint+'] strategy='+e.strategy+' found='+e.found; - else if(t==='no_match')extra=' ['+e.endpoint+'] ALL STRATEGIES FAILED hex='+e.hex_preview; + else if(t==='no_match'){extra=' ['+e.endpoint+'] bytes='+e.bytes+' hex='+e.hex_preview;if(e.snap_item_hex)extra+=' | snap_item('+e.snap_item_size+'B)='+e.snap_item_hex;} el.textContent='['+ts(e.ts||'0')+'] '+t.toUpperCase()+extra; q('sd-log').appendChild(el); }// ---- Shared ----