From c882c52b2719a6590e1486d78966780ffcd942ba Mon Sep 17 00:00:00 2001 From: fxiii Date: Sun, 11 Jul 2021 21:07:20 +0200 Subject: [PATCH 1/5] Black Jack game: ignoring buttons events on pauses --- apps.json | 2 +- apps/blackjack/ChangeLog | 3 ++- apps/blackjack/blackjack.app.js | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps.json b/apps.json index cc21f3115..25a947b87 100644 --- a/apps.json +++ b/apps.json @@ -1746,7 +1746,7 @@ "name": "Black Jack game", "shortName":"Black Jack game", "icon": "blackjack.png", - "version":"0.01", + "version":"0.02", "description": "Simple implementation of card game Black Jack", "tags": "game", "allow_emulator":true, diff --git a/apps/blackjack/ChangeLog b/apps/blackjack/ChangeLog index c941d90e5..25b5f9195 100644 --- a/apps/blackjack/ChangeLog +++ b/apps/blackjack/ChangeLog @@ -1 +1,2 @@ -0.01: New game! BTN4- Hit card, BTN5- Stand \ No newline at end of file +0.01: New game! BTN4- Hit card, BTN5- Stand +0.02: ignore buttons on pauses \ No newline at end of file diff --git a/apps/blackjack/blackjack.app.js b/apps/blackjack/blackjack.app.js index bbee8137b..b88432fd9 100644 --- a/apps/blackjack/blackjack.app.js +++ b/apps/blackjack/blackjack.app.js @@ -18,6 +18,7 @@ const Diamonds = { width : 48, height : 48, bpp : 4, var deck = []; var player = {Hand:[]}; var computer = {Hand:[]}; +var ctx = {ready:true}; function createDeck() { var suits = ["Spades", "Hearts", "Diamonds", "Clubs"]; @@ -44,6 +45,7 @@ function shuffle(a) { } function EndGameMessdage(msg){ + ctx.ready = false; g.drawString(msg, 155, 200); setTimeout(function(){ startGame(); @@ -52,6 +54,7 @@ function EndGameMessdage(msg){ } function hitMe() { + if (!ctx.ready) return; player.Hand.push(deck.pop()); renderOnScreen(1); var playerWeight = calcWeight(player.Hand, 0); @@ -97,6 +100,8 @@ function calcWeight(hand, hideCard) { } function stand(){ + if (!ctx.ready) return; + ctx.ready = false; function sleepFor( sleepDuration ){ console.log("Sleeping..."); var now = new Date().getTime(); @@ -156,6 +161,7 @@ function renderOnScreen(HideCard) { function dealHands() { player.Hand= []; computer.Hand= []; + ctx.ready = false; setTimeout(function(){ player.Hand.push(deck.pop()); @@ -175,6 +181,7 @@ function dealHands() { setTimeout(function(){ computer.Hand.push(deck.pop()); renderOnScreen(1); + ctx.ready = true; }, 2000); } From fc0989ca41456ea9914e5929b8acbcf3d46fb12a Mon Sep 17 00:00:00 2001 From: fxiii Date: Sun, 11 Jul 2021 21:13:18 +0200 Subject: [PATCH 2/5] Desktop Launcher: reset to clock after 2 mins of inactivity --- apps.json | 2 +- apps/dtlaunch/ChangeLog | 2 +- apps/dtlaunch/app.js | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps.json b/apps.json index 25a947b87..1b30a782a 100644 --- a/apps.json +++ b/apps.json @@ -2667,7 +2667,7 @@ { "id": "dtlaunch", "name": "Desktop Launcher", "icon": "icon.png", - "version":"0.03", + "version":"0.04", "description": "Desktop style App Launcher with six apps per page - fast access if you have lots of apps installed.", "readme": "README.md", "tags": "tool,system,launcher", diff --git a/apps/dtlaunch/ChangeLog b/apps/dtlaunch/ChangeLog index 3df4ab63b..985321e91 100644 --- a/apps/dtlaunch/ChangeLog +++ b/apps/dtlaunch/ChangeLog @@ -1,4 +1,4 @@ 0.01: Initial version 0.02: Multiple pages 0.03: cycle thru pages - +0.04: reset to clock after 2 mins of inactivity diff --git a/apps/dtlaunch/app.js b/apps/dtlaunch/app.js index 329a96958..9bbf3e219 100644 --- a/apps/dtlaunch/app.js +++ b/apps/dtlaunch/app.js @@ -2,6 +2,20 @@ * */ +function wdog(handle,timeout){ + if(handle !== undefined){ + wdog.handle = handle; + wdog.timeout = timeout; + } + if(wdog.timer){ + clearTimeout(wdog.timer) + } + wdog.timer = setTimeout(wdog.handle,wdog.timeout) +} + +// reset after two minutes of inactivity +wdog(load,120000) + var s = require("Storage"); var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type)); apps.sort((a,b)=>{ @@ -42,6 +56,7 @@ function drawPage(p){ } Bangle.on("swipe",(dir)=>{ + wdog() selected = 0; oldselected=-1; if (dir<0){ @@ -54,6 +69,7 @@ Bangle.on("swipe",(dir)=>{ }); function nextapp(d){ + wdog(); oldselected = selected; selected+=d; selected = selected<0?5:selected>5?0:selected; From d62dec7dcbbffa756c8688ca685e13002c3d54ac Mon Sep 17 00:00:00 2001 From: fxiii Date: Sun, 11 Jul 2021 21:18:13 +0200 Subject: [PATCH 3/5] App Manager: Allow negative numbers when manual-sorting --- apps.json | 2 +- apps/files/ChangeLog | 3 ++- apps/files/files.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps.json b/apps.json index 1b30a782a..8f9f86918 100644 --- a/apps.json +++ b/apps.json @@ -533,7 +533,7 @@ { "id": "files", "name": "App Manager", "icon": "files.png", - "version":"0.06", + "version":"0.07", "description": "Show currently installed apps, free space, and allow their deletion from the watch", "tags": "tool,system,files", "storage": [ diff --git a/apps/files/ChangeLog b/apps/files/ChangeLog index b4037a733..1908f7e5c 100644 --- a/apps/files/ChangeLog +++ b/apps/files/ChangeLog @@ -2,4 +2,5 @@ 0.03: Add support for data files 0.04: Add functionality to sort apps manually or alphabetically ascending/descending. 0.05: Tweaks to help with memory usage -0.06: Reduce memory usage \ No newline at end of file +0.06: Reduce memory usage +0.07: Allow negative numbers when manual-sorting \ No newline at end of file diff --git a/apps/files/files.js b/apps/files/files.js index 9e6c97702..9ac6ebb35 100644 --- a/apps/files/files.js +++ b/apps/files/files.js @@ -180,7 +180,7 @@ function showSortAppsManually() { appList.reduce((menu, app) => { menu[app.name] = { value: app.sortorder || 0, - min: 0, + min: -appList.length, max: appList.length, step: 1, onchange: val => setSortorder(app, val) From c341947e7d48a78c5b58e1d39d419b2b7edfcbaf Mon Sep 17 00:00:00 2001 From: fxiii Date: Sun, 11 Jul 2021 21:27:09 +0200 Subject: [PATCH 4/5] Red torch: like torch but red :D --- apps.json | 13 +++++++++++++ apps/rtorch/ChangeLog | 1 + apps/rtorch/app-icon.js | 1 + apps/rtorch/app.js | 22 ++++++++++++++++++++++ apps/rtorch/app.png | Bin 0 -> 1770 bytes apps/rtorch/widget.js | 26 ++++++++++++++++++++++++++ 6 files changed, 63 insertions(+) create mode 100644 apps/rtorch/ChangeLog create mode 100644 apps/rtorch/app-icon.js create mode 100644 apps/rtorch/app.js create mode 100644 apps/rtorch/app.png create mode 100644 apps/rtorch/widget.js diff --git a/apps.json b/apps.json index 8f9f86918..a2ff7b571 100644 --- a/apps.json +++ b/apps.json @@ -1244,6 +1244,19 @@ {"name":"torch.img","url":"app-icon.js","evaluate":true} ] }, + { "id": "rtorch", + "name": "Red Torch", + "shortName":"RedTorch", + "icon": "app.png", + "version":"0.01", + "description": "Turns screen RED to help you see in the dark without breaking your night vision. Select from the launcher or press BTN3,BTN1,BTN3,BTN1 quickly to start when in any app that shows widgets", + "tags": "tool,torch", + "storage": [ + {"name":"rtorch.app.js","url":"app.js"}, + {"name":"rtorch.wid.js","url":"widget.js"}, + {"name":"rtorch.img","url":"app-icon.js","evaluate":true} + ] + }, { "id": "wohrm", "name": "Workout HRM", "icon": "app.png", diff --git a/apps/rtorch/ChangeLog b/apps/rtorch/ChangeLog new file mode 100644 index 000000000..06f10fe08 --- /dev/null +++ b/apps/rtorch/ChangeLog @@ -0,0 +1 @@ +0.01: Cloning torch and making it red :D diff --git a/apps/rtorch/app-icon.js b/apps/rtorch/app-icon.js new file mode 100644 index 000000000..ff1265c9b --- /dev/null +++ b/apps/rtorch/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UA///oP4gH+t9TCQ1VAAYLpgILunoLK/4LJgf/6oLIh//+oLK/oLIhapBBZEqBYIwDBYu/GAgLE1WvGAgLF1YwEBQcC1WqGAgLGGAgLDhQLBGAdQBYwwCBQgLDGASlFlQLC3/8BYoIBGAXwBQkCFgILC4AuFBYeAFw2v/wLBBQqNCBYOgBQp1B1/qCw5dDFoxdEBQwuBAAOoBQykCHI4uXgZPBFxEP/QuJn5/CFw7DBLpILB9QuHEYP//QuHHYP//wuHKYL0HGAoLJn/8BZMP+ALJgfABRA=")) \ No newline at end of file diff --git a/apps/rtorch/app.js b/apps/rtorch/app.js new file mode 100644 index 000000000..4f6b1d6f7 --- /dev/null +++ b/apps/rtorch/app.js @@ -0,0 +1,22 @@ +Bangle.setLCDPower(1); +Bangle.setLCDTimeout(0); +g.reset(); +c = 1; +function setColor(delta){ + c+=delta; + c = Math.max(c,0); + c = Math.min(c,2); + if (c<1){ + g.setColor(c,0,0); + }else{ + g.setColor(1,c-1,c-1); + } + g.fillRect(0,0,g.getWidth(),g.getHeight()); +} +setColor(0) +// BTN1 light up toward white +// BTN3 light down to red +// BTN2 to reset +setWatch(()=>setColor(0.1), BTN1, { repeat:true, edge:"rising", debounce: 50 }); +setWatch(()=>load(), BTN2); +setWatch(()=>setColor(-0.1), BTN3, { repeat:true, edge:"rising", debounce: 50 }); diff --git a/apps/rtorch/app.png b/apps/rtorch/app.png new file mode 100644 index 0000000000000000000000000000000000000000..17b0f3efcd9ff5c3fb5656b12b728983569670c9 GIT binary patch literal 1770 zcmVpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H126stB zK~!jg?U`+CTUQy!f9GDuzVW+bJ9VE0hf~F-=-07%v|-B-Acw0*$p}w1Nr* zlTeZN0av0R_(FwHp%V}d_My;qA2zX}ma(oc+8P#G&{n1;OVT8c7bmfuI8OX}4en=Q-y&_g=v!OWJII2U?AaCyWYIjwG0{*^+=}R7}OFKxN5h z+l7Jfu-OLS9-{;P-@q#MziU9wY#4itj=Om?plMBj$LO#dzKyoEwW)ed%c*=a@z_&W zFWVlu*==Zi)zD~*6bglusOoOdPEQ_;4h@d6U1#@R3fzCHIpo0&>IIU4Rq*L7?z51v3Rx~lSLhZ|Qm zOUrm{-Q#T7BF;`F+ZMwMARdhn8;|poh8_GfP`B=JHe?2*M4_kfc*h4Wzt!oUhadis z$wUkx1m4;@I(P1-Wu57?AqGO9(SG=`n&_j=wRyVJcazuES@13Dz z`8p@>G)ctMt33mAgIdj=YHj}2pVqx1Xd^$DBNVp%V>%3dv#68LJveU^h1EGWF z{Rr_?sWv`q62CJ~M}IB8x_S7~@Bm1O-Q~d*sO8PZJ8>t*%Bt9gorUBZmUF=8%4z%F zXe~d!vY+^PmYFK8W1p=R0P*1nsfh&NAM9ga&-;tJ_afyr;5u;K5CifREZ8h@pt9UP z*FUW1$a8xr6bjhgUOd|ZbMUunMX)+h_~Y3>yoeAt(DhCfh_ zHax)HfdP&_QpvBcd=i`Yzv2CLL3FFVU8GXqC>3YjYd}f_{pzoPW+`E4NYc{_kqG4S z;B*SaGV{E%dW_b_7;g^lWF!)ug;(&`1+iG|r$?*IU+=qcLEkU~QX;r$IRSiCO6dMT z^4hteyOL&`J&nt?e5U%WNjw!Jyn4-!K*3iRM4N$s{6Z*{Ux{JO28!Un za|`}&@3=T|aR;faiF97!y{LnSnxfRylwN9$RSC>m<-c!cc2{UN`|~2w_2erjPZrAI zSkoF<01x!^N-o}T@{3E`$?Nm56{p)Wht%Nz*0{>9*5s{ z1Q)=Y6v=_se*C_Y(XOJ%BjSNv=W03T##v3{<=_Ddi{QWgKo1Y@o}jjN$@J+3ah$!G zb|F~xQ!iH8b)Xm?ARLy2hvx79BkZERBS?iRIe2eBd+(Z{dB=hsXbQN5l&6ShUlL!Y ztQY+|unm9*W`5Ba~xUr#nO|%k)^gR!pI#;7e%{{3-Tkz9|H9eVEIu z|5l09GM|C|n63yIy$NQMo6%T}41`nVp ziu`MQIMQ_MEIxCbwb3==c6G%0Lu8-usRQ4k7l(|$mbau=wo;+!{O7P zWh!S`s_&M=11LhbJZRP`CuZR7bMU!r2D7H2xSBY2v5jPA!5iak0S~i_xb4tU7n=H) zd_I3)F}%O75k*x=rzXiHhIsD48`L-_ZX0+g+D6+B9d(Vw!rjx!q_4uFQC&X+Kb=f6 z7Mb}$5v+40{`8yYt8N!~C@N%Up8e^#+2s6$)!~ZH!Kadp&cZvYyhvL^@9hK+iz@NC z(a?uI*OpKcY#^>NgRF(EtDd M07*qoM6N<$f@-*GDF6Tf literal 0 HcmV?d00001 diff --git a/apps/rtorch/widget.js b/apps/rtorch/widget.js new file mode 100644 index 000000000..89009266d --- /dev/null +++ b/apps/rtorch/widget.js @@ -0,0 +1,26 @@ +(function() { + var clickTimes = []; + var clickPattern = ""; + var TAPS = 4; // number of taps + var PERIOD = 1; // seconds + + // we don't actually create/draw a widget here at all... + Bangle.on("lcdPower",function(on) { + // First click (that turns LCD on) isn't given to + // setWatch, so handle it here + if (!on) return; + clickTimes=[getTime()]; + clickPattern="x"; + }); + function tap(e,c) { + clickPattern = clickPattern.substr(-3)+c; + while (clickTimes.length>=TAPS) clickTimes.shift(); + clickTimes.push(e.time); + var clickPeriod = e.time-clickTimes[0]; + if (clickPeriod Date: Sun, 11 Jul 2021 21:40:17 +0200 Subject: [PATCH 5/5] Magic 8 Ball Italiano : like Magic 8 Ball but in italian :D --- apps.json | 14 +++++++ apps/jbm8b_IT/ChangeLog | 1 + apps/jbm8b_IT/app-icon.js | 1 + apps/jbm8b_IT/app.js | 79 ++++++++++++++++++++++++++++++++++++++ apps/jbm8b_IT/app.png | Bin 0 -> 1548 bytes 5 files changed, 95 insertions(+) create mode 100644 apps/jbm8b_IT/ChangeLog create mode 100644 apps/jbm8b_IT/app-icon.js create mode 100644 apps/jbm8b_IT/app.js create mode 100644 apps/jbm8b_IT/app.png diff --git a/apps.json b/apps.json index a2ff7b571..ed49e19ac 100644 --- a/apps.json +++ b/apps.json @@ -2110,6 +2110,20 @@ { "name": "jbm8b.img", "url": "app-icon.js", "evaluate": true } ] }, + { + "id": "jbm8b_IT", + "name": "Magic 8 Ball Italiano", + "shortName": "Magic 8 Ball IT", + "icon": "app.png", + "description": "La palla predice il futuro", + "tags": "game", + "version": "0.03", + "allow_emulator":true, + "storage": [ + { "name": "jbm8b_IT.app.js", "url": "app.js" }, + { "name": "jbm8b_IT.img", "url": "app-icon.js", "evaluate": true } + ] + }, { "id": "BLEcontroller", "name": "BLE Customisable Controller with Joystick", "shortName": "BLE Controller", diff --git a/apps/jbm8b_IT/ChangeLog b/apps/jbm8b_IT/ChangeLog new file mode 100644 index 000000000..b7b783924 --- /dev/null +++ b/apps/jbm8b_IT/ChangeLog @@ -0,0 +1 @@ +0.01: Cloning Magic 8 Ball and make it speak italian \ No newline at end of file diff --git a/apps/jbm8b_IT/app-icon.js b/apps/jbm8b_IT/app-icon.js new file mode 100644 index 000000000..09bf032a6 --- /dev/null +++ b/apps/jbm8b_IT/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwhBC/AGMrq2B1gAEwNWlYthq2s64AKGYIydFpoAEGLUrFqIADqxcXFqhiDFymBFy7GCF1owTRjCSVlYudeiGsF7/XlaNqSKBeP1mBwJxQMBReO1gaEleBMDBLN1hAC1hhBAoIwNCwQAGlZINqxvFGAIXOSBAXQN4hPBC5yQIVBxfBCAgvQSBC+NFAYRDMwJHOF654DqxkBYooALF6+sbIhkEF8Z3CRIWBR6AvXFAzvQF6wnIYQJgNd5AWNdoLoGBBAvPO5pfYH4IvUUwS/GVBzXBYCpHCq2s1mBDwKOWDwRgNPAwVVMCRLCwIABCZ6OJJSAATLxZgRACJeLAAMrFz9WFxiRgRpoADwIub1guQGDmsXhqSfRiL0G1jqkMRYxRwKLUGK2sFryVEq2B1gAEwNWFkIA/AH4A/AH4AQ")) \ No newline at end of file diff --git a/apps/jbm8b_IT/app.js b/apps/jbm8b_IT/app.js new file mode 100644 index 000000000..13ab3d39d --- /dev/null +++ b/apps/jbm8b_IT/app.js @@ -0,0 +1,79 @@ +const affirmative = [ + 'È certo.', + 'È decisamente\ncosì.', + 'Senza alcun\ndubbio.', + 'Sì,\nsenza dubbio.', + 'Ci puoi\ncontare.', + 'Da quanto\nvedo,\nsì.', + 'Molto\nprobabilmente.', + 'Le prospettive\nsono buone.', + 'Sì.', + 'I segni\nindicano\ndi sì.' +]; +const nonCommittal = [ + 'È difficile\ndirlo,\nprova di nuovo.', + 'Rifai la domanda\npiù tardi.', + 'Meglio non\nrisponderti\nadesso.', + 'Non posso\npredirlo ora.', + 'Concentrati e\nrifai la\ndomanda.' +]; +const negative = [ + 'Non ci\ncontare.', + 'La mia\nrisposta\nè no.', + 'Le mie\nfonti dicono\ndi no.', + 'Le prospettive\nnon sono\nbuone.', + 'È molto\ndubbio.' +]; +const title = 'Magic 8 Ball'; + +const answers = [affirmative, nonCommittal, negative]; + +function getRandomArbitrary(min, max) { + return Math.random() * (max - min) + min; +} + +function predict() { + // affirmative, negative or non-committal + let max = answers.length; + const a = Math.floor(getRandomArbitrary(0, max)); + // sets max compared to answer category + max = answers[a].length; + const b = Math.floor(getRandomArbitrary(0, max)); + // get the answer + const response = answers[a][b]; + return response; +} + +function draw(msg) { + // console.log(msg); + g.clear(); + E.showMessage(msg, title); +} + +function reply(button) { + const theButton = (typeof button === 'undefined' || isNaN(button)) ? 1 : button; + const timer = Math.floor(getRandomArbitrary(0, theButton) * 1000); + // Thinking... + draw('...'); + setTimeout('draw(predict());', timer); +} + +function ask() { + draw('Ponimi una\ndomanda\nSì/No e\ntocca lo\nschermo'); +} + +g.clear(); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +ask(); + +// Event Handlers + +Bangle.on('touch', (button) => reply(button)); + +setWatch(ask, BTN1, { repeat: true, edge: "falling" }); +setWatch(reply, BTN3, { repeat: true, edge: "falling" }); + +// Back to launcher +setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" }); \ No newline at end of file diff --git a/apps/jbm8b_IT/app.png b/apps/jbm8b_IT/app.png new file mode 100644 index 0000000000000000000000000000000000000000..24c3013de011db9c1517ed544e9d383f5c60378c GIT binary patch literal 1548 zcmeH_{WsGK9LK-L%+owW>~?OVSQ<5jdzFVG z7KTZRJWrN~OKhyo#XZ;1!5ArX5tskq{sH%M&g=6&?{nTieLn9rUvGD)hOq_!08md4 zSHEpNvV)rP_9l(0*KLau&ClH#sOsN6vu#wuusAFL)I8MOAcFxw8SwoP@3s?CQUZZM z%F4SrKP2nm6f%%wT+F9t*xz{ zo!zlx$L#IxF&K=aqocF4^O-Ye+}+)AI2<02_xJZF5C{PQ0YoB^L?V&Nh&d$!w&CScp%g@g*C@3f_EG#ZAE-5J~EiGlU z*=1#A<>loS6%`x~r?RrLy1JUn<<`{H)YjJ4)z!Uy`?jH>p|P>Csi~>Cxw)mKrM0!S zt*wp6<8^d&bar;~`TP$bK6G_;b$567^z`)h_V)Gl4Gatn4h{+g0-;biJUlECiNs>D zL?Rg(85tcN9UB`PA0MBXn2<`PQ&UsZ)6+9EGqba^b8~YtnQVT3eqmujE|)JZE-o!C zEiW&B{rdIWw{I&eD++~Tb#--ZZEbyhePd(e`}gmgo10r(Tib8_fBwq_1kdR=w{z+- z9sx7}FhK91^i-dZ002<7rz;lk;Wd^mxh!--q=Y>&&l(UI44!qv`yxh>c)U{^O1JFU z%a;}ljh)Wt4!yc(lI+jS(N5&_O}>~%hcC4sX#H*D+Vm)f7G{2Yve4IU+B&)Y z?$GYABWY+zFG-Kpm_z2WjDs}KYx^G4fTKz8{Q=y znAAd`uHOq9nnlG&5e52-Jn?}ZIE6l?!8crQIGwGipOq&~#_GtX zD~=*r(WQ3Ws}1WYw0Hk7s}L*Y_TFdz8p>hw=$GoNY8}NwHyK=%CDZoxj9q=&`O)F? zFYqmdD{5tmeB<-{!{ESspW6NiN0f4}>{GzaqJ-qY-0Ea~S=Wr@I@;&SMcbbb*IPJT z4e#>niaQQAnGzM)Fupi=8I*C?gtXV2ciZ28Vc~8;FCM25x-Kz6ck~n2)5~CQ#Dpt_ zdpH<_?oEccI&B%8cz^dN{vASx^s!Lxt{PbGrAhhdlpj&{sd6L#pDhOKv`zbaUMoJO QXs6wtZr-j{&ZOjj0hD%CN&o-= literal 0 HcmV?d00001