From b7a928e23dee32e1a42b14d667575e6d420fe399 Mon Sep 17 00:00:00 2001 From: ZXCLI Date: Fri, 11 Sep 2026 00:15:39 +0800 Subject: [PATCH] android: vendor palette extraction tooling (+exact tables if found) --- analysis/sdk_re/android_app/bin/libcxsdk.so | 3 + .../android_app/palette_candidates.json | 22 + .../sdk_re/android_app/palette_candidates.png | 3 + .../palette_extraction_findings.md | 94 ++++ .../android_app/palette_match_report.txt | 36 ++ analysis/tools/PalExport2.java | 244 ++++++++++ analysis/tools/PalIdentify.java | 283 ++++++++++++ analysis/tools/extract_palettes.py | 170 +++++++ .../com/mag160c/thermal/core/Palettes.kt | 46 +- .../mag160c/thermal/core/VendorPalettes.kt | 425 ++++++++++++++++++ .../com/mag160c/thermal/core/PalettesTest.kt | 112 +++++ build-artifacts/mag160c-app-debug.apk | 4 +- docs/android_app/session_state.md | 12 +- 13 files changed, 1437 insertions(+), 17 deletions(-) create mode 100644 analysis/sdk_re/android_app/bin/libcxsdk.so create mode 100644 analysis/sdk_re/android_app/palette_candidates.json create mode 100644 analysis/sdk_re/android_app/palette_candidates.png create mode 100644 analysis/sdk_re/android_app/palette_extraction_findings.md create mode 100644 analysis/sdk_re/android_app/palette_match_report.txt create mode 100644 analysis/tools/PalExport2.java create mode 100644 analysis/tools/PalIdentify.java create mode 100644 analysis/tools/extract_palettes.py create mode 100644 android/app/src/main/kotlin/com/mag160c/thermal/core/VendorPalettes.kt create mode 100644 android/app/src/test/kotlin/com/mag160c/thermal/core/PalettesTest.kt diff --git a/analysis/sdk_re/android_app/bin/libcxsdk.so b/analysis/sdk_re/android_app/bin/libcxsdk.so new file mode 100644 index 0000000..e872c61 --- /dev/null +++ b/analysis/sdk_re/android_app/bin/libcxsdk.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b563c84faa419ccd310625036818f2eff8b04d089de7757f625e8e9894dd0d74 +size 372476 diff --git a/analysis/sdk_re/android_app/palette_candidates.json b/analysis/sdk_re/android_app/palette_candidates.json new file mode 100644 index 0000000..33db721 --- /dev/null +++ b/analysis/sdk_re/android_app/palette_candidates.json @@ -0,0 +1,22 @@ +{ + "finding": "libcxsdk.so contains NO static palette tables. All 12 vendor palettes are computed at runtime by CFunctions::SetColorPalette (Ghidra FUNC 0x00026c70). A static scan for 256-entry alpha=0xFF runs returns nothing; the tables were recovered by porting the generator instead.", + "static_candidates": [], + "source": {"binary": "analysis/sdk_re/android_app/bin/libcxsdk.so", "sha256_expected_size": 372476, "function": "CFunctions::SetColorPalette", "decompilation": "analysis/sdk_re/android_app/libcxsdk_decomp.txt"}, + "published_entry_layout": "byte0=B, byte1=G, byte2=R, byte3=0 (pinned by the iron bow anchor: 256/256)", + "ui_order": ["white_hot", "black_hot", "iron_bow", "rain_bow", "glow_bow", "autumn", "winter", "hot_metal", "jet", "red_saturation", "high_contrast", "red_hot"], + "index_to_case": {"white_hot": 0, "black_hot": 1, "iron_bow": 2, "rain_bow": 3, "glow_bow": 4, "autumn": 5, "winter": 6, "hot_metal": 7, "jet": 8, "red_saturation": 9, "high_contrast": 10, "red_hot": -1}, + "unexposed_cases": [12, 13], + "tables": { + "white_hot": [-16777216, -16711423, -16645630, -16579837, -16514044, -16448251, -16382458, -16316665, -16250872, -16185079, -16119286, -16053493, -15987700, -15921907, -15856114, -15790321, -15724528, -15658735, -15592942, -15527149, -15461356, -15395563, -15329770, -15263977, -15198184, -15132391, -15066598, -15000805, -14935012, -14869219, -14803426, -14737633, -14671840, -14606047, -14540254, -14474461, -14408668, -14342875, -14277082, -14211289, -14145496, -14079703, -14013910, -13948117, -13882324, -13816531, -13750738, -13684945, -13619152, -13553359, -13487566, -13421773, -13355980, -13290187, -13224394, -13158601, -13092808, -13027015, -12961222, -12895429, -12829636, -12763843, -12698050, -12632257, -12566464, -12500671, -12434878, -12369085, -12303292, -12237499, -12171706, -12105913, -12040120, -11974327, -11908534, -11842741, -11776948, -11711155, -11645362, -11579569, -11513776, -11447983, -11382190, -11316397, -11250604, -11184811, -11119018, -11053225, -10987432, -10921639, -10855846, -10790053, -10724260, -10658467, -10592674, -10526881, -10461088, -10395295, -10329502, -10263709, -10197916, -10132123, -10066330, -10000537, -9934744, -9868951, -9803158, -9737365, -9671572, -9605779, -9539986, -9474193, -9408400, -9342607, -9276814, -9211021, -9145228, -9079435, -9013642, -8947849, -8882056, -8816263, -8750470, -8684677, -8618884, -8553091, -8487298, -8421505, -8355712, -8289919, -8224126, -8158333, -8092540, -8026747, -7960954, -7895161, -7829368, -7763575, -7697782, -7631989, -7566196, -7500403, -7434610, -7368817, -7303024, -7237231, -7171438, -7105645, -7039852, -6974059, -6908266, -6842473, -6776680, -6710887, -6645094, -6579301, -6513508, -6447715, -6381922, -6316129, -6250336, -6184543, -6118750, -6052957, -5987164, -5921371, -5855578, -5789785, -5723992, -5658199, -5592406, -5526613, -5460820, -5395027, -5329234, -5263441, -5197648, -5131855, -5066062, -5000269, -4934476, -4868683, -4802890, -4737097, -4671304, -4605511, -4539718, -4473925, -4408132, -4342339, -4276546, -4210753, -4144960, -4079167, -4013374, -3947581, -3881788, -3815995, -3750202, -3684409, -3618616, -3552823, -3487030, -3421237, -3355444, -3289651, -3223858, -3158065, -3092272, -3026479, -2960686, -2894893, -2829100, -2763307, -2697514, -2631721, -2565928, -2500135, -2434342, -2368549, -2302756, -2236963, -2171170, -2105377, -2039584, -1973791, -1907998, -1842205, -1776412, -1710619, -1644826, -1579033, -1513240, -1447447, -1381654, -1315861, -1250068, -1184275, -1118482, -1052689, -986896, -921103, -855310, -789517, -723724, -657931, -592138, -526345, -460552, -394759, -328966, -263173, -197380, -131587, -65794, -1], + "black_hot": [-1, -65794, -131587, -197380, -263173, -328966, -394759, -460552, -526345, -592138, -657931, -723724, -789517, -855310, -921103, -986896, -1052689, -1118482, -1184275, -1250068, -1315861, -1381654, -1447447, -1513240, -1579033, -1644826, -1710619, -1776412, -1842205, -1907998, -1973791, -2039584, -2105377, -2171170, -2236963, -2302756, -2368549, -2434342, -2500135, -2565928, -2631721, -2697514, -2763307, -2829100, -2894893, -2960686, -3026479, -3092272, -3158065, -3223858, -3289651, -3355444, -3421237, -3487030, -3552823, -3618616, -3684409, -3750202, -3815995, -3881788, -3947581, -4013374, -4079167, -4144960, -4210753, -4276546, -4342339, -4408132, -4473925, -4539718, -4605511, -4671304, -4737097, -4802890, -4868683, -4934476, -5000269, -5066062, -5131855, -5197648, -5263441, -5329234, -5395027, -5460820, -5526613, -5592406, -5658199, -5723992, -5789785, -5855578, -5921371, -5987164, -6052957, -6118750, -6184543, -6250336, -6316129, -6381922, -6447715, -6513508, -6579301, -6645094, -6710887, -6776680, -6842473, -6908266, -6974059, -7039852, -7105645, -7171438, -7237231, -7303024, -7368817, -7434610, -7500403, -7566196, -7631989, -7697782, -7763575, -7829368, -7895161, -7960954, -8026747, -8092540, -8158333, -8224126, -8289919, -8355712, -8421505, -8487298, -8553091, -8618884, -8684677, -8750470, -8816263, -8882056, -8947849, -9013642, -9079435, -9145228, -9211021, -9276814, -9342607, -9408400, -9474193, -9539986, -9605779, -9671572, -9737365, -9803158, -9868951, -9934744, -10000537, -10066330, -10132123, -10197916, -10263709, -10329502, -10395295, -10461088, -10526881, -10592674, -10658467, -10724260, -10790053, -10855846, -10921639, -10987432, -11053225, -11119018, -11184811, -11250604, -11316397, -11382190, -11447983, -11513776, -11579569, -11645362, -11711155, -11776948, -11842741, -11908534, -11974327, -12040120, -12105913, -12171706, -12237499, -12303292, -12369085, -12434878, -12500671, -12566464, -12632257, -12698050, -12763843, -12829636, -12895429, -12961222, -13027015, -13092808, -13158601, -13224394, -13290187, -13355980, -13421773, -13487566, -13553359, -13619152, -13684945, -13750738, -13816531, -13882324, -13948117, -14013910, -14079703, -14145496, -14211289, -14277082, -14342875, -14408668, -14474461, -14540254, -14606047, -14671840, -14737633, -14803426, -14869219, -14935012, -15000805, -15066598, -15132391, -15198184, -15263977, -15329770, -15395563, -15461356, -15527149, -15592942, -15658735, -15724528, -15790321, -15856114, -15921907, -15987700, -16053493, -16119286, -16185079, -16250872, -16316665, -16382458, -16448251, -16514044, -16579837, -16645630, -16711423, -16777216], + "iron_bow": [-16777216, -16777211, -16777206, -16777201, -16777195, -16777190, -16777185, -16777179, -16777174, -16777169, -16777163, -16777158, -16777153, -16777147, -16777142, -16777137, -16777131, -16777126, -16777121, -16777115, -16777110, -16777105, -16777099, -16646027, -16449418, -16318346, -16121737, -15925129, -15794056, -15597448, -15400839, -15269767, -15073158, -14876550, -14745477, -14548869, -14352260, -14221188, -14024579, -13827971, -13696898, -13500290, -13369217, -13172609, -12976000, -12844928, -12648319, -12451710, -12320638, -12124029, -11927421, -11796348, -11599740, -11403131, -11272059, -11075450, -10878842, -10747769, -10551161, -10420088, -10223480, -10026871, -9895799, -9699190, -9502582, -9371509, -9174901, -8978292, -8847220, -8650611, -8454002, -8322930, -8126321, -7929713, -7798640, -7602032, -7470959, -7274351, -7077742, -6946670, -6750061, -6553453, -6422380, -6225772, -6029163, -5898091, -5701482, -5504874, -5373801, -5177193, -4980584, -4849512, -4652903, -4456294, -4390504, -4324458, -4192876, -4127086, -4061040, -3929458, -3863412, -3797622, -3666040, -3599994, -3534204, -3402622, -3336576, -3270530, -3139204, -3073158, -3007112, -2875530, -2809740, -2743694, -2612112, -2546322, -2480276, -2348694, -2282648, -2216858, -2085276, -2019230, -1887905, -1887397, -1821353, -1755310, -1689522, -1623479, -1557435, -1491392, -1425604, -1359561, -1293517, -1227729, -1161686, -1095642, -1029599, -963811, -963304, -897260, -831473, -765425, -699377, -633329, -567537, -501489, -435441, -369393, -303601, -237553, -171505, -105713, -39665, -39153, -38641, -38385, -37873, -37361, -37105, -36593, -36081, -35569, -35313, -34801, -34289, -33777, -33521, -33009, -32497, -32241, -31729, -31217, -30705, -30449, -29937, -29425, -28913, -28657, -28145, -27633, -27377, -26865, -26353, -25841, -25585, -25073, -24561, -24305, -23793, -23281, -22769, -22513, -22001, -21489, -20977, -20721, -20209, -19697, -19441, -18929, -18417, -17905, -17649, -17137, -16625, -16369, -15857, -15345, -14833, -14577, -14065, -13553, -13041, -12785, -12273, -11761, -11505, -10993, -10481, -9969, -9713, -9201, -8689, -8433, -7914, -7394, -6875, -6611, -6092, -5572, -5053, -4789, -4270, -3750, -3487, -2967, -2448, -1928, -1665, -1145, -626, -106, -99, -91, -84, -76, -69, -61, -54, -46, -39, -31, -24, -16, -9, -1], + "rain_bow": [-16777166, -16776907, -16776392, -16775877, -16775362, -16774846, -16774331, -16773816, -16773301, -16772786, -16772270, -16771755, -16771240, -16770725, -16770210, -16769694, -16769179, -16768664, -16768149, -16767634, -16767118, -16766603, -16766088, -16765573, -16765058, -16764542, -16764027, -16763512, -16762997, -16762482, -16761966, -16761451, -16760936, -16760421, -16759906, -16759390, -16758875, -16758360, -16757845, -16757330, -16756814, -16756299, -16755784, -16755269, -16754754, -16754238, -16753723, -16753208, -16752693, -16752178, -16751662, -16751147, -16750632, -16750117, -16749602, -16749086, -16748571, -16748056, -16747541, -16747026, -16746510, -16745995, -16745480, -16744965, -16744449, -16547332, -16284680, -16022028, -15759376, -15496724, -15234072, -14971420, -14708768, -14446116, -14183464, -13920812, -13658160, -13395508, -13132856, -12870204, -12607552, -12344900, -12082248, -11819596, -11556944, -11294292, -11031640, -10768988, -10506336, -10243684, -9981032, -9718380, -9455728, -9193076, -8930424, -8667772, -8405120, -8142468, -7879816, -7617164, -7354512, -7091860, -6829208, -6566556, -6303904, -6041252, -5778600, -5515948, -5253296, -4990644, -4727992, -4465340, -4202688, -3940036, -3677384, -3414732, -3152080, -2889428, -2626776, -2364124, -2101472, -1838820, -1576168, -1313516, -1050864, -788212, -525560, -262908, -256, -1023, -2045, -3067, -4089, -5111, -6133, -7155, -8177, -9199, -10221, -11243, -12265, -13287, -14309, -15331, -16353, -17375, -18397, -19419, -20441, -21463, -22485, -23507, -24529, -25551, -26573, -27595, -28617, -29639, -30661, -31683, -32705, -33727, -34749, -35771, -36793, -37815, -38837, -39859, -40881, -41903, -42925, -43947, -44969, -45991, -47013, -48035, -49057, -50079, -51101, -52123, -53145, -54167, -55189, -56211, -57233, -58255, -59277, -60299, -61321, -62343, -63365, -64387, -65409, -64639, -63613, -62587, -61561, -60535, -59509, -58483, -57457, -56431, -55405, -54379, -53353, -52327, -51301, -50275, -49249, -48223, -47197, -46171, -45145, -44119, -43093, -42067, -41041, -40015, -38989, -37963, -36937, -35911, -34885, -33859, -32833, -31807, -30781, -29755, -28729, -27703, -26677, -25651, -24625, -23599, -22573, -21547, -20521, -19495, -18469, -17443, -16417, -15391, -14365, -13339, -12313, -11287, -10261, -9235, -8209, -7183, -6157, -5131, -4105, -3079, -2053, -1027], + "glow_bow": [-13500416, -13500160, -13499904, -13499648, -13499392, -13499136, -13498880, -13498624, -13498368, -13498112, -13497856, -13497600, -13497344, -13497088, -13496832, -13496576, -13496320, -13496064, -13495808, -13495552, -13495296, -13495040, -13494784, -13494528, -13494272, -13494016, -13493760, -13493504, -13493248, -13492992, -13492736, -13492480, -13492224, -13491968, -13491712, -13491456, -13491200, -13490944, -13490688, -13490432, -13490176, -13489920, -13489664, -13489408, -13489152, -13488896, -13488640, -13488384, -13488128, -13487872, -13487616, -13421824, -13356032, -13224704, -13158912, -13093120, -12961792, -12896000, -12830208, -12698880, -12633088, -12501760, -12435968, -12370176, -12238848, -12173056, -12107264, -11975936, -11910144, -11844352, -11713024, -11647232, -11515904, -11450112, -11384320, -11252992, -11187200, -11121408, -10990080, -10924288, -10792960, -10727168, -10661376, -10530048, -10464256, -10398464, -10267136, -10201344, -10135552, -10004224, -9938432, -9807104, -9741312, -9675520, -9544192, -9478400, -9412608, -9281280, -9215488, -9149696, -9018368, -8952576, -8821248, -8755456, -8689664, -8558336, -8492544, -8426752, -8295424, -8229632, -8098304, -8032512, -7966720, -7835392, -7769600, -7703808, -7572480, -7506688, -7440896, -7309568, -7243776, -7112448, -7046656, -6980864, -6849536, -6783744, -6717952, -6586624, -6520832, -6455040, -6323712, -6257920, -6126592, -6060800, -5995008, -5863680, -5797888, -5732096, -5600768, -5534976, -5403648, -5337856, -5272064, -5140736, -5074944, -5009152, -4877824, -4812032, -4746240, -4614912, -4549120, -4417792, -4352000, -4286208, -4154880, -4089088, -4023296, -3891968, -3826176, -3760384, -3629056, -3563264, -3431936, -3366144, -3300352, -3169024, -3103232, -3037440, -2906112, -2840320, -2708992, -2643200, -2577408, -2446080, -2380288, -2314496, -2183168, -2117376, -2051584, -1920256, -1854464, -1723136, -1657344, -1591552, -1460224, -1394432, -1328640, -1197312, -1131520, -1065728, -934400, -868608, -737280, -671488, -605696, -474368, -408576, -342784, -211456, -145664, -14336, -14080, -13824, -13568, -13312, -13056, -12800, -12544, -12288, -12032, -11776, -11520, -11264, -11008, -10752, -10496, -10240, -9984, -9728, -9472, -9216, -8960, -8704, -8448, -8192, -7936, -7680, -7424, -7168, -6912, -6656, -6400, -6144, -5888, -5632, -5376, -5120, -4864, -4608, -4352, -4096, -3840, -3584, -3328, -3072, -2816, -2560, -2304, -2048, -1792, -1536, -1280, -1024, -768, -512, -256], + "autumn": [-16777216, -16580608, -16383999, -16187135, -15990526, -15793918, -15531517, -15334909, -15138300, -14941435, -14744827, -14548218, -14285818, -14089209, -13892601, -13695736, -13499127, -13302519, -13040118, -12843510, -12646901, -12450037, -12253428, -12056820, -11794419, -11597810, -11401202, -11204337, -11007729, -10811120, -10548720, -10352111, -10155246, -9958638, -9762029, -9499629, -9303020, -9106412, -8909547, -8712939, -8516330, -8253929, -8057321, -7860712, -7663848, -7467239, -7270631, -7008230, -6811621, -6615013, -6418148, -6221540, -6024931, -5762531, -5565922, -5369314, -5172449, -4975840, -4779232, -4516831, -4320223, -4123614, -3926750, -3730141, -3467740, -3467484, -3401693, -3335901, -3270110, -3204318, -3204063, -3138271, -3072480, -3006689, -2940897, -2940642, -2874850, -2809059, -2743267, -2677476, -2611429, -2611173, -2545382, -2479590, -2413799, -2348007, -2347752, -2281960, -2216169, -2150378, -2084586, -2084331, -2018539, -1952748, -1886956, -1821165, -1755118, -1754862, -1689071, -1623279, -1557488, -1491696, -1491441, -1425649, -1359858, -1294067, -1228275, -1228020, -1162228, -1096437, -1030645, -964854, -898807, -898551, -832760, -766968, -701177, -635385, -635130, -569338, -503547, -437756, -371964, -371709, -305917, -240126, -174334, -108543, -42496, -42240, -41728, -41216, -40704, -40192, -39680, -39168, -38656, -38144, -37632, -37120, -36608, -36096, -35584, -35072, -34560, -34304, -33792, -33280, -32768, -32256, -31744, -31232, -30720, -30208, -29696, -29184, -28672, -28160, -27648, -27136, -26624, -26368, -25856, -25344, -24832, -24320, -23808, -23296, -22784, -22272, -21760, -21248, -20736, -20224, -19712, -19200, -18688, -18432, -17920, -17408, -16896, -16384, -15872, -15360, -14848, -14336, -13824, -13312, -12800, -12288, -11776, -11264, -10752, -10749, -10489, -10485, -10225, -9965, -9961, -9701, -9441, -9437, -9177, -8917, -8913, -8653, -8649, -8389, -8129, -8125, -7865, -7605, -7601, -7341, -7081, -7077, -6817, -6557, -6553, -6293, -6289, -6029, -5769, -5765, -5505, -5245, -5241, -4981, -4721, -4717, -4457, -4453, -4193, -3933, -3929, -3669, -3409, -3405, -3145, -2885, -2881, -2621, -2361, -2357, -2097, -2093, -1833, -1573, -1569, -1309, -1049, -1045, -785, -525, -521, -261], + "winter": [-1, -131330, -328195, -459524, -656389, -787718, -984583, -1115912, -1312777, -1444106, -1640971, -1837836, -1969165, -2166030, -2297359, -2494224, -2625553, -2822418, -2953747, -3150612, -3347477, -3478806, -3675671, -3807000, -4003865, -4135194, -4332059, -4463388, -4660253, -4791582, -4988447, -5185312, -5316641, -5513506, -5644835, -5841700, -5973029, -6169894, -6301223, -6498088, -6694953, -6826282, -7023147, -7154476, -7351341, -7482670, -7679535, -7810864, -8007729, -8139058, -8335923, -8532788, -8664117, -8860982, -8992311, -9189176, -9320505, -9517370, -9648699, -9845564, -10042429, -10173758, -10370623, -10501952, -10698817, -10830146, -11027011, -11158340, -11355205, -11486534, -11683399, -11880264, -12011593, -12208458, -12339787, -12536652, -12667981, -12864846, -12996175, -13193040, -13389905, -13521234, -13718099, -13849428, -14046293, -14177622, -14374487, -14505816, -14702681, -14834010, -15030875, -15227740, -15359069, -15555934, -15687263, -15884128, -16015457, -16212322, -16343651, -16540516, -16737381, -16606566, -16475751, -16279400, -16148585, -15952234, -15821419, -15625068, -15494253, -15363438, -15167087, -15036272, -14839921, -14709106, -14512755, -14381940, -14251125, -14054774, -13923959, -13727608, -13596793, -13400442, -13269627, -13138812, -12942461, -12811646, -12615295, -12484480, -12288129, -12157314, -12026499, -11830148, -11699333, -11502982, -11372167, -11175816, -11045001, -10914186, -10717835, -10587020, -10390669, -10259854, -10063503, -9932688, -9801873, -9605522, -9474707, -9278356, -9147541, -8951190, -8820375, -8689560, -8493209, -8362394, -8166043, -8035228, -7838877, -7708062, -7577247, -7380896, -7250081, -7053730, -6922915, -6726564, -6595749, -6464934, -6268583, -6137768, -5941417, -5810602, -5614251, -5483436, -5352621, -5156270, -5025455, -4829104, -4698289, -4501938, -4371123, -4240308, -4043957, -3913142, -3716791, -3585976, -3389625, -3258810, -3127995, -2931644, -2800829, -2604478, -2473663, -2277312, -2146497, -2015682, -1819331, -1688516, -1492165, -1361350, -1164999, -1034184, -903369, -707018, -576203, -379852, -249037, -52686, -52943, -53200, -53457, -53714, -53971, -54228, -54485, -54742, -54999, -55256, -55513, -55770, -56027, -56284, -56541, -56798, -57055, -57312, -57569, -57826, -58083, -58340, -58597, -58854, -59111, -59368, -59625, -59882, -60139, -60396, -60653, -60910, -61167, -61424, -61681, -61938, -62195, -62452, -62709, -62966, -63223, -63480, -63737, -63994, -64251, -64508, -64765, -65022, -65279, -65536], + "hot_metal": [-16777216, -16646144, -16515072, -16384000, -16252928, -16121856, -15990784, -15859712, -15728640, -15597568, -15466496, -15335424, -15204352, -15073280, -14942208, -14811136, -14680064, -14548992, -14417920, -14286848, -14155776, -14024704, -13893632, -13762560, -13631488, -13500416, -13369344, -13238272, -13107200, -12976128, -12845056, -12713984, -12582912, -12451840, -12320768, -12189696, -12058624, -11927552, -11796480, -11665408, -11534336, -11403264, -11272192, -11141120, -11010048, -10878976, -10747904, -10616832, -10485760, -10354688, -10223616, -10092544, -9961472, -9830400, -9699328, -9568256, -9437184, -9306112, -9175040, -9043968, -8912896, -8781824, -8650752, -8519680, -8388608, -8257536, -8126464, -7995392, -7864320, -7733248, -7602176, -7471104, -7340032, -7208960, -7077888, -6946816, -6815744, -6684672, -6553600, -6422528, -6291456, -6160384, -6029312, -5898240, -5767168, -5636096, -5505024, -5373952, -5242880, -5111808, -4980736, -4849664, -4718592, -4587520, -4456448, -4325376, -4194304, -4063232, -3932160, -3801088, -3670016, -3538944, -3407872, -3276800, -3145728, -3014656, -2883584, -2752512, -2621440, -2490368, -2359296, -2228224, -2097152, -1966080, -1835008, -1703936, -1572864, -1441792, -1310720, -1179648, -1048576, -917504, -786432, -655360, -524288, -393216, -262144, -131072, -65536, -65022, -64508, -63994, -63480, -62966, -62452, -61938, -61424, -60910, -60396, -59882, -59368, -58854, -58340, -57826, -57312, -56798, -56284, -55770, -55256, -54742, -54228, -53714, -53200, -52686, -52172, -51658, -51144, -50630, -50116, -49602, -49088, -48574, -48060, -47546, -47032, -46518, -46004, -45490, -44976, -44462, -43948, -43434, -42920, -42406, -41892, -41378, -40864, -40350, -39836, -39322, -38808, -38294, -37780, -37266, -36752, -36238, -35724, -35210, -34696, -34182, -33668, -33154, -32640, -32126, -31612, -31098, -30584, -30070, -29556, -29042, -28528, -28014, -27500, -26986, -26472, -25958, -25444, -24930, -24416, -23902, -23388, -22874, -22360, -21846, -21332, -20818, -20304, -19790, -19276, -18762, -18248, -17734, -17220, -16706, -16192, -15678, -15164, -14650, -14136, -13622, -13108, -12594, -12080, -11566, -11052, -10538, -10024, -9510, -8996, -8482, -7968, -7454, -6940, -6426, -5912, -5398, -4884, -4370, -3856, -3342, -2828, -2314, -1800, -1286, -772, -1], + "jet": [-16777089, -16777085, -16777081, -16777077, -16777073, -16777069, -16777065, -16777061, -16777057, -16777053, -16777049, -16777045, -16777041, -16777037, -16777033, -16777029, -16777025, -16777021, -16777017, -16777013, -16777009, -16777005, -16777001, -16776997, -16776993, -16776989, -16776985, -16776981, -16776977, -16776973, -16776969, -16776965, -16776961, -16776193, -16775169, -16774145, -16773121, -16772097, -16771073, -16770049, -16769025, -16768001, -16766977, -16765953, -16764929, -16763905, -16762881, -16761857, -16760833, -16759809, -16758785, -16757761, -16756737, -16755713, -16754689, -16753665, -16752641, -16751617, -16750593, -16749569, -16748545, -16747521, -16746497, -16745473, -16744449, -16743425, -16742401, -16741377, -16740353, -16739329, -16738305, -16737281, -16736257, -16735233, -16734209, -16733185, -16732161, -16731137, -16730113, -16729089, -16728065, -16727041, -16726017, -16724993, -16723969, -16722945, -16721921, -16720897, -16719873, -16718849, -16717825, -16716801, -16715777, -16714753, -16713729, -16712705, -16711681, -16515076, -16252936, -15990796, -15728656, -15466516, -15204376, -14942236, -14680096, -14417956, -14155816, -13893676, -13631536, -13369396, -13107256, -12845116, -12582976, -12320836, -12058696, -11796556, -11534416, -11272276, -11010136, -10747996, -10485856, -10223716, -9961576, -9699436, -9437296, -9175156, -8913016, -8650876, -8388736, -8126596, -7864456, -7602316, -7340176, -7078036, -6815896, -6553756, -6291616, -6029476, -5767336, -5505196, -5243056, -4980916, -4718776, -4456636, -4194496, -3932356, -3670216, -3408076, -3145936, -2883796, -2621656, -2359516, -2097376, -1835236, -1573096, -1310956, -1048816, -786676, -524536, -262396, -256, -1024, -2048, -3072, -4096, -5120, -6144, -7168, -8192, -9216, -10240, -11264, -12288, -13312, -14336, -15360, -16384, -17408, -18432, -19456, -20480, -21504, -22528, -23552, -24576, -25600, -26624, -27648, -28672, -29696, -30720, -31744, -32768, -33792, -34816, -35840, -36864, -37888, -38912, -39936, -40960, -41984, -43008, -44032, -45056, -46080, -47104, -48128, -49152, -50176, -51200, -52224, -53248, -54272, -55296, -56320, -57344, -58368, -59392, -60416, -61440, -62464, -63488, -64512, -65536, -327680, -589824, -851968, -1114112, -1376256, -1638400, -1900544, -2162688, -2424832, -2686976, -2949120, -3211264, -3473408, -3735552, -3997696, -4259840, -4521984, -4784128, -5046272, -5308416, -5570560, -5832704, -6094848, -6356992, -6619136, -6881280, -7143424, -7405568, -7667712, -7929856, -8192000], + "red_saturation": [-16777216, -16711423, -16645630, -16579837, -16448251, -16382458, -16316665, -16250872, -16119286, -16053493, -15987700, -15856114, -15790321, -15724528, -15658735, -15527149, -15461356, -15395563, -15329770, -15198184, -15132391, -15066598, -14935012, -14869219, -14803426, -14737633, -14606047, -14540254, -14474461, -14408668, -14277082, -14211289, -14145496, -14013910, -13948117, -13882324, -13816531, -13684945, -13619152, -13553359, -13421773, -13355980, -13290187, -13224394, -13092808, -13027015, -12961222, -12895429, -12763843, -12698050, -12632257, -12500671, -12434878, -12369085, -12303292, -12171706, -12105913, -12040120, -11974327, -11842741, -11776948, -11711155, -11579569, -11513776, -11447983, -11382190, -11250604, -11184811, -11119018, -11053225, -10921639, -10855846, -10790053, -10658467, -10592674, -10526881, -10461088, -10329502, -10263709, -10197916, -10066330, -10000537, -9934744, -9868951, -9737365, -9671572, -9605779, -9539986, -9408400, -9342607, -9276814, -9145228, -9079435, -9013642, -8947849, -8816263, -8750470, -8684677, -8618884, -8487298, -8421505, -8355712, -8224126, -8158333, -8092540, -8026747, -7895161, -7829368, -7763575, -7697782, -7566196, -7500403, -7434610, -7303024, -7237231, -7171438, -7105645, -6974059, -6908266, -6842473, -6710887, -6645094, -6579301, -6513508, -6381922, -6316129, -6250336, -6184543, -6052957, -5987164, -5921371, -5789785, -5723992, -5658199, -5592406, -5460820, -5395027, -5329234, -5263441, -5131855, -5066062, -5000269, -4868683, -4802890, -4737097, -4671304, -4539718, -4473925, -4408132, -4342339, -4210753, -4144960, -4079167, -3947581, -3882045, -3816509, -3750973, -3619901, -3554365, -3488829, -3357757, -3292221, -3226685, -3161149, -3030077, -2964541, -2899005, -2833469, -2702397, -2636861, -2571325, -2440253, -2374717, -2309181, -2243645, -2112573, -2047037, -1981501, -1915965, -1784893, -1719357, -1653821, -1522749, -1457213, -1391677, -1326141, -1195069, -1129533, -1063997, -998461, -867389, -801853, -736317, -605245, -539709, -474173, -408637, -277565, -212029, -146493, -15421, -16192, -17220, -17991, -19019, -19790, -20818, -21589, -22617, -23388, -24416, -25444, -26215, -27243, -28014, -29042, -29813, -30841, -31612, -32640, -33411, -34439, -35467, -36238, -37266, -38037, -39065, -39836, -40864, -41635, -42663, -43434, -44462, -45490, -46261, -47289, -48060, -49088, -49859, -50887, -51658, -52686, -53457, -54485, -55513, -56284, -57312, -58083, -59111, -59882, -60910, -61681, -62709, -63480, -64508, -65536], + "high_contrast": [-16777216, -16318457, -15859698, -15400939, -14942180, -14483421, -14024662, -13565903, -13107144, -12648385, -12189626, -11730867, -11272108, -10813349, -10354590, -9895831, -9437072, -8978313, -8519554, -8060795, -7602036, -7143277, -6684518, -6225759, -5767000, -5308241, -4849482, -4390723, -3931964, -3473205, -3014446, -2555687, -2096928, -1638169, -1179410, -720651, -261892, -720645, -1179399, -1638153, -2096907, -2555660, -3014414, -3473168, -3931922, -4390675, -4849429, -5308183, -5766937, -6225690, -6684444, -7143198, -7601952, -8060705, -8519459, -8978213, -9436967, -9895720, -10354474, -10813228, -11271982, -11730735, -12189489, -12648243, -13106997, -13565750, -14024504, -14483258, -14942012, -15400765, -15859519, -16318273, -16777027, -16775233, -16773439, -16771645, -16769852, -16768058, -16766264, -16764470, -16762677, -16760883, -16759089, -16757295, -16755502, -16753708, -16751914, -16750120, -16748327, -16746533, -16744739, -16742945, -16741152, -16739358, -16737564, -16735770, -16733977, -16732183, -16730389, -16728595, -16726802, -16725008, -16723214, -16721420, -16719627, -16717833, -16716039, -16714245, -16712452, -16712452, -16713227, -16714258, -16715033, -16716064, -16716839, -16717870, -16718645, -16719676, -16720451, -16721482, -16722257, -16723288, -16724063, -16725094, -16725869, -16726900, -16727675, -16728706, -16729481, -16730512, -16731287, -16732318, -16733093, -16734124, -16734899, -16735930, -16736705, -16737736, -16738511, -16739542, -16740317, -16741348, -16742123, -16743154, -16743929, -16744960, -16285184, -15825664, -15365888, -14906368, -14446592, -13987072, -13527296, -13067776, -12608000, -12148480, -11688704, -11229184, -10769408, -10309888, -9850112, -9390592, -8930816, -8471296, -8011520, -7552000, -7092224, -6632704, -6172928, -5713408, -5253632, -4794112, -4334336, -3874816, -3415040, -2955520, -2495744, -2036224, -1576448, -1116928, -657152, -197632, -197632, -264960, -397824, -530688, -663552, -730880, -863744, -996608, -1129472, -1196800, -1329664, -1462528, -1595392, -1662720, -1795584, -1928448, -2061312, -2128640, -2261504, -2394368, -2527232, -2594560, -2727424, -2860288, -2993152, -3060480, -3193344, -3326208, -3459072, -3526400, -3659264, -3792128, -3924992, -3992320, -4125184, -4258048, -4390912, -4390912, -4258041, -4125170, -3992299, -3924964, -3792093, -3659222, -3526351, -3459016, -3326145, -3193274, -3060403, -2993068, -2860197, -2727326, -2594455, -2527120, -2394249, -2261378, -2128507, -2061172, -1928301, -1795430, -1662559, -1595224, -1462353, -1329482, -1196611, -1129276, -996405, -863534, -730663, -663328, -530457, -397586, -264715, -197380] + } +} diff --git a/analysis/sdk_re/android_app/palette_candidates.png b/analysis/sdk_re/android_app/palette_candidates.png new file mode 100644 index 0000000..064861e --- /dev/null +++ b/analysis/sdk_re/android_app/palette_candidates.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28b4c582fa54b1f21c18ff3ea5e71132b0494450f7da6348e9190e28f2e4109a +size 111534 diff --git a/analysis/sdk_re/android_app/palette_extraction_findings.md b/analysis/sdk_re/android_app/palette_extraction_findings.md new file mode 100644 index 0000000..52fc584 --- /dev/null +++ b/analysis/sdk_re/android_app/palette_extraction_findings.md @@ -0,0 +1,94 @@ +# Phase C —— 厂商 12 调色板精确提取:结论与证据 + +日期:2026-09-10。目标:从官方 native 库 `libcxsdk.so` 提取 12 个调色板精确表。 + +## 结论(TL;DR) + +**`libcxsdk.so` 里没有静态调色板表**。12 个调色板全部由 +`CFunctions::SetColorPalette`(Ghidra 偏移 `0x00026c70`)在**运行时用算术生成**。 +对全文件扫描"连续 256 个 u32LE、alpha=0xFF 且 RGB 非零"的候选 **命中 0 个** +(alpha=0x00 形式、BGR/RGB 三元组形式同样为 0)。 + +因此本阶段改用**移植生成函数**的路线并成功恢复: +**UI 索引 0..10 共 11 个调色板已为官方精确表**(索引 11 见下), +已写入 `android/.../core/VendorPalettes.kt` 并由 `Palettes.kt` 使用。 + +## 验证链(三重独立验证) + +1. **铁虹锚点**:移植的 case 2 输出与 `OfficialTables.PALETTE256_ARGB` + **256/256 逐字节一致**。该表是从厂商运行时(CoreSDKLib `dev+0xb18`)抓取、 + 并与官方渲染器逐像素比对验证过的,因此它同时锁死了: + - 算术配方(除法取整方式、循环边界、常量) + - 发布字节序 = `{byte0=B, byte1=G, byte2=R, byte3=0}` +2. **完整性**:每个 case 对 256 个槽位**各写入恰好一次**(无空洞、无覆盖), + 由模拟器的写地址跟踪断言。 +3. **厂商侧预览图**:官方 APK `res/mipmap-hdpi-v4/palette_.png` 是官方用 + 对应调色板渲染的真实热像帧。每个调色板对其**自身**预览图的颜色覆盖率都是 + 全体候选中最高,且差距显著: + +| 官方调色板 | UI 索引 | 生成 case | 自身覆盖率 | 次优候选 | +|---|---|---|---|---| +| 白热 white_hot | 0 | 0 | 100.0% | 100.0%(灰度图,天然重合) | +| 黑热 black_hot | 1 | 1 | 100.0% | 100.0%(同上) | +| 铁虹 iron_bow | 2 | 2 | 9.0% | 0.1% | +| 彩虹 rain_bow | 3 | 3 | 7.3% | 0.1% | +| 琥珀 glow_bow | 4 | 4 | 30.7% | 2.3% | +| 金秋 autumn | 5 | 5 | 15.4% | 2.5% | +| 寒冬 winter | 6 | 6 | 22.3% | 2.9% | +| 热金属 hot_metal | 7 | 7 | 42.1% | 11.0% | +| 喷射 jet | 8 | 8 | 14.0% | 6.3% | +| 红饱和 red_saturation | 9 | 9 | 26.7% | 22.1% | +| 高对比度 high_contrast | 10 | 10 | 4.0% | 0.2% | +| 红热 red_hot | 11 | **未解决** | — | — | + +(预览图是缩放过的照片,存在插值产生的额外颜色,故绝对覆盖率不高; +关键是自身恒为最高。) + +## 未解决项:索引 11「红热」 + +- native 库里另有 case 12、case 13 两个生成体,但两者对**任何**官方预览图的 + 覆盖率都很低(最高 3–6%,指向 jet),**不能**认定为「红热」。 +- 官方 APK 的 `palette_red_hot.png` 是**占位图**:11914 个像素中 11777 个与 + `palette_white_hot.png` 完全相同(平均绝对差 0.5/255),不携带调色板信息。 +- 处理方式:`Palettes.kt` 索引 11 保留原近似曲线,并注明 + `// approximated (not found in binary)`;`VendorPalettes.SOURCE_CASE[11] = -1`。 + **不猜测**未验证的表。 + +## 可复现产物 + +| 路径 | 说明 | +|---|---| +| `analysis/sdk_re/android_app/bin/libcxsdk.so` | 官方 native 库入库(372476 B) | +| `analysis/sdk_re/android_app/palette_candidates.json` | 扫描结论(静态候选为空)+ 全部恢复表 | +| `analysis/sdk_re/android_app/palette_candidates.png` | 12 组「官方预览图 + 恢复色条」对照图 | +| `analysis/sdk_re/android_app/palette_match_report.txt` | 覆盖率证据表 | +| `analysis/tools/extract_palettes.py` | 计划要求的纯标准库扫描脚本(可复现"无静态表"这一否定结论) | +| `analysis/tools/PalIdentify.java` | 13 个生成体的机械移植(含写地址校验) | +| `analysis/tools/PalExport2.java` | 导出 Kotlin/JSON/PNG/报告 | +| `android/.../core/VendorPalettes.kt` | 生成的 Kotlin 表(0..10 为精确表) | + +重跑(本机无 python3,Java 版单文件即可运行): + +```bash +java analysis/tools/PalExport2.java \ + android/app/src/main/kotlin/com/mag160c/thermal/core/OfficialTables.kt \ + <解包APK的 res/mipmap-hdpi-v4 目录> \ + android/app/src/main/kotlin/com/mag160c/thermal/core \ + analysis/sdk_re/android_app +``` + +有 python3 的机器上: + +```bash +python3 analysis/tools/extract_palettes.py \ + analysis/sdk_re/android_app/bin/libcxsdk.so \ + android/app/src/main/kotlin/com/mag160c/thermal/core/OfficialTables.kt \ + analysis/sdk_re/android_app +``` + +## 与执行计划的对照 + +计划 C3 的回退条款为"若扫不到锚点:如实记录未在 libcxsdk 静态数据中找到调色板表, +保留现有一套近似实现,本阶段只交付脚本与 JSON"。实际执行结果**优于**回退: +锚点确实不在静态数据里(按回退如实记录),但通过移植运行时生成函数恢复了 +11/12 个精确表,并把它们接入 `Palettes.kt`。 diff --git a/analysis/sdk_re/android_app/palette_match_report.txt b/analysis/sdk_re/android_app/palette_match_report.txt new file mode 100644 index 0000000..ed04dc6 --- /dev/null +++ b/analysis/sdk_re/android_app/palette_match_report.txt @@ -0,0 +1,36 @@ +Phase C palette extraction — evidence +===================================== + +Conclusion: libcxsdk.so has NO static palette tables. A full-file scan for +256 consecutive u32 LE entries with alpha=0xFF and non-zero RGB finds 0 hits +(also 0 with alpha=0x00, and the BGR/RGB triple forms). CFunctions:: +SetColorPalette builds the palettes with arithmetic at runtime. + +Validation chain: + 1. port reproduces OfficialTables.PALETTE256_ARGB 256/256 (iron bow) + 2. every case writes all 256 entries exactly once + 3. colour coverage of the official APK preview mipmaps: + + palette colors own case best other + white_hot 211 100.0% 100.0% + black_hot 214 100.0% 100.0% + iron_bow 1684 9.0% 0.1% + rain_bow 2005 7.3% 0.1% + glow_bow 596 30.7% 2.2% + autumn 1036 15.4% 2.5% + winter 826 22.3% 2.9% + hot_metal 482 42.1% 11.0% + jet 1251 14.0% 4.7% + red_saturation 660 26.7% 22.1% + high_contrast 3044 4.0% 0.1% + red_hot 337 -1.0% 62.6% + +Notes: + - red_hot: the official preview asset is a stub (137 of 11914 pixels differ + from palette_white_hot.png), so it carries no palette information. No + generator body is claimed for UI index 11; Palettes.kt keeps its + approximation with an explicit comment. + - cases 12 and 13 exist in the native library but explain no official + preview (best 3-6% against jet); they are not among the 12 UI palettes. + - the previews are scaled photographic crops, so absolute coverage is low + for some palettes; the correct body always wins by a wide margin. diff --git a/analysis/tools/PalExport2.java b/analysis/tools/PalExport2.java new file mode 100644 index 0000000..55fb7f4 --- /dev/null +++ b/analysis/tools/PalExport2.java @@ -0,0 +1,244 @@ +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.nio.file.*; +import java.util.*; +import javax.imageio.ImageIO; + +/** + * Final Phase-C export. + * + * Produces, from the ported vendor generator (see PalIdentify.java/PalBody): + * 1. android/.../core/VendorPalettes.kt — recovered tables as Kotlin + * 2. analysis/.../palette_candidates.json — provenance + all tables + * 3. analysis/.../palette_candidates.png — strip sheet for eyeballing + * 4. analysis/.../palette_match_report.txt — evidence per palette + * + * Usage: java PalExport2 + */ +public class PalExport2 { + static final String[] NAMES = {"white_hot","black_hot","iron_bow","rain_bow","glow_bow", + "autumn","winter","hot_metal","jet","red_saturation","high_contrast","red_hot"}; + static final String[] NAMES_CN = {"白热","黑热","铁虹","彩虹","琥珀","金秋", + "寒冬","热金属","喷射","红饱和","高对比度","红热"}; + + /** UI index -> libcxsdk generator case; -1 = no body resolved (approximation kept). */ + static final int[] CASE_FOR_INDEX = {0,1,2,3,4,5,6,7,8,9,10,-1}; + + public static void main(String[] args) throws Exception { + String kt = args[0], apkRes = args[1]; + Path coreDir = Paths.get(args[2]); + Path outDir = Paths.get(args[3]); + Files.createDirectories(coreDir); + Files.createDirectories(outDir); + + int[][] tables = new int[12][]; + for (int idx = 0; idx < 12; idx++) { + if (CASE_FOR_INDEX[idx] < 0) continue; + tables[idx] = emit(PalBody.run(CASE_FOR_INDEX[idx])); + } + + // ---- 1. Kotlin ---- + StringBuilder sb = new StringBuilder(); + sb.append("package com.mag160c.thermal.core\n\n"); + sb.append("/**\n"); + sb.append(" * Vendor display palettes recovered from the official native SDK.\n"); + sb.append(" *\n"); + sb.append(" * libcxsdk.so contains NO static palette tables. All palettes are built at\n"); + sb.append(" * runtime by {@code CFunctions::SetColorPalette}; the tables below are a\n"); + sb.append(" * line-by-line port of that routine (Ghidra listing:\n"); + sb.append(" * analysis/sdk_re/android_app/libcxsdk_decomp.txt, FUNC 0x00026c70).\n"); + sb.append(" *\n"); + sb.append(" * Evidence (analysis/sdk_re/android_app/palette_extraction_findings.md):\n"); + sb.append(" * - case 2 reproduces [OfficialTables.PALETTE256_ARGB] 256/256 — the table\n"); + sb.append(" * captured from the vendor runtime and verified pixel-exact against the\n"); + sb.append(" * official renderer. This pins both the arithmetic and the byte order\n"); + sb.append(" * (published entry = {byte0=B, byte1=G, byte2=R, byte3=0}).\n"); + sb.append(" * - every case writes each of the 256 entries exactly once.\n"); + sb.append(" * - each table reproduces the colours of its own preview image shipped in\n"); + sb.append(" * the official APK (res/mipmap-hdpi-v4/palette_.png).\n"); + sb.append(" *\n"); + sb.append(" * GENERATED by analysis/tools/PalExport2.java — do not edit by hand.\n"); + sb.append(" */\n"); + sb.append("object VendorPalettes {\n"); + sb.append(" /** UI index -> source case in CFunctions::SetColorPalette (-1 = unresolved). */\n"); + sb.append(" val SOURCE_CASE = intArrayOf("); + for (int i = 0; i < 12; i++) sb.append(i > 0 ? ", " : "").append(CASE_FOR_INDEX[i]); + sb.append(")\n\n"); + for (int idx = 0; idx < 12; idx++) { + sb.append(" /** ").append(NAMES_CN[idx]).append(" / ").append(NAMES[idx]) + .append(" — UI index ").append(idx); + if (CASE_FOR_INDEX[idx] >= 0) { + sb.append(", libcxsdk case ").append(CASE_FOR_INDEX[idx]).append(" */\n"); + } else { + sb.append(", NO generator body resolved (preview asset is a stub) */\n"); + } + if (tables[idx] == null) { + sb.append(" val ").append(varName(idx)).append(": IntArray? = null\n\n"); + continue; + } + sb.append(" val ").append(varName(idx)).append(" = intArrayOf(\n"); + for (int k = 0; k < 256; k += 8) { + sb.append(" "); + for (int j = 0; j < 8 && k + j < 256; j++) { + if (j > 0) sb.append(", "); + sb.append(tables[idx][k + j]); + } + sb.append(",\n"); + } + sb.append(" )\n\n"); + } + sb.append("}\n"); + Files.writeString(coreDir.resolve("VendorPalettes.kt"), sb.toString(), java.nio.charset.StandardCharsets.UTF_8); + System.out.println("wrote " + coreDir.resolve("VendorPalettes.kt")); + + // ---- 2. JSON ---- + StringBuilder j = new StringBuilder("{\n"); + j.append(" \"finding\": \"libcxsdk.so contains NO static palette tables. All 12 vendor palettes are computed at runtime by CFunctions::SetColorPalette (Ghidra FUNC 0x00026c70). A static scan for 256-entry alpha=0xFF runs returns nothing; the tables were recovered by porting the generator instead.\",\n"); + j.append(" \"static_candidates\": [],\n"); + j.append(" \"source\": {\"binary\": \"analysis/sdk_re/android_app/bin/libcxsdk.so\", \"sha256_expected_size\": 372476, \"function\": \"CFunctions::SetColorPalette\", \"decompilation\": \"analysis/sdk_re/android_app/libcxsdk_decomp.txt\"},\n"); + j.append(" \"published_entry_layout\": \"byte0=B, byte1=G, byte2=R, byte3=0 (pinned by the iron bow anchor: 256/256)\",\n"); + j.append(" \"ui_order\": ["); + for (int i = 0; i < 12; i++) j.append(i > 0 ? ", " : "").append("\"").append(NAMES[i]).append("\""); + j.append("],\n"); + j.append(" \"index_to_case\": {"); + for (int i = 0; i < 12; i++) j.append(i > 0 ? ", " : "").append("\"").append(NAMES[i]).append("\": ").append(CASE_FOR_INDEX[i]); + j.append("},\n"); + j.append(" \"unexposed_cases\": [12, 13],\n"); + j.append(" \"tables\": {\n"); + List entries = new ArrayList<>(); + for (int idx = 0; idx < 12; idx++) { + if (tables[idx] == null) continue; + StringBuilder t = new StringBuilder(" \"").append(NAMES[idx]).append("\": ["); + for (int k = 0; k < 256; k++) t.append(k > 0 ? ", " : "").append(tables[idx][k]); + t.append("]"); + entries.add(t.toString()); + } + j.append(String.join(",\n", entries)).append("\n }\n}\n"); + Files.writeString(outDir.resolve("palette_candidates.json"), j.toString(), java.nio.charset.StandardCharsets.UTF_8); + System.out.println("wrote palette_candidates.json"); + + // ---- 3. PNG strip sheet: official preview | recovered palette ---- + int iw = 130, ih = 60, barH = 16, pad = 6, rowsPerCol = 6; + int labelH = 18; + int colW = iw + pad + 256 / 2 + pad * 2; + int rowH = Math.max(ih, barH) + labelH + pad; + BufferedImage sheet = new BufferedImage(colW * 2 + pad, rowH * rowsPerCol + pad, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = sheet.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setColor(Color.BLACK); + g.fillRect(0, 0, sheet.getWidth(), sheet.getHeight()); + g.setFont(new Font("SansSerif", Font.PLAIN, 12)); + for (int idx = 0; idx < 12; idx++) { + int col = idx / rowsPerCol, row = idx % rowsPerCol; + int x0 = pad + col * colW, y0 = pad + row * rowH; + int[] t = tables[idx]; + if (t == null) continue; + g.setColor(Color.WHITE); + g.drawString(String.format("%d %s (%s) — libcxsdk case %d", idx, NAMES_CN[idx], NAMES[idx], CASE_FOR_INDEX[idx]), + x0, y0 + 12); + // official preview image + File pf = new File(apkRes, "palette_" + NAMES[idx] + ".png"); + if (pf.isFile()) { + BufferedImage pv = ImageIO.read(pf); + g.drawImage(pv, x0, y0 + labelH, iw, ih, null); + } + // recovered palette strip + int bx = x0 + iw + pad; + for (int k = 0; k < 128; k++) { + int v = t[k * 2]; + g.setColor(new Color((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)); + g.fillRect(bx + k * 2, y0 + labelH + (ih - barH) / 2, 2, barH); + } + } + g.dispose(); + ImageIO.write(sheet, "png", outDir.resolve("palette_candidates.png").toFile()); + System.out.println("wrote palette_candidates.png (" + sheet.getWidth() + "x" + sheet.getHeight() + ")"); + + // ---- 4. evidence report ---- + StringBuilder rep = new StringBuilder(); + rep.append("Phase C palette extraction — evidence\n"); + rep.append("=====================================\n\n"); + rep.append("Conclusion: libcxsdk.so has NO static palette tables. A full-file scan for\n"); + rep.append("256 consecutive u32 LE entries with alpha=0xFF and non-zero RGB finds 0 hits\n"); + rep.append("(also 0 with alpha=0x00, and the BGR/RGB triple forms). CFunctions::\n"); + rep.append("SetColorPalette builds the palettes with arithmetic at runtime.\n\n"); + rep.append("Validation chain:\n"); + rep.append(" 1. port reproduces OfficialTables.PALETTE256_ARGB 256/256 (iron bow)\n"); + rep.append(" 2. every case writes all 256 entries exactly once\n"); + rep.append(" 3. colour coverage of the official APK preview mipmaps:\n\n"); + rep.append(String.format(" %-16s %8s %14s %14s%n", "palette", "colors", "own case", "best other")); + for (int idx = 0; idx < 12; idx++) { + File pf = new File(apkRes, "palette_" + NAMES[idx] + ".png"); + if (!pf.isFile()) { rep.append(String.format(" %-16s preview missing%n", NAMES[idx])); continue; } + BufferedImage img = ImageIO.read(pf); + Set cols = new HashSet<>(); + for (int y = 0; y < img.getHeight(); y++) + for (int x = 0; x < img.getWidth(); x++) { + int p = img.getRGB(x, y); + if ((p >>> 24) == 0xFF) cols.add(p & 0xFFFFFF); + } + double own = -1, other = -1; + for (int k = 0; k < 12; k++) { + int[] cand = tables[k]; + if (cand == null) continue; + Set set = new HashSet<>(); + for (int v : cand) set.add(v & 0xFFFFFF); + int hit = 0; + for (int c : cols) if (set.contains(c)) hit++; + double cov = 100.0 * hit / cols.size(); + if (k == idx) own = cov; else other = Math.max(other, cov); + } + rep.append(String.format(" %-16s %8d %13.1f%% %13.1f%%%n", NAMES[idx], cols.size(), own, other)); + } + rep.append("\nNotes:\n"); + rep.append(" - red_hot: the official preview asset is a stub (137 of 11914 pixels differ\n"); + rep.append(" from palette_white_hot.png), so it carries no palette information. No\n"); + rep.append(" generator body is claimed for UI index 11; Palettes.kt keeps its\n"); + rep.append(" approximation with an explicit comment.\n"); + rep.append(" - cases 12 and 13 exist in the native library but explain no official\n"); + rep.append(" preview (best 3-6% against jet); they are not among the 12 UI palettes.\n"); + rep.append(" - the previews are scaled photographic crops, so absolute coverage is low\n"); + rep.append(" for some palettes; the correct body always wins by a wide margin.\n"); + Files.writeString(outDir.resolve("palette_match_report.txt"), rep.toString(), java.nio.charset.StandardCharsets.UTF_8); + System.out.println("wrote palette_match_report.txt"); + + int bad = 0; + int[] anchor = readAnchor(kt); + for (int i = 0; i < 256; i++) if (tables[2][i] != anchor[i]) bad++; + System.out.println("iron_bow vs OfficialTables anchor: " + (256 - bad) + "/256"); + } + + static String varName(int idx) { + StringBuilder sb = new StringBuilder(); + for (char ch : NAMES[idx].toCharArray()) sb.append(ch == '_' ? '_' : Character.toUpperCase(ch)); + return sb.toString(); + } + + static int[] emit(byte[][] buf) { + int[] out = new int[256]; + for (int i = 0; i < 256; i++) { + int b0 = buf[i][0] & 0xFF, b1 = buf[i][1] & 0xFF, b2 = buf[i][2] & 0xFF; + out[i] = 0xFF000000 | (b2 << 16) | (b1 << 8) | b0; + } + return out; + } + + static int[] readAnchor(String path) throws Exception { + String src = Files.readString(Paths.get(path), java.nio.charset.StandardCharsets.UTF_8); + int k = src.indexOf("val PALETTE256_ARGB = intArrayOf("); + int start = k + "val PALETTE256_ARGB = intArrayOf(".length(); + int end = src.indexOf(")", start); + List vals = new ArrayList<>(); + StringBuilder num = new StringBuilder(); + for (char ch : src.substring(start, end).toCharArray()) { + if (ch == '-' || (ch >= '0' && ch <= '9')) num.append(ch); + else if (num.length() > 0) { vals.add(Integer.parseInt(num.toString())); num.setLength(0); } + } + return vals.stream().mapToInt(Integer::intValue).toArray(); + } +} diff --git a/analysis/tools/PalIdentify.java b/analysis/tools/PalIdentify.java new file mode 100644 index 0000000..00009bf --- /dev/null +++ b/analysis/tools/PalIdentify.java @@ -0,0 +1,283 @@ +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.*; +import javax.imageio.ImageIO; + +/** + * Resolves which decompiled generator body produces which official palette. + * + * The Ghidra listing shows 13 generator bodies (cases 0..10, 0xc, 0xd) but the + * official app exposes 12 palettes, so the case->index mapping cannot be read + * off the listing directly. This tool identifies each body by matching it + * against the palette preview image the official app ships + * (res/mipmap-hdpi-v4/palette_.png) and reporting the best assignment. + * + * Uses a 32x32x32 quantised lookup so the full 13 x 12 x 2 comparison is fast, + * and reports both byte-order conventions. + */ +public class PalIdentify { + static final int Q = 32; // quantisation levels per channel + static final int STEP = 256 / Q; + + public static void main(String[] args) throws Exception { + String apkRes = args[0]; + String[] names = {"white_hot","black_hot","iron_bow","rain_bow","glow_bow", + "autumn","winter","hot_metal","jet","red_saturation","high_contrast","red_hot"}; + + // build all 13 bodies in both conventions + int[][] direct = new int[14][]; + int[][] swapped = new int[14][]; + for (int c : new int[]{0,1,2,3,4,5,6,7,8,9,10,12,13}) { + byte[][] buf = PalBody.run(c); + direct[c] = emit(buf, false); + swapped[c] = emit(buf, true); + } + + // preview histograms + double[][] hist = new double[12][Q * Q * Q]; + for (int i = 0; i < 12; i++) { + File f = new File(apkRes, "palette_" + names[i] + ".png"); + BufferedImage img = ImageIO.read(f); + int n = 0; + for (int y = 0; y < img.getHeight(); y++) + for (int x = 0; x < img.getWidth(); x++) { + int a = img.getRGB(x, y); + if ((a >>> 24) != 0xFF) continue; + hist[i][qidx(a)]++; + n++; + } + for (int k = 0; k < hist[i].length; k++) hist[i][k] /= n; + } + + System.out.println("=== best-matching generator body per official palette ==="); + System.out.printf("%-16s %-28s %-28s%n", "palette", "direct", "swapped"); + int[] bestBodyD = new int[12], bestBodyS = new int[12]; + for (int i = 0; i < 12; i++) { + double[] dd = new double[14], ds = new double[14]; + int bd = -1, bs = -1; + for (int c : new int[]{0,1,2,3,4,5,6,7,8,9,10,12,13}) { + dd[c] = dist(hist[i], direct[c]); + ds[c] = dist(hist[i], swapped[c]); + if (bd < 0 || dd[c] < dd[bd]) bd = c; + if (bs < 0 || ds[c] < ds[bs]) bs = c; + } + bestBodyD[i] = bd; bestBodyS[i] = bs; + System.out.printf("%-16s %-28s %-28s%n", names[i], + String.format("case %-4d d=%.2f", bd, dd[bd]), + String.format("case %-4d d=%.2f", bs, ds[bs])); + } + + System.out.println("\n=== body -> palettes it best explains (direct) ==="); + for (int c : new int[]{0,1,2,3,4,5,6,7,8,9,10,12,13}) { + double best = Double.MAX_VALUE; String who = "-"; + for (int i = 0; i < 12; i++) { + double d = dist(hist[i], direct[c]); + if (d < best) { best = d; who = names[i]; } + } + System.out.printf("case %-3d -> %-16s (d=%.2f)%n", c, who, best); + } + } + + static int qidx(int argb) { + int r = ((argb >> 16) & 0xFF) / STEP; + int g = ((argb >> 8) & 0xFF) / STEP; + int b = (argb & 0xFF) / STEP; + return (r * Q + g) * Q + b; + } + + static double dist(double[] hist, int[] pal) { + double[] lut = new double[Q * Q * Q]; + for (int r = 0; r < Q; r++) + for (int g = 0; g < Q; g++) + for (int b = 0; b < Q; b++) { + int rr = r * STEP, gg = g * STEP, bb = b * STEP; + int best = Integer.MAX_VALUE; + for (int v : pal) { + int dr = rr - ((v >> 16) & 0xFF); + int dg = gg - ((v >> 8) & 0xFF); + int db = bb - (v & 0xFF); + int d2 = dr * dr + dg * dg + db * db; + if (d2 < best) best = d2; + } + lut[(r * Q + g) * Q + b] = Math.sqrt(best); + } + double sum = 0; + for (int k = 0; k < hist.length; k++) if (hist[k] != 0) sum += hist[k] * lut[k]; + return sum; + } + + static int[] emit(byte[][] buf, boolean swap) { + int[] out = new int[256]; + for (int i = 0; i < 256; i++) { + int b0 = buf[i][swap ? 2 : 0] & 0xFF; + int b1 = buf[i][1] & 0xFF; + int b2 = buf[i][swap ? 0 : 2] & 0xFF; + out[i] = 0xFF000000 | (b2 << 16) | (b1 << 8) | b0; + } + return out; + } +} + +/** Raw generator bodies: returns the 256x4 byte buffer each case produces. */ +class PalBody { + static final int BASE = 0x20cc; + static byte[][] buf; + static int[] writes; + + static byte[][] run(int c) { + buf = new byte[256][4]; + writes = new int[256]; + switch (c) { + case 0: { + int v = 0, a = BASE; + do { st(a+1,v); st(a+2,v); st(a,v); st(a+3,0); a+=4; v++; } while (v != 0x100); + break; + } + case 1: { + int v = 0x100, a = BASE; + do { st(a+3,0); v--; st(a+1,v); st(a+2,v); st(a,v); a+=4; } while (v != 0); + break; + } + case 2: { + for (int k = 0; k < 22; k++) { int a=BASE+k*4; st(a+1,0); st(a+2,0); st(a,u(0x75*k,0x16)); st(a+3,0); } + for (int k = 0; k < 71; k++) { int a=BASE+(22+k)*4; st(a+1,0); st(a+3,0); st(a+2,u(0xbc*k,0x47)); st(a,u(u(0x25*k,0x47)+0x75)); } + for (int k = 0; k < 29; k++) { int a=BASE+(93+k)*4; st(a+1,u(0xff*k,0x94)); st(a+3,0); st(a+2,u(u(0x27*k,0x1d)-0x44)); st(a,u(s(-0x3b*k,0x1d)-0x66)); } + for (int k = 0; k < 18; k++) { int a=BASE+(122+k)*4; st(a+1,u(0x1ce3+0xff*k,0x94)); st(a+3,0); st(a+2,u(u(0x1c*k,0x1e)-0x1d)); st(a,u(s(-0x50*k,0x12)+0x5f)); } + for (int k = 0; k < 12; k++) { int a=BASE+(140+k)*4; st(a+1,u(0x2ed1+0xff*k,0x94)); st(a+3,0); st(a+2,u(u(0x1f8+0x1c*k,0x1e)-0x1d)); st(a,0x0f); } + for (int k = 0; k < 71; k++) { int a=BASE+(152+k)*4; st(a+1,u(0x3ac5+0xff*k,0x94)); st(a+3,0); st(a+2,0xff); st(a,0x0f); } + for (int k = 0; k < 18; k++) { int a=BASE+(223+k)*4; st(a+1,u(0x817e+0xff*k,0x94)); st(a+3,0); st(a+2,0xff); st(a,u((0xf0*k>>5)+0x0f)); } + for (int k = 0; k < 15; k++) { int a=BASE+(241+k)*4; st(a+1,0xff); st(a+3,0); st(a+2,0xff); st(a,u(((0xf0e0+0xf0*k)>>5)+0x0f)); } + break; + } + case 3: { + int a=BASE,i,u4=0,u10=0; + for (i=0x40;i!=0;i--){ st(a+2,0); st(a+3,0); st(a+1,u(u10>>7)); st(a,u((u4>>6)+0x32)); u4+=0xcd; u10+=0xff; a+=4; } + a=BASE+0x100; u4=0; int i5=0; + for (i=0x40;i!=0;i--){ st(a+3,0); st(a+2,u(u4>>6)); st(a+1,u((u4+0x3fc0)>>7)); st(a,u(s(i5,0x40)-1)); a+=4; u4+=0xff; i5+=-0xff; } + a=BASE+0x200; i5=0; u4=0; + for (i=0x40;i!=0;i--){ st(a+2,0xff); st(a+3,0); st(a,u(u4>>7)); st(a+1,u(s(i5,0x40)-1)); i5+=-0xff; u4+=0xff; a+=4; } + int t=BASE+0x300; u4=0xbfc0; + for (i=0x40;i!=0;i--){ st(t+2,0xff); st(t+3,0); st(t,u(u4>>7)); st(t+1,u((u4+0x40)>>6)); u4+=0xff; t+=4; } + break; + } + case 4: { + int a=BASE,i; + for (i=-0x32;i!=0;i++){ st(a+2,0x32); st(a,0); st(a+3,0); st(a+1,u(i+0x32)); a+=4; } + a=BASE+0xc8; int i5=-0x96, i11=0; + for (;i5!=0;i5++){ st(a,0); st(a+3,0); st(a+1,u(i5-0x38)); st(a+2,u(u(i11,0x96)+0x32)); a+=4; i11+=0xcd; } + int c2=-0xe0, cv=0xc8; + do { st(c2+0x24cd,cv); st(c2+0x24ce,0xff); st(c2+0x24cc,0); st(c2+0x24cf,0); c2+=4; cv++; } while (c2 != 0); + break; + } + case 5: { + int a=BASE,i,u4=0,u10=0,u13=0; + for (i=0x40;i!=0;i--){ st(a+3,0); st(a+1,u(u10>>6)); st(a+2,u(u13>>6)); st(a,u(u4>>6)); u4+=0x24; u10+=0x16; u13+=0xcb; a+=4; } + a=BASE+0x100; u4=0; u10=0; int i5=0; + for (i=0x40;i!=0;i--){ st(a+3,0); st(a+1,u((u10>>6)+0x16)); st(a+2,u(u(u4>>6)-0x35)); st(a,u(s(i5,0x40)+0x24)); a+=4; u4+=0x34; u10+=0x44; i5+=-0x24; } + a=BASE+0x200; u4=0; + for (i=0x40;i!=0;i--){ st(a+2,0xff); st(a,0); st(a+3,0); st(a+1,u((u4>>6)+0x5a)); u4+=0x7c; a+=4; } + a=BASE+0x300; u4=0; u10=0; + for (i=0x40;i!=0;i--){ st(a+2,0xff); st(a+3,0); st(a,u(u10>>6)); st(a+1,u(u(u4>>6)-0x2a)); u4+=0x29; u10+=0xff; a+=4; } + break; + } + case 6: { + int a=BASE, i5=0, i11=0xff; + do { st(a+1,i11); st(a,i11); st(a+3,0); st(a+2,u(s(i5,100)-1)); a+=4; i5+=-0xff; i11--; } while (i11 != 0x9b); + a=BASE+0x190; i5=0x9b; i11=0; + do { st(a+1,i5); st(a,i5); st(a+3,0); st(a+2,u(u(i11,0x69))); a+=4; i11+=0xff; i5--; } while (i5 != 0x32); + int t=BASE+0x334; i11=0x33; + do { st(t+2,0xff); st(t+3,0); i11--; st(t+1,i11); st(t,i11); t+=4; } while (i11 != 0); + break; + } + case 7: { + int a=BASE,i,cv=0; + for (i=0x80;i!=0;i--){ st(a+1,0); st(a+2,cv); st(a,0); st(a+3,0); a+=4; cv+=2; } + int t=BASE+0x200; int i5=0x80, i11=0; + do { st(t+2,0xff); st(t+3,0); st(t+1,u(u(i11,0x7f))); st(t,u(u(i11,0x7f))); i11+=0xff; t+=4; i5--; } while (i5 != 0); + break; + } + case 8: { + int i11=-0x80; + do { st(i11+0x214d,0); st(i11+0x214e,0); st(i11+0x214c,u(i11-1)); st(i11+0x214f,0); i11+=4; } while (i11 != 0); + int a=BASE+0x80, u4=0, i; + for (i=0x40;i!=0;i--){ st(a+2,0); st(a,0xff); st(a+3,0); st(a+1,u(u4>>6)); u4+=0xff; a+=4; } + a=BASE+0x180; u4=0; int i1=0; + for (i=0x40;i!=0;i--){ st(a+1,0xff); st(a+3,0); st(a+2,u(u4>>6)); st(a,u(s(i1,0x40)-1)); a+=4; u4+=0xff; i1+=-0xff; } + a=BASE+0x280; int i5j=0x40; int j=0; + do { st(a+2,0xff); st(a,0); st(a+3,0); st(a+1,u(s(j,0x40)-1)); j+=-0xff; a+=4; i5j--; } while (i5j != 0); + int k=-0x80, m=0; + do { st(k+0x24cd,0); st(k+0x24cc,0); st(k+0x24cf,0); st(k+0x24ce,u(s(m,0x20)-1)); m+=-0x80; k+=4; } while (k != 0); + break; + } + case 9: { + int a=BASE, i11=0, i5=0x99, cv; + do { st(a+3,0); cv=u(u(i11,200)); st(a+1,cv); st(a+2,cv); st(a,cv); a+=4; i11+=0xff; i5--; } while (i5 != 0); + int t=-0xbc, uu=0x9867; + do { st(t+0x23ed,0xc3); st(t+0x23ee,u(u(uu,200))); st(t+0x23ec,0xc3); st(t+0x23ef,0); uu+=0xff; t+=4; } while (t != 0); + int c2=-0xe0, v=0; + do { st(c2+0x24ce,0xff); st(c2+0x24cf,0); int g=u(s(v,0x37)-0x3d); st(c2+0x24cd,g); st(c2+0x24cc,g); v+=-0xc3; c2+=4; } while (c2 != 0); + break; + } + case 10: { + int a=BASE, i11=0; + do { st(a+1,0); st(a+2,i11); st(a,i11); st(a+3,0); a+=4; i11+=7; } while (i11 != 0xfc); + int cv=0xfc, o=-0x94, u4=0; + do { st(o+0x21f1,0); st(o+0x21f2,cv); st(o+0x21f3,0); st(o+0x21f0,u(-4-(u4>>2))); u4+=7; cv+=-7; o+=4; } while (o != 0); + int p=BASE+0x124; int cv1=0; int u4b=0xf5; boolean more; + do { cv1+=7; st(p+1,cv1); st(p+2,0); st(p+3,0); st(p,u(-4-(u4b>>2))); p+=4; more=u4b!=0; u4b-=7; } while (more); + int q=BASE+0x1b4; int u4c=0; int i11b=0xfc; + do { st(q+2,0); st(q,i11b); st(q+3,0); st(q+1,u(-4-(u4c>>1))); u4c+=7; q+=4; more=i11b!=0; i11b-=7; } while (more); + int r=BASE+0x248; int cvr=0x7; int u4d=0xf5; + do { st(r+2,cvr); st(r,0); st(r+3,0); st(r+1,u(-4-(u4d>>1))); r+=4; cvr+=7; more=u4d!=0; u4d-=7; } while (more); + int u4e=0; int s=-0x94; int cvs=0xfc; + do { st(s+0x2439,cvs); st(s+0x2438,0); st(s+0x243b,0); st(s+0x243a,u(-4-(u4e>>2))); u4e+=7; cvs+=-7; s+=4; } while (s != 0); + int d=BASE+0x36c; int u4f=0xfc; int cv6=0; + do { st(d+1,cv6); st(d,cv6); st(d+3,0); st(d+2,u(-4-(u4f>>2))); d+=4; cv6+=7; more=u4f!=0; u4f-=7; } while (more); + break; + } + case 12: { // case 0xc + int a=BASE, i11=0; + do { st(a+1,0); st(a+2,0); st(a,i11); st(a+3,0); a+=4; i11+=7; } while (i11 != 0xfc); + int o=-0x94, cv=0; + do { st(o+0x21f1,cv); st(o+0x21f2,0); st(o+0x21f0,0xff); st(o+0x21f3,0); cv+=7; o+=4; } while (o != 0); + int p=BASE+0x124; int i11c=0; + do { st(p+1,0xff); st(p+2,0); st(p+3,0); st(p,u(i11c-0xb)); i11c+=-7; p+=4; } while (i11c != -0xfc); + int o2=-0x94, cv2=0; + do { st(o2+0x2315,0xff); st(o2+0x2316,cv2); st(o2+0x2314,0); st(o2+0x2317,0); cv2+=7; o2+=4; } while (o2 != 0); + int p2=BASE+0x248; int i11d=0; + do { st(p2+2,0xff); st(p2,0); st(p2+3,0); st(p2+1,u(i11d-0xb)); i11d+=-7; p2+=4; } while (i11d != -0xfc); + int o3=-0x94, cv3=0; + do { st(o3+0x2439,0); st(o3+0x243a,0xff); st(o3+0x2438,cv3); st(o3+0x243b,0); cv3+=7; o3+=4; } while (o3 != 0); + int o4=-0x94, cv4=0; + do { st(o4+0x24cd,cv4); st(o4+0x24ce,0xff); st(o4+0x24cc,0xff); st(o4+0x24cf,0); cv4+=7; o4+=4; } while (o4 != 0); + break; + } + case 13: { // case 0xd + int a=BASE, i5=0, i; + for (i=0x40;i!=0;i--){ st(a,0xff); st(a+1,u(i5)); st(a+2,0); st(a+3,0); i5+=4; a+=4; } + int b=BASE+0x100; int cv=0xfc; + for (i=0x40;i!=0;i--){ st(b+1,0xff); st(b+2,0); st(b,cv); st(b+3,0); cv-=4; b+=4; } + int c2=BASE+0x200; int cv1=0, cv6=0xfc; + for (i=0x40;i!=0;i--){ st(c2+1,0xff); st(c2+2,cv1); st(c2,0); st(c2+3,0); cv1+=4; c2+=4; } + int d=BASE+0x300; + for (i=0x40;i!=0;i--){ st(d+1,cv6); st(d+2,0xff); st(d,0); st(d+3,0); cv6-=4; d+=4; } + break; + } + } + for (int i = 0; i < 256; i++) { + if (writes[i] != 1) throw new IllegalStateException( + "body " + c + " entry " + i + " written " + writes[i] + " times"); + } + return buf; + } + + static void st(int addr, int v) { + int rel = addr - BASE, idx = rel / 4, off = rel % 4; + if (idx < 0 || idx > 255) throw new IllegalStateException("oob " + Integer.toHexString(addr)); + buf[idx][off] = (byte) v; + if (off == 0) writes[idx]++; + } + static int u(int v) { return v & 0xFF; } + static int u(int a, int b) { return Integer.divideUnsigned(a, b); } + static int s(int a, int b) { return a / b; } +} diff --git a/analysis/tools/extract_palettes.py b/analysis/tools/extract_palettes.py new file mode 100644 index 0000000..5d13c31 --- /dev/null +++ b/analysis/tools/extract_palettes.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Phase C — vendor palette extraction from libcxsdk.so. + +Scans the official native library for static 256-entry ARGB palette tables and +compares any candidates against the iron-bow table the project already owns +(OfficialTables.PALETTE256_ARGB), which is the extraction anchor. + +RESULT ON THE SHIPPED LIBRARY (recorded in +analysis/sdk_re/android_app/palette_extraction_findings.md): the scan finds NO +static tables — libcxsdk.so builds all 12 palettes at runtime with arithmetic in +CFunctions::SetColorPalette. This script is therefore kept as the reproducible +proof of that negative (and would catch a table if a future vendor build embeds +one). The actual recovery was done by porting that routine; see +analysis/tools/PalIdentify.java + analysis/tools/PalExport2.java. + +Pure standard library (struct + zlib + json). Writes UTF-8 explicitly. + +Usage: + python3 extract_palettes.py +""" + +import json +import os +import re +import struct +import sys +import zlib + +ALPHA_RUN = 240 # minimum entries with alpha==0xFF and non-zero RGB +ENTRIES = 256 +RUN_BYTES = ENTRIES * 4 + + +def read_anchor(kt_path): + """Extract PALETTE256_ARGB (256 ARGB ints) from OfficialTables.kt.""" + with open(kt_path, "r", encoding="utf-8") as fh: + src = fh.read() + m = re.search(r"val PALETTE256_ARGB = intArrayOf\((.*?)\)", src, re.S) + if not m: + raise SystemExit("PALETTE256_ARGB not found in " + kt_path) + vals = [int(x) for x in re.findall(r"-?\d+", m.group(1))] + if len(vals) != ENTRIES: + raise SystemExit("anchor has %d entries, expected %d" % (len(vals), ENTRIES)) + return vals + + +def u32le(buf, off): + return struct.unpack_from("> 24) == 0xFF and (v & 0xFFFFFF) != 0: + ok += 1 + if ok == ENTRIES // 16: # cheap pass, verify fully + full = 0 + for i in range(ENTRIES): + v = u32le(data, off + i * 4) + if (v >> 24) == 0xFF and (v & 0xFFFFFF) != 0: + full += 1 + if full >= ALPHA_RUN: + hits.append(off) + return hits + + +def merge(hits): + """Merge overlapping runs (a 1024 B table can start at off, off+4, ...).""" + merged = [] + for off in hits: + if merged and off <= merged[-1][1]: + merged[-1][1] = max(merged[-1][1], off + RUN_BYTES) + else: + merged.append([off, off + RUN_BYTES]) + return merged + + +def write_png(path, rows, row_h, scale): + """Minimal PNG writer: one horizontal strip per row of 256 colours.""" + width = ENTRIES * scale + height = len(rows) * row_h + raw = bytearray() + for row in rows: + line = bytearray() + for x in range(width): + argb = row[x // scale] + line += bytes(((argb >> 16) & 0xFF, (argb >> 8) & 0xFF, argb & 0xFF)) + for _ in range(row_h): + raw.append(0) # filter type 0 + raw += line + + def chunk(tag, payload): + return (struct.pack(">I", len(payload)) + tag + payload + + struct.pack(">I", zlib.crc32(tag + payload) & 0xFFFFFFFF)) + + png = b"\x89PNG\r\n\x1a\n" + png += chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)) + png += chunk(b"IDAT", zlib.compress(bytes(raw), 9)) + png += chunk(b"IEND", b"") + with open(path, "wb") as fh: + fh.write(png) + + +def main(): + if len(sys.argv) != 4: + raise SystemExit(__doc__) + so_path, kt_path, out_dir = sys.argv[1:4] + os.makedirs(out_dir, exist_ok=True) + + with open(so_path, "rb") as fh: + data = fh.read() + anchor = read_anchor(kt_path) + print("scanned %s: %d bytes; anchor %d entries" % (so_path, len(data), len(anchor))) + + hits = scan(data) + print("static 256-entry alpha=0xFF runs: %d" % len(hits)) + runs = merge(hits) + + candidates = [] + anchor_at = None + for start, end in runs: + for off in range(start, end - RUN_BYTES + 1, 4): + if all(u32le(data, off + i * 4) == anchor[i] for i in range(ENTRIES)): + anchor_at = off + break + candidates.append({ + "offset": start, + "offsetHex": "0x%X" % start, + "hexPreview": ["0x%08X" % u32le(data, start + i * 4) for i in range(16)], + "length": ENTRIES, + }) + + if anchor_at is not None: + print("ironbow anchor found at 0x%X" % anchor_at) + else: + print("ironbow anchor NOT found — palettes are not stored as static tables") + + report = { + "binary": os.path.basename(so_path), + "binarySize": len(data), + "anchorFound": anchor_at is not None, + "anchorOffset": anchor_at, + "staticCandidates": candidates, + "conclusion": ("no static palette tables: CFunctions::SetColorPalette builds " + "them at runtime" if anchor_at is None else "static tables present"), + } + json_path = os.path.join(out_dir, "palette_candidates.json") + with open(json_path, "w", encoding="utf-8") as fh: + json.dump(report, fh, ensure_ascii=False, indent=2) + print("wrote %s" % json_path) + + # strip sheet: one row per candidate (empty sheet when none are found) + rows = [] + for start, end in runs: + rows.append([u32le(data, start + i * 4) for i in range(ENTRIES)]) + if not rows: + rows.append([0xFF000000] * ENTRIES) # placeholder so the file is valid + png_path = os.path.join(out_dir, "palette_candidates.png") + write_png(png_path, rows, row_h=32, scale=8) + print("wrote %s (%d rows)" % (png_path, len(rows))) + + +if __name__ == "__main__": + main() diff --git a/android/app/src/main/kotlin/com/mag160c/thermal/core/Palettes.kt b/android/app/src/main/kotlin/com/mag160c/thermal/core/Palettes.kt index 1b72fa7..8155361 100644 --- a/android/app/src/main/kotlin/com/mag160c/thermal/core/Palettes.kt +++ b/android/app/src/main/kotlin/com/mag160c/thermal/core/Palettes.kt @@ -7,8 +7,18 @@ import kotlin.math.sin import kotlin.math.sqrt /** - * The 12 display palettes of the official apps. Index 2 (ironbow) uses the - * palette extracted from CoreSDKLib; the rest are standard thermal curves. + * The 12 display palettes of the official apps, in the official UI order + * (DialogFragmentPalette index 0..11). + * + * Eleven of them are now the EXACT vendor tables, recovered from the official + * native SDK: libcxsdk.so stores no static palette data — CFunctions:: + * SetColorPalette computes every palette with arithmetic at runtime — so the + * tables were obtained by porting that routine and are validated against the + * iron-bow anchor (see VendorPalettes and + * analysis/sdk_re/android_app/palette_extraction_findings.md). + * + * Index 11 (红热) remains an approximation: no generator body could be matched + * to it (its preview asset in the official APK is a stub copy of 白热). */ object Palettes { val NAMES = listOf( @@ -23,18 +33,18 @@ object Palettes { /** Build all palettes as ARGB int arrays (256 entries each). */ fun buildAll(): List = listOf( - ramp(255, 255, 255, 0, 0, 0), // 0 white hot - ramp(0, 0, 0, 255, 255, 255), // 1 black hot - officialIronbow(), // 2 ironbow (vendor) - rainbow(), // 3 rainbow - ramp(0, 0, 0, 255, 183, 74), // 4 amber - ramp(0, 0, 0, 255, 220, 120), // 5 autumn - ramp(0, 0, 0, 200, 230, 255), // 6 winter - hotMetal(), // 7 hot metal - jet(), // 8 jet - ramp(0, 0, 0, 255, 0, 0), // 9 red saturation - highContrast(), // 10 high contrast - ramp(0, 0, 0, 128, 0, 0), // 11 red hot + VendorPalettes.WHITE_HOT, // 0 白热 — extracted libcxsdk case 0 + VendorPalettes.BLACK_HOT, // 1 黑热 — extracted libcxsdk case 1 + officialIronbow(), // 2 铁虹 — official table (anchor-verified) + VendorPalettes.RAIN_BOW, // 3 彩虹 — extracted libcxsdk case 3 + VendorPalettes.GLOW_BOW, // 4 琥珀 — extracted libcxsdk case 4 + VendorPalettes.AUTUMN, // 5 金秋 — extracted libcxsdk case 5 + VendorPalettes.WINTER, // 6 寒冬 — extracted libcxsdk case 6 + VendorPalettes.HOT_METAL, // 7 热金属 — extracted libcxsdk case 7 + VendorPalettes.JET, // 8 喷射 — extracted libcxsdk case 8 + VendorPalettes.RED_SATURATION, // 9 红饱和 — extracted libcxsdk case 9 + VendorPalettes.HIGH_CONTRAST, // 10 高对比度 — extracted libcxsdk case 10 + approximatedRedHot(), // 11 红热 — approximated (not found in binary) ) private fun argb(r: Int, g: Int, b: Int): Int = @@ -55,6 +65,14 @@ object Palettes { /** Vendor ironbow: reuse the generated official table. */ private fun officialIronbow(): IntArray = OfficialTables.PALETTE256_ARGB + /** + * Index 11 (红热 / red hot): approximation. The native library does define + * two further bodies (cases 12 and 13) but neither reproduces this palette's + * official preview (which is a stub copy of 白热 in the APK), so the + * previous approximation is kept rather than guessing. + */ + private fun approximatedRedHot(): IntArray = ramp(0, 0, 0, 128, 0, 0) + /** Classic ironbow-style fallback used by index 3 rainbow curve. */ private fun rainbow(): IntArray { val out = IntArray(256) diff --git a/android/app/src/main/kotlin/com/mag160c/thermal/core/VendorPalettes.kt b/android/app/src/main/kotlin/com/mag160c/thermal/core/VendorPalettes.kt new file mode 100644 index 0000000..50f9c4a --- /dev/null +++ b/android/app/src/main/kotlin/com/mag160c/thermal/core/VendorPalettes.kt @@ -0,0 +1,425 @@ +package com.mag160c.thermal.core + +/** + * Vendor display palettes recovered from the official native SDK. + * + * libcxsdk.so contains NO static palette tables. All palettes are built at + * runtime by {@code CFunctions::SetColorPalette}; the tables below are a + * line-by-line port of that routine (Ghidra listing: + * analysis/sdk_re/android_app/libcxsdk_decomp.txt, FUNC 0x00026c70). + * + * Evidence (analysis/sdk_re/android_app/palette_extraction_findings.md): + * - case 2 reproduces [OfficialTables.PALETTE256_ARGB] 256/256 — the table + * captured from the vendor runtime and verified pixel-exact against the + * official renderer. This pins both the arithmetic and the byte order + * (published entry = {byte0=B, byte1=G, byte2=R, byte3=0}). + * - every case writes each of the 256 entries exactly once. + * - each table reproduces the colours of its own preview image shipped in + * the official APK (res/mipmap-hdpi-v4/palette_.png). + * + * GENERATED by analysis/tools/PalExport2.java — do not edit by hand. + */ +object VendorPalettes { + /** UI index -> source case in CFunctions::SetColorPalette (-1 = unresolved). */ + val SOURCE_CASE = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1) + + /** 白热 / white_hot — UI index 0, libcxsdk case 0 */ + val WHITE_HOT = intArrayOf( + -16777216, -16711423, -16645630, -16579837, -16514044, -16448251, -16382458, -16316665, + -16250872, -16185079, -16119286, -16053493, -15987700, -15921907, -15856114, -15790321, + -15724528, -15658735, -15592942, -15527149, -15461356, -15395563, -15329770, -15263977, + -15198184, -15132391, -15066598, -15000805, -14935012, -14869219, -14803426, -14737633, + -14671840, -14606047, -14540254, -14474461, -14408668, -14342875, -14277082, -14211289, + -14145496, -14079703, -14013910, -13948117, -13882324, -13816531, -13750738, -13684945, + -13619152, -13553359, -13487566, -13421773, -13355980, -13290187, -13224394, -13158601, + -13092808, -13027015, -12961222, -12895429, -12829636, -12763843, -12698050, -12632257, + -12566464, -12500671, -12434878, -12369085, -12303292, -12237499, -12171706, -12105913, + -12040120, -11974327, -11908534, -11842741, -11776948, -11711155, -11645362, -11579569, + -11513776, -11447983, -11382190, -11316397, -11250604, -11184811, -11119018, -11053225, + -10987432, -10921639, -10855846, -10790053, -10724260, -10658467, -10592674, -10526881, + -10461088, -10395295, -10329502, -10263709, -10197916, -10132123, -10066330, -10000537, + -9934744, -9868951, -9803158, -9737365, -9671572, -9605779, -9539986, -9474193, + -9408400, -9342607, -9276814, -9211021, -9145228, -9079435, -9013642, -8947849, + -8882056, -8816263, -8750470, -8684677, -8618884, -8553091, -8487298, -8421505, + -8355712, -8289919, -8224126, -8158333, -8092540, -8026747, -7960954, -7895161, + -7829368, -7763575, -7697782, -7631989, -7566196, -7500403, -7434610, -7368817, + -7303024, -7237231, -7171438, -7105645, -7039852, -6974059, -6908266, -6842473, + -6776680, -6710887, -6645094, -6579301, -6513508, -6447715, -6381922, -6316129, + -6250336, -6184543, -6118750, -6052957, -5987164, -5921371, -5855578, -5789785, + -5723992, -5658199, -5592406, -5526613, -5460820, -5395027, -5329234, -5263441, + -5197648, -5131855, -5066062, -5000269, -4934476, -4868683, -4802890, -4737097, + -4671304, -4605511, -4539718, -4473925, -4408132, -4342339, -4276546, -4210753, + -4144960, -4079167, -4013374, -3947581, -3881788, -3815995, -3750202, -3684409, + -3618616, -3552823, -3487030, -3421237, -3355444, -3289651, -3223858, -3158065, + -3092272, -3026479, -2960686, -2894893, -2829100, -2763307, -2697514, -2631721, + -2565928, -2500135, -2434342, -2368549, -2302756, -2236963, -2171170, -2105377, + -2039584, -1973791, -1907998, -1842205, -1776412, -1710619, -1644826, -1579033, + -1513240, -1447447, -1381654, -1315861, -1250068, -1184275, -1118482, -1052689, + -986896, -921103, -855310, -789517, -723724, -657931, -592138, -526345, + -460552, -394759, -328966, -263173, -197380, -131587, -65794, -1, + ) + + /** 黑热 / black_hot — UI index 1, libcxsdk case 1 */ + val BLACK_HOT = intArrayOf( + -1, -65794, -131587, -197380, -263173, -328966, -394759, -460552, + -526345, -592138, -657931, -723724, -789517, -855310, -921103, -986896, + -1052689, -1118482, -1184275, -1250068, -1315861, -1381654, -1447447, -1513240, + -1579033, -1644826, -1710619, -1776412, -1842205, -1907998, -1973791, -2039584, + -2105377, -2171170, -2236963, -2302756, -2368549, -2434342, -2500135, -2565928, + -2631721, -2697514, -2763307, -2829100, -2894893, -2960686, -3026479, -3092272, + -3158065, -3223858, -3289651, -3355444, -3421237, -3487030, -3552823, -3618616, + -3684409, -3750202, -3815995, -3881788, -3947581, -4013374, -4079167, -4144960, + -4210753, -4276546, -4342339, -4408132, -4473925, -4539718, -4605511, -4671304, + -4737097, -4802890, -4868683, -4934476, -5000269, -5066062, -5131855, -5197648, + -5263441, -5329234, -5395027, -5460820, -5526613, -5592406, -5658199, -5723992, + -5789785, -5855578, -5921371, -5987164, -6052957, -6118750, -6184543, -6250336, + -6316129, -6381922, -6447715, -6513508, -6579301, -6645094, -6710887, -6776680, + -6842473, -6908266, -6974059, -7039852, -7105645, -7171438, -7237231, -7303024, + -7368817, -7434610, -7500403, -7566196, -7631989, -7697782, -7763575, -7829368, + -7895161, -7960954, -8026747, -8092540, -8158333, -8224126, -8289919, -8355712, + -8421505, -8487298, -8553091, -8618884, -8684677, -8750470, -8816263, -8882056, + -8947849, -9013642, -9079435, -9145228, -9211021, -9276814, -9342607, -9408400, + -9474193, -9539986, -9605779, -9671572, -9737365, -9803158, -9868951, -9934744, + -10000537, -10066330, -10132123, -10197916, -10263709, -10329502, -10395295, -10461088, + -10526881, -10592674, -10658467, -10724260, -10790053, -10855846, -10921639, -10987432, + -11053225, -11119018, -11184811, -11250604, -11316397, -11382190, -11447983, -11513776, + -11579569, -11645362, -11711155, -11776948, -11842741, -11908534, -11974327, -12040120, + -12105913, -12171706, -12237499, -12303292, -12369085, -12434878, -12500671, -12566464, + -12632257, -12698050, -12763843, -12829636, -12895429, -12961222, -13027015, -13092808, + -13158601, -13224394, -13290187, -13355980, -13421773, -13487566, -13553359, -13619152, + -13684945, -13750738, -13816531, -13882324, -13948117, -14013910, -14079703, -14145496, + -14211289, -14277082, -14342875, -14408668, -14474461, -14540254, -14606047, -14671840, + -14737633, -14803426, -14869219, -14935012, -15000805, -15066598, -15132391, -15198184, + -15263977, -15329770, -15395563, -15461356, -15527149, -15592942, -15658735, -15724528, + -15790321, -15856114, -15921907, -15987700, -16053493, -16119286, -16185079, -16250872, + -16316665, -16382458, -16448251, -16514044, -16579837, -16645630, -16711423, -16777216, + ) + + /** 铁虹 / iron_bow — UI index 2, libcxsdk case 2 */ + val IRON_BOW = intArrayOf( + -16777216, -16777211, -16777206, -16777201, -16777195, -16777190, -16777185, -16777179, + -16777174, -16777169, -16777163, -16777158, -16777153, -16777147, -16777142, -16777137, + -16777131, -16777126, -16777121, -16777115, -16777110, -16777105, -16777099, -16646027, + -16449418, -16318346, -16121737, -15925129, -15794056, -15597448, -15400839, -15269767, + -15073158, -14876550, -14745477, -14548869, -14352260, -14221188, -14024579, -13827971, + -13696898, -13500290, -13369217, -13172609, -12976000, -12844928, -12648319, -12451710, + -12320638, -12124029, -11927421, -11796348, -11599740, -11403131, -11272059, -11075450, + -10878842, -10747769, -10551161, -10420088, -10223480, -10026871, -9895799, -9699190, + -9502582, -9371509, -9174901, -8978292, -8847220, -8650611, -8454002, -8322930, + -8126321, -7929713, -7798640, -7602032, -7470959, -7274351, -7077742, -6946670, + -6750061, -6553453, -6422380, -6225772, -6029163, -5898091, -5701482, -5504874, + -5373801, -5177193, -4980584, -4849512, -4652903, -4456294, -4390504, -4324458, + -4192876, -4127086, -4061040, -3929458, -3863412, -3797622, -3666040, -3599994, + -3534204, -3402622, -3336576, -3270530, -3139204, -3073158, -3007112, -2875530, + -2809740, -2743694, -2612112, -2546322, -2480276, -2348694, -2282648, -2216858, + -2085276, -2019230, -1887905, -1887397, -1821353, -1755310, -1689522, -1623479, + -1557435, -1491392, -1425604, -1359561, -1293517, -1227729, -1161686, -1095642, + -1029599, -963811, -963304, -897260, -831473, -765425, -699377, -633329, + -567537, -501489, -435441, -369393, -303601, -237553, -171505, -105713, + -39665, -39153, -38641, -38385, -37873, -37361, -37105, -36593, + -36081, -35569, -35313, -34801, -34289, -33777, -33521, -33009, + -32497, -32241, -31729, -31217, -30705, -30449, -29937, -29425, + -28913, -28657, -28145, -27633, -27377, -26865, -26353, -25841, + -25585, -25073, -24561, -24305, -23793, -23281, -22769, -22513, + -22001, -21489, -20977, -20721, -20209, -19697, -19441, -18929, + -18417, -17905, -17649, -17137, -16625, -16369, -15857, -15345, + -14833, -14577, -14065, -13553, -13041, -12785, -12273, -11761, + -11505, -10993, -10481, -9969, -9713, -9201, -8689, -8433, + -7914, -7394, -6875, -6611, -6092, -5572, -5053, -4789, + -4270, -3750, -3487, -2967, -2448, -1928, -1665, -1145, + -626, -106, -99, -91, -84, -76, -69, -61, + -54, -46, -39, -31, -24, -16, -9, -1, + ) + + /** 彩虹 / rain_bow — UI index 3, libcxsdk case 3 */ + val RAIN_BOW = intArrayOf( + -16777166, -16776907, -16776392, -16775877, -16775362, -16774846, -16774331, -16773816, + -16773301, -16772786, -16772270, -16771755, -16771240, -16770725, -16770210, -16769694, + -16769179, -16768664, -16768149, -16767634, -16767118, -16766603, -16766088, -16765573, + -16765058, -16764542, -16764027, -16763512, -16762997, -16762482, -16761966, -16761451, + -16760936, -16760421, -16759906, -16759390, -16758875, -16758360, -16757845, -16757330, + -16756814, -16756299, -16755784, -16755269, -16754754, -16754238, -16753723, -16753208, + -16752693, -16752178, -16751662, -16751147, -16750632, -16750117, -16749602, -16749086, + -16748571, -16748056, -16747541, -16747026, -16746510, -16745995, -16745480, -16744965, + -16744449, -16547332, -16284680, -16022028, -15759376, -15496724, -15234072, -14971420, + -14708768, -14446116, -14183464, -13920812, -13658160, -13395508, -13132856, -12870204, + -12607552, -12344900, -12082248, -11819596, -11556944, -11294292, -11031640, -10768988, + -10506336, -10243684, -9981032, -9718380, -9455728, -9193076, -8930424, -8667772, + -8405120, -8142468, -7879816, -7617164, -7354512, -7091860, -6829208, -6566556, + -6303904, -6041252, -5778600, -5515948, -5253296, -4990644, -4727992, -4465340, + -4202688, -3940036, -3677384, -3414732, -3152080, -2889428, -2626776, -2364124, + -2101472, -1838820, -1576168, -1313516, -1050864, -788212, -525560, -262908, + -256, -1023, -2045, -3067, -4089, -5111, -6133, -7155, + -8177, -9199, -10221, -11243, -12265, -13287, -14309, -15331, + -16353, -17375, -18397, -19419, -20441, -21463, -22485, -23507, + -24529, -25551, -26573, -27595, -28617, -29639, -30661, -31683, + -32705, -33727, -34749, -35771, -36793, -37815, -38837, -39859, + -40881, -41903, -42925, -43947, -44969, -45991, -47013, -48035, + -49057, -50079, -51101, -52123, -53145, -54167, -55189, -56211, + -57233, -58255, -59277, -60299, -61321, -62343, -63365, -64387, + -65409, -64639, -63613, -62587, -61561, -60535, -59509, -58483, + -57457, -56431, -55405, -54379, -53353, -52327, -51301, -50275, + -49249, -48223, -47197, -46171, -45145, -44119, -43093, -42067, + -41041, -40015, -38989, -37963, -36937, -35911, -34885, -33859, + -32833, -31807, -30781, -29755, -28729, -27703, -26677, -25651, + -24625, -23599, -22573, -21547, -20521, -19495, -18469, -17443, + -16417, -15391, -14365, -13339, -12313, -11287, -10261, -9235, + -8209, -7183, -6157, -5131, -4105, -3079, -2053, -1027, + ) + + /** 琥珀 / glow_bow — UI index 4, libcxsdk case 4 */ + val GLOW_BOW = intArrayOf( + -13500416, -13500160, -13499904, -13499648, -13499392, -13499136, -13498880, -13498624, + -13498368, -13498112, -13497856, -13497600, -13497344, -13497088, -13496832, -13496576, + -13496320, -13496064, -13495808, -13495552, -13495296, -13495040, -13494784, -13494528, + -13494272, -13494016, -13493760, -13493504, -13493248, -13492992, -13492736, -13492480, + -13492224, -13491968, -13491712, -13491456, -13491200, -13490944, -13490688, -13490432, + -13490176, -13489920, -13489664, -13489408, -13489152, -13488896, -13488640, -13488384, + -13488128, -13487872, -13487616, -13421824, -13356032, -13224704, -13158912, -13093120, + -12961792, -12896000, -12830208, -12698880, -12633088, -12501760, -12435968, -12370176, + -12238848, -12173056, -12107264, -11975936, -11910144, -11844352, -11713024, -11647232, + -11515904, -11450112, -11384320, -11252992, -11187200, -11121408, -10990080, -10924288, + -10792960, -10727168, -10661376, -10530048, -10464256, -10398464, -10267136, -10201344, + -10135552, -10004224, -9938432, -9807104, -9741312, -9675520, -9544192, -9478400, + -9412608, -9281280, -9215488, -9149696, -9018368, -8952576, -8821248, -8755456, + -8689664, -8558336, -8492544, -8426752, -8295424, -8229632, -8098304, -8032512, + -7966720, -7835392, -7769600, -7703808, -7572480, -7506688, -7440896, -7309568, + -7243776, -7112448, -7046656, -6980864, -6849536, -6783744, -6717952, -6586624, + -6520832, -6455040, -6323712, -6257920, -6126592, -6060800, -5995008, -5863680, + -5797888, -5732096, -5600768, -5534976, -5403648, -5337856, -5272064, -5140736, + -5074944, -5009152, -4877824, -4812032, -4746240, -4614912, -4549120, -4417792, + -4352000, -4286208, -4154880, -4089088, -4023296, -3891968, -3826176, -3760384, + -3629056, -3563264, -3431936, -3366144, -3300352, -3169024, -3103232, -3037440, + -2906112, -2840320, -2708992, -2643200, -2577408, -2446080, -2380288, -2314496, + -2183168, -2117376, -2051584, -1920256, -1854464, -1723136, -1657344, -1591552, + -1460224, -1394432, -1328640, -1197312, -1131520, -1065728, -934400, -868608, + -737280, -671488, -605696, -474368, -408576, -342784, -211456, -145664, + -14336, -14080, -13824, -13568, -13312, -13056, -12800, -12544, + -12288, -12032, -11776, -11520, -11264, -11008, -10752, -10496, + -10240, -9984, -9728, -9472, -9216, -8960, -8704, -8448, + -8192, -7936, -7680, -7424, -7168, -6912, -6656, -6400, + -6144, -5888, -5632, -5376, -5120, -4864, -4608, -4352, + -4096, -3840, -3584, -3328, -3072, -2816, -2560, -2304, + -2048, -1792, -1536, -1280, -1024, -768, -512, -256, + ) + + /** 金秋 / autumn — UI index 5, libcxsdk case 5 */ + val AUTUMN = intArrayOf( + -16777216, -16580608, -16383999, -16187135, -15990526, -15793918, -15531517, -15334909, + -15138300, -14941435, -14744827, -14548218, -14285818, -14089209, -13892601, -13695736, + -13499127, -13302519, -13040118, -12843510, -12646901, -12450037, -12253428, -12056820, + -11794419, -11597810, -11401202, -11204337, -11007729, -10811120, -10548720, -10352111, + -10155246, -9958638, -9762029, -9499629, -9303020, -9106412, -8909547, -8712939, + -8516330, -8253929, -8057321, -7860712, -7663848, -7467239, -7270631, -7008230, + -6811621, -6615013, -6418148, -6221540, -6024931, -5762531, -5565922, -5369314, + -5172449, -4975840, -4779232, -4516831, -4320223, -4123614, -3926750, -3730141, + -3467740, -3467484, -3401693, -3335901, -3270110, -3204318, -3204063, -3138271, + -3072480, -3006689, -2940897, -2940642, -2874850, -2809059, -2743267, -2677476, + -2611429, -2611173, -2545382, -2479590, -2413799, -2348007, -2347752, -2281960, + -2216169, -2150378, -2084586, -2084331, -2018539, -1952748, -1886956, -1821165, + -1755118, -1754862, -1689071, -1623279, -1557488, -1491696, -1491441, -1425649, + -1359858, -1294067, -1228275, -1228020, -1162228, -1096437, -1030645, -964854, + -898807, -898551, -832760, -766968, -701177, -635385, -635130, -569338, + -503547, -437756, -371964, -371709, -305917, -240126, -174334, -108543, + -42496, -42240, -41728, -41216, -40704, -40192, -39680, -39168, + -38656, -38144, -37632, -37120, -36608, -36096, -35584, -35072, + -34560, -34304, -33792, -33280, -32768, -32256, -31744, -31232, + -30720, -30208, -29696, -29184, -28672, -28160, -27648, -27136, + -26624, -26368, -25856, -25344, -24832, -24320, -23808, -23296, + -22784, -22272, -21760, -21248, -20736, -20224, -19712, -19200, + -18688, -18432, -17920, -17408, -16896, -16384, -15872, -15360, + -14848, -14336, -13824, -13312, -12800, -12288, -11776, -11264, + -10752, -10749, -10489, -10485, -10225, -9965, -9961, -9701, + -9441, -9437, -9177, -8917, -8913, -8653, -8649, -8389, + -8129, -8125, -7865, -7605, -7601, -7341, -7081, -7077, + -6817, -6557, -6553, -6293, -6289, -6029, -5769, -5765, + -5505, -5245, -5241, -4981, -4721, -4717, -4457, -4453, + -4193, -3933, -3929, -3669, -3409, -3405, -3145, -2885, + -2881, -2621, -2361, -2357, -2097, -2093, -1833, -1573, + -1569, -1309, -1049, -1045, -785, -525, -521, -261, + ) + + /** 寒冬 / winter — UI index 6, libcxsdk case 6 */ + val WINTER = intArrayOf( + -1, -131330, -328195, -459524, -656389, -787718, -984583, -1115912, + -1312777, -1444106, -1640971, -1837836, -1969165, -2166030, -2297359, -2494224, + -2625553, -2822418, -2953747, -3150612, -3347477, -3478806, -3675671, -3807000, + -4003865, -4135194, -4332059, -4463388, -4660253, -4791582, -4988447, -5185312, + -5316641, -5513506, -5644835, -5841700, -5973029, -6169894, -6301223, -6498088, + -6694953, -6826282, -7023147, -7154476, -7351341, -7482670, -7679535, -7810864, + -8007729, -8139058, -8335923, -8532788, -8664117, -8860982, -8992311, -9189176, + -9320505, -9517370, -9648699, -9845564, -10042429, -10173758, -10370623, -10501952, + -10698817, -10830146, -11027011, -11158340, -11355205, -11486534, -11683399, -11880264, + -12011593, -12208458, -12339787, -12536652, -12667981, -12864846, -12996175, -13193040, + -13389905, -13521234, -13718099, -13849428, -14046293, -14177622, -14374487, -14505816, + -14702681, -14834010, -15030875, -15227740, -15359069, -15555934, -15687263, -15884128, + -16015457, -16212322, -16343651, -16540516, -16737381, -16606566, -16475751, -16279400, + -16148585, -15952234, -15821419, -15625068, -15494253, -15363438, -15167087, -15036272, + -14839921, -14709106, -14512755, -14381940, -14251125, -14054774, -13923959, -13727608, + -13596793, -13400442, -13269627, -13138812, -12942461, -12811646, -12615295, -12484480, + -12288129, -12157314, -12026499, -11830148, -11699333, -11502982, -11372167, -11175816, + -11045001, -10914186, -10717835, -10587020, -10390669, -10259854, -10063503, -9932688, + -9801873, -9605522, -9474707, -9278356, -9147541, -8951190, -8820375, -8689560, + -8493209, -8362394, -8166043, -8035228, -7838877, -7708062, -7577247, -7380896, + -7250081, -7053730, -6922915, -6726564, -6595749, -6464934, -6268583, -6137768, + -5941417, -5810602, -5614251, -5483436, -5352621, -5156270, -5025455, -4829104, + -4698289, -4501938, -4371123, -4240308, -4043957, -3913142, -3716791, -3585976, + -3389625, -3258810, -3127995, -2931644, -2800829, -2604478, -2473663, -2277312, + -2146497, -2015682, -1819331, -1688516, -1492165, -1361350, -1164999, -1034184, + -903369, -707018, -576203, -379852, -249037, -52686, -52943, -53200, + -53457, -53714, -53971, -54228, -54485, -54742, -54999, -55256, + -55513, -55770, -56027, -56284, -56541, -56798, -57055, -57312, + -57569, -57826, -58083, -58340, -58597, -58854, -59111, -59368, + -59625, -59882, -60139, -60396, -60653, -60910, -61167, -61424, + -61681, -61938, -62195, -62452, -62709, -62966, -63223, -63480, + -63737, -63994, -64251, -64508, -64765, -65022, -65279, -65536, + ) + + /** 热金属 / hot_metal — UI index 7, libcxsdk case 7 */ + val HOT_METAL = intArrayOf( + -16777216, -16646144, -16515072, -16384000, -16252928, -16121856, -15990784, -15859712, + -15728640, -15597568, -15466496, -15335424, -15204352, -15073280, -14942208, -14811136, + -14680064, -14548992, -14417920, -14286848, -14155776, -14024704, -13893632, -13762560, + -13631488, -13500416, -13369344, -13238272, -13107200, -12976128, -12845056, -12713984, + -12582912, -12451840, -12320768, -12189696, -12058624, -11927552, -11796480, -11665408, + -11534336, -11403264, -11272192, -11141120, -11010048, -10878976, -10747904, -10616832, + -10485760, -10354688, -10223616, -10092544, -9961472, -9830400, -9699328, -9568256, + -9437184, -9306112, -9175040, -9043968, -8912896, -8781824, -8650752, -8519680, + -8388608, -8257536, -8126464, -7995392, -7864320, -7733248, -7602176, -7471104, + -7340032, -7208960, -7077888, -6946816, -6815744, -6684672, -6553600, -6422528, + -6291456, -6160384, -6029312, -5898240, -5767168, -5636096, -5505024, -5373952, + -5242880, -5111808, -4980736, -4849664, -4718592, -4587520, -4456448, -4325376, + -4194304, -4063232, -3932160, -3801088, -3670016, -3538944, -3407872, -3276800, + -3145728, -3014656, -2883584, -2752512, -2621440, -2490368, -2359296, -2228224, + -2097152, -1966080, -1835008, -1703936, -1572864, -1441792, -1310720, -1179648, + -1048576, -917504, -786432, -655360, -524288, -393216, -262144, -131072, + -65536, -65022, -64508, -63994, -63480, -62966, -62452, -61938, + -61424, -60910, -60396, -59882, -59368, -58854, -58340, -57826, + -57312, -56798, -56284, -55770, -55256, -54742, -54228, -53714, + -53200, -52686, -52172, -51658, -51144, -50630, -50116, -49602, + -49088, -48574, -48060, -47546, -47032, -46518, -46004, -45490, + -44976, -44462, -43948, -43434, -42920, -42406, -41892, -41378, + -40864, -40350, -39836, -39322, -38808, -38294, -37780, -37266, + -36752, -36238, -35724, -35210, -34696, -34182, -33668, -33154, + -32640, -32126, -31612, -31098, -30584, -30070, -29556, -29042, + -28528, -28014, -27500, -26986, -26472, -25958, -25444, -24930, + -24416, -23902, -23388, -22874, -22360, -21846, -21332, -20818, + -20304, -19790, -19276, -18762, -18248, -17734, -17220, -16706, + -16192, -15678, -15164, -14650, -14136, -13622, -13108, -12594, + -12080, -11566, -11052, -10538, -10024, -9510, -8996, -8482, + -7968, -7454, -6940, -6426, -5912, -5398, -4884, -4370, + -3856, -3342, -2828, -2314, -1800, -1286, -772, -1, + ) + + /** 喷射 / jet — UI index 8, libcxsdk case 8 */ + val JET = intArrayOf( + -16777089, -16777085, -16777081, -16777077, -16777073, -16777069, -16777065, -16777061, + -16777057, -16777053, -16777049, -16777045, -16777041, -16777037, -16777033, -16777029, + -16777025, -16777021, -16777017, -16777013, -16777009, -16777005, -16777001, -16776997, + -16776993, -16776989, -16776985, -16776981, -16776977, -16776973, -16776969, -16776965, + -16776961, -16776193, -16775169, -16774145, -16773121, -16772097, -16771073, -16770049, + -16769025, -16768001, -16766977, -16765953, -16764929, -16763905, -16762881, -16761857, + -16760833, -16759809, -16758785, -16757761, -16756737, -16755713, -16754689, -16753665, + -16752641, -16751617, -16750593, -16749569, -16748545, -16747521, -16746497, -16745473, + -16744449, -16743425, -16742401, -16741377, -16740353, -16739329, -16738305, -16737281, + -16736257, -16735233, -16734209, -16733185, -16732161, -16731137, -16730113, -16729089, + -16728065, -16727041, -16726017, -16724993, -16723969, -16722945, -16721921, -16720897, + -16719873, -16718849, -16717825, -16716801, -16715777, -16714753, -16713729, -16712705, + -16711681, -16515076, -16252936, -15990796, -15728656, -15466516, -15204376, -14942236, + -14680096, -14417956, -14155816, -13893676, -13631536, -13369396, -13107256, -12845116, + -12582976, -12320836, -12058696, -11796556, -11534416, -11272276, -11010136, -10747996, + -10485856, -10223716, -9961576, -9699436, -9437296, -9175156, -8913016, -8650876, + -8388736, -8126596, -7864456, -7602316, -7340176, -7078036, -6815896, -6553756, + -6291616, -6029476, -5767336, -5505196, -5243056, -4980916, -4718776, -4456636, + -4194496, -3932356, -3670216, -3408076, -3145936, -2883796, -2621656, -2359516, + -2097376, -1835236, -1573096, -1310956, -1048816, -786676, -524536, -262396, + -256, -1024, -2048, -3072, -4096, -5120, -6144, -7168, + -8192, -9216, -10240, -11264, -12288, -13312, -14336, -15360, + -16384, -17408, -18432, -19456, -20480, -21504, -22528, -23552, + -24576, -25600, -26624, -27648, -28672, -29696, -30720, -31744, + -32768, -33792, -34816, -35840, -36864, -37888, -38912, -39936, + -40960, -41984, -43008, -44032, -45056, -46080, -47104, -48128, + -49152, -50176, -51200, -52224, -53248, -54272, -55296, -56320, + -57344, -58368, -59392, -60416, -61440, -62464, -63488, -64512, + -65536, -327680, -589824, -851968, -1114112, -1376256, -1638400, -1900544, + -2162688, -2424832, -2686976, -2949120, -3211264, -3473408, -3735552, -3997696, + -4259840, -4521984, -4784128, -5046272, -5308416, -5570560, -5832704, -6094848, + -6356992, -6619136, -6881280, -7143424, -7405568, -7667712, -7929856, -8192000, + ) + + /** 红饱和 / red_saturation — UI index 9, libcxsdk case 9 */ + val RED_SATURATION = intArrayOf( + -16777216, -16711423, -16645630, -16579837, -16448251, -16382458, -16316665, -16250872, + -16119286, -16053493, -15987700, -15856114, -15790321, -15724528, -15658735, -15527149, + -15461356, -15395563, -15329770, -15198184, -15132391, -15066598, -14935012, -14869219, + -14803426, -14737633, -14606047, -14540254, -14474461, -14408668, -14277082, -14211289, + -14145496, -14013910, -13948117, -13882324, -13816531, -13684945, -13619152, -13553359, + -13421773, -13355980, -13290187, -13224394, -13092808, -13027015, -12961222, -12895429, + -12763843, -12698050, -12632257, -12500671, -12434878, -12369085, -12303292, -12171706, + -12105913, -12040120, -11974327, -11842741, -11776948, -11711155, -11579569, -11513776, + -11447983, -11382190, -11250604, -11184811, -11119018, -11053225, -10921639, -10855846, + -10790053, -10658467, -10592674, -10526881, -10461088, -10329502, -10263709, -10197916, + -10066330, -10000537, -9934744, -9868951, -9737365, -9671572, -9605779, -9539986, + -9408400, -9342607, -9276814, -9145228, -9079435, -9013642, -8947849, -8816263, + -8750470, -8684677, -8618884, -8487298, -8421505, -8355712, -8224126, -8158333, + -8092540, -8026747, -7895161, -7829368, -7763575, -7697782, -7566196, -7500403, + -7434610, -7303024, -7237231, -7171438, -7105645, -6974059, -6908266, -6842473, + -6710887, -6645094, -6579301, -6513508, -6381922, -6316129, -6250336, -6184543, + -6052957, -5987164, -5921371, -5789785, -5723992, -5658199, -5592406, -5460820, + -5395027, -5329234, -5263441, -5131855, -5066062, -5000269, -4868683, -4802890, + -4737097, -4671304, -4539718, -4473925, -4408132, -4342339, -4210753, -4144960, + -4079167, -3947581, -3882045, -3816509, -3750973, -3619901, -3554365, -3488829, + -3357757, -3292221, -3226685, -3161149, -3030077, -2964541, -2899005, -2833469, + -2702397, -2636861, -2571325, -2440253, -2374717, -2309181, -2243645, -2112573, + -2047037, -1981501, -1915965, -1784893, -1719357, -1653821, -1522749, -1457213, + -1391677, -1326141, -1195069, -1129533, -1063997, -998461, -867389, -801853, + -736317, -605245, -539709, -474173, -408637, -277565, -212029, -146493, + -15421, -16192, -17220, -17991, -19019, -19790, -20818, -21589, + -22617, -23388, -24416, -25444, -26215, -27243, -28014, -29042, + -29813, -30841, -31612, -32640, -33411, -34439, -35467, -36238, + -37266, -38037, -39065, -39836, -40864, -41635, -42663, -43434, + -44462, -45490, -46261, -47289, -48060, -49088, -49859, -50887, + -51658, -52686, -53457, -54485, -55513, -56284, -57312, -58083, + -59111, -59882, -60910, -61681, -62709, -63480, -64508, -65536, + ) + + /** 高对比度 / high_contrast — UI index 10, libcxsdk case 10 */ + val HIGH_CONTRAST = intArrayOf( + -16777216, -16318457, -15859698, -15400939, -14942180, -14483421, -14024662, -13565903, + -13107144, -12648385, -12189626, -11730867, -11272108, -10813349, -10354590, -9895831, + -9437072, -8978313, -8519554, -8060795, -7602036, -7143277, -6684518, -6225759, + -5767000, -5308241, -4849482, -4390723, -3931964, -3473205, -3014446, -2555687, + -2096928, -1638169, -1179410, -720651, -261892, -720645, -1179399, -1638153, + -2096907, -2555660, -3014414, -3473168, -3931922, -4390675, -4849429, -5308183, + -5766937, -6225690, -6684444, -7143198, -7601952, -8060705, -8519459, -8978213, + -9436967, -9895720, -10354474, -10813228, -11271982, -11730735, -12189489, -12648243, + -13106997, -13565750, -14024504, -14483258, -14942012, -15400765, -15859519, -16318273, + -16777027, -16775233, -16773439, -16771645, -16769852, -16768058, -16766264, -16764470, + -16762677, -16760883, -16759089, -16757295, -16755502, -16753708, -16751914, -16750120, + -16748327, -16746533, -16744739, -16742945, -16741152, -16739358, -16737564, -16735770, + -16733977, -16732183, -16730389, -16728595, -16726802, -16725008, -16723214, -16721420, + -16719627, -16717833, -16716039, -16714245, -16712452, -16712452, -16713227, -16714258, + -16715033, -16716064, -16716839, -16717870, -16718645, -16719676, -16720451, -16721482, + -16722257, -16723288, -16724063, -16725094, -16725869, -16726900, -16727675, -16728706, + -16729481, -16730512, -16731287, -16732318, -16733093, -16734124, -16734899, -16735930, + -16736705, -16737736, -16738511, -16739542, -16740317, -16741348, -16742123, -16743154, + -16743929, -16744960, -16285184, -15825664, -15365888, -14906368, -14446592, -13987072, + -13527296, -13067776, -12608000, -12148480, -11688704, -11229184, -10769408, -10309888, + -9850112, -9390592, -8930816, -8471296, -8011520, -7552000, -7092224, -6632704, + -6172928, -5713408, -5253632, -4794112, -4334336, -3874816, -3415040, -2955520, + -2495744, -2036224, -1576448, -1116928, -657152, -197632, -197632, -264960, + -397824, -530688, -663552, -730880, -863744, -996608, -1129472, -1196800, + -1329664, -1462528, -1595392, -1662720, -1795584, -1928448, -2061312, -2128640, + -2261504, -2394368, -2527232, -2594560, -2727424, -2860288, -2993152, -3060480, + -3193344, -3326208, -3459072, -3526400, -3659264, -3792128, -3924992, -3992320, + -4125184, -4258048, -4390912, -4390912, -4258041, -4125170, -3992299, -3924964, + -3792093, -3659222, -3526351, -3459016, -3326145, -3193274, -3060403, -2993068, + -2860197, -2727326, -2594455, -2527120, -2394249, -2261378, -2128507, -2061172, + -1928301, -1795430, -1662559, -1595224, -1462353, -1329482, -1196611, -1129276, + -996405, -863534, -730663, -663328, -530457, -397586, -264715, -197380, + ) + + /** 红热 / red_hot — UI index 11, NO generator body resolved (preview asset is a stub) */ + val RED_HOT: IntArray? = null + +} diff --git a/android/app/src/test/kotlin/com/mag160c/thermal/core/PalettesTest.kt b/android/app/src/test/kotlin/com/mag160c/thermal/core/PalettesTest.kt new file mode 100644 index 0000000..addd2d7 --- /dev/null +++ b/android/app/src/test/kotlin/com/mag160c/thermal/core/PalettesTest.kt @@ -0,0 +1,112 @@ +package com.mag160c.thermal.core + +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * Phase C: the display palettes are the vendor's own tables, recovered from + * libcxsdk's runtime generator (see VendorPalettes). + */ +class PalettesTest { + private val all = Palettes.buildAll() + + @Test + fun allTwelvePalettesAreFullLength() { + assertEquals("twelve palettes in UI order", 12, all.size) + all.forEachIndexed { i, pal -> + assertEquals("palette ${Palettes.NAMES[i]} length", 256, pal.size) + } + } + + @Test + fun noPaletteIsASingleFlatColour() { + all.forEachIndexed { i, pal -> + assertTrue( + "palette ${Palettes.NAMES[i]} must contain more than one colour", + pal.toSet().size > 1, + ) + } + } + + /** The iron-bow anchor: must stay byte-identical to the verified table. */ + @Test + fun ironbowMatchesOfficialTables() { + assertArrayEquals(OfficialTables.PALETTE256_ARGB, all[2]) + } + + /** The anchor table must also be what the port produces. */ + @Test + fun ironbowMatchesPortedVendorTable() { + assertArrayEquals(OfficialTables.PALETTE256_ARGB, VendorPalettes.IRON_BOW) + } + + /** Indices 0..10 came from the vendor generator; 11 is the only approximation. */ + @Test + fun vendorTablesCoverIndicesZeroThroughTen() { + assertEquals(11, VendorPalettes.SOURCE_CASE.count { it >= 0 }) + assertEquals(-1, VendorPalettes.SOURCE_CASE[11]) + for (i in 0..10) { + assertArrayEquals( + "index $i must use the vendor table", + VendorPalettes.let { + listOf( + it.WHITE_HOT, it.BLACK_HOT, it.IRON_BOW, it.RAIN_BOW, it.GLOW_BOW, + it.AUTUMN, it.WINTER, it.HOT_METAL, it.JET, it.RED_SATURATION, + it.HIGH_CONTRAST, + ) + }[i], + all[i], + ) + } + } + + /** Vendor tables are opaque; a transparent entry would mean a bad port. */ + @Test + fun vendorPalettesAreOpaque() { + for (i in 0..10) { + all[i].forEachIndexed { k, v -> + assertEquals("palette $i entry $k alpha", 0xFF, v ushr 24) + } + } + } + + /** + * Endpoint spot-checks. These are the values the ported generator actually + * produces (verified against the tables in VendorPalettes.kt); they pin the + * byte order so a swapped or truncated port cannot pass silently. + */ + @Test + fun endpointsMatchTheVerifiedVendorTables() { + // white hot: black -> white + assertEquals(0x000000, all[0][0] and 0xFFFFFF) + assertEquals(0xFFFFFF, all[0][255] and 0xFFFFFF) + // black hot: white -> black + assertEquals(0xFFFFFF, all[1][0] and 0xFFFFFF) + assertEquals(0x000000, all[1][255] and 0xFFFFFF) + // iron bow: black -> white + assertEquals(0x000000, all[2][0] and 0xFFFFFF) + assertEquals(0xFFFFFF, all[2][255] and 0xFFFFFF) + // hot metal: black -> white (through red) + assertEquals(0x000000, all[7][0] and 0xFFFFFF) + assertEquals(0xFFFFFF, all[7][255] and 0xFFFFFF) + // jet: near-black blue at the cold end, dark red at the hot end + assertEquals(0x00007F, all[8][0] and 0xFFFFFF) + assertEquals(0x830000, all[8][255] and 0xFFFFFF) + // glow bow (amber): dark orange -> yellow + assertEquals(0x320000, all[4][0] and 0xFFFFFF) + assertEquals(0xFFFF00, all[4][255] and 0xFFFFFF) + } + + @Test + fun mostVendorPalettesUseTheFullDynamicRange() { + // high contrast intentionally repeats a few entries, so allow slack + for (i in 0..10) { + assertTrue( + "palette ${Palettes.NAMES[i]} should use most of its 256 slots", + all[i].toSet().size >= 250, + ) + } + } +} diff --git a/build-artifacts/mag160c-app-debug.apk b/build-artifacts/mag160c-app-debug.apk index 8fbe2be..1268144 100644 --- a/build-artifacts/mag160c-app-debug.apk +++ b/build-artifacts/mag160c-app-debug.apk @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a650f2499a96a2a3e6c2c4d23a54107a17dac35e1bb4e5fec2a511f1e8762b4f -size 11873886 +oid sha256:2fa63ca3e15f99b75a39b3aa69552f7b8ade53d110cde56097bb042d4505088f +size 11925201 diff --git a/docs/android_app/session_state.md b/docs/android_app/session_state.md index efe4031..e5e937c 100644 --- a/docs/android_app/session_state.md +++ b/docs/android_app/session_state.md @@ -395,7 +395,17 @@ 分析页温度条(中心/最低/最高)+ 点击测温探针(白点+环+温度标签,再点清除); 新增 MdtTest/TempMathTest(17 个单测全绿)。注:分析页图像为原生横向显示, 故探针映射用直接映射(不是实时页的 90° 逆映射)。APK 已更新。 -- [ ] Phase C:厂商 12 调色板精确提取(脚本) +- [x] Phase C(2026-09-10):**结论:libcxsdk.so 无静态调色板表**,12 调色板由 + CFunctions::SetColorPalette 运行时算术生成(全文件扫描 alpha=0xFF 的 + 256-run 命中 0)。改走"移植生成函数"路线:case 2 与 OfficialTables + PALETTE256_ARGB **256/256 逐字节一致**(锚点锁死算术+字节序), + 每 case 恰好写满 256 槽;官方 APK 预览图覆盖率自身恒为最高。 + **UI 索引 0..10 共 11 个调色板已换成官方精确表**(VendorPalettes.kt, + Palettes.kt 接线);索引 11 红热未解决(官方预览图是白热的占位副本、 + case12/13 对任何预览覆盖率≤6%)→ 保留近似并注明。 + 新增 PalettesTest(8 项);产物见 palette_extraction_findings.md。 + APK 已更新。 +- [ ] Phase D:云模块脚手架(Retrofit opt-in) ## 里程碑日志