commit 3d19c4d34f0e3117d97cc3248f02b383e04cc785 Author: douboer@gmail.com Date: Sun Mar 15 09:30:40 2026 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aeee04e --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# 依赖目录 +node_modules/ + +# 构建产物 +**/dist/ +**/coverage/ + +# 本地环境变量 +.env +.env.* +!.env.example + +# 日志与缓存 +npm-debug.log* +.DS_Store +.npm-cache/ +data/tts-cache/ + +# 小程序本地运维配置(由 .env 生成) +apps/miniprogram/utils/opsEnv.js + +calendar/node_modules +dash/backups +dash/downloads + +/dist +/kindle-dash-*.tgz +/tmp diff --git a/calendar/index.html b/calendar/index.html new file mode 100644 index 0000000..2b50609 --- /dev/null +++ b/calendar/index.html @@ -0,0 +1,12 @@ + + + + + + Calendar Dashboard + + +
+ + + diff --git a/calendar/package-lock.json b/calendar/package-lock.json new file mode 100644 index 0000000..06d42fd --- /dev/null +++ b/calendar/package-lock.json @@ -0,0 +1,1226 @@ +{ + "name": "calendar", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "calendar", + "version": "0.0.0", + "dependencies": { + "lunar-typescript": "^1.8.6", + "vue": "^3.5.30" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.5", + "typescript": "^5.9.3", + "vite": "^8.0.0", + "vue-tsc": "^3.2.5" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", + "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", + "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@oxc-project/runtime": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", + "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz", + "integrity": "sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz", + "integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/shared": "3.5.30", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz", + "integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz", + "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/compiler-core": "3.5.30", + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.8", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz", + "integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/language-core": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.5.tgz", + "integrity": "sha512-d3OIxN/+KRedeM5wQ6H6NIpwS3P5gC9nmyaHgBk+rO6dIsjY+tOh4UlPpiZbAh3YtLdCGEX4M16RmsBqPmJV+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.0.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz", + "integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz", + "integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz", + "integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.30", + "@vue/runtime-core": "3.5.30", + "@vue/shared": "3.5.30", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz", + "integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30" + }, + "peerDependencies": { + "vue": "3.5.30" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz", + "integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==", + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz", + "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lunar-typescript": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/lunar-typescript/-/lunar-typescript-1.8.6.tgz", + "integrity": "sha512-5Eo4T/cnuXfrgO4k5LCpOGHIUOuz5hCF/IfNv0T29WY2shR36Hiz+ecN9WjnUuxUKhql9gbOkPaQoqLFKtPRNA==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", + "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/runtime": "0.115.0", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.9", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.0.0-alpha.31", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz", + "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-sfc": "3.5.30", + "@vue/runtime-dom": "3.5.30", + "@vue/server-renderer": "3.5.30", + "@vue/shared": "3.5.30" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.5.tgz", + "integrity": "sha512-/htfTCMluQ+P2FISGAooul8kO4JMheOTCbCy4M6dYnYYjqLe3BExZudAua6MSIKSFYQtFOYAll7XobYwcpokGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.2.5" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/calendar/package.json b/calendar/package.json new file mode 100644 index 0000000..56bce6b --- /dev/null +++ b/calendar/package.json @@ -0,0 +1,22 @@ +{ + "name": "calendar", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "typecheck": "vue-tsc --noEmit", + "preview": "vite preview" + }, + "dependencies": { + "lunar-typescript": "^1.8.6", + "vue": "^3.5.30" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.5", + "typescript": "^5.9.3", + "vite": "^8.0.0", + "vue-tsc": "^3.2.5" + } +} diff --git a/calendar/src/App.vue b/calendar/src/App.vue new file mode 100644 index 0000000..bd728c4 --- /dev/null +++ b/calendar/src/App.vue @@ -0,0 +1,72 @@ + + + diff --git a/calendar/src/components/CalendarCard.vue b/calendar/src/components/CalendarCard.vue new file mode 100644 index 0000000..ace9385 --- /dev/null +++ b/calendar/src/components/CalendarCard.vue @@ -0,0 +1,70 @@ + + + diff --git a/calendar/src/components/QuoteCard.vue b/calendar/src/components/QuoteCard.vue new file mode 100644 index 0000000..6e48a5a --- /dev/null +++ b/calendar/src/components/QuoteCard.vue @@ -0,0 +1,27 @@ + + + diff --git a/calendar/src/components/WeatherCard.vue b/calendar/src/components/WeatherCard.vue new file mode 100644 index 0000000..da667a4 --- /dev/null +++ b/calendar/src/components/WeatherCard.vue @@ -0,0 +1,158 @@ + + + diff --git a/calendar/src/components/WeatherGlyph.vue b/calendar/src/components/WeatherGlyph.vue new file mode 100644 index 0000000..b51cee2 --- /dev/null +++ b/calendar/src/components/WeatherGlyph.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/calendar/src/env.d.ts b/calendar/src/env.d.ts new file mode 100644 index 0000000..1b62241 --- /dev/null +++ b/calendar/src/env.d.ts @@ -0,0 +1,11 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + + const component: DefineComponent, Record, unknown>; + export default component; +} + +declare module '*.md?raw' { + const content: string; + export default content; +} diff --git a/calendar/src/lib/calendar.ts b/calendar/src/lib/calendar.ts new file mode 100644 index 0000000..a859672 --- /dev/null +++ b/calendar/src/lib/calendar.ts @@ -0,0 +1,132 @@ +import { HolidayUtil, Solar } from 'lunar-typescript'; + +export interface CalendarBadge { + label: string; + tone: 'holiday' | 'workday' | 'festival' | 'term'; +} + +export interface CalendarCell { + date: Date; + day: number; + subLabel: string; + currentMonth: boolean; + isToday: boolean; + badges: CalendarBadge[]; +} + +export interface CalendarModel { + largeDay: string; + gregorianLabel: string; + weekdayLabel: string; + lunarYearLabel: string; + lunarDayLabel: string; + summaryBadges: CalendarBadge[]; + weekLabels: string[]; + cells: CalendarCell[]; +} + +const WEEK_LABELS = ['日', '一', '二', '三', '四', '五', '六']; + +function isSameDay(left: Date, right: Date) { + return ( + left.getFullYear() === right.getFullYear() && + left.getMonth() === right.getMonth() && + left.getDate() === right.getDate() + ); +} + +function badgeFromHoliday(dateKey: string): CalendarBadge[] { + const holiday = HolidayUtil.getHoliday(dateKey); + + if (!holiday) { + return []; + } + + return [ + { + label: holiday.getName(), + tone: holiday.isWork() ? 'workday' : 'holiday', + }, + ]; +} + +function buildSolar(date: Date) { + return Solar.fromYmd(date.getFullYear(), date.getMonth() + 1, date.getDate()); +} + +function buildCellBadges(date: Date) { + const solar = buildSolar(date); + const lunar = solar.getLunar(); + const solarFestivals = solar.getFestivals(); + const lunarFestivals = lunar.getFestivals(); + const otherFestivals = solar.getOtherFestivals(); + const jieQi = lunar.getJieQi(); + + return [ + ...badgeFromHoliday(solar.toYmd()), + ...solarFestivals.map((label) => ({ label, tone: 'festival' as const })), + ...lunarFestivals.map((label) => ({ label, tone: 'festival' as const })), + ...otherFestivals.map((label) => ({ label, tone: 'festival' as const })), + ...(jieQi ? [{ label: jieQi, tone: 'term' as const }] : []), + ]; +} + +function buildCellSubLabel(date: Date) { + const solar = buildSolar(date); + const lunar = solar.getLunar(); + const badges = buildCellBadges(date); + + if (badges.length > 0) { + return badges[0].label; + } + + const lunarDay = lunar.getDayInChinese(); + + if (lunarDay === '初一') { + return lunar.getMonthInChinese(); + } + + return lunarDay; +} + +export function buildCalendarModel(currentDate: Date): CalendarModel { + const currentYear = currentDate.getFullYear(); + const currentMonth = currentDate.getMonth(); + const monthStart = new Date(currentYear, currentMonth, 1); + const monthEnd = new Date(currentYear, currentMonth + 1, 0); + const gridStart = new Date(monthStart); + gridStart.setDate(monthStart.getDate() - monthStart.getDay()); + const gridEnd = new Date(monthEnd); + gridEnd.setDate(monthEnd.getDate() + (6 - monthEnd.getDay())); + + const cells: CalendarCell[] = []; + const cursor = new Date(gridStart); + + while (cursor <= gridEnd) { + const cellDate = new Date(cursor); + cells.push({ + date: cellDate, + day: cellDate.getDate(), + subLabel: buildCellSubLabel(cellDate), + badges: buildCellBadges(cellDate), + currentMonth: cellDate.getMonth() === currentMonth, + isToday: isSameDay(cellDate, currentDate), + }); + cursor.setDate(cursor.getDate() + 1); + } + + const solar = buildSolar(currentDate); + const lunar = solar.getLunar(); + const summaryBadges = buildCellBadges(currentDate).slice(0, 3); + + return { + largeDay: String(currentDate.getDate()).padStart(2, '0'), + gregorianLabel: `${currentYear}年${currentMonth + 1}月${currentDate.getDate()}日`, + weekdayLabel: `星期${solar.getWeekInChinese()}`, + lunarYearLabel: `${lunar.getYearInGanZhi()}${lunar.getYearShengXiao()}年`, + lunarDayLabel: `${lunar.getMonthInChinese()}${lunar.getDayInChinese()}`, + summaryBadges, + weekLabels: WEEK_LABELS, + cells, + }; +} diff --git a/calendar/src/lib/quotes.ts b/calendar/src/lib/quotes.ts new file mode 100644 index 0000000..2b93beb --- /dev/null +++ b/calendar/src/lib/quotes.ts @@ -0,0 +1,49 @@ +import quoteMarkdown from '../../../日知录.md?raw'; + +export interface QuoteEntry { + date: string; + text: string; +} + +const QUOTE_PATTERN = /^>\s*(.+?)\s*--\s*(\d{4}-\d{2}-\d{2})$/gm; + +const QUOTE_ENTRIES: QuoteEntry[] = Array.from(quoteMarkdown.matchAll(QUOTE_PATTERN)).map( + (match) => ({ + text: match[1].trim(), + date: match[2], + }), +); + +function pad(value: number) { + return String(value).padStart(2, '0'); +} + +function toMonthDay(date: Date) { + return `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; +} + +function toIsoDate(date: Date) { + return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`; +} + +export function getQuoteForDate(date: Date) { + const isoDate = toIsoDate(date); + const monthDay = toMonthDay(date); + const exactMatch = QUOTE_ENTRIES.find((entry) => entry.date === isoDate); + + if (exactMatch) { + return exactMatch; + } + + const monthDayMatch = QUOTE_ENTRIES.find((entry) => entry.date.slice(5) === monthDay); + + if (monthDayMatch) { + return monthDayMatch; + } + + // 文件日期不覆盖全年时,回退到一个稳定的循环选择,保证每天都有内容可显示。 + const startOfYear = new Date(date.getFullYear(), 0, 1); + const dayIndex = Math.floor((date.getTime() - startOfYear.getTime()) / 86_400_000); + + return QUOTE_ENTRIES[(dayIndex % QUOTE_ENTRIES.length + QUOTE_ENTRIES.length) % QUOTE_ENTRIES.length]; +} diff --git a/calendar/src/lib/weather.ts b/calendar/src/lib/weather.ts new file mode 100644 index 0000000..d733765 --- /dev/null +++ b/calendar/src/lib/weather.ts @@ -0,0 +1,203 @@ +export interface LocationCoordinates { + latitude: number; + longitude: number; + label: string; +} + +export interface ForecastDay { + label: string; + weatherCode: number; + condition: string; + high: number; + low: number; +} + +export interface WeatherSnapshot { + temperature: number; + condition: string; + weatherCode: number; + humidity: number; + windSpeed: number; + visibilityKm: number; + forecast: ForecastDay[]; + sunrise: string; + sunset: string; + aqi: number | null; + aqiLabel: string; +} + +const DEFAULT_LOCATION: LocationCoordinates = { + latitude: 31.2304, + longitude: 121.4737, + label: '上海', +}; + +const WEATHER_CODE_LABELS: Record = { + 0: '晴朗', + 1: '晴间多云', + 2: '多云', + 3: '阴天', + 45: '有雾', + 48: '冻雾', + 51: '毛毛雨', + 53: '小雨', + 55: '中雨', + 56: '冻毛雨', + 57: '冻毛雨', + 61: '小雨', + 63: '中雨', + 65: '大雨', + 66: '冻雨', + 67: '冻雨', + 71: '小雪', + 73: '中雪', + 75: '大雪', + 77: '阵雪', + 80: '阵雨', + 81: '强阵雨', + 82: '暴雨', + 85: '阵雪', + 86: '大阵雪', + 95: '雷阵雨', + 96: '雷暴冰雹', + 99: '强雷暴', +}; + +function formatClock(value: string) { + const date = new Date(value); + + return new Intl.DateTimeFormat('zh-CN', { + hour: '2-digit', + minute: '2-digit', + hour12: false, + }).format(date); +} + +function formatForecastLabel(value: string, index: number) { + if (index === 0) { + return '今天'; + } + + if (index === 1) { + return '明天'; + } + + return new Intl.DateTimeFormat('zh-CN', { + weekday: 'short', + }).format(new Date(value)); +} + +function aqiToLabel(aqi: number | null) { + if (aqi === null) { + return '暂无'; + } + + if (aqi <= 50) { + return '优'; + } + + if (aqi <= 100) { + return '良'; + } + + if (aqi <= 150) { + return '轻度'; + } + + if (aqi <= 200) { + return '中度'; + } + + if (aqi <= 300) { + return '重度'; + } + + return '严重'; +} + +export function weatherCodeToLabel(code: number) { + return WEATHER_CODE_LABELS[code] ?? '天气'; +} + +export async function resolveLocation(): Promise { + if (!('geolocation' in navigator)) { + return DEFAULT_LOCATION; + } + + return new Promise((resolve) => { + navigator.geolocation.getCurrentPosition( + (position) => { + resolve({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + label: '当前位置', + }); + }, + () => resolve(DEFAULT_LOCATION), + { + enableHighAccuracy: false, + timeout: 5000, + maximumAge: 10 * 60 * 1000, + }, + ); + }); +} + +export async function fetchWeather(location: LocationCoordinates): Promise { + const weatherParams = new URLSearchParams({ + latitude: String(location.latitude), + longitude: String(location.longitude), + current: [ + 'temperature_2m', + 'relative_humidity_2m', + 'weather_code', + 'wind_speed_10m', + 'visibility', + ].join(','), + daily: ['weather_code', 'temperature_2m_max', 'temperature_2m_min', 'sunrise', 'sunset'].join(','), + forecast_days: '5', + timezone: 'auto', + }); + + const airParams = new URLSearchParams({ + latitude: String(location.latitude), + longitude: String(location.longitude), + current: 'us_aqi', + timezone: 'auto', + }); + + const [weatherResponse, airResponse] = await Promise.all([ + fetch(`https://api.open-meteo.com/v1/forecast?${weatherParams.toString()}`), + fetch(`https://air-quality-api.open-meteo.com/v1/air-quality?${airParams.toString()}`), + ]); + + if (!weatherResponse.ok) { + throw new Error(`天气接口返回异常:${weatherResponse.status}`); + } + + const weatherJson = await weatherResponse.json(); + const airJson = airResponse.ok ? await airResponse.json() : null; + + const aqi = airJson?.current?.us_aqi ?? null; + const daily = weatherJson.daily; + + return { + temperature: Math.round(weatherJson.current.temperature_2m), + condition: weatherCodeToLabel(weatherJson.current.weather_code), + weatherCode: weatherJson.current.weather_code, + humidity: Math.round(weatherJson.current.relative_humidity_2m), + windSpeed: Math.round(weatherJson.current.wind_speed_10m), + visibilityKm: Number((weatherJson.current.visibility / 1000).toFixed(1)), + forecast: daily.time.map((value: string, index: number) => ({ + label: formatForecastLabel(value, index), + weatherCode: daily.weather_code[index], + condition: weatherCodeToLabel(daily.weather_code[index]), + high: Math.round(daily.temperature_2m_max[index]), + low: Math.round(daily.temperature_2m_min[index]), + })), + sunrise: formatClock(daily.sunrise[0]), + sunset: formatClock(daily.sunset[0]), + aqi, + aqiLabel: aqiToLabel(aqi), + }; +} diff --git a/calendar/src/main.ts b/calendar/src/main.ts new file mode 100644 index 0000000..3cb2340 --- /dev/null +++ b/calendar/src/main.ts @@ -0,0 +1,6 @@ +import { createApp } from 'vue'; + +import App from './App.vue'; +import './style.css'; + +createApp(App).mount('#app'); diff --git a/calendar/src/style.css b/calendar/src/style.css new file mode 100644 index 0000000..629f6fa --- /dev/null +++ b/calendar/src/style.css @@ -0,0 +1,448 @@ +:root { + font-family: + 'Hiragino Sans GB', + 'PingFang SC', + 'Noto Sans SC', + sans-serif; + color: #0f172a; + background: + radial-gradient(circle at 18% 12%, rgba(255, 255, 255, 0.88), transparent 30%), + radial-gradient(circle at 82% 18%, rgba(241, 238, 232, 0.92), transparent 24%), + linear-gradient(160deg, #f4f1eb 0%, #ebe5dc 52%, #e4ddd4 100%); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +html, +body, +#app { + margin: 0; + min-height: 100%; +} + +body { + min-height: 100vh; +} + +.page-shell { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 1.5rem; +} + +.dashboard-frame { + width: min(100%, 1448px); + aspect-ratio: 1448 / 1072; + padding: clamp(1.2rem, 1vw + 0.9rem, 2rem); + border-radius: 2rem; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.38), rgba(255, 255, 255, 0.18)), + rgba(255, 255, 255, 0.14); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.55), + 0 38px 72px rgba(64, 52, 38, 0.14); + backdrop-filter: blur(14px); +} + +.dashboard-grid { + display: grid; + grid-template-columns: 1.08fr 1fr; + grid-template-rows: auto auto; + gap: clamp(1rem, 1vw + 0.75rem, 2rem); + height: 100%; + align-content: start; + align-items: stretch; +} + +.card { + border-radius: 1.6rem; + background: rgba(255, 255, 255, 0.94); + box-shadow: + 0 20px 36px rgba(72, 58, 44, 0.12), + inset 0 1px 0 rgba(255, 255, 255, 0.65); + border: 1px solid rgba(70, 58, 46, 0.08); +} + +.calendar-card, +.weather-card { + padding: clamp(1.4rem, 1.2vw + 1rem, 2rem); + min-height: 0; + height: 100%; +} + +.calendar-card { + display: grid; + grid-template-rows: auto 1fr; + gap: 0.8rem; +} + +.calendar-card__header { + display: flex; + align-items: center; + gap: 1.6rem; +} + +.calendar-card__day { + font-family: + 'Iowan Old Style', + 'Baskerville', + serif; + font-size: clamp(4.4rem, 5vw, 7rem); + line-height: 0.95; + letter-spacing: -0.08em; + color: #101010; +} + +.calendar-card__meta { + display: grid; + gap: 0.65rem; +} + +.calendar-card__date-line, +.calendar-card__lunar { + display: flex; + gap: 1rem; + align-items: baseline; + margin: 0; + font-size: clamp(1rem, 0.7vw + 0.7rem, 1.55rem); +} + +.calendar-card__weekday { + color: #556274; +} + +.calendar-card__lunar { + color: #5f4b32; + flex-wrap: wrap; +} + +.calendar-card__badges { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; +} + +.calendar-card__badges--inline { + display: inline-flex; + align-items: center; + margin-left: 0.1rem; +} + +.calendar-card__badge { + display: inline-flex; + align-items: center; + min-height: 1.6rem; + padding: 0.2rem 0.55rem; + border-radius: 999px; + border: 1px solid currentColor; + font-size: 0.76rem; + line-height: 1; +} + +.calendar-card__badge--holiday { + color: #2f2b26; + background: #ece5db; +} + +.calendar-card__badge--workday { + color: #68593f; + background: #f2eadb; +} + +.calendar-card__badge--festival { + color: #4f4436; + background: #f5f0e8; +} + +.calendar-card__badge--term { + color: #3a4a4a; + background: #e9efef; +} + +.calendar-card__grid { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + grid-template-rows: auto; + grid-auto-rows: minmax(0, 1fr); + gap: 0.35rem 0.3rem; + align-content: start; + height: 100%; +} + +.calendar-card__week-label { + text-align: center; + color: #798395; + font-size: 0.92rem; + padding-bottom: 0.45rem; +} + +.calendar-card__cell { + min-height: 0; + display: grid; + place-items: center; + align-content: center; + padding: 0.2rem 0; + border-radius: 0.8rem; + color: #304153; + transition: background-color 150ms ease; +} + +.calendar-card__cell--muted { + color: #bac4d2; +} + +.calendar-card__cell--today { + background: linear-gradient(180deg, #303640 0%, #1f252d 100%); + color: #ffffff; + box-shadow: 0 14px 24px rgba(31, 37, 45, 0.24); +} + +.calendar-card__cell--holiday .calendar-card__solar, +.calendar-card__cell--holiday .calendar-card__lunar-day { + color: #6a4a1c; +} + +.calendar-card__solar { + font-size: 1.3rem; + line-height: 1.1; +} + +.calendar-card__lunar-day { + font-size: 0.68rem; + line-height: 1.2; + opacity: 0.82; +} + +.weather-card { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 0.8rem; +} + +.weather-card__heading { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; +} + +.weather-card__title { + margin: 0; + font-size: clamp(1.5rem, 1vw + 1rem, 2rem); + font-weight: 700; +} + +.weather-card__subtitle { + margin: 0.35rem 0 0; + color: #778396; + font-size: 0.95rem; +} + +.weather-card__hero { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + padding: 1.05rem 1.2rem; + border-radius: 1.2rem; + background: linear-gradient(180deg, #ece8e1 0%, #e2ddd4 100%); +} + +.weather-card__hero--placeholder { + justify-content: center; + color: #64748b; +} + +.weather-card__hero-main { + display: flex; + align-items: center; + gap: 1rem; +} + +.weather-card__temperature { + font-size: clamp(2.2rem, 1.2vw + 1.6rem, 3.2rem); + line-height: 1; + font-family: + 'Iowan Old Style', + 'Baskerville', + serif; +} + +.weather-card__condition { + margin-top: 0.35rem; + font-size: 1.15rem; + color: #243041; +} + +.weather-card__facts { + display: grid; + gap: 0.45rem; + color: #475569; +} + +.weather-card__fact { + display: flex; + align-items: center; + gap: 0.45rem; + font-size: 0.98rem; +} + +.weather-card__forecast { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 0.65rem; + align-items: stretch; +} + +.forecast-pill { + display: grid; + justify-items: center; + align-content: center; + gap: 0.45rem; + padding: 0.8rem 0.55rem; + border-radius: 1rem; + background: #f7f4ef; + border: 1px solid rgba(82, 70, 56, 0.08); + height: 100%; +} + +.forecast-pill__label { + margin: 0; + color: #556274; + font-size: 0.9rem; + line-height: 1.1; +} + +.forecast-pill__temp { + margin: 0; + font-size: 1rem; + font-weight: 700; +} + +.forecast-pill__temp span { + margin-left: 0.15rem; + color: #7d8798; + font-weight: 500; +} + +.weather-card__metrics { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0.65rem; +} + +.metric-pill { + display: grid; + align-content: center; + gap: 0.45rem; + padding: 0.85rem 0.9rem 0.9rem; + border-radius: 1rem; +} + +.metric-pill__label { + display: flex; + align-items: center; + gap: 0.45rem; + font-size: 0.88rem; +} + +.metric-pill__value { + margin: 0; + font-size: 1.25rem; + font-weight: 700; +} + +.metric--sunrise { + background: linear-gradient(150deg, #f3eee5, #ebe3d6); + color: #5c4631; +} + +.metric--sunset { + background: linear-gradient(150deg, #efebe6, #e5dfd6); + color: #4f463d; +} + +.metric--air { + background: linear-gradient(150deg, #eef0eb, #e3e7de); + color: #354338; +} + +.metric--visibility { + background: linear-gradient(150deg, #eef1f0, #e2e5e4); + color: #3b4a4d; +} + +.quote-card { + grid-column: 1 / -1; + display: grid; + gap: 1.1rem; + padding: clamp(1.5rem, 1.2vw + 1rem, 2rem); + background: linear-gradient(168deg, rgba(249, 245, 236, 0.98), rgba(244, 239, 229, 0.94)); +} + +.quote-card__content { + margin: 0; + color: #1e293b; + line-height: 1.65; + font-family: + 'Iowan Old Style', + 'Baskerville', + 'Noto Serif SC', + serif; +} + +@media (max-width: 1080px) { + .page-shell { + padding: 0.9rem; + } + + .dashboard-frame { + aspect-ratio: auto; + width: min(100%, 1448px); + } + + .dashboard-grid { + grid-template-columns: 1fr; + grid-template-rows: auto; + } + + .quote-card { + grid-column: auto; + } + + .weather-card__forecast, + .weather-card__metrics { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 720px) { + .calendar-card__header, + .weather-card__heading, + .weather-card__hero { + grid-template-columns: 1fr; + display: grid; + } + + .calendar-card__header { + gap: 1rem; + } + + .weather-card__forecast, + .weather-card__metrics { + grid-template-columns: 1fr 1fr; + } + + .quote-card__content { + font-size: 1.45rem !important; + } +} diff --git a/calendar/tsconfig.json b/calendar/tsconfig.json new file mode 100644 index 0000000..cb34591 --- /dev/null +++ b/calendar/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "baseUrl": ".", + "module": "ESNext", + "moduleResolution": "Node", + "paths": { + "@/*": ["src/*"] + }, + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "types": ["vite/client"] + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/calendar/vite.config.ts b/calendar/vite.config.ts new file mode 100644 index 0000000..d0c2760 --- /dev/null +++ b/calendar/vite.config.ts @@ -0,0 +1,19 @@ +import { fileURLToPath, URL } from 'node:url'; + +import vue from '@vitejs/plugin-vue'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + server: { + fs: { + // 允许直接读取仓库根目录下的《日知录》原始 Markdown。 + allow: ['..'], + }, + }, +}); diff --git a/dash/.github/workflows/ci.yml b/dash/.github/workflows/ci.yml new file mode 100644 index 0000000..163b6ab --- /dev/null +++ b/dash/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + sh-checker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Run sh-checker + uses: luizm/action-sh-checker@v0.1.12 + env: + SHELLCHECK_OPTS: -s ash + SHFMT_OPTS: -i 2 + with: + sh_checker_comment: false diff --git a/dash/.gitignore b/dash/.gitignore new file mode 100644 index 0000000..3cbf92b --- /dev/null +++ b/dash/.gitignore @@ -0,0 +1,3 @@ +/dist +/kindle-dash-*.tgz +/tmp diff --git a/dash/CHANGELOG.md b/dash/CHANGELOG.md new file mode 100644 index 0000000..07f4a7b --- /dev/null +++ b/dash/CHANGELOG.md @@ -0,0 +1,43 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [v1.0.0-beta.4] - 2022-07-27 + +### Changed + +- Only call eips if fetch-dashboard succesfully completes +- Ensure a full screen refresh is triggered after wake from sleep +- Build ht from upstream sources, using rusttls instead of vendored openssl +- Replace ht 0.4.0 with xh 0.16.1 (project was renamed) + +## [v1.0.0-beta.3] - 2020-02-03 + +### Changed + +- Use 1.1.1.1 as default Wi-Fi test ip +- Use a more standards-compliant cron parser (BREAKING) + +### Added + +- Add low battery reporting (`local/low-battery.sh`) +- Add debug mode (DEBUG=true start.sh) +- SSH server prerequisite in docs (@julianlam) + +### Fixed + +- Typos (@jcmiller11, @starcoat) + +## [v1.0.0-beta-2] - 2020-01-26 + +### Removed + +- Power state logging + +## [v1.0.0-beta-1] - 2020-01-26 + +Initial release 🎉 diff --git a/dash/KUAL/kindle-dash/config.xml b/dash/KUAL/kindle-dash/config.xml new file mode 100644 index 0000000..da580b2 --- /dev/null +++ b/dash/KUAL/kindle-dash/config.xml @@ -0,0 +1,10 @@ + + + + Kindle dashboard + pascalw-kindle-dash + + + menu.json + + \ No newline at end of file diff --git a/dash/KUAL/kindle-dash/menu.json b/dash/KUAL/kindle-dash/menu.json new file mode 100644 index 0000000..cb75b2d --- /dev/null +++ b/dash/KUAL/kindle-dash/menu.json @@ -0,0 +1,5 @@ +{ + "items": [ + {"name": "Kindle Dashboard", "action": "/mnt/us/dashboard/start.sh"} + ] +} \ No newline at end of file diff --git a/dash/LICENSE b/dash/LICENSE new file mode 100644 index 0000000..05de768 --- /dev/null +++ b/dash/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021 Pascal Widdershoven + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dash/Makefile b/dash/Makefile new file mode 100644 index 0000000..639d45b --- /dev/null +++ b/dash/Makefile @@ -0,0 +1,44 @@ +VERSION := v1.0.0-beta.4 +SRC_FILES := $(shell find src -name '*.sh' -o -name '*.png') +NEXT_WAKEUP_SRC_FILES := $(shell find src/next-wakeup/src -name '*.rs') +TARGET_FILES := $(SRC_FILES:src/%=dist/%) + +dist: dist/next-wakeup dist/xh dist/local/state ${TARGET_FILES} + +tarball: dist + tar -C dist -cvzf kindle-dash-${VERSION}.tgz ./ + +dist/%: src/% + @echo "Copying $<" + @mkdir -p $(@D) + @cp "$<" "$@" + +dist/next-wakeup: ${NEXT_WAKEUP_SRC_FILES} + cd src/next-wakeup && cross build --release --target arm-unknown-linux-musleabi + cp src/next-wakeup/target/arm-unknown-linux-musleabi/release/next-wakeup dist/ + +dist/xh: tmp/xh + cd tmp/xh && cross build --release --target arm-unknown-linux-musleabi + docker run --rm \ + -v $(shell pwd)/tmp/xh:/src \ + rustembedded/cross:arm-unknown-linux-musleabi-0.2.1 \ + /usr/local/arm-linux-musleabi/bin/strip /src/target/arm-unknown-linux-musleabi/release/xh + cp tmp/xh/target/arm-unknown-linux-musleabi/release/xh dist/ + +tmp/xh: + mkdir -p tmp/ + git clone --depth 1 --branch v0.16.1 https://github.com/ducaale/xh.git tmp/xh + +dist/local/state: + mkdir -p dist/local/state + +clean: + rm -r dist/* + +watch: + watchexec -w src/ -p -- make + +format: + shfmt -i 2 -w -l src/**/*.sh + +.PHONY: clean watch tarball format diff --git a/dash/README.md b/dash/README.md new file mode 100644 index 0000000..bf9b99d --- /dev/null +++ b/dash/README.md @@ -0,0 +1,59 @@ +# Low-power Kindle dashboard + +Turns out old Kindle devices make great, energy efficient dashboards :-) + +![](./example/photo.jpg) + +## What this repo is + +This repo only contains the code that runs on the Kindle. It periodically fetches an image to be displayed on the screen and suspends the device to RAM (which is very power efficient) until the next screen update. + +This code _does not_ render the dashboard itself. It's expected that what to display on the screen is rendered elsewhere and can be fetchd via HTTP(s). This is both more power efficient and allows you to use any tool you like to produce the dashboard image. + +In my case I use a [dashbling](https://github.com/pascalw/dashbling) dashboard that I render into a PNG screenshot on a server. See [here](https://github.com/pascalw/kindle-dash/blob/main/docs/tipstricks.md#producing-dashboard-images-from-a-webpage) for information on how these PNGs should be produced, including some sample code. + +## Prerequisites + +* A jailbroken Kindle, with Wi-Fi configured. +* An SSH server on the Kindle (via [USBNetwork](https://wiki.mobileread.com/wiki/USBNetwork)) +* Tested only on a Kindle 4 NT. Should work on other Kindle devices as well with minor modifications. + +## Installation + +1. Download the [latest release](https://github.com/pascalw/kindle-dash/releases) on your computer and extract it. +2. Modify `local/fetch-dashboard.sh` and optionally `local/env.sh`. +3. Copy the files to the Kindle, for example: `rsync -vr ./ kindle:/mnt/us/dashboard`. +4. Start dashboard with `/mnt/us/dashboard/start.sh`. + Note that the device will go into suspend about 10-15 seconds after you start the dashboard. + +## Upgrading + +If you're running kindle-dash already and want to update to the latest version follow the following steps. + +1. Download the [latest release](https://github.com/pascalw/kindle-dash/releases) on your computer and extract it. +2. Review the release notes. Some releases might require changes to files in `local/`. +3. Copy the files to the Kindle, excluding the `local` directory. For example: `rsync -vur --exclude=local ./ kindle:/mnt/us/dashboard`. +4. Modify files in `/mnt/us/dashboard/local` if applicable. +5. Start dashboard with `/mnt/us/dashboard/start.sh`. + Note that the device will go into suspend about 10-15 seconds after you start the dashboard. + +## KUAL + +If you're using KUAL you can use simple extension to start this Dashboard + +1. Copy folder `kindle-dash` from `KUAL` folder to the kual `extensions` folder. (located in `/mnt/us/extensions`) + +## How this works + +* This code periodically downloads a dashboard image from an HTTP(s) endpoint. +* The interval can be configured in `dist/local/env.sh` using a cron expression. +* During the update intervals the device is suspended to RAM to save power. + +## Notes + +* The releases contain a pre-compiled binary of the [ht](https://github.com/ducaale/ht) command-line HTTP client. This fully supports modern HTTPS crypto, wheras the built-in `curl` and `wget` commands don't (because they rely on a very old `openssl` library). +* For a detailed Kindle Voyage 5.13.6 jailbreak and deployment walkthrough, see [docs/kindle-voyage-5.13.6-watchthis-zh.md](./docs/kindle-voyage-5.13.6-watchthis-zh.md). + +## Credits + +Thanks to [davidhampgonsalves/life-dashboard](https://github.com/davidhampgonsalves/life-dashboard) for the inspiration! diff --git a/dash/docs/kindle-voyage-5.13.6-watchthis-zh.md b/dash/docs/kindle-voyage-5.13.6-watchthis-zh.md new file mode 100644 index 0000000..81c5c49 --- /dev/null +++ b/dash/docs/kindle-voyage-5.13.6-watchthis-zh.md @@ -0,0 +1,238 @@ +# Kindle Voyage 5.13.6 一次成功路径 + +这篇文档只覆盖下面这个组合: + +- 机型:`Kindle Voyage (KV)` +- 固件:`5.13.6` +- 目标:完成越狱,并部署 `KUAL`、`MRPI`、`renameotabin` 和 `kindle-dash` + +如果设备型号或固件版本不同,不要直接照抄本文。 + +## 核心结论 + +`Kindle Voyage 5.13.6` 应该走 `WatchThis`,不要走 `LanguageBreak`。 + +这次实操里,前面大部分失败都来自两个错误: + +- 误走了 `LanguageBreak` +- 在 demo 菜单里点错了分支,提前进入了 `Resell Device` / `销售设备` + +对这台设备,正确思路非常简单: + +1. 用 `WatchThis` 进入 demo mode +2. 只在正确的 `Sideload Content` 时机导入 `KV-5.13.6.zip` +3. 用 `Get Started` 触发越狱脚本 +4. 安装 `KUAL/MRPI` +5. 用 `renameotabin` 关闭 OTA +6. 再部署并启动 `kindle-dash` + +## 需要准备的文件 + +### WatchThis + +来自 `watchthis-jailbreak-r03.zip`: + +- `KV-5.13.6.zip` +- `demo.json` +- `Update_hotfix_watchthis_custom.bin` + +在本仓库里对应的是: + +- `staging/watchthis/KV-5.13.6/KV-5.13.6.zip` +- `staging/watchthis/KV-5.13.6/demo.json` +- `staging/watchthis/Update_hotfix_watchthis_custom.bin` + +### 越狱后安装包 + +- `extensions/MRInstaller` +- `mrpackages/Update_KUALBooklet_HDRepack.bin` +- `extensions/renameotabin` +- `extensions/kindle-dash` +- `dashboard/` + +在本仓库里已经整理到: + +- `staging/post-jailbreak-root/extensions/` +- `staging/post-jailbreak-root/mrpackages/` +- `staging/post-jailbreak-root/dashboard/` + +## 一次成功的正确路径 + +### 1. 恢复出厂并进入 demo mode + +1. 先恢复出厂设置。 +2. 语言选择页只选 `English (United Kingdom)`。 + 这一步非常关键,不要选中文。 +3. 到 Wi‑Fi 页面后,随便点一个网络,再立刻退回,不要真的联网。 +4. 在搜索栏输入 `;enter_demo`。 +5. 如果 `;enter_demo` 没反应,走备用入口: + - 用 USB 连接电脑 + - 在 Kindle 根目录创建空文件 `DONT_CHECK_BATTERY` + - 弹出设备 + - 回到 Kindle 搜索输入 `;demo` +6. 如果看到 `Demo Activation`,点 `Yes`。 +7. 设备重启并进入 demo 流程后: + - 跳过 Wi‑Fi + - 店铺注册信息全部填假值 + - `Fetching available demo types` 选 `Skip` + - demo type 选 `standard` + +### 2. 第一次出现 Sideload Content 时不要导入 payload + +1. 第一次出现 `Add Content` / `Sideload Content` 提示时,只点 `Done`。 +2. 这一步不要接 USB。 +3. 这一步也不要导入 `KV-5.13.6.zip`。 + +这是最容易做错的一步。第一次 `Done` 只是让 demo setup 继续往下走,不是真正的 payload 导入点。 + +### 3. 跳过 misconfiguration 锁页 + +demo setup 完成后,大概率会落到 `Configure Device` / misconfiguration 页面。 + +不要点 `Configure Device`,直接做隐藏手势: + +1. 在屏幕右下角用两根手指同时轻点一下 +2. 两指立刻抬起 +3. 马上用一根手指从右下向左滑 + +触发成功后会回到可操作界面。 + +### 4. 真正的 payload 导入点 + +1. 回到可操作界面后,搜索输入 `;demo` +2. 进入 demo menu +3. 选择 `Sideload Content` / `导入内容` +4. 到这一步再接 USB +5. 在 Kindle 根目录创建 `.demo/` +6. 把下面三个东西放进去: + +```text +.demo/KV-5.13.6.zip +.demo/demo.json +.demo/goodreads/ <- 空目录 +``` + +如果你在 Mac 上操作,可以直接用: + +```sh +mkdir -p /Volumes/Kindle/.demo/goodreads +cp staging/watchthis/KV-5.13.6/KV-5.13.6.zip /Volumes/Kindle/.demo/ +cp staging/watchthis/KV-5.13.6/demo.json /Volumes/Kindle/.demo/ +``` + +然后: + +1. 弹出 Kindle +2. 在 Kindle 上点 `Done` +3. 退出 demo menu + +## 5. 触发越狱脚本 + +1. 退出 demo menu 后,输入 `;dsts` + 如果 `;dsts` 没反应,也可以从顶部下拉进入设置。 +2. 打开 `Help & User Guides` +3. 再点 `Get Started` +4. 设备会重启 +5. 越狱脚本会在下次启动时运行 + +如果这里弹 `Application Error`,官方补救是: + +1. 长按电源键强制重启 +2. 再进 demo menu +3. 再执行一次 `Sideload Content -> Done` +4. 这次不要再接 USB + +## 6. 成功判据 + +对这台设备,下面这些现象说明越狱已经落地: + +- Kindle 用户存储根目录出现 `mkk` +- Kindle 用户存储根目录出现 `libkh` +- Kindle 用户存储根目录出现 `rp` + +如果这三个目录都没有,基本就是前面的 `WatchThis` 没真正成功。 + +## 7. 安装 KUAL / MRPI / kindle-dash + +越狱落地后,把这些目录复制到 Kindle: + +```sh +rsync -a staging/post-jailbreak-root/extensions/ /Volumes/Kindle/extensions/ +rsync -a staging/post-jailbreak-root/mrpackages/ /Volumes/Kindle/mrpackages/ +rsync -a staging/post-jailbreak-root/dashboard/ /Volumes/Kindle/dashboard/ +``` + +然后: + +1. 弹出 Kindle +2. 回到首页搜索输入 `;log mrpi` +3. 等安装完成 +4. 首页会出现 `KUAL` 卡片 + +## 8. 启动顺序 + +进入 `KUAL` 后,先做这个顺序: + +1. `Rename OTA Binaries -> Rename` +2. 再运行 `Kindle Dashboard` + +不要先跑 `Kindle Dashboard`,否则后面如果 OTA 没关掉,还存在自动升级把越狱覆盖掉的风险。 + +## 9. kindle-dash 默认行为 + +本项目默认不会在 Kindle 本机实时渲染页面,而是定时去下载一张图片来显示。 + +因此: + +- 如果没有联网,`Kindle Dashboard` 看起来会像“卡住” +- 如果刷新计划不覆盖当前时间,会显示 `kindle is sleeping` +- 图片最好直接按 Voyage 原生分辨率出图:`1072 x 1448` + +默认抓图脚本在: + +- `src/local/fetch-dashboard.sh` + +默认刷新计划在: + +- `src/local/env.sh` + +## 10. 这台设备上确认过的坑 + +### 不要走 `LanguageBreak` + +`KV + 5.13.6` 应走 `WatchThis`。误走 `LanguageBreak` 会导致: + +- `;demo -> Yes -> 重启 -> 回普通系统` +- `;uzb`、`;dsts` 行为异常 +- 反复进入错误的 demo 分支 + +### 不要点 `Resell Device` / `销售设备` + +这个分支会把流程带到 shipping mode / demo 出厂流程,和 `WatchThis` 正常路径无关。 + +如果你是在 `WatchThis` 流程里,demo menu 里真正要点的是: + +- `Sideload Content` + +不是: + +- `Resell Device` +- `Remote Reset` +- `Configure WiFi` + +### 第一次 `Add Content` 只能点 `Done` + +真正要接 USB 导 payload 的时机,是秘密手势之后再次 `;demo -> Sideload Content` 的那一次。 + +### 看到左上角只有一小块图片,不一定是失败 + +这通常只是图片尺寸不匹配。 + +例如本项目自带的 `sleeping.png` 只有 `600x800`,放到 Voyage 上就只会显示在左上角一部分区域。 + +## 参考 + +- WatchThis 包内说明:`watchthis-jailbreak-r03.zip` 中的 `watchthis-release/README.md` +- 项目主说明:`README.md` +- 图片抓取说明:`src/local/fetch-dashboard.sh` +- 本地调度配置:`src/local/env.sh` diff --git a/dash/docs/layered-clock-plan.zh.md b/dash/docs/layered-clock-plan.zh.md new file mode 100644 index 0000000..00968d9 --- /dev/null +++ b/dash/docs/layered-clock-plan.zh.md @@ -0,0 +1,449 @@ +# Kindle Dashboard 分层时钟方案 + +## 1. 背景 + +当前仓库里有两部分: + +- `calendar/`:负责渲染仪表盘网页与导出背景素材 +- `dash/`:运行在 Kindle 上,负责拉图、刷屏、休眠与唤醒 + +最新设计稿来自 Figma: + +- 文件:`calendar` +- 节点:`6:2` +- 链接:`https://www.figma.com/design/3bXFNM5nM6mCq0TpL3nPYK/calendar?node-id=6-2&m=dev` + +该节点 annotation 已明确: + +- `阳历当天` +- `星期` +- `农历日` +- `时钟区域` +- `日历` +- `天气预报卡片` +- `书摘卡片` + +本方案基于这些约束,采用“低频背景 + 本地时钟”的拆分方式: + +- `calendar/` 每 `2 小时` 生成一次背景图 +- 背景图直接写到: + - `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png` +- Kindle 通过固定 HTTPS 地址拉取背景图: + - `https://shell.biboer.cn:20001/kindlebg.png` +- Kindle 端每分钟`不联网` +- Kindle 端只在本地重画时钟区域 + +## 2. 目标 + +这次改造不是单纯调样式,而是把页面拆成两类刷新节奏完全不同的素材: + +1. 低频背景层 +2. 高频时钟层 + +目标收益: + +- 背景内容不再分钟级刷新 +- 分钟级变化只限制在时钟区域 +- 降低 Wi-Fi 唤醒次数 +- 降低全屏刷新频率 +- 减少墨水屏闪烁与残影 + +## 3. 分层边界 + +### 3.1 全屏背景层 + +背景层文件名固定为: + +- `kindlebg.png` + +背景层包含: + +- 整体外层容器、圆角、阴影、背景渐变 +- 日历卡片中的: + - 阳历当天数字 + - 星期 + - 农历日 + - 下方月历区域 +- 天气卡片全部内容 +- 书摘卡片全部内容 + +背景层**不包含**: + +- 时钟表盘主体 +- 时钟刻度 +- 时针 +- 分针 +- 中心圆点 + +也就是说,背景图在时钟区域只保留布局占位,不直接承载任何分钟级内容。 + +### 3.2 静态表盘 patch + +静态表盘 patch 为一个独立本地素材: + +- `clock-face.png` + +该素材包含: + +- 圆形表盘主体 +- 刻度 +- 中心底座或中心圆盘 + +建议尺寸直接对齐 Figma 时钟区域: + +- 节点:`24:74` +- 设计尺寸:`220 x 220` + +这张图应当保存在 Kindle 本地,例如: + +- `/mnt/us/dashboard/assets/clock-face.png` + +它不需要分钟级联网拉取,只需要在部署时同步到设备,或者在更换设计稿时重新同步。 + +### 3.3 指针层 + +高频变化层只包含: + +- 时针 +- 分针 + +这里不建议包含秒针: + +- 墨水屏收益低 +- 刷新频率会显著上升 +- 更容易产生残影 + +由于当前 `dash/` 链路本质是 `eips` 刷图,不是 SVG/Canvas 实时渲染,所以推荐使用`分层素材方案`: + +- `minute-hand/00.png` 到 `minute-hand/59.png` +- `hour-hand/000.png` 到 `hour-hand/719.png` + +说明: + +- 分针每分钟一个角度,共 `60` 张 +- 时针如果要做到真正随分钟连续移动,需要 `12 * 60 = 720` 张 +- 这些都是小尺寸 patch,不是整屏图,体积可控 + +如果后续确认 Kindle 端存在稳定的本地绘线工具,再考虑把指针改成算法绘制;当前版本不依赖这个前提。 + +## 4. 为什么不能只把表盘放进整页背景 + +这个问题必须单独说明。 + +如果: + +- 表盘主体和刻度只存在于 `kindlebg.png` +- Kindle 每分钟只覆盖新的时针/分针 + +那么上一分钟留下的旧指针就无法被干净擦除。 + +因此 Kindle 端每分钟的正确流程应该是: + +1. 先重画一张本地 `clock-face.png` +2. 再叠加新的时针素材 +3. 再叠加新的分针素材 + +这等价于“先擦除,再重画”,并且整个流程不依赖网络。 + +所以本方案不是“背景图 + 两根指针”两层,而是: + +1. `kindlebg.png`:全屏低频背景 +2. `clock-face.png`:本地静态表盘 patch +3. `hour-hand/*.png + minute-hand/*.png`:本地高频指针素材 + +## 5. 数据刷新策略 + +### 5.1 背景层刷新 + +背景层刷新触发条件: + +- 每 `2 小时` 一次 +- 跨天时立即刷新一次 +- 天气接口异常恢复后可补刷一次 + +推荐调度: + +- `00:00 / 02:00 / 04:00 / ... / 22:00` + +背景层刷新输出: + +- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png` + +背景层对 Kindle 的访问地址固定为: + +- `https://shell.biboer.cn:20001/kindlebg.png` + +### 5.2 静态表盘 patch 刷新 + +静态表盘 patch 不参加分钟级调度。 + +建议刷新方式: + +- 随部署同步一次 +- Figma 设计或尺寸变化时重新导出并同步 + +### 5.3 指针层刷新 + +指针层刷新触发条件: + +- 每分钟一次 + +指针层数据只依赖 Kindle 本地时间: + +- 小时 +- 分钟 + +分钟刷新不需要联网。 + +## 6. 网页渲染模式设计 + +为了让 `calendar/` 稳定产出背景与表盘素材,建议支持下面 3 种模式。 + +### 6.1 `full` + +用途: + +- 本地开发预览 +- 对照 Figma 联调 + +输出内容: + +- 背景层 +- 表盘 +- 指针预览 + +### 6.2 `background` + +用途: + +- 生成 `kindlebg.png` + +输出内容: + +- 只渲染背景层 +- 时钟区域保留占位,但不绘制表盘与指针 + +### 6.3 `clock-face` + +用途: + +- 生成静态表盘 patch + +输出内容: + +- 只渲染表盘主体、刻度和中心底座 +- 不绘制时针、分针 + +### 6.4 URL 约定 + +建议页面支持如下参数: + +```text +/?mode=full +/?mode=background +/?mode=clock-face +``` + +## 7. 产物约定 + +推荐最终产出这几类文件: + +```text +calendar/dist/kindlebg.png +calendar/dist/dashboard-manifest.json + +kindle local: + /mnt/us/dashboard/assets/clock-face.png + /mnt/us/dashboard/assets/hour-hand/000.png ... 719.png + /mnt/us/dashboard/assets/minute-hand/00.png ... 59.png +``` + +`manifest` 建议至少包含这些字段: + +```json +{ + "background": { + "path": "kindlebg.png", + "url": "https://shell.biboer.cn:20001/kindlebg.png", + "updatedAt": "2026-03-15T10:00:00+08:00", + "refreshIntervalMinutes": 120 + }, + "clockRegion": { + "x": 313, + "y": 0, + "width": 220, + "height": 220 + }, + "clockFace": { + "path": "clock-face.png", + "managedOnKindle": true + }, + "clockHands": { + "hourPattern": "assets/hour-hand/%03d.png", + "minutePattern": "assets/minute-hand/%02d.png", + "refreshIntervalMinutes": 1, + "networkRequired": false + } +} +``` + +说明: + +- `x/y/width/height` 先按设计稿记录 +- 真正接入 Kindle 时,要换算成最终截图分辨率下的实际像素值 + +## 8. Kindle 侧刷新策略 + +### 8.1 已确认的前提 + +`eips` 支持把 PNG/JPG 绘制到指定坐标,参数包含: + +- `-g` +- `-x` +- `-y` +- `-f` + +MobileRead Wiki 明确写了: + +- `eips -g|-b image_path [-w waveform -f -x xpos -y ypos -v]` +- `-x` 与 `-y` 以像素为单位 + 来源: + +因此,对 Kindle Voyage 而言,“在固定时钟区域重画小图”这个前提是成立的。 + +### 8.2 推荐流程 + +#### 启动或背景刷新时 + +1. 通过 HTTPS 拉取: + - `https://shell.biboer.cn:20001/kindlebg.png` +2. 保存为本地背景缓存 +3. 使用全屏刷新显示背景图 +4. 在时钟区域重画一次: + - `clock-face.png` + - 当前时针 + - 当前分针 + +#### 每分钟刷新时 + +1. 读取 Kindle 本机时间 +2. 计算: + - `minute_index = 00..59` + - `hour_index = ((hour % 12) * 60 + minute) = 000..719` +3. 在固定坐标先画: + - `clock-face.png` +4. 再画: + - `hour-hand/.png` +5. 再画: + - `minute-hand/.png` +6. 默认做局部/普通刷新 +7. 每 `10` 到 `15` 分钟对时钟区域补一次全刷,清理残影 + +### 8.3 功耗模型 + +这套方式的功耗来源拆成两类: + +- 背景刷新: + - 每 2 小时联网一次 + - 全屏刷新一次 +- 时钟刷新: + - 每分钟本地刷一次小区域 + - 不联网 + +相比“每分钟拉一张整屏背景图”,这会明显省电。 + +## 9. 代码改造建议 + +### 9.1 `calendar/` + +建议改造点: + +- 新增 `mode=background` +- 新增 `mode=clock-face` +- 时钟区域从日历/天气/书摘中完全拆开 +- 增加一个定时生成任务,每 2 小时把背景图写到: + - `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png` +- 生成 `dashboard-manifest.json` + +### 9.2 `dash/` + +建议改造点: + +- `dash/src/local/fetch-dashboard.sh` + - 改成只拉取 `kindlebg.png` +- `dash/src/dash.sh` + - 从“单一刷新循环”改为“背景刷新 + 本地时钟刷新”双节奏 +- 新增例如: + - `dash/src/local/render-clock.sh` + - `dash/src/local/render-clock-face.sh` + - `dash/src/local/clock-index.sh` + +推荐新增配置项: + +```sh +export BACKGROUND_URL="https://shell.biboer.cn:20001/kindlebg.png" +export BACKGROUND_REFRESH_SCHEDULE="0 */2 * * *" +export CLOCK_REGION_X=313 +export CLOCK_REGION_Y=0 +export CLOCK_REGION_WIDTH=220 +export CLOCK_REGION_HEIGHT=220 +export CLOCK_FULL_REFRESH_INTERVAL_MINUTES=15 +``` + +## 10. 实施顺序 + +建议按下面顺序落地,避免一次改太多导致链路难排查。 + +### 阶段 1:`calendar/` 分层输出 + +目标: + +- `full/background/clock-face` 三种模式跑通 +- 每 2 小时把背景图写到 `calendar/dist/kindlebg.png` + +输出: + +- 浏览器可预览 +- `kindlebg.png` 可被 nginx 直接访问 + +### 阶段 2:时钟静态素材准备 + +目标: + +- 产出 `clock-face.png` +- 产出 `hour-hand` 与 `minute-hand` 素材库 + +输出: + +- Kindle 本地时钟素材目录结构确定 + +### 阶段 3:Kindle 本地分钟时钟 + +目标: + +- Kindle 每分钟本地重画时钟 +- 不联网 + +输出: + +- 背景低频更新 + 时钟本地高频更新的完整链路 + +## 11. 推荐结论 + +当前最稳妥的推进顺序是: + +1. 先让 `calendar/` 每 2 小时稳定生成: + - `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png` +2. 再让 Kindle 只从: + - `https://shell.biboer.cn:20001/kindlebg.png` + 拉背景图 +3. 时钟区域完全本地化: + - 本地 `clock-face.png` + - 本地 `hour-hand/*.png` + - 本地 `minute-hand/*.png` +4. 分钟刷新时: + - 先重画表盘 + - 再重画时针 + - 再重画分针 + +也就是说,**背景是远端低频资源,时钟是本地高频资源,二者不要混在同一个刷新链路里**。 diff --git a/dash/docs/screenshotter/.dockerignore b/dash/docs/screenshotter/.dockerignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/dash/docs/screenshotter/.dockerignore @@ -0,0 +1 @@ +/node_modules diff --git a/dash/docs/screenshotter/.gitignore b/dash/docs/screenshotter/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/dash/docs/screenshotter/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/dash/docs/screenshotter/Dockerfile b/dash/docs/screenshotter/Dockerfile new file mode 100644 index 0000000..9af7db3 --- /dev/null +++ b/dash/docs/screenshotter/Dockerfile @@ -0,0 +1,30 @@ +FROM node:12-buster-slim + +RUN apt-get update \ + && apt-get install -y wget gnupg \ + && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ + && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ + && apt-get update \ + && apt-get install -y google-chrome-stable fonts-freefont-ttf libxss1 fonts-noto-color-emoji \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 1 +ENV PUPPETEER_EXECUTABLE_PATH /usr/bin/google-chrome-stable + +ADD package.json yarn.lock /app/ + +RUN cd /app && yarn install \ + # Add user so we don't need --no-sandbox. + # same layer as npm install to keep re-chowned files from using up several hundred MBs more space + && groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \ + && mkdir -p /home/pptruser/Downloads \ + && chown -R pptruser:pptruser /home/pptruser \ + && chown -R pptruser:pptruser /app/node_modules + +ADD . /app/ + +# Run everything after as non-privileged user. +USER pptruser + +CMD ["node", "/app/screenshot.js"] diff --git a/dash/docs/screenshotter/package.json b/dash/docs/screenshotter/package.json new file mode 100644 index 0000000..da2bd8e --- /dev/null +++ b/dash/docs/screenshotter/package.json @@ -0,0 +1,9 @@ +{ + "name": "screenshotter", + "version": "1.0.0", + "private": true, + "dependencies": { + "pngjs": "^6.0.0", + "puppeteer": "^5.5.0" + } +} diff --git a/dash/docs/screenshotter/screenshot.js b/dash/docs/screenshotter/screenshot.js new file mode 100644 index 0000000..85e10fc --- /dev/null +++ b/dash/docs/screenshotter/screenshot.js @@ -0,0 +1,38 @@ +const puppeteer = require("puppeteer"); +const fs = require("fs"); +const PNG = require("pngjs").PNG; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +const URL = process.env.URL || "CHANGEME"; + +(async () => { + const browser = await puppeteer.launch({ + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }); + + const page = await browser.newPage(); + + /* Kindle 4 NT resolution */ + await page.setViewport({ width: 600, height: 800 }); + + /* Might want to use networkidle0 here, depending on the type of page */ + /* See https://github.com/puppeteer/puppeteer/blob/main/docs/api.md */ + await page.goto(URL, { waitUntil: "networkidle2" }); + + /* This is a bit silly. ¯\_(ツ)_/¯ + Networkidle2 doesn't always seem to wait long enough. */ + await sleep(3000); + + await page.screenshot({ path: "dash.png" }); + + await fs.createReadStream("dash.png") + .pipe(new PNG({ colorType: 0 })) + .on("parsed", function () { + this.pack().pipe(fs.createWriteStream("dash.png")); + }); + + browser.close(); +})(); diff --git a/dash/docs/screenshotter/yarn.lock b/dash/docs/screenshotter/yarn.lock new file mode 100644 index 0000000..fa0c2a0 --- /dev/null +++ b/dash/docs/screenshotter/yarn.lock @@ -0,0 +1,373 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@*": + version "14.14.22" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" + integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== + +"@types/yauzl@^2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" + integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== + dependencies: + "@types/node" "*" + +agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bl@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" + integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer@^5.2.1, buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +debug@4, debug@^4.1.0, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +devtools-protocol@0.0.818844: + version "0.0.818844" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.818844.tgz#d1947278ec85b53e4c8ca598f607a28fa785ba9e" + integrity sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +extract-zip@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +https-proxy-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" + integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== + dependencies: + agent-base "5" + debug "4" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pngjs@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" + integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== + +progress@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +proxy-from-env@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +puppeteer@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.5.0.tgz#331a7edd212ca06b4a556156435f58cbae08af00" + integrity sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg== + dependencies: + debug "^4.1.0" + devtools-protocol "0.0.818844" + extract-zip "^2.0.0" + https-proxy-agent "^4.0.0" + node-fetch "^2.6.1" + pkg-dir "^4.2.0" + progress "^2.0.1" + proxy-from-env "^1.0.0" + rimraf "^3.0.2" + tar-fs "^2.0.0" + unbzip2-stream "^1.3.3" + ws "^7.2.3" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +unbzip2-stream@^1.3.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^7.2.3: + version "7.4.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd" + integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA== + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" diff --git a/dash/docs/tipstricks.md b/dash/docs/tipstricks.md new file mode 100644 index 0000000..4a6e9bc --- /dev/null +++ b/dash/docs/tipstricks.md @@ -0,0 +1,12 @@ +# Tips & tricks + +## Producing dashboard images from a webpage + +A common way to produce dashboard images for the Kindle is to take a screenshot of a website. +This can be done in a variety of ways. A few things to keep in mind are: + +1. The images should be grayscale PNG images, without any alpha layers. +2. The resolution should match the display resolution of the Kindle. For example the Kindle 4 NT has a resolution of 800x600 pixels. + +I personally use a headless Chrome instance with [Puppeteer](https://pptr.dev/). +The code I use can be found [here](./screenshotter/screenshot.js) as a reference. diff --git a/dash/example/example.png b/dash/example/example.png new file mode 100644 index 0000000..4053538 Binary files /dev/null and b/dash/example/example.png differ diff --git a/dash/example/photo.jpg b/dash/example/photo.jpg new file mode 100644 index 0000000..79062d4 Binary files /dev/null and b/dash/example/photo.jpg differ diff --git a/dash/src/dash.sh b/dash/src/dash.sh new file mode 100755 index 0000000..a1eaa09 --- /dev/null +++ b/dash/src/dash.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +DASH_PNG="$DIR/dash.png" +FETCH_DASHBOARD_CMD="$DIR/local/fetch-dashboard.sh" +LOW_BATTERY_CMD="$DIR/local/low-battery.sh" + +REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"2,32 8-17 * * MON-FRI"} +FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} +SLEEP_SCREEN_INTERVAL=${SLEEP_SCREEN_INTERVAL:-3600} +RTC=/sys/devices/platform/mxc_rtc.0/wakeup_enable + +LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +LOW_BATTERY_THRESHOLD_PERCENT=${LOW_BATTERY_THRESHOLD_PERCENT:-10} + +num_refresh=0 + +init() { + if [ -z "$TIMEZONE" ] || [ -z "$REFRESH_SCHEDULE" ]; then + echo "Missing required configuration." + echo "Timezone: ${TIMEZONE:-(not set)}." + echo "Schedule: ${REFRESH_SCHEDULE:-(not set)}." + exit 1 + fi + + echo "Starting dashboard with $REFRESH_SCHEDULE refresh..." + + /etc/init.d/framework stop + initctl stop webreader >/dev/null 2>&1 + echo powersave >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + lipc-set-prop com.lab126.powerd preventScreenSaver 1 +} + +prepare_sleep() { + echo "Preparing sleep" + + /usr/sbin/eips -f -g "$DIR/sleeping.png" + + # Give screen time to refresh + sleep 2 + + # Ensure a full screen refresh is triggered after wake from sleep + num_refresh=$FULL_DISPLAY_REFRESH_RATE +} + +refresh_dashboard() { + echo "Refreshing dashboard" + "$DIR/wait-for-wifi.sh" "$WIFI_TEST_IP" + + "$FETCH_DASHBOARD_CMD" "$DASH_PNG" + fetch_status=$? + + if [ "$fetch_status" -ne 0 ]; then + echo "Not updating screen, fetch-dashboard returned $fetch_status" + return 1 + fi + + if [ "$num_refresh" -eq "$FULL_DISPLAY_REFRESH_RATE" ]; then + num_refresh=0 + + # trigger a full refresh once in every 4 refreshes, to keep the screen clean + echo "Full screen refresh" + /usr/sbin/eips -f -g "$DASH_PNG" + else + echo "Partial screen refresh" + /usr/sbin/eips -g "$DASH_PNG" + fi + + num_refresh=$((num_refresh + 1)) +} + +log_battery_stats() { + battery_level=$(gasgauge-info -c) + echo "$(date) Battery level: $battery_level." + + if [ "$LOW_BATTERY_REPORTING" = true ]; then + battery_level_numeric=${battery_level%?} + if [ "$battery_level_numeric" -le "$LOW_BATTERY_THRESHOLD_PERCENT" ]; then + "$LOW_BATTERY_CMD" "$battery_level_numeric" + fi + fi +} + +rtc_sleep() { + duration=$1 + + if [ "$DEBUG" = true ]; then + sleep "$duration" + else + # shellcheck disable=SC2039 + [ "$(cat "$RTC")" -eq 0 ] && echo -n "$duration" >"$RTC" + echo "mem" >/sys/power/state + fi +} + +main_loop() { + while true; do + log_battery_stats + + next_wakeup_secs=$("$DIR/next-wakeup" --schedule="$REFRESH_SCHEDULE" --timezone="$TIMEZONE") + + if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ]; then + action="sleep" + prepare_sleep + else + action="suspend" + refresh_dashboard + fi + + # take a bit of time before going to sleep, so this process can be aborted + sleep 10 + + echo "Going to $action, next wakeup in ${next_wakeup_secs}s" + + rtc_sleep "$next_wakeup_secs" + done +} + +init +main_loop diff --git a/dash/src/local/env.sh b/dash/src/local/env.sh new file mode 100644 index 0000000..2e74d96 --- /dev/null +++ b/dash/src/local/env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Export environment variables here +export WIFI_TEST_IP=${WIFI_TEST_IP:-1.1.1.1} +# 测试配置:全天每分钟刷新一次,便于验证图片拉取与屏幕刷新是否正常。 +export REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"* * * * *"} +export TIMEZONE=${TIMEZONE:-"Asia/Shanghai"} + +# By default, partial screen updates are used to update the screen, +# to prevent the screen from flashing. After a few partial updates, +# the screen will start to look a bit distorted (due to e-ink ghosting). +# 测试阶段强制每次都做一次全刷,避免首页残影和局部刷新的旧内容干扰验证。 +# 等图片尺寸与刷新逻辑确认无误后,再改回 4 之类的值以节省功耗。 +export FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} + +# When the time until the next wakeup is greater or equal to this number, +# the dashboard will not be refreshed anymore, but instead show a +# 'kindle is sleeping' screen. This can be useful if your schedule only runs +# during the day, for example. +export SLEEP_SCREEN_INTERVAL=3600 + +export LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +export LOW_BATTERY_THRESHOLD_PERCENT=10 diff --git a/dash/src/local/fetch-dashboard.sh b/dash/src/local/fetch-dashboard.sh new file mode 100755 index 0000000..0733233 --- /dev/null +++ b/dash/src/local/fetch-dashboard.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +# Fetch a new dashboard image, make sure to output it to "$1". +# For example: +"$(dirname "$0")/../xh" -d -q -o "$1" get https://raw.githubusercontent.com/pascalw/kindle-dash/master/example/example.png diff --git a/dash/src/local/low-battery.sh b/dash/src/local/low-battery.sh new file mode 100644 index 0000000..dc70ced --- /dev/null +++ b/dash/src/local/low-battery.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +battery_level_percentage=$1 +last_battery_report_state="$(dirname "$0")/state/last_battery_report" + +previous_report_timestamp=$(cat "$last_battery_report_state" 2>/dev/null || echo '-1') +now=$(date +%s) + +# Implement desired logic here. The example below for example only reports low +# battery every 24 hours. + +if [ "$previous_report_timestamp" -eq -1 ] || + [ $((now - previous_report_timestamp)) -gt 86400 ]; then + # Replace this with for example an HTTP call via curl, or xh + echo "Reporting low battery: $battery_level_percentage%" + + echo "$now" >"$last_battery_report_state" +fi diff --git a/dash/src/next-wakeup/.gitignore b/dash/src/next-wakeup/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/dash/src/next-wakeup/.gitignore @@ -0,0 +1 @@ +/target diff --git a/dash/src/next-wakeup/Cargo.lock b/dash/src/next-wakeup/Cargo.lock new file mode 100644 index 0000000..8928a45 --- /dev/null +++ b/dash/src/next-wakeup/Cargo.lock @@ -0,0 +1,143 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "chrono-tz" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2554a3155fec064362507487171dcc4edc3df60cb10f3a1fb10ed8094822b120" +dependencies = [ + "chrono", + "parse-zoneinfo", +] + +[[package]] +name = "cron-parser" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8446d1ce86096fd2260e732c6f5dffc752cbfa6cb14371d057bc1de3a1831b6" +dependencies = [ + "chrono", +] + +[[package]] +name = "kindle-dash-next-wakeup" +version = "0.1.0" +dependencies = [ + "chrono", + "chrono-tz", + "cron-parser", + "pico-args", +] + +[[package]] +name = "libc" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] +name = "pico-args" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6" + +[[package]] +name = "regex" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/dash/src/next-wakeup/Cargo.toml b/dash/src/next-wakeup/Cargo.toml new file mode 100644 index 0000000..de5b41b --- /dev/null +++ b/dash/src/next-wakeup/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "kindle-dash-next-wakeup" +version = "0.1.0" +authors = ["Pascal Widdershoven "] +edition = "2018" + +[dependencies] +cron-parser = "0.7.7" +chrono = "0.4.19" +chrono-tz = "0.5.3" +pico-args = "0.4.0" + +[[bin]] +name = "next-wakeup" +path = "src/main.rs" diff --git a/dash/src/next-wakeup/src/main.rs b/dash/src/next-wakeup/src/main.rs new file mode 100644 index 0000000..265864f --- /dev/null +++ b/dash/src/next-wakeup/src/main.rs @@ -0,0 +1,54 @@ +use chrono::Utc; +use chrono_tz::Tz; + +const HELP: &str = "\ +USAGE: + next-wakeup --schedule '2,32 8-17 * * MON-FRI' --timezone 'Europe/Amsterdam' + next-wakeup -s='2,32 8-17 * * MON-FRI' -tz='Europe/Amsterdam' + +OPTIONS: + -tz, --timezone STRING Timezone used to interpret the cron schedule + -s, --schedule STRING Cron schedule to calculate next wakeup + -h, --help Prints help information +"; + +#[derive(Debug)] +struct Args { + timezone: Tz, + schedule: String, +} + +fn main() { + let args = match parse_args() { + Ok(v) => v, + Err(e) => { + eprintln!("Error: {}.", e); + std::process::exit(1); + } + }; + + let schedule = args.schedule; + + let now = Utc::now().with_timezone(&args.timezone); + let next = cron_parser::parse(&schedule, &now).expect("Invalid cron schedule"); + + let diff = next - now; + + println!("{}", diff.num_seconds()); +} + +fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + + if pargs.contains(["-h", "--help"]) { + print!("{}", HELP); + std::process::exit(1); + } + + let args = Args { + timezone: pargs.value_from_str(["-tz", "--timezone"])?, + schedule: pargs.value_from_str(["-s", "--schedule"])? + }; + + Ok(args) +} diff --git a/dash/src/sleeping.png b/dash/src/sleeping.png new file mode 100644 index 0000000..5da94ac Binary files /dev/null and b/dash/src/sleeping.png differ diff --git a/dash/src/start.sh b/dash/src/start.sh new file mode 100755 index 0000000..f4f38c1 --- /dev/null +++ b/dash/src/start.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +ENV_FILE="$DIR/local/env.sh" +LOG_FILE="$DIR/logs/dash.log" + +mkdir -p "$(dirname "$LOG_FILE")" + +# shellcheck disable=SC1090 +[ -f "$ENV_FILE" ] && . "$ENV_FILE" + +if [ "$DEBUG" = true ]; then + "$DIR/dash.sh" +else + "$DIR/dash.sh" >>"$LOG_FILE" 2>&1 & +fi diff --git a/dash/src/stop.sh b/dash/src/stop.sh new file mode 100755 index 0000000..ecc884e --- /dev/null +++ b/dash/src/stop.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +pkill -f dash.sh diff --git a/dash/src/wait-for-wifi.sh b/dash/src/wait-for-wifi.sh new file mode 100755 index 0000000..7785fc6 --- /dev/null +++ b/dash/src/wait-for-wifi.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +test_ip=$1 + +if [ -z "$test_ip" ]; then + echo "No test ip specified" + exit 1 +fi + +wait_for_wifi() { + max_retry=30 + counter=0 + + ping -c 1 "$test_ip" >/dev/null 2>&1 + + # shellcheck disable=SC2181 + while [ $? -ne 0 ]; do + [ $counter -eq $max_retry ] && echo "Couldn't connect to Wi-Fi" && exit 1 + counter=$((counter + 1)) + + sleep 1 + ping -c 1 "$test_ip" >/dev/null 2>&1 + done +} + +wait_for_wifi +echo "Wi-Fi connected" diff --git a/dash/staging/device/dashboard/dash.sh b/dash/staging/device/dashboard/dash.sh new file mode 100755 index 0000000..a1eaa09 --- /dev/null +++ b/dash/staging/device/dashboard/dash.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +DASH_PNG="$DIR/dash.png" +FETCH_DASHBOARD_CMD="$DIR/local/fetch-dashboard.sh" +LOW_BATTERY_CMD="$DIR/local/low-battery.sh" + +REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"2,32 8-17 * * MON-FRI"} +FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} +SLEEP_SCREEN_INTERVAL=${SLEEP_SCREEN_INTERVAL:-3600} +RTC=/sys/devices/platform/mxc_rtc.0/wakeup_enable + +LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +LOW_BATTERY_THRESHOLD_PERCENT=${LOW_BATTERY_THRESHOLD_PERCENT:-10} + +num_refresh=0 + +init() { + if [ -z "$TIMEZONE" ] || [ -z "$REFRESH_SCHEDULE" ]; then + echo "Missing required configuration." + echo "Timezone: ${TIMEZONE:-(not set)}." + echo "Schedule: ${REFRESH_SCHEDULE:-(not set)}." + exit 1 + fi + + echo "Starting dashboard with $REFRESH_SCHEDULE refresh..." + + /etc/init.d/framework stop + initctl stop webreader >/dev/null 2>&1 + echo powersave >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + lipc-set-prop com.lab126.powerd preventScreenSaver 1 +} + +prepare_sleep() { + echo "Preparing sleep" + + /usr/sbin/eips -f -g "$DIR/sleeping.png" + + # Give screen time to refresh + sleep 2 + + # Ensure a full screen refresh is triggered after wake from sleep + num_refresh=$FULL_DISPLAY_REFRESH_RATE +} + +refresh_dashboard() { + echo "Refreshing dashboard" + "$DIR/wait-for-wifi.sh" "$WIFI_TEST_IP" + + "$FETCH_DASHBOARD_CMD" "$DASH_PNG" + fetch_status=$? + + if [ "$fetch_status" -ne 0 ]; then + echo "Not updating screen, fetch-dashboard returned $fetch_status" + return 1 + fi + + if [ "$num_refresh" -eq "$FULL_DISPLAY_REFRESH_RATE" ]; then + num_refresh=0 + + # trigger a full refresh once in every 4 refreshes, to keep the screen clean + echo "Full screen refresh" + /usr/sbin/eips -f -g "$DASH_PNG" + else + echo "Partial screen refresh" + /usr/sbin/eips -g "$DASH_PNG" + fi + + num_refresh=$((num_refresh + 1)) +} + +log_battery_stats() { + battery_level=$(gasgauge-info -c) + echo "$(date) Battery level: $battery_level." + + if [ "$LOW_BATTERY_REPORTING" = true ]; then + battery_level_numeric=${battery_level%?} + if [ "$battery_level_numeric" -le "$LOW_BATTERY_THRESHOLD_PERCENT" ]; then + "$LOW_BATTERY_CMD" "$battery_level_numeric" + fi + fi +} + +rtc_sleep() { + duration=$1 + + if [ "$DEBUG" = true ]; then + sleep "$duration" + else + # shellcheck disable=SC2039 + [ "$(cat "$RTC")" -eq 0 ] && echo -n "$duration" >"$RTC" + echo "mem" >/sys/power/state + fi +} + +main_loop() { + while true; do + log_battery_stats + + next_wakeup_secs=$("$DIR/next-wakeup" --schedule="$REFRESH_SCHEDULE" --timezone="$TIMEZONE") + + if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ]; then + action="sleep" + prepare_sleep + else + action="suspend" + refresh_dashboard + fi + + # take a bit of time before going to sleep, so this process can be aborted + sleep 10 + + echo "Going to $action, next wakeup in ${next_wakeup_secs}s" + + rtc_sleep "$next_wakeup_secs" + done +} + +init +main_loop diff --git a/dash/staging/device/dashboard/local/env.sh b/dash/staging/device/dashboard/local/env.sh new file mode 100644 index 0000000..2e74d96 --- /dev/null +++ b/dash/staging/device/dashboard/local/env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Export environment variables here +export WIFI_TEST_IP=${WIFI_TEST_IP:-1.1.1.1} +# 测试配置:全天每分钟刷新一次,便于验证图片拉取与屏幕刷新是否正常。 +export REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"* * * * *"} +export TIMEZONE=${TIMEZONE:-"Asia/Shanghai"} + +# By default, partial screen updates are used to update the screen, +# to prevent the screen from flashing. After a few partial updates, +# the screen will start to look a bit distorted (due to e-ink ghosting). +# 测试阶段强制每次都做一次全刷,避免首页残影和局部刷新的旧内容干扰验证。 +# 等图片尺寸与刷新逻辑确认无误后,再改回 4 之类的值以节省功耗。 +export FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} + +# When the time until the next wakeup is greater or equal to this number, +# the dashboard will not be refreshed anymore, but instead show a +# 'kindle is sleeping' screen. This can be useful if your schedule only runs +# during the day, for example. +export SLEEP_SCREEN_INTERVAL=3600 + +export LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +export LOW_BATTERY_THRESHOLD_PERCENT=10 diff --git a/dash/staging/device/dashboard/local/fetch-dashboard.sh b/dash/staging/device/dashboard/local/fetch-dashboard.sh new file mode 100755 index 0000000..0733233 --- /dev/null +++ b/dash/staging/device/dashboard/local/fetch-dashboard.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +# Fetch a new dashboard image, make sure to output it to "$1". +# For example: +"$(dirname "$0")/../xh" -d -q -o "$1" get https://raw.githubusercontent.com/pascalw/kindle-dash/master/example/example.png diff --git a/dash/staging/device/dashboard/local/low-battery.sh b/dash/staging/device/dashboard/local/low-battery.sh new file mode 100644 index 0000000..dc70ced --- /dev/null +++ b/dash/staging/device/dashboard/local/low-battery.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +battery_level_percentage=$1 +last_battery_report_state="$(dirname "$0")/state/last_battery_report" + +previous_report_timestamp=$(cat "$last_battery_report_state" 2>/dev/null || echo '-1') +now=$(date +%s) + +# Implement desired logic here. The example below for example only reports low +# battery every 24 hours. + +if [ "$previous_report_timestamp" -eq -1 ] || + [ $((now - previous_report_timestamp)) -gt 86400 ]; then + # Replace this with for example an HTTP call via curl, or xh + echo "Reporting low battery: $battery_level_percentage%" + + echo "$now" >"$last_battery_report_state" +fi diff --git a/dash/staging/device/dashboard/next-wakeup b/dash/staging/device/dashboard/next-wakeup new file mode 100755 index 0000000..382d28d Binary files /dev/null and b/dash/staging/device/dashboard/next-wakeup differ diff --git a/dash/staging/device/dashboard/sleeping.png b/dash/staging/device/dashboard/sleeping.png new file mode 100644 index 0000000..5da94ac Binary files /dev/null and b/dash/staging/device/dashboard/sleeping.png differ diff --git a/dash/staging/device/dashboard/start.sh b/dash/staging/device/dashboard/start.sh new file mode 100755 index 0000000..f4f38c1 --- /dev/null +++ b/dash/staging/device/dashboard/start.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +ENV_FILE="$DIR/local/env.sh" +LOG_FILE="$DIR/logs/dash.log" + +mkdir -p "$(dirname "$LOG_FILE")" + +# shellcheck disable=SC1090 +[ -f "$ENV_FILE" ] && . "$ENV_FILE" + +if [ "$DEBUG" = true ]; then + "$DIR/dash.sh" +else + "$DIR/dash.sh" >>"$LOG_FILE" 2>&1 & +fi diff --git a/dash/staging/device/dashboard/stop.sh b/dash/staging/device/dashboard/stop.sh new file mode 100755 index 0000000..ecc884e --- /dev/null +++ b/dash/staging/device/dashboard/stop.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +pkill -f dash.sh diff --git a/dash/staging/device/dashboard/wait-for-wifi.sh b/dash/staging/device/dashboard/wait-for-wifi.sh new file mode 100755 index 0000000..7785fc6 --- /dev/null +++ b/dash/staging/device/dashboard/wait-for-wifi.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +test_ip=$1 + +if [ -z "$test_ip" ]; then + echo "No test ip specified" + exit 1 +fi + +wait_for_wifi() { + max_retry=30 + counter=0 + + ping -c 1 "$test_ip" >/dev/null 2>&1 + + # shellcheck disable=SC2181 + while [ $? -ne 0 ]; do + [ $counter -eq $max_retry ] && echo "Couldn't connect to Wi-Fi" && exit 1 + counter=$((counter + 1)) + + sleep 1 + ping -c 1 "$test_ip" >/dev/null 2>&1 + done +} + +wait_for_wifi +echo "Wi-Fi connected" diff --git a/dash/staging/device/dashboard/xh b/dash/staging/device/dashboard/xh new file mode 100755 index 0000000..b52ba80 Binary files /dev/null and b/dash/staging/device/dashboard/xh differ diff --git a/dash/staging/device/extensions/kindle-dash/config.xml b/dash/staging/device/extensions/kindle-dash/config.xml new file mode 100644 index 0000000..da580b2 --- /dev/null +++ b/dash/staging/device/extensions/kindle-dash/config.xml @@ -0,0 +1,10 @@ + + + + Kindle dashboard + pascalw-kindle-dash + + + menu.json + + \ No newline at end of file diff --git a/dash/staging/device/extensions/kindle-dash/menu.json b/dash/staging/device/extensions/kindle-dash/menu.json new file mode 100644 index 0000000..cb75b2d --- /dev/null +++ b/dash/staging/device/extensions/kindle-dash/menu.json @@ -0,0 +1,5 @@ +{ + "items": [ + {"name": "Kindle Dashboard", "action": "/mnt/us/dashboard/start.sh"} + ] +} \ No newline at end of file diff --git a/dash/staging/kindle-dash-release/dash.sh b/dash/staging/kindle-dash-release/dash.sh new file mode 100755 index 0000000..a1eaa09 --- /dev/null +++ b/dash/staging/kindle-dash-release/dash.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +DASH_PNG="$DIR/dash.png" +FETCH_DASHBOARD_CMD="$DIR/local/fetch-dashboard.sh" +LOW_BATTERY_CMD="$DIR/local/low-battery.sh" + +REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"2,32 8-17 * * MON-FRI"} +FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} +SLEEP_SCREEN_INTERVAL=${SLEEP_SCREEN_INTERVAL:-3600} +RTC=/sys/devices/platform/mxc_rtc.0/wakeup_enable + +LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +LOW_BATTERY_THRESHOLD_PERCENT=${LOW_BATTERY_THRESHOLD_PERCENT:-10} + +num_refresh=0 + +init() { + if [ -z "$TIMEZONE" ] || [ -z "$REFRESH_SCHEDULE" ]; then + echo "Missing required configuration." + echo "Timezone: ${TIMEZONE:-(not set)}." + echo "Schedule: ${REFRESH_SCHEDULE:-(not set)}." + exit 1 + fi + + echo "Starting dashboard with $REFRESH_SCHEDULE refresh..." + + /etc/init.d/framework stop + initctl stop webreader >/dev/null 2>&1 + echo powersave >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + lipc-set-prop com.lab126.powerd preventScreenSaver 1 +} + +prepare_sleep() { + echo "Preparing sleep" + + /usr/sbin/eips -f -g "$DIR/sleeping.png" + + # Give screen time to refresh + sleep 2 + + # Ensure a full screen refresh is triggered after wake from sleep + num_refresh=$FULL_DISPLAY_REFRESH_RATE +} + +refresh_dashboard() { + echo "Refreshing dashboard" + "$DIR/wait-for-wifi.sh" "$WIFI_TEST_IP" + + "$FETCH_DASHBOARD_CMD" "$DASH_PNG" + fetch_status=$? + + if [ "$fetch_status" -ne 0 ]; then + echo "Not updating screen, fetch-dashboard returned $fetch_status" + return 1 + fi + + if [ "$num_refresh" -eq "$FULL_DISPLAY_REFRESH_RATE" ]; then + num_refresh=0 + + # trigger a full refresh once in every 4 refreshes, to keep the screen clean + echo "Full screen refresh" + /usr/sbin/eips -f -g "$DASH_PNG" + else + echo "Partial screen refresh" + /usr/sbin/eips -g "$DASH_PNG" + fi + + num_refresh=$((num_refresh + 1)) +} + +log_battery_stats() { + battery_level=$(gasgauge-info -c) + echo "$(date) Battery level: $battery_level." + + if [ "$LOW_BATTERY_REPORTING" = true ]; then + battery_level_numeric=${battery_level%?} + if [ "$battery_level_numeric" -le "$LOW_BATTERY_THRESHOLD_PERCENT" ]; then + "$LOW_BATTERY_CMD" "$battery_level_numeric" + fi + fi +} + +rtc_sleep() { + duration=$1 + + if [ "$DEBUG" = true ]; then + sleep "$duration" + else + # shellcheck disable=SC2039 + [ "$(cat "$RTC")" -eq 0 ] && echo -n "$duration" >"$RTC" + echo "mem" >/sys/power/state + fi +} + +main_loop() { + while true; do + log_battery_stats + + next_wakeup_secs=$("$DIR/next-wakeup" --schedule="$REFRESH_SCHEDULE" --timezone="$TIMEZONE") + + if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ]; then + action="sleep" + prepare_sleep + else + action="suspend" + refresh_dashboard + fi + + # take a bit of time before going to sleep, so this process can be aborted + sleep 10 + + echo "Going to $action, next wakeup in ${next_wakeup_secs}s" + + rtc_sleep "$next_wakeup_secs" + done +} + +init +main_loop diff --git a/dash/staging/kindle-dash-release/local/env.sh b/dash/staging/kindle-dash-release/local/env.sh new file mode 100644 index 0000000..2e74d96 --- /dev/null +++ b/dash/staging/kindle-dash-release/local/env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Export environment variables here +export WIFI_TEST_IP=${WIFI_TEST_IP:-1.1.1.1} +# 测试配置:全天每分钟刷新一次,便于验证图片拉取与屏幕刷新是否正常。 +export REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"* * * * *"} +export TIMEZONE=${TIMEZONE:-"Asia/Shanghai"} + +# By default, partial screen updates are used to update the screen, +# to prevent the screen from flashing. After a few partial updates, +# the screen will start to look a bit distorted (due to e-ink ghosting). +# 测试阶段强制每次都做一次全刷,避免首页残影和局部刷新的旧内容干扰验证。 +# 等图片尺寸与刷新逻辑确认无误后,再改回 4 之类的值以节省功耗。 +export FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} + +# When the time until the next wakeup is greater or equal to this number, +# the dashboard will not be refreshed anymore, but instead show a +# 'kindle is sleeping' screen. This can be useful if your schedule only runs +# during the day, for example. +export SLEEP_SCREEN_INTERVAL=3600 + +export LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +export LOW_BATTERY_THRESHOLD_PERCENT=10 diff --git a/dash/staging/kindle-dash-release/local/fetch-dashboard.sh b/dash/staging/kindle-dash-release/local/fetch-dashboard.sh new file mode 100755 index 0000000..0733233 --- /dev/null +++ b/dash/staging/kindle-dash-release/local/fetch-dashboard.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +# Fetch a new dashboard image, make sure to output it to "$1". +# For example: +"$(dirname "$0")/../xh" -d -q -o "$1" get https://raw.githubusercontent.com/pascalw/kindle-dash/master/example/example.png diff --git a/dash/staging/kindle-dash-release/local/low-battery.sh b/dash/staging/kindle-dash-release/local/low-battery.sh new file mode 100644 index 0000000..dc70ced --- /dev/null +++ b/dash/staging/kindle-dash-release/local/low-battery.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +battery_level_percentage=$1 +last_battery_report_state="$(dirname "$0")/state/last_battery_report" + +previous_report_timestamp=$(cat "$last_battery_report_state" 2>/dev/null || echo '-1') +now=$(date +%s) + +# Implement desired logic here. The example below for example only reports low +# battery every 24 hours. + +if [ "$previous_report_timestamp" -eq -1 ] || + [ $((now - previous_report_timestamp)) -gt 86400 ]; then + # Replace this with for example an HTTP call via curl, or xh + echo "Reporting low battery: $battery_level_percentage%" + + echo "$now" >"$last_battery_report_state" +fi diff --git a/dash/staging/kindle-dash-release/next-wakeup b/dash/staging/kindle-dash-release/next-wakeup new file mode 100755 index 0000000..382d28d Binary files /dev/null and b/dash/staging/kindle-dash-release/next-wakeup differ diff --git a/dash/staging/kindle-dash-release/sleeping.png b/dash/staging/kindle-dash-release/sleeping.png new file mode 100644 index 0000000..5da94ac Binary files /dev/null and b/dash/staging/kindle-dash-release/sleeping.png differ diff --git a/dash/staging/kindle-dash-release/start.sh b/dash/staging/kindle-dash-release/start.sh new file mode 100755 index 0000000..f4f38c1 --- /dev/null +++ b/dash/staging/kindle-dash-release/start.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +ENV_FILE="$DIR/local/env.sh" +LOG_FILE="$DIR/logs/dash.log" + +mkdir -p "$(dirname "$LOG_FILE")" + +# shellcheck disable=SC1090 +[ -f "$ENV_FILE" ] && . "$ENV_FILE" + +if [ "$DEBUG" = true ]; then + "$DIR/dash.sh" +else + "$DIR/dash.sh" >>"$LOG_FILE" 2>&1 & +fi diff --git a/dash/staging/kindle-dash-release/stop.sh b/dash/staging/kindle-dash-release/stop.sh new file mode 100755 index 0000000..ecc884e --- /dev/null +++ b/dash/staging/kindle-dash-release/stop.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +pkill -f dash.sh diff --git a/dash/staging/kindle-dash-release/wait-for-wifi.sh b/dash/staging/kindle-dash-release/wait-for-wifi.sh new file mode 100755 index 0000000..7785fc6 --- /dev/null +++ b/dash/staging/kindle-dash-release/wait-for-wifi.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +test_ip=$1 + +if [ -z "$test_ip" ]; then + echo "No test ip specified" + exit 1 +fi + +wait_for_wifi() { + max_retry=30 + counter=0 + + ping -c 1 "$test_ip" >/dev/null 2>&1 + + # shellcheck disable=SC2181 + while [ $? -ne 0 ]; do + [ $counter -eq $max_retry ] && echo "Couldn't connect to Wi-Fi" && exit 1 + counter=$((counter + 1)) + + sleep 1 + ping -c 1 "$test_ip" >/dev/null 2>&1 + done +} + +wait_for_wifi +echo "Wi-Fi connected" diff --git a/dash/staging/kindle-dash-release/xh b/dash/staging/kindle-dash-release/xh new file mode 100755 index 0000000..b52ba80 Binary files /dev/null and b/dash/staging/kindle-dash-release/xh differ diff --git a/dash/staging/languagebreak/DEVICES.txt b/dash/staging/languagebreak/DEVICES.txt new file mode 100644 index 0000000..a0e74b5 --- /dev/null +++ b/dash/staging/languagebreak/DEVICES.txt @@ -0,0 +1 @@ +All the things! diff --git a/dash/staging/languagebreak/LanguageBreak/.demo/boot.flag b/dash/staging/languagebreak/LanguageBreak/.demo/boot.flag new file mode 100644 index 0000000..e69de29 diff --git a/dash/staging/languagebreak/LanguageBreak/DONT_CHECK_BATTERY b/dash/staging/languagebreak/LanguageBreak/DONT_CHECK_BATTERY new file mode 100644 index 0000000..e69de29 diff --git a/dash/staging/languagebreak/LanguageBreak/documents/dictionaries/a; export SLASH=$(awk 'BEGIN {print substr(ARGV[1], 0, 1)}' ${PWD}); sh ${SLASH}mnt${SLASH}us${SLASH}jb b/dash/staging/languagebreak/LanguageBreak/documents/dictionaries/a; export SLASH=$(awk 'BEGIN {print substr(ARGV[1], 0, 1)}' ${PWD}); sh ${SLASH}mnt${SLASH}us${SLASH}jb new file mode 100644 index 0000000..e69de29 diff --git a/dash/staging/languagebreak/LanguageBreak/documents/dictionaries/amisane b/dash/staging/languagebreak/LanguageBreak/documents/dictionaries/amisane new file mode 100644 index 0000000..e69de29 diff --git a/dash/staging/languagebreak/LanguageBreak/jb b/dash/staging/languagebreak/LanguageBreak/jb new file mode 100755 index 0000000..e93d336 --- /dev/null +++ b/dash/staging/languagebreak/LanguageBreak/jb @@ -0,0 +1,135 @@ +#!/bin/sh +# +# Quick'n dirty JB key install script for LanguageBarrier. +# Based on the "emergency" script from the Hotfix/Bridge restoration package. +# +# $Id: jb.sh 18327 2021-03-24 18:08:54Z NiLuJe $ +# +## + +# Helper functions, in case the bridge was still kicking. +touch /mnt/us/LanguageBreakRan +make_mutable() { + local my_path="${1}" + # NOTE: Can't do that on symlinks, hence the hoop-jumping... + if [ -d "${my_path}" ] ; then + find "${my_path}" -type d -exec chattr -i '{}' \; + find "${my_path}" -type f -exec chattr -i '{}' \; + elif [ -f "${my_path}" ] ; then + chattr -i "${my_path}" + fi +} + +# We actually do need that one +make_immutable() { + local my_path="${1}" + if [ -d "${my_path}" ] ; then + find "${my_path}" -type d -exec chattr +i '{}' \; + find "${my_path}" -type f -exec chattr +i '{}' \; + elif [ -f "${my_path}" ] ; then + chattr +i "${my_path}" + fi +} + +POS=1 +LANGBREAK_LOG="/mnt/us/languagebreak_log" +UKSSQSH="/etc/uks.sqsh" +jb_log() { + f_log "I" "languagebreak" "${2}" "" "${1}" + echo "${1}" >> "${LANGBREAK_LOG}" + eips 1 "${POS}" "${1}" + POS=$((POS+1)) + sleep 0.2 +} + +# For logging +[ -f "/etc/upstart/functions" ] && source "/etc/upstart/functions" +rm -f "${LANGBREAK_LOG}" +touch "${LANGBREAK_LOG}" +jb_log "LanguageBreak by Marek" "info" +jb_log "It was the chinese all along." "info" +POS=$((POS+1)) +jb_log "big thanks to bluebotlabs, GeorgeYellow and Niluje" "info" +jb_log "Loaded logging functions" "main" +jb_log "I am $(whoami) - $(id)" +# Duh' +mntroot rw + +# JB first +if [ -f $UKSSQSH ] ; then + jb_log "${UKSSQSH} - exists - replacing whole sqshfs" + make_mutable "${UKSSQSH}" + LOOP=$(mount | grep ' on /etc/uks ' | awk '{print $1}') + jb_log "Got uks loop device at $LOOP" + umount $LOOP + losetup -d $LOOP + cp /mnt/us/patchedUks.sqsh ${UKSSQSH} + mount -o loop=$LOOP,norelatime,nodiratime,noatime -t squashfs ${UKSSQSH} /etc/uks + RET=$? + if [ $RET -eq 0 ] ; then + jb_log "Added developer key :)" "jb" + else + jb_log "Unable to add developer key (${RET})" "jb" + fi + POS=$((POS+1)) + jb_log "$(ls /etc/uks)" + + chown root:root "${UKSSQSH}" + chmod 0644 "${UKSSQSH}" + make_immutable "${UKSSQSH}" + jb_log "Updated permissions for new squashfs keystore" "jb" +else + jb_log "${UKSSQSH} - doesn't exist - using legacy method" + + if [ -f "/etc/uks/pubdevkey01.pem" ] ; then + make_mutable "/etc/uks/pubdevkey01.pem" + rm -f "/etc/uks/pubdevkey01.pem" + wt_log "Removed existing developer key" "jb" + else + wt_log "Didn't find existing developer key" "jb" + fi + cat > "/etc/uks/pubdevkey01.pem" << EOF +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJn1jWU+xxVv/eRKfCPR9e47lP +WN2rH33z9QbfnqmCxBRLP6mMjGy6APyycQXg3nPi5fcb75alZo+Oh012HpMe9Lnp +eEgloIdm1E4LOsyrz4kttQtGRlzCErmBGt6+cAVEV86y2phOJ3mLk0Ek9UQXbIUf +rvyJnS2MKLG2cczjlQIDAQAB +-----END PUBLIC KEY----- +EOF + RET="$?" + + if [ -f "/etc/uks/pubdevkey01.pem" ] ; then + wt_log "Created developer key (${RET})" "jb" + else + wt_log "Unable to create developer key (${RET})" "jb" + fi + + chown root:root "/etc/uks/pubdevkey01.pem" + chmod 0644 "/etc/uks/pubdevkey01.pem" + make_immutable "/etc/uks/pubdevkey01.pem" + + wt_log "Updated permissions for developer key" "jb" +fi + + +# Make sure we can use UYK for OTA packages on FW >= 5.12.x +make_mutable "/PRE_GM_DEBUGGING_FEATURES_ENABLED__REMOVE_AT_GMC" +rm -rf "/PRE_GM_DEBUGGING_FEATURES_ENABLED__REMOVE_AT_GMC" +touch "/PRE_GM_DEBUGGING_FEATURES_ENABLED__REMOVE_AT_GMC" +make_immutable "/PRE_GM_DEBUGGING_FEATURES_ENABLED__REMOVE_AT_GMC" +jb_log "Enabled developer flag" "br" + +make_mutable "/MNTUS_EXEC" +rm -rf "/MNTUS_EXEC" +touch "/MNTUS_EXEC" +make_immutable "/MNTUS_EXEC" +jb_log "Enabled mntus exec flag" "br" + +# Bye +sync +mntroot ro + +# Finally, change language back to english +lipc-send-event com.lab126.blanket.langpicker changeLocale -s "en-US" + +jb_log "Finished installing jailbreak!" "main" diff --git a/dash/staging/languagebreak/LanguageBreak/patchedUks.sqsh b/dash/staging/languagebreak/LanguageBreak/patchedUks.sqsh new file mode 100644 index 0000000..ada9aad Binary files /dev/null and b/dash/staging/languagebreak/LanguageBreak/patchedUks.sqsh differ diff --git a/dash/staging/languagebreak/README.MD b/dash/staging/languagebreak/README.MD new file mode 100644 index 0000000..b491413 --- /dev/null +++ b/dash/staging/languagebreak/README.MD @@ -0,0 +1,123 @@ +# **LanguageBreak** +Jailbreak for any kindle running FW 5.16.2.1.1 or **LOWER** + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E1QLG4D) + +**The exploit works best around version 5.16.2, so if you are on lower firmware you should consider updating** + +Do not update past 5.16.2.1.1 even after jailbreak, there have been big changes since and **everything** is broken, only thing you can do on these versions is downgrade (if your jailbreak survived). + +Big thanks to Bluebotlabs for all the help along the way and GeorgeYellow and bulltricks for bringing the vulnerability to light + +The latest tarball can always be found [here]("https://github.com/notmarek/LanguageBreak/releases/latest") + +## +Make sure to remove any kind of password lock - if you forget to this and are stuck on the password screen enter 111222777 and the kindle will factory reset. + + +Your files **will** be deleted make sure to make a backup. + +# Installation + +## Before jailbreak + +1. Make sure to read the entirety of the instructions **before** proceeding. +2. Enable airplane mode +3. Make sure that there are no stray .bin files or update.bin.tmp.partial files on the kindle +4. Repeat number 3 troughout the proccess + +## Jailbreak + +1. Type ;enter_demo in the Kindle search bar +2. Reboot the device +3. Once in demo mode, skip setting up wifi and enter random values for store registration +4. Skip searching for a demo payload +5. Select the "standard" demo type +6. Press "Done" at the prompt to sideload content. +7. Once the demo is setup, do the "secret gesture" (double finger tap on bottom right of screen then swipe left) +8. Enter the demo configuration menu by typing ;demo into the search bar +9. Select the "Sideload Content" option +10. Copy the contents of the LanguageBreak folder to the Kindle - merging and replacing all files +11. Unplug your kindle and go back to the demo menu (viz. step 8) +12. Select the "Resell Device" option press Yes/Resell +13. Now wait for the press power button to start +14. The second it appears plug your kindle back into your computer and copy the contents of the LanguageBreak folder into it once again, overwrite files then safely eject +15. Hold the power button as instructed on screen +16. A language selection menu should appear in a few seconds +17. Choose Chinese (The one above the odd Pseudot language, and/or below Japanese) +18. Your kindle should reboot and you should see some log message on the screen + +## After jailbreak + +1. After the device has rebooted, type ;uzb into the search bar +2. Connect the device to a PC and copy `Update_hotfix_languagebreak-{language you want to end up with}.bin` to the root of the Kindle storage +3. Eject the device and either enter ;dsts or swipe down and select the settings icon to enter the device settings menu +4. Select `Update Your Kindle` to install the hotfix +5. This will take your device out of demo mode and clean up unneeded jailbreak files. +6. You will now probably be in `managed mode` + +## Exiting managed/demo mode after jailbreak + +### Unregistered kindle +1. Enter `;demo` into the search bar +2. Press the right button +3. The device will say that its "entering demo", but will actually reset into normal mode in English +4. After this check if you have an mkk folder on your kindle - if it's missing reinstall then hotfix and have fun :) + +### Registered kindle +1. Enter `;enter_demo` into the search bar +2. Reboot your device +3. The device will be in full demo mode so do the setup without wifi and with random values +4. Do the secret gesture to get into the kindle UI +5. Enter `;demo` into the search bar +6. Choose `Resell device` and press `Resell/Yes` +7. The device will actually reset into normal mode in English +4. After this check if you have an mkk folder on your kindle - if it's missing reinstall then hotfix and have fun :) + + +# FAQ +``` +Q: How do i check that it worked? +A (before installing hotfix): Install hotfix, if you can do that then it worked. +A (after installing hotfix): Type `;log` into the search bar, this should show some text at the top of the screen. + +Q: Where are the hotfix files? +A: The structure of the tarball is as follows +LanguageBreak.tar.gz +|-- LanguageBreak +| |-- documents +| | |-- dictionaries +| | | |-- a; export SLASH=$(awk 'BEGIN {print substr(ARGV[1], 0, 1)}' ${PWD}); sh ${SLASH}mnt${SLASH}us${SLASH}jb +| | | |-- amisane +| |-- DONT_CHECK_BATTERY +| |-- jb +| |-- patchedUks +| |-- .demo +| | |-- boot.flag +|-- Update_hotfix_languagebreak-*.bin +``` + +# Troubleshooting +Can't seem to get it to work? + +The exploit works best around version 5.16.2, so if you are on lower firmware you should consider updating + +Download the update file of the kindle version you are currently on from amazon install it and try again. + +``` +PW5: https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_paperwhite_11th_5.XX.X.bin +PW4: https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_paperwhite_v2_5.XX.X.bin +PW3: https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_paperwhite_5.XX.X.bin +Kindle 11th Gen: https://s3.amazonaws.com/firmwaredownloads/update_kindle_11th_5.XX.X.bin +Kindle 10th Gen: https://s3.amazonaws.com/firmwaredownloads/update_kindle_10th_5.XX.X.bin +Kindle 8th Gen: https://s3.amazonaws.com/firmwaredownloads/update_kindle_8th_5.XX.X.bin +Scribe: https://s3.amazonaws.com/firmwaredownloads/update_kindle_scribe_5.XX.X.bin +Oasis 10th Gen: https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_oasis_v2_5.XX.X.bin +Oasis 9th Gen: https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_oasis_5.XX.X.bin +Oasis 8th Gen: https://s3.amazonaws.com/firmwaredownloads/update_kindle_oasis_5.XX.X.bin +``` + +So version 5.16.2.1.1 for PW4 would be [https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_paperwhite_v2_5.16.2.1.1.bin](]https://s3.amazonaws.com/firmwaredownloads/update_kindle_all_new_paperwhite_v2_5.16.2.1.1.bin) + + +[Consider buying me a coffee :)]("https://ko-fi.com/notmarek") diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-de-DE.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-de-DE.bin new file mode 100644 index 0000000..a60be43 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-de-DE.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-en-GB.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-en-GB.bin new file mode 100644 index 0000000..e519b55 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-en-GB.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-en-US.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-en-US.bin new file mode 100644 index 0000000..ab8ceb9 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-en-US.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-es-AR.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-AR.bin new file mode 100644 index 0000000..3ed7635 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-AR.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-es-CL.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-CL.bin new file mode 100644 index 0000000..27f2ba4 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-CL.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-es-CO.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-CO.bin new file mode 100644 index 0000000..3d15d5d Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-CO.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-es-ES.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-ES.bin new file mode 100644 index 0000000..cb15fa7 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-ES.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-es-MX.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-MX.bin new file mode 100644 index 0000000..58616df Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-es-MX.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-fr-CA.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-fr-CA.bin new file mode 100644 index 0000000..788dc9d Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-fr-CA.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-fr-FR.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-fr-FR.bin new file mode 100644 index 0000000..6213f6b Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-fr-FR.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-it-IT.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-it-IT.bin new file mode 100644 index 0000000..96eca79 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-it-IT.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-ja-JP.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-ja-JP.bin new file mode 100644 index 0000000..549ee27 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-ja-JP.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-nl-NL.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-nl-NL.bin new file mode 100644 index 0000000..a7906fa Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-nl-NL.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-pt-BR.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-pt-BR.bin new file mode 100644 index 0000000..6e36fa3 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-pt-BR.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-ru-RU.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-ru-RU.bin new file mode 100644 index 0000000..4a6b96a Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-ru-RU.bin differ diff --git a/dash/staging/languagebreak/Update_hotfix_languagebreak-zh-Hans-CN.bin b/dash/staging/languagebreak/Update_hotfix_languagebreak-zh-Hans-CN.bin new file mode 100644 index 0000000..3e9d109 Binary files /dev/null and b/dash/staging/languagebreak/Update_hotfix_languagebreak-zh-Hans-CN.bin differ diff --git a/dash/staging/mrpi/ChangeLog.txt b/dash/staging/mrpi/ChangeLog.txt new file mode 100644 index 0000000..108930d --- /dev/null +++ b/dash/staging/mrpi/ChangeLog.txt @@ -0,0 +1,3259 @@ +2023-11-06 17:06 NiLuJe + + * [r19303] extensions/MRInstaller/bin/mrinstaller.sh: + + Jot down some notes to pinpoint the awk failures... + +2023-11-04 18:41 NiLuJe + + * [r19302] extensions/MRInstaller/bin/mrinstaller.sh: + + That was tripping on linkss, so change tacks + +2023-11-04 17:16 NiLuJe + + * [r19301] extensions/MRInstaller/bin/mrinstaller.sh: + + Extend the second ro remount delay, just in case that helps... + + On the second failure, print another line recommending a reboot. + + This behavior's been experienced for 5 years or so, assume it's there + to stay ;). + +2023-11-04 17:06 NiLuJe + + * [r19300] extensions/MRInstaller/bin/mrinstaller.sh: + + Yeaaah, the awk thing is weird. + + Some simple 'print field' seem to behave, but anything even remotely + more complex wilkl horribly segfault... + +2023-11-04 01:55 NiLuJe + + * [r19298] extensions/MRInstaller/bin/mrinstaller.sh: + + Dumb down the free space checks, because something appears to be + szeriously wrong with awk on >= 5.16.x + + Something that I can't reproduce with affected busybox binaries + running on a previous FW version, so, no clue. + +2023-11-04 01:26 NiLuJe + + * [r19297] extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Update FBInk binaries to hotfix broken fbink -e output + +2023-11-04 01:06 NiLuJe + + * [r19295] extensions/MRInstaller/bin/mrinstaller.sh: + + And some more stuff TODO... + +2023-11-02 22:36 NiLuJe + + * [r19294] extensions/MRInstaller/bin/mrinstaller.sh: + + Oh, joy, new stuff to deal with... + +2023-11-01 17:17 NiLuJe + + * [r19293] extensions/MRInstaller/bin/mrinstaller.sh: + + *facepalm* + +2023-10-31 20:33 NiLuJe + + * [r19292] extensions/MRInstaller/bin/mrinstaller.sh: + + Nope, even with a much closer test case, can't repro :s + +2023-10-31 19:20 NiLuJe + + * [r19291] extensions/MRInstaller/bin/mrinstaller.sh: + + I did try the relevant busybox build, and it #worksforme + + So I'm going to assume the original scoping issue was actually the + real change... + +2023-10-31 05:35 NiLuJe + + * [r19289] extensions/MRInstaller/bin/mrinstaller.sh: + + Press X for doubt... + +2023-10-31 05:31 NiLuJe + + * [r19288] extensions/MRInstaller/bin/mrinstaller.sh: + + Compute OTA build number w/ sed & cut only, in case awk really is + borked on some devices... + + (I, err, somehow doubt that, or KUAL would be hella broken) + +2023-10-30 20:05 NiLuJe + + * [r19274] extensions/MRInstaller/bin/mrinstaller.sh: + + Oh, joy. I just remembered about the whole "I forgot Kindle Basics + were a thing" episode -_-". + +2023-10-30 19:48 NiLuJe + + * [r19273] extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Patch in an up-to-date FBInk binary in MRPI binaries + +2023-10-27 18:06 NiLuJe + + * [r19265] extensions/MRInstaller/bin/mrinstaller.sh: + + See if that helps MRPI picking up build numbers on some FW versions? + +2022-10-02 01:51 NiLuJe + + * [r18983] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: Jot that down, because I'd forgotten which one was the supserset + of the other... + +2022-10-02 00:59 NiLuJe + + * [r18982] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: Update the silly filename dance + +2022-10-02 00:40 NiLuJe + + * [r18978] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: Silence some shellchecks warnings + +2022-10-01 21:08 NiLuJe + + * [r18976] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: Possibly fix restarting the UI on >= 5.15.x + +2022-10-01 21:03 NiLuJe + + * [r18975] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: More linting + +2022-10-01 20:57 NiLuJe + + * [r18974] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: Random drive-by linting + +2022-03-08 19:02 NiLuJe + + * [r18896] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Shorter platform names, as they currently overflow ;). + +2022-03-08 18:30 NiLuJe + + * [r18895] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Hotswap FBInk binaries for PW2+ builds for PW5 support + +2022-02-13 19:16 NiLuJe + + * [r18870] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Tone down the silly package filenames with the ever-growing list of + devices + +2022-02-08 00:23 NiLuJe + + * [r18867] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI & GAwk: + * Detect Bellatrix properly + +2021-11-14 23:28 NiLuJe + + * [r18833] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (httpie hotfix) + +2021-11-14 15:58 NiLuJe + + * [r18829] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-10-27 18:30 NiLuJe + + * [r18797] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * X-TC: + * Refresh binaries (requests hotfix) + +2021-10-26 21:36 NiLuJe + + * [r18795] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-09-02 18:32 NiLuJe + + * [r18721] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-09-02 10:27 NiLuJe + + * [r18717] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-08-09 18:32 NiLuJe + + * [r18689] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-06-18 18:19 NiLuJe + + * [r18575] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-05-17 04:25 NiLuJe + + * [r18509] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (TC update: the great GCC 11 update) + +2021-04-14 18:19 NiLuJe + + * [r18435] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * X-TC: + * Refresh binaries + +2021-04-05 15:14 NiLuJe + + * [r18392] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * X-TC: + * Refresh binaries (numpy hotfix) + +2021-04-04 14:09 NiLuJe + + * [r18386] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-03-25 17:16 NiLuJe + + * [r18331] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Update version check to mimic FW 5.12.2.1.1 behavior: same-to-same + is now forbidden :/. + (Might be a bug, as that doesn't apply to the 5.13.x branch, and the + VoiceView packages are *not* following this rule). + +2021-03-15 00:46 NiLuJe + + * [r18286] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-03-10 19:50 NiLuJe + + * [r18245] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-02-27 06:29 NiLuJe + + * [r18219] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (Python 2.7 hotfix) + +2021-02-21 03:12 NiLuJe + + * [r18190] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2021-01-29 03:28 NiLuJe + + * [r18137] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (busybox & requests hotfix) + +2021-01-28 18:52 NiLuJe + + * [r18128] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-12-16 01:58 NiLuJe + + * [r18009] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-11-14 13:10 NiLuJe + + * [r17940] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-11-06 21:03 NiLuJe + + * [r17915] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-10-21 12:30 NiLuJe + + * [r17883] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-10-11 13:05 NiLuJe + + * [r17853] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (requests hotfix) + +2020-10-10 23:46 NiLuJe + + * [r17851] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-10-10 15:47 NiLuJe + + * [r17847] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * X-TC: + * Refresh binaries + +2020-10-01 20:54 NiLuJe + + * [r17821] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-09-27 20:58 NiLuJe + + * [r17805] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-09-07 10:57 NiLuJe + + * [r17754] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-08-20 17:50 NiLuJe + + * [r17718] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-08-03 03:32 NiLuJe + + * [r17684] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (more polished zlib-ng fixes, unbreak HTTPie) + +2020-08-02 15:18 NiLuJe + + * [r17679] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (hotfix the hotfix of the hotfix of the hotfix of + the zlib-ng hotfix ;p) + +2020-08-01 20:09 NiLuJe + + * [r17672] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * X-TC: + * Refresh binaries (zlib hotfix) + +2020-08-01 05:07 NiLuJe + + * [r17665] extensions/MRInstaller/bin/mrinstaller.sh: + + Revert r17658 + +2020-08-01 00:30 NiLuJe + + * [r17659] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Duh, stupid typo. + +2020-08-01 00:24 NiLuJe + + * [r17658] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Also handle the libz rename... + +2020-07-31 23:13 NiLuJe + + * [r17648] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-06-13 20:55 NiLuJe + + * [r17508] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-06-05 00:39 NiLuJe + + * [r17466] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-06-02 01:48 NiLuJe + + * [r17441] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-05-18 20:05 NiLuJe + + * [r17349] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-05-13 02:19 NiLuJe + + * [r17279] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-05-01 21:37 NiLuJe + + * [r17174] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-04-06 04:25 NiLuJe + + * [r17028] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-03-10 15:28 NiLuJe + + * [r16952] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-03-06 00:06 NiLuJe + + * [r16914] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Log KindleTool's output in case parsing fails... + +2020-02-11 03:08 NiLuJe + + * [r16858] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (fix gdb) + +2020-02-10 16:58 NiLuJe + + * [r16851] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2020-02-04 18:01 NiLuJe + + * [r16835] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-11-25 22:32 NiLuJe + + * [r16737] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update binaries + +2019-11-23 23:12 NiLuJe + + * [r16715] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-11-14 23:29 NiLuJe + + * [r16672] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries: + * Fix some readline & Python 2 EoL shenanigans. + +2019-11-14 15:43 NiLuJe + + * [r16658] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-10-11 21:21 NiLuJe + + * [r16572] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-10-06 19:37 NiLuJe + + * [r16535] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-10-01 02:33 NiLuJe + + * [r16517] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-09-15 22:01 NiLuJe + + * [r16474] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-09-02 11:12 NiLuJe + + * [r16438] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-08-14 18:50 NiLuJe + + * [r16309] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (complete bs4 support) + +2019-08-14 11:31 NiLuJe + + * [r16306] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-07-30 22:02 NiLuJe + + * [r16286] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-07-27 17:36 NiLuJe + + * [r16264] extensions/MRInstaller/CREDITS: + + Kindle Hacks: + * Prepare for FBInk's relicensing + +2019-07-23 22:06 NiLuJe + + * [r16257] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Handle some more potential filenames... + +2019-07-21 14:33 NiLuJe + + * [r16247] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + • Oops, we need the hex encoded device ID for the new scheme now, not + the string one... + +2019-07-20 17:23 NiLuJe + + * [r16245] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Oops, fix device id on legacy Kindles + +2019-07-20 17:20 NiLuJe + + * [r16244] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Missed an icon refresh on legacy Kindles + +2019-07-20 16:35 NiLuJe + + * [r16241] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Rotate the logs after 1MB to avoid keeping a gigantic main log file. + +2019-07-20 15:59 NiLuJe + + * [r16240] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Use a dedicated icon for RP/CRP + +2019-07-20 15:56 NiLuJe + + * [r16237] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-07-19 17:14 NiLuJe + + * [r16216] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Compute icon size in pixels. Makes sure it'll never encroach on + text, and will look less tiny on weirder AR (i.e., DX). + +2019-07-19 15:09 NiLuJe + + * [r16214] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * SImplify that in preparation of upcoming changes + +2019-07-19 04:30 NiLuJe + + * [r16212] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Don't flash icons, turns out it's annoying ;D. + +2019-07-19 04:27 NiLuJe + + * [r16211] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Slightly less terrible MRPI icon + +2019-07-19 04:24 NiLuJe + + * [r16210] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Only show the OK checkmark once, at the end, for multi-script + packages + +2019-07-19 04:15 NiLuJe + + * [r16209] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Display a scary bomb when rootfs fails to remount ro ;p + +2019-07-19 04:06 NiLuJe + + * [r16208] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Show the failure/success icon earlier + +2019-07-19 04:01 NiLuJe + + * [r16207] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Show an early splash screen icon, too. + I'm sure that'll annoy me enough to try to find a better icon :D. + +2019-07-19 02:30 NiLuJe + + * [r16206] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Ups, really unbreak device checks... + +2019-07-19 02:11 NiLuJe + + * [r16205] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Ups, need to setup FBInk first thing, otherwise, no prints ;p + +2019-07-19 02:07 NiLuJe + + * [r16204] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Ooops, unbreak everything by actually storing our device ID as + expected... + +2019-07-19 01:49 NiLuJe + + * [r16203] extensions/MRInstaller/data/icons.py[ADD]: + + MRPI: + * Import the Python script I used to test the glyphs. + Because I initally started testing this on a Kobo, where locales are + borked as hell, so going through Python's unicode handling seemed a + safer bet... + +2019-07-19 01:47 NiLuJe + + * [r16202] extensions/MRInstaller/CREDITS, + extensions/MRInstaller/bin/mrinstaller.sh, + extensions/MRInstaller/data/BigBlue_Terminal.ttf[ADD]: + + MRPI: + * Import the NerdFont patched BigBlue Terminal font, and update + CREDITS + +2019-07-19 01:37 NiLuJe + + * [r16201] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Flash icons, to see how that works timing-wise in case of quick + image succession... + +2019-07-19 01:36 NiLuJe + + * [r16200] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Start displaying relevant icons... + +2019-07-19 01:11 NiLuJe + + * [r16199] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Tweak a few things pointed out by shellcheck. + Some of them are even actual bugs! + +2019-07-19 01:05 NiLuJe + + * [r16198] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Start working on icon display... + +2019-07-19 00:48 NiLuJe + + * [r16197] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Okay, got rid of eips :) + +2019-07-19 00:45 NiLuJe + + * [r16196] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Update variable names + +2019-07-19 00:44 NiLuJe + + * [r16195] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL: + * Update function name + +2019-07-19 00:43 NiLuJe + + * [r16194] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Start getting rid of eips handling in MRPI, as w'll eventually ship + with a custom FBInk build. + +2019-07-17 13:58 NiLuJe + + * [r16193] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * More accurate log when the version check fails. + +2019-07-11 04:37 NiLuJe + + * [r16144] extensions/MRInstaller/data/mrpi-K3.tar.gz: + + Kindle Hacks: + * Refresh K3 binaries (Fix Python 3) + +2019-07-10 17:21 NiLuJe + + * [r16139] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-07-07 03:50 NiLuJe + + * [r16071] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-06-18 04:56 NiLuJe + + * [r16043] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Use the exact range of characters used in the weird base32 variant + of the new S/N prefixes + This fixes a nasty bug that prevented some KT4 IDs to be picked up... + +2019-06-06 00:29 NiLuJe + + * [r16027] extensions/MRInstaller/data/mrpi-K3.tar.gz: + + Kindle Hacks: + * Refresh K3 binaries to fix IM & gdb + +2019-06-04 01:56 NiLuJe + + * [r15989] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Update the filenames to reflect the fact that stuff is packaged for + the KT4 now + +2019-06-03 12:40 NiLuJe + + * [r15983] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-06-01 13:18 NiLuJe + + * [r15968] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Plug in a few new PW4 IDs... + +2019-04-30 14:17 NiLuJe + + * [r15920] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-04-20 02:53 NiLuJe + + * [r15898] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-04-12 03:14 NiLuJe + + * [r15868] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Detect the KT4 + +2019-04-06 05:00 NiLuJe + + * [r15860] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-03-10 18:16 NiLuJe + + * [r15828] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-02-21 16:49 NiLuJe + + * [r15788] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2019-02-01 23:32 NiLuJe + + * [r15759] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (TC update) + +2019-02-01 14:16 NiLuJe + + * [r15753] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Fix i.MX 6/7 detection regex... + Some variants have a space between the MX and the number, others + don't... + +2019-01-29 23:29 NiLuJe + + * [r15746] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Fix return code logging on package parsing failure. (And add a + comment for the most common cause). + +2019-01-03 16:20 NiLuJe + + * [r15706] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (fix wand) + +2019-01-03 04:29 NiLuJe + + * [r15700] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-12-11 22:44 NiLuJe + + * [r15595] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Raise the tmpfs floor to 56MB + According to my tests, most of this is extremely overkill: even on the + K2, resize_mrpi_tmpfs is basically a nop ;). + +2018-12-11 22:20 NiLuJe + + * [r15594] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Raise the tmpfs floor (32MB -> 48MB), as USBNet requires ~40MB + +2018-12-11 22:07 NiLuJe + + * [r15593] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Listen to shellcheck a tiny bit + +2018-12-11 22:04 NiLuJe + + * [r15592] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Add some more sanity checks around the tmpfs size... + * Make sure awk will always print it as an integer + * Compute a fraction of available memory we can spare to resize the + tmpfs at runtime, once the framework is down. + This should ensure we'll never go OOM, which would be bad, because we + don't have swap. + Abort if we can't spare more than 32MB, which should be enough to + avoid ENOSPC in most cases ;). + +2018-12-11 01:05 NiLuJe + + * [r15589] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Check for FBInk install between each package, to avoid mixing eips & + fbink during a chained package install following the initial FBInk + install... + +2018-12-11 00:51 NiLuJe + + * [r15588] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Restore the logging lost in the FBInk move in r15360 + +2018-12-10 23:06 NiLuJe + + * [r15587] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Double-check tmpfs mount/umount, like we do for the rootfs... + +2018-12-10 22:58 NiLuJe + + * [r15586] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Move staging directory to a tmpfs, to speed things up + +2018-12-09 22:43 NiLuJe + + * [r15585] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-12-09 04:37 NiLuJe + + * [r15581] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Add potentially bogus new PW4 IDs... + +2018-11-22 05:26 NiLuJe + + * [r15562] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-11-13 02:50 NiLuJe + + * [r15539] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Err, only be persistent on *failure*... -_-" + +2018-11-13 02:47 NiLuJe + + * [r15538] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Be a little more persistent on rootfs remount failures... + +2018-11-09 16:42 NiLuJe + + * [r15529] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-11-08 23:12 NiLuJe + + * [r15516] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh KindleTool binaries + +2018-11-08 23:02 NiLuJe + + * [r15515] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Tweak comments: Rex uses an i.MX6SL + +2018-11-08 21:24 NiLuJe + + * [r15506] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Detect the PW4 + +2018-10-17 22:07 NiLuJe + + * [r15483] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Log return code on kindletool-run failure... + +2018-09-28 02:00 NiLuJe + + * [r15470] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (better requests fix, and actually ship OpenSSH.. + ;D) + +2018-09-27 19:23 NiLuJe + + * [r15463] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (Unbreak requests) + +2018-09-10 20:17 NiLuJe + + * [r15434] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (unbreak Python's SQLite module) + +2018-09-10 02:10 NiLuJe + + * [r15423] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries... (the great gold to bfd switch...) + +2018-09-01 18:02 NiLuJe + + * [r15378] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Oops, unbreak progress handling :D. + +2018-09-01 17:49 NiLuJe + + * [r15376] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * *sigh*, stupid c/py typo is stupid. + +2018-09-01 17:39 NiLuJe + + * [r15374] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Rework FBInk detection + Do it an source-time instead of on each print call. + * Switch to real progress bars for libotautils + FBInk :) + +2018-08-31 20:40 NiLuJe + + * [r15368] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Blank the screen before starting to process packages + +2018-08-31 20:38 NiLuJe + + * [r15367] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Don't break the "empty string == blank line" idiom when using FBInk + +2018-08-31 18:49 NiLuJe + + * [r15365] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * We don't care about line count in libotautils (and a non-0 return + code is problematic in every case there anyway). + +2018-08-31 18:14 NiLuJe + + * [r15362] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Prefer hack-specific fbink binaries over libkh's one. + They're likely to be more up to date than the bridge, and they'll be + platform-specific, while the bridge's is more generic. + +2018-08-31 17:59 NiLuJe + + * [r15360] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Merge otautils updates in MRPI + +2018-08-22 14:30 NiLuJe + + * [r15338] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-08-06 03:19 NiLuJe + + * [r15299] extensions/MRInstaller/bin/mrinstaller.sh: + + KIndle Hacks: + * MRPI: Add a comment about the weird I/O redirection issue fixed in + the previous commit... + +2018-08-06 02:45 NiLuJe + + * [r15297] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Prevent a weird interaction between I/O redirections and KindleTool + from truncating the log file... + +2018-07-25 00:05 NiLuJe + + * [r15265] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-07-24 06:29 NiLuJe + + * [r15253] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (unbreak dropbear) + +2018-07-24 00:33 NiLuJe + + * [r15249] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (dropbear CFLAGS fix) + +2018-07-23 16:11 NiLuJe + + * [r15247] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-07-20 14:41 NiLuJe + + * [r15235] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-07-02 18:41 NiLuJe + + * [r15128] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-06-21 23:03 NiLuJe + + * [r15108] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-06-21 16:34 NiLuJe + + * [r15100] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh KindleTool & FBInk binaries (proper version tags) + +2018-06-21 15:00 NiLuJe + + * [r15092] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (Updated TC) + +2018-06-03 17:07 NiLuJe + + * [r15037] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Confirmed the eips block size on the KOA2 :) + It hasn't changed, which means it'll look tiny as fuck on that + screen... + +2018-06-02 23:17 NiLuJe + + * [r15025] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * And follow my usual formatting for this while I'm there ;) + +2018-06-02 23:16 NiLuJe + + * [r15024] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Be more verbose when we're passed a bogus argument + +2018-06-02 16:58 NiLuJe + + * [r15011] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Use grep -q instead of IO reidrections where applicable + +2018-05-31 15:17 NiLuJe + + * [r14996] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Oh, busybox is the pits. (Unbreak). + +2018-05-31 15:06 NiLuJe + + * [r14995] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Bump free space check to 150MB (Python + bytecode now takes up to + roughly ~110MB, plus the ~20MB package). + +2018-05-31 15:00 NiLuJe + + * [r14994] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * MRPI: + * Log KindleTool's output when extracting, to have an idea of why it + might have failed. + +2018-05-30 22:35 NiLuJe + + * [r14989] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2018-05-16 01:11 NiLuJe + + * [r14941] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh PW2 binaries + +2018-05-16 00:40 NiLuJe + + * [r14940] extensions/MRInstaller/data/mrpi-K3.tar.gz: + + Kindle Hacks: + * Refresh K3 binaries + +2018-05-16 00:14 NiLuJe + + * [r14939] extensions/MRInstaller/data/mrpi-K5.tar.gz: + + Kindle Hacks: + * Refresh K4 binaries + +2018-05-15 20:56 NiLuJe + + * [r14935] extensions/MRInstaller/data/mrpi-K5.tar.gz: + + Kindle Hacks: + * Okay, static-libgcc experiment, let's see if that shakes things + up... + +2018-05-14 20:45 NiLuJe + + * [r14915] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh PW2 binaries + +2018-05-14 18:30 NiLuJe + + * [r14910] extensions/MRInstaller/data/mrpi-K5.tar.gz: + + Kindle Hacks: + * Refresh K5 binaries + +2018-05-14 15:27 NiLuJe + + * [r14902] extensions/MRInstaller/data/mrpi-K3.tar.gz: + + Kindle Hacks: + * Refresh K3 binaries + +2017-11-28 00:54 NiLuJe + + * [r14398] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Confirm some eips stuff thanks to knc1 + +2017-11-26 06:25 NiLuJe + + * [r14394] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Flag Zelda only packages as such + +2017-11-25 23:28 NiLuJe + + * [r14393] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Slightly more accurate KOA2 screen size... + +2017-11-25 04:49 NiLuJe + + * [r14389] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Fix MRPI binaries + +2017-11-25 04:41 NiLuJe + + * [r14387] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update FBGrab & KindleTool binaries + +2017-11-24 22:21 NiLuJe + + * [r14380] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Another pass of KOA2 stuff... + +2017-11-24 22:12 NiLuJe + + * [r14379] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * First pass of KOA2 detection + +2017-11-24 20:53 NiLuJe + + * [r14375] extensions/MRInstaller/bin/mrinstaller.sh: + + Hopefully properly detect the imx7d CPU on Zelda devices... + +2016-07-30 15:34 NiLuJe + + * [r13408] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-07-12 13:18 NiLuJe + + * [r13359] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * Log the current MRPI version + +2016-07-11 18:12 NiLuJe + + * [r13353] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update KindleTool + +2016-07-11 15:33 NiLuJe + + * [r13343] extensions/MRInstaller/bin/mrinstaller.sh: + + MRPI: + * (Hopefully) handle being called from anywhere, not only KUAL + +2016-07-11 12:24 NiLuJe + + * [r13342] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Fix some device codes... + +2016-07-08 21:13 NiLuJe + + * [r13339] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Some more stuff missing for KOA/KT3 support... + +2016-07-08 19:54 NiLuJe + + * [r13338] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update KindleTool builds + +2016-07-08 18:09 NiLuJe + + * [r13337] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * *Potentially* slightly more accurate device code for the third + KT3... + +2016-07-08 16:35 NiLuJe + + * [r13336] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-07-08 03:34 NiLuJe + + * [r13334] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * Detect the White PW3, the Oasis and the KT3 + +2016-06-21 02:49 NiLuJe + + * [r13309] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update binaries (TC update) + +2016-06-20 13:40 NiLuJe + + * [r13306] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-05-18 16:37 NiLuJe + + * [r13265] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (update TC) + +2016-04-24 13:53 NiLuJe + + * [r13216] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-04-18 14:01 NiLuJe + + * [r13189] extensions/MRInstaller/bin/mrinstaller.sh: + + KIndle Hacks: + * MRPI: + * Unbreak OTAv1 handling... + A stray c/p duplicate slipped in way back in r12041... :( + +2016-04-17 23:32 NiLuJe + + * [r13188] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (Linaro 5.3 2016.04) + +2016-04-17 13:34 NiLuJe + + * [r13182] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (2016.04 test batch) + +2016-04-11 15:32 NiLuJe + + * [r13157] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * And refresh PW2 binaries (w/ the famous GCC 5.3 + Glibc 2.19 TC... + But Glibc 2.12 friendly). + +2016-04-11 15:04 NiLuJe + + * [r13156] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-04-11 01:21 NiLuJe + + * [r13152] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Initial set of nearly compatible PW2 binaries built w/ GCC 5.3 and a + TC compiled against glibc 2.19... + +2016-04-04 21:29 NiLuJe + + * [r13104] extensions/MRInstaller/data/mrpi-K3.tar.gz: + + Kindle Hacks: + * Fix K3 sscanf mess, part II + +2016-04-04 18:39 NiLuJe + + * [r13102] extensions/MRInstaller/data/mrpi-K3.tar.gz: + + Kindle Hacks: + * Refresh K3 binaries to fix the sscanf mess, part I + +2016-04-03 17:04 NiLuJe + + * [r13099] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + KIndle Hacks: + * Refresh binaries + +2016-03-02 20:43 NiLuJe + + * [r13003] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * refresh binaries (initial GDB import) + +2016-02-21 00:10 NiLuJe + + * [r12956] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-02-14 01:23 NiLuJe + + * [r12903] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (Linaro 5.2 2015.11-2) + +2016-02-13 21:37 NiLuJe + + * [r12900] extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (incomplete, from my test buildbox) + +2016-02-07 18:12 NiLuJe + + * [r12851] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2016-01-18 03:13 NiLuJe + + * [r12798] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (downgrade TC to 2015.11, for some strange reason I + don't have time to investigate, my 2016.01 builds are utterly + broken...) + +2016-01-16 20:41 NiLuJe + + * [r12795] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (update TC) + +2015-12-04 18:48 NiLuJe + + * [r12672] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-11-28 00:59 NiLuJe + + * [r12659] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-11-20 19:02 NiLuJe + + * [r12644] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-11-14 21:14 NiLuJe + + * [r12632] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaires (TC update). + The Kobo set is incomplete because sourceforge went down during the + build, and is currently still down... + +2015-11-13 17:06 NiLuJe + + * [r12624] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-11-06 21:03 NiLuJe + + * [r12603] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (stop playing with FT's new TT stem darkening) + +2015-11-06 11:28 NiLuJe + + * [r12589] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-11-05 16:24 NiLuJe + + * [r12580] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-10-31 18:46 NiLuJe + + * [r12573] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (fix dropbear interactive logins) + +2015-10-27 18:56 NiLuJe + + * [r12558] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-10-22 16:23 NiLuJe + + * [r12546] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-10-16 18:15 NiLuJe + + * [r12526] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (TC update) + +2015-10-10 13:30 NiLuJe + + * [r12507] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-10-03 23:11 NiLuJe + + * [r12498] extensions/MRInstaller/data/mrpi-K5.tar.gz: + + Kindle Hacks: + * Refresh binaries (split K4/K5 FT binaries for the whole GCC 5 crashy + weirdness) + +2015-10-03 21:06 NiLuJe + + * [r12491] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (hopefully unbreaks FT overrides on legacy devices) + +2015-10-03 15:47 NiLuJe + + * [r12487] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-09-27 14:01 NiLuJe + + * [r12478] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-09-26 13:22 NiLuJe + + * [r12465] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-09-25 22:32 NiLuJe + + * [r12464] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-09-25 21:43 NiLuJe + + * [r12463] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (kindletool update) + +2015-09-24 18:45 NiLuJe + + * [r12450] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (nano fixes) + +2015-09-24 15:00 NiLuJe + + * [r12441] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-09-20 00:16 NiLuJe + + * [r12428] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaris (TC Update) + +2015-09-11 01:36 NiLuJe + + * [r12385] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-09-05 02:01 NiLuJe + + * [r12335] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (the great mmap enfixening!) + +2015-08-28 17:34 NiLuJe + + * [r12304] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * refresh binaries + +2015-08-20 16:50 NiLuJe + + * [r12267] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-08-18 20:28 NiLuJe + + * [r12248] extensions/MRInstaller/config.xml: + + KUAL Extensions: + * BatteryStatus: + * Tag v1.1.N + * GNU Awk Installer: + * Tag v1.5.N + * Helper: + * Tag v0.5.N + * MRPI: + * Tag v1.6.N + +2015-08-17 13:27 NiLuJe + + * [r12224] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Fix a comment, we're good on the K4 :) + +2015-08-17 13:23 NiLuJe + + * [r12223] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * More logging tweaks... + +2015-08-17 11:28 NiLuJe + + * [r12221] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Simplify the handling of the new device ID scheme. It's both simpler + and more accurate. + +2015-08-17 11:20 NiLuJe + + * [r12220] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Err, it's actually better if I actually write the log message *to* + the log, and not the ether... -_-" + +2015-08-17 11:19 NiLuJe + + * [r12219] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Make package switches clearer in the logs + +2015-08-17 11:13 NiLuJe + + * [r12218] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * I'm fairly stupid. That had approximately zero chance to work, since + ash only supports 32bit ints... -_-" + TL;DR: Don't try to be smart, assume our ota version parsing works, + and let the version check actually fail later if it somehow borked the + value. + +2015-08-17 11:08 NiLuJe + + * [r12217] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Fix version checks on legacy devices... (printf is being stupid + there...) + +2015-08-17 10:58 NiLuJe + + * [r12216] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Argh, forgot we can't do / substitutions on legacy devices... ;'( + +2015-08-17 10:53 NiLuJe + + * [r12215] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Fix the device code extraction regex, it wasn't precise enough... + +2015-08-17 10:31 NiLuJe + + * [r12214] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * More logging! + +2015-08-17 10:28 NiLuJe + + * [r12213] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * More debug logging... + +2015-08-17 10:25 NiLuJe + + * [r12212] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Typo + +2015-08-17 10:24 NiLuJe + + * [r12211] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Make the log more useful for debugging... + +2015-08-16 10:33 NiLuJe + + * [r12203] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (TC update) + +2015-08-15 13:31 NiLuJe + + * [r12176] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-08-15 09:46 NiLuJe + + * [r12172] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-08-14 17:52 NiLuJe + + * [r12153] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Cover all bases when detecting models to avoid potential false + positives with the new device ID scheme... + +2015-08-14 14:10 NiLuJe + + * [r12151] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Rework the model detection to handle the PW3 properly... + +2015-08-14 12:35 NiLuJe + + * [r12149] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MPRI: + * Err, simplify the new device ID scheme handling... + I kind of forgot that I made sure that KindleTool would be doing the + heavy lifting yesterday.... -_-" + +2015-08-14 12:31 NiLuJe + + * [r12148] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Oops, fix device code checking. + Always get a device code in hex from KindleTool, we handle the + base32hex conversion ourselves later as needed. + +2015-08-14 12:25 NiLuJe + + * [r12147] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * More comment tweaks + +2015-08-14 12:23 NiLuJe + + * [r12146] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAl Extensions: + * MRPI: + * Tweak a comment + +2015-08-14 12:23 NiLuJe + + * [r12145] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Handle the new device ID scheme found on the PW3 when matching + package device IDs... + Playing with base32hex in fun as hell in shell... >_<". + +2015-08-14 09:39 NiLuJe + + * [r12137] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-08-13 17:22 NiLuJe + + * [r12127] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Missed a spot ;p + +2015-08-13 17:19 NiLuJe + + * [r12126] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Update device code parsing to handle the new KindleTool format. + Yup, that regex is beastly. + + Special "locked myself out of the flat" edition. + +2015-08-01 16:03 NiLuJe + + * [r12110] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Touch Hacks: + * Package stuff for the PW3, and update docs accordingly. + +2015-08-01 15:49 NiLuJe + + * [r12109] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * First innacurate stab at detecting the PW3. + This will probably need to be fixed at one point... + +2015-07-30 06:39 NiLuJe + + * [r12103] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-07-27 03:04 NiLuJe + + * [r12097] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-07-25 18:30 NiLuJe + + * [r12084] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Fix some more nonsensical keywords on binary files... + +2015-07-25 17:45 NiLuJe + + * [r12080] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-06-17 14:53 NiLuJe + + * [r12048] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Minor tweaks to the input sanitization for the version checks + +2015-06-17 05:54 NiLuJe + + * [r12047] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * Nope, there truly was an issue, but only with really, really ancient + builds. + +2015-06-17 05:40 NiLuJe + + * [r12046] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * Unbreak OTA version computations. (Also, stop trying to do maths at + night). + +2015-06-17 05:11 NiLuJe + + * [r12045] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Fix version check by working around a busybox limitation... + +2015-06-17 04:33 NiLuJe + + * [r12044] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Arrrgh. BB does arithmetics w/ signed 32bit ints... Our version + checks almost always overflows. :( + +2015-06-17 03:48 NiLuJe + + * [r12043] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * Make it more explicit when we're pulling OTA build numbers from thin + air on weird builds, when we don't have a type contraint on the value + +2015-06-17 03:46 NiLuJe + + * [r12042] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * Cut those silly bangs + +2015-06-17 03:40 NiLuJe + + * [r12041] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Honor the version constraints of a package + +2015-06-17 01:45 NiLuJe + + * [r12038] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (update IM to fix a ScreenSavers regression) + +2015-06-13 14:57 NiLuJe + + * [r12025] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-06-05 22:17 NiLuJe + + * [r12019] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + • Refresh binaries + +2015-05-31 01:14 NiLuJe + + * [r12014] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-05-25 18:33 NiLuJe + + * [r12003] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-05-07 22:32 NiLuJe + + * [r11963] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update binaries (updated TC) + +2015-05-01 02:02 NiLuJe + + * [r11924] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update binaries + +2015-04-29 11:29 NiLuJe + + * [r11911] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Confirmed the eips stuff on the KT2. Thanks to @zfu on MR :). + +2015-04-28 16:23 NiLuJe + + * [r11906] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Fix eips prints on the KT2... + It appears to behave like the eips used on newer devices... + Except on a much smaller screen. This probably means that most of my + prints + will be cropped on a KT2... + Thanks to @zfu on MR for pointing that out :). + +2015-03-11 00:58 NiLuJe + + * [r11729] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-03-07 16:23 NiLuJe + + * [r11725] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-03-07 01:28 NiLuJe + + * [r11720] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-03-06 17:44 NiLuJe + + * [r11712] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MRPI: + * Reminder to myself on why we're more or less forced to down usbnet + during updates... + +2015-03-06 17:37 NiLuJe + + * [r11711] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Tweak the header for Legacy/Wario packages + +2015-02-26 19:01 NiLuJe + + * [r11698] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (fix fc-scan) + +2015-02-26 13:36 NiLuJe + + * [r11688] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (fix Python) + +2015-02-26 02:23 NiLuJe + + * [r11684] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh bianries... + (LTO, AKA, The Breakening!) + +2015-02-25 15:00 NiLuJe + + * [r11663] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + • Resync binaries + +2015-02-23 21:03 NiLuJe + + * [r11616] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-02-23 02:18 NiLuJe + + * [r11604] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * refresh binaries (fix readline) + +2015-02-22 20:47 NiLuJe + + * [r11589] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update binaries (misc zsh support) + +2015-02-22 14:25 NiLuJe + + * [r11576] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries + +2015-02-20 06:30 NiLuJe + + * [r11558] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (fix strace) + +2015-02-19 23:37 NiLuJe + + * [r11554] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (strace w/ libunwind, Python & SQLite3 w/ readline) + +2015-02-18 21:19 NiLuJe + + * [r11541] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Update binaries (widec ncurses) + +2015-02-17 23:35 NiLuJe + + * [r11537] extensions/MRInstaller/data/mrpi-K3.tar.gz, + extensions/MRInstaller/data/mrpi-K5.tar.gz, + extensions/MRInstaller/data/mrpi-PW2.tar.gz: + + Kindle Hacks: + * Refresh binaries (Linaro 4.9 2015.02) + +2015-02-14 00:06 NiLuJe + + * [r11513] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Minor tweak to the staging directory handling (absolute paths, avoid + surprises in case of funky/missing cd's) + +2015-02-12 20:47 NiLuJe + + * [r11505] extensions/MRInstaller/config.xml: + + KUAL Extensions: + * MRPI: + * Tag v1.5.N + +2015-02-12 20:44 NiLuJe + + * [r11504] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MRPI: + * Fix failures on legacy for good. + I actually can't read svn blame, apparently... :D + +2015-02-12 19:22 NiLuJe + + * [r11503] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Fix binaries install... + +2015-02-12 19:14 NiLuJe + + * [r11502] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Nice typo. + +2015-02-12 19:10 NiLuJe + + * [r11501] extensions/MRInstaller/bin/K3/kindletool[DEL], + extensions/MRInstaller/bin/K5/kindletool[DEL], + extensions/MRInstaller/bin/PW2/kindletool[DEL], + extensions/MRInstaller/bin/mrinstaller.sh, + extensions/MRInstaller/data[ADD], + extensions/MRInstaller/data/mrpi-K3.tar.gz[ADD], + extensions/MRInstaller/data/mrpi-K5.tar.gz[ADD], + extensions/MRInstaller/data/mrpi-PW2.tar.gz[ADD], + extensions/MRInstaller/lib/K3/libz.so.1[DEL], + extensions/MRInstaller/lib/K5/libz.so.1[DEL], + extensions/MRInstaller/lib/PW2/libz.so.1[DEL]: + + KUAL Extensions: + * MRPI: + * Tweak binary handling. Only install the relevant ones. + +2015-02-12 18:30 NiLuJe + + * [r11500] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MRPI: + * Amend last commit. Revert it, and instead add a comment aimed at + package authors. + +2015-02-12 18:23 NiLuJe + + * [r11499] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool: + + Kindle Hacks: + * Update binaries + +2015-02-12 18:11 NiLuJe + + * [r11498] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MRPI: + * Err, unbreak on legacy devices? + Apparently, we can't use set -e there, because sourcing _FUNCTIONS + isn't actually errorless... -_-" + +2015-02-12 17:33 NiLuJe + + * [r11497] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MRPI: + * Update comments + +2015-02-12 16:19 NiLuJe + + * [r11496] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MRPI: + * Moar logging + +2015-02-12 15:47 NiLuJe + + * [r11494] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + • MRPI: + • Minor tweaks to aid debugging... + +2015-01-31 19:58 NiLuJe + + * [r11464] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool: + + Kindle Hacks: + * Refresh binaries + +2015-01-17 20:34 NiLuJe + + * [r11432] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool: + + Kindle Hacks: + * Refresh binaries + +2015-01-07 23:20 NiLuJe + + * [r11355] extensions/MRInstaller/CREDITS: + + Kobo Hacks: + * KoboStuff: + * Add a readme + +2015-01-05 23:16 NiLuJe + + * [r11332] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool: + + Kindle Hacks: + * Refresh binaries + +2014-12-23 14:56 NiLuJe + + * [r11305] extensions/MRInstaller/menu.json: + + KUAL Extensions: + * Make sure the "Helper" menu is always right below the "KUAL" one. + * Make sure MRI is always at the top of the "Helper" menu. + +2014-12-18 13:05 NiLuJe + + * [r11252] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool, + extensions/MRInstaller/lib/K3/libz.so.1, + extensions/MRInstaller/lib/K5/libz.so.1, + extensions/MRInstaller/lib/PW2/libz.so.1: + + Kindle Hacks: + * Refresh binaires (updated TC) + +2014-12-04 14:02 NiLuJe + + * [r11223] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + • MR Installer: + • Don't source _FUNCTIONS twice on legacy devices. Good catch, @knc1 + ;). + +2014-11-29 15:11 NiLuJe + + * [r11181] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Babysit AcXE some more (its start on/stop on stanzas make it so that + it doesn't depend on the UI, so we need to stop/start it ourselves) + +2014-11-29 14:58 NiLuJe + + * [r11180] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Move the sync calls to the make_rootfs functions themselves + +2014-11-29 01:46 NiLuJe + + * [r11178] extensions/MRInstaller/config.xml: + + KUAL Extensions: + * MRPI: + * Tag v1.4.N + +2014-11-29 01:45 NiLuJe + + * [r11177] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Cleanup properly on error + +2014-11-29 01:30 NiLuJe + + * [r11176] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * Reimplement mntroot ourselves, in a more robust manner. + +2014-11-29 01:12 NiLuJe + + * [r11174] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MPRI: + * Raise the priority of RP installers + +2014-11-29 01:08 NiLuJe + + * [r11173] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MRPI: + * More accurate rw/ro rootfs check... + +2014-11-28 20:46 NiLuJe + + * [r11172] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool, + extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Refresh binaries + +2014-11-25 21:29 NiLuJe + + * [r11168] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool: + + Kindle Hacks: + * Refresh binaries + +2014-11-25 19:14 NiLuJe + + * [r11166] extensions/MRInstaller/config.xml: + + KUAL Extensions: + * MR Installer: + * Tag v1.3.N + +2014-11-23 20:44 NiLuJe + + * [r11150] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Oooh, I can't spell ;). + +2014-11-23 16:18 NiLuJe + + * [r11142] extensions/MRInstaller/config.xml: + + Kindle Touch Hacks: + * JailBreak: + * Tag v1.13.N + * USBNetwork: + * Tag v0.18.N + * MR Installer: + * Tag v1.2.N + +2014-11-22 18:45 NiLuJe + + * [r11135] extensions/MRInstaller/bin/K3/kindletool, + extensions/MRInstaller/bin/K5/kindletool, + extensions/MRInstaller/bin/PW2/kindletool: + + Kindle Hacks: + * Refresh binaries + +2014-11-22 16:23 NiLuJe + + * [r11129] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Use a more reliable method to check the current UID, + the env cannot be trusted. + +2014-11-22 15:30 NiLuJe + + * [r11127] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * More safety nets around the FW 5.6.1 mess... + +2014-11-17 18:33 NiLuJe + + * [r11115] extensions/MRInstaller/CREDITS[ADD]: + + KUAl extensions: + * MR Installer: + * Forgot the credits file for the binaries + +2014-11-17 18:28 NiLuJe + + * [r11114] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Actually, die earlier on FW 5.6.1, we might not be able to bring the + UI back up properly otherwise. + +2014-11-17 18:15 NiLuJe + + * [r11113] extensions/MRInstaller/config.xml: + + KUAl extensions: + * MR Installer: + * Tag v1.1.N + +2014-11-17 17:46 NiLuJe + + * [r11111] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Tweak a few more eips calls + +2014-11-17 17:36 NiLuJe + + * [r11110] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Add some more error checking, to avoid going kablooey in case stuff + doesn't go as planned... + (FW 5.6.1, i'm looking at you...) + +2014-11-16 14:01 NiLuJe + + * [r11109] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Actually destroy invalid packages ;) + +2014-11-16 02:49 NiLuJe + + * [r11108] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MR Installer: + * Try harder not to let our last eips call get eaten by the driver... + +2014-11-15 23:47 NiLuJe + + * [r11101] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAl extensions: + * MR Installer: + * Shorten an eips msg + +2014-11-15 22:18 NiLuJe + + * [r11094] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * More sleepytime tweaks... + +2014-11-15 22:14 NiLuJe + + * [r11093] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MR Installer: + * Down usbnet manually on legacy devices. + (fixes USBNet not coming back up if USE_VOLUMD is true) + +2014-11-15 22:05 NiLuJe + + * [r11092] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAl extensions: + * MR Installer: + * Wait a bit for X to get torn down on K5 + +2014-11-15 21:52 NiLuJe + + * [r11091] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MR Installer: + * More sleepytime on legacy devices + +2014-11-15 21:51 NiLuJe + + * [r11090] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Wait much longer for us to really have switched to single-user + runlevel + +2014-11-15 21:42 NiLuJe + + * [r11089] extensions/MRInstaller/bin/mrinstaller.sh: + + Kindle Hacks: + * Oops, fix progress outside of the updater ;) + +2014-11-15 21:36 NiLuJe + + * [r11088] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MR Installer: + * The parser doesn't care if the code is reachable or not ;'( + +2014-11-15 21:33 NiLuJe + + * [r11087] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Multiple fixes for legacy devices + +2014-11-15 21:03 NiLuJe + + * [r11085] extensions/MRInstaller/bin/K3/kindletool[ADD], + extensions/MRInstaller/bin/K5/kindletool[ADD], + extensions/MRInstaller/bin/PW2/kindletool[ADD], + extensions/MRInstaller/lib/K3/libz.so.1[ADD], + extensions/MRInstaller/lib/K5/libz.so.1[ADD], + extensions/MRInstaller/lib/PW2/libz.so.1[ADD]: + + Kindle Hacks: + * Refresh binaries + +2014-11-15 19:57 NiLuJe + + * [r11084] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MR Installer: + * Should now handle legacy devices, too... + +2014-11-15 18:55 NiLuJe + + * [r11082] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL Extensions: + * MR Installer: + * More extensive package validation + +2014-11-15 18:02 NiLuJe + + * [r11081] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * More eips msg tweaks + +2014-11-15 17:55 NiLuJe + + * [r11080] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * More eips tweaks ;) + +2014-11-15 17:51 NiLuJe + + * [r11078] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Tweak some eips msgs some more + +2014-11-15 17:42 NiLuJe + + * [r11077] extensions/MRInstaller/bin/mrinstaller.sh, + extensions/MRInstaller/log[ADD]: + + KUAL Extensions: + * MR Installer: + * Tweak some eips messages, and add some more logging + +2014-11-15 17:12 NiLuJe + + * [r11076] extensions/MRInstaller/bin/mrinstaller.sh: + + KUAL extensions: + * MR Installer: + * Make sure the package directory exists first + +2014-11-15 17:11 NiLuJe + + * [r11075] .[ADD], extensions[ADD], extensions/MRInstaller[ADD], + extensions/MRInstaller/bin[ADD], extensions/MRInstaller/bin/K3[ADD], + extensions/MRInstaller/bin/K5[ADD], + extensions/MRInstaller/bin/PW2[ADD], + extensions/MRInstaller/bin/mrinstaller.sh[ADD], + extensions/MRInstaller/config.xml[ADD], + extensions/MRInstaller/lib[ADD], extensions/MRInstaller/lib/K3[ADD], + extensions/MRInstaller/lib/K5[ADD], + extensions/MRInstaller/lib/PW2[ADD], + extensions/MRInstaller/menu.json[ADD], mrpackages[ADD]: + + Kindle Hacks, KUAL Extensions: + * Initial import of the K5 MR Package Installer :) + diff --git a/dash/staging/mrpi/VERSION b/dash/staging/mrpi/VERSION new file mode 100644 index 0000000..499a602 --- /dev/null +++ b/dash/staging/mrpi/VERSION @@ -0,0 +1 @@ +1.7.N @ r19303 on 2023-Nov-06 @ 18:18 - Patched for FW >= 5.16.3 diff --git a/dash/staging/mrpi/extensions/MRInstaller/CREDITS b/dash/staging/mrpi/extensions/MRInstaller/CREDITS new file mode 100644 index 0000000..5507d8a --- /dev/null +++ b/dash/staging/mrpi/extensions/MRInstaller/CREDITS @@ -0,0 +1,27 @@ + kindletool: KindleTool, Copyright (C) 2011-2015 Yifan Lu, licensed under the GNU General Public License version 3+ (http://www.gnu.org/licenses/gpl.html). +(https://github.com/NiLuJe/KindleTool/) + + | + |-> libarchive, Copyright (C) Tim Kientzle, licensed under the New BSD License (http://www.opensource.org/licenses/bsd-license.php) + | (http://libarchive.github.com/) + | + |-> GMP, GNU MP Library, Copyright 1991-2013 Free Software Foundation, Inc., + | licensed under the GNU Lesser General Public License version 3+ (http://www.gnu.org/licenses/lgpl.html). + | (http://gmplib.org/) + | + `-> nettle, Copyright (C) 2001-2013 Niels Möller, + licensed under the GNU Lesser General Public License version 2.1+ (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html). + (http://www.lysator.liu.se/~nisse/nettle) + + libz: zlib, Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler, + Licensed under the zlib license (http://zlib.net/zlib_license.html) + (http://zlib.net/) + + fbink: FBInk (FrameBuffer eInker), Copyright (C) 2018-2019 NiLuJe , + Released under the GNU General Public License version 3+ (https://www.gnu.org/licenses/gpl.html) + (https://github.com/NiLuJe/FBInk) + + BigBlue_Terminal.ttf: BigBlue Terminal +, Copyright (C) VileR, , + Licensed under a Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/) + (https://int10h.org/blog/2015/12/bigblue-terminal-oldschool-fixed-width-font/). + Patched with extra glyphs via https://github.com/ryanoasis/nerd-fonts/, see the WiKi for individual licenses. diff --git a/dash/staging/mrpi/extensions/MRInstaller/bin/mrinstaller.sh b/dash/staging/mrpi/extensions/MRInstaller/bin/mrinstaller.sh new file mode 100755 index 0000000..ad0da9d --- /dev/null +++ b/dash/staging/mrpi/extensions/MRInstaller/bin/mrinstaller.sh @@ -0,0 +1,1183 @@ +#!/bin/sh +## +# +# MR Package Installer +# +# $Id: mrinstaller.sh 19303 2023-11-06 17:06:17Z NiLuJe $ +# +# shellcheck disable=SC1090,SC3037,SC3043,SC2164 +# +## + +# Remember our current revision for logging purposes... +MRPI_REV="$( echo '$Revision: 19303 $' | cut -d ' ' -f 2 )" + +## Logging +# Pull some helper functions for logging +_FUNCTIONS=/etc/upstart/functions +if [ -f "${_FUNCTIONS}" ] ; then + . "${_FUNCTIONS}" +else + # legacy... + _FUNCTIONS=/etc/rc.d/functions + [ -f "${_FUNCTIONS}" ] && . "${_FUNCTIONS}" +fi + +# Are we on FW 5.x? +IS_K5="true" +if [ -f "/etc/rc.d/functions" ] && grep -q "EIPS" "/etc/rc.d/functions" ; then + IS_K5="false" +fi +# We'll also need to know our device ID... +kmodel="??" + +## Check if we're a K5 +check_is_touch_device() +{ + [ "${IS_K5}" = "true" ] && return 0 + + # We're not! + return 1 +} + +# Logging... +logmsg() +{ + if check_is_touch_device ; then + # Adapt the K5 logging calls to the simpler legacy syntax + f_log "${1}" "mr_installer" "${2}" "${3}" "${4}" + else + # Slightly tweaked version of msg() (from ${_FUNCTIONS}, where the constants are defined) + local _NVPAIRS + local _FREETEXT + local _MSG_SLLVL + local _MSG_SLNUM + + _MSG_LEVEL="${1}" + _MSG_COMP="${2}" + + { [ $# -ge 4 ] && _NVPAIRS="${3}" && shift ; } + + _FREETEXT="${3}" + + eval _MSG_SLLVL=\${MSG_SLLVL_$_MSG_LEVEL} + eval _MSG_SLNUM=\${MSG_SLNUM_$_MSG_LEVEL} + + local _CURLVL + + { [ -f "${MSG_CUR_LVL}" ] && _CURLVL=$(cat "${MSG_CUR_LVL}") ; } || _CURLVL=1 + + if [ "${_MSG_SLNUM}" -ge "${_CURLVL}" ] ; then + /usr/bin/logger -p local4."${_MSG_SLLVL}" -t "mr_installer" "${_MSG_LEVEL} def:${_MSG_COMP}:${_NVPAIRS}:${_FREETEXT}" + fi + + [ "${_MSG_LEVEL}" != "D" ] && echo "mr_installer: ${_MSG_LEVEL} def:${_MSG_COMP}:${_NVPAIRS}:${_FREETEXT}" + fi +} + +# From libotautils[5], adapted from libkh[5] +do_fbink_print() +{ + # We need at least two args + if [ $# -lt 2 ] ; then + logmsg "W" "do_fbink_print" "" "not enough arguments passed to do_fbink_print ($# while we need at least 2)" + return 1 + fi + + kh_string="${1}" + kh_y_shift_up="${2}" + + # Unlike eips, we need at least a single space to even try to print something ;). + if [ "${kh_string}" = "" ] ; then + kh_string=" " + fi + + # Check if we asked for a highlighted message... + if [ "${3}" = "h" ] ; then + fbink_extra_args="h" + else + fbink_extra_args="" + fi + + # NOTE: FBInk will handle the padding. FBInk's default font is square, not tall like eips, + # so we compensate by tweaking the baseline ;). + ${FBINK_BIN} -qpm${fbink_extra_args} -y $(( -4 - kh_y_shift_up )) "${kh_string}" +} + +print_bottom_centered() +{ + # We need at least two args + if [ $# -lt 2 ] ; then + logmsg "W" "print_bottom_centered" "" "not enough arguments passed to print_bottom_centered ($# while we need at least 2)" + return 1 + fi + + kh_string="${1}" + kh_y_shift_up="${2}" + + # Log it, too + logmsg "I" "print_bottom_centered" "" "${kh_string}" + + # Sleep a tiny bit to workaround the logic in the 'new' (K4+) eInk controllers that tries to bundle updates + if [ "${PRINT_SLEEP}" = "true" ] ; then + usleep 150000 # 150ms + fi + + do_fbink_print "${kh_string}" "${kh_y_shift_up}" +} + +## Check if arg is an int +is_integer() +{ + # Cheap trick ;) + [ "${1}" -eq "${1}" ] 2>/dev/null + return $? +} + +## Compute our current OTA version (NOTE: Pilfered from Helper's device_id.sh ;)) +fw_build="0" +compute_current_ota_version() +{ + fw_build_maj="$(awk '/Version:/ { print $NF }' /etc/version.txt | awk -F- '{ print $NF }')" + # NOTE: Apparently, awk is deficient (?!) on some FW versions... (I cannot reproduce this in a sandbox, FWIW) + # FIXME: This appears to be related to something in the env (an LD_PRELOAD?), as it behaves when run in a clean env... + if [ -z "${fw_build_maj}" ] ; then + fw_build_maj="$(sed -ne '/Version:/p' /etc/version.txt | sed 's/^.*-//')" + fi + fw_build_min="$(awk '/Version:/ { print $NF }' /etc/version.txt | awk -F- '{ print $1 }')" + if [ -z "${fw_build_min}" ] ; then + fw_build_min="$(sed -ne '/Version:/p' /etc/version.txt | sed -e 's/^.*Version: //' | cut -f1 -d '-')" + fi + # Legacy major versions used to have a leading zero, which is stripped from the complete build number. Except on really ancient builds, that (or an extra) 0 is always used as a separator between maj and min... + fw_build_maj_pp="${fw_build_maj#0}" + # That only leaves some weird diags build that handle this stuff in potentially even weirder ways to take care of... + if [ "${fw_build_maj}" -eq "${fw_build_min}" ] ; then + # Weird diags builds... (5.0.0) + fw_build="${fw_build_maj_pp}0???" + else + # Most common instance... maj#6 + 0 + min#3 or maj#5 + 0 + min#3 (potentially with a leading 0 stripped from maj#5) + if [ ${#fw_build_min} -eq 3 ] ; then + fw_build="${fw_build_maj_pp}0${fw_build_min}" + else + # Truly ancient builds... For instance, 2.5.6, which is maj#5 + min#4 (with a leading 0 stripped from maj#5) + fw_build="${fw_build_maj_pp}${fw_build_min}" + fi + fi +} + +# Our packages live in a specific directory +MRPI_PKGDIR="/mnt/us/mrpackages" +# We're using our own tmpfs +MRPI_TMPFS="/var/tmp/mrpi" +# We're working in a staging directory, in our tmpfs +MRPI_WORKDIR="${MRPI_TMPFS}/staging" +# We're making KindleTool use our own tmpfs as a temp directory +MRPI_TMPDIR="${MRPI_TMPFS}/tmpdir" + +## Call kindletool with the right environment setup +MRINSTALLER_BINDIR="$(dirname "$(realpath "${0}")")" +MRINSTALLER_BASEDIR="${MRINSTALLER_BINDIR%*/bin}" +run_kindletool() +{ + # Check that our binary actually is available... + if [ ! -x "${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/kindletool" ] ; then + print_bottom_centered "No KindleTool binary, aborting" 1 + echo -e "\nCould not find a proper KindleTool binary for the current arch (${BINARIES_TC}), aborting . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Pick up our own libz build... + env KT_WITH_UNKNOWN_DEVCODES="true" TMPDIR="${MRPI_TMPDIR}" LD_LIBRARY_PATH="${MRINSTALLER_BASEDIR}/lib/${BINARIES_TC}" "${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/kindletool" "$@" +} + +# Default to something that won't horribly blow up... +FBINK_BIN="true" +ICON_SIZE="450" +check_fbink() +{ + # Pick the right binary for our device... + # FIXME: That'll be easier for >= 5.16.3, just need to check for /lib/ld-linux-armhf.so.3 + # That'll of course require a new TC: armhf, glibc 2.20, kernel 4.1.15 (i.e., PW4+), tuned for whichever big cortex is on the MTK SoCs (A15?) + # And new packages, with a breakpoint to avoid footguns at OTA number 4110100057 (lowest 5.16.3 release) + MACHINE_ARCH="$(uname -m)" + if [ "${MACHINE_ARCH}" = "armv7l" ] ; then + if [ -e "/lib/ld-linux-armhf.so.3" ] ; then + # Very cheap hard float detection + BINARIES_TC="KHF" + elif grep -e '^Hardware' /proc/cpuinfo | grep -q -e 'i\.MX[[:space:]]\?[6-7]' ; then + # NOTE: Slightly crappy Wario/Rex & Zelda detection ;p + BINARIES_TC="PW2" + elif grep -e '^Hardware' /proc/cpuinfo | grep -q -e 'MT8110' ; then + # Similarly cheap Bellatrix detection + BINARIES_TC="PW2" + else + BINARIES_TC="K5" + fi + else + BINARIES_TC="K3" + fi + + # Check if we have a tarball of binaries to install... + if [ -f "${MRINSTALLER_BASEDIR}/data/mrpi-${BINARIES_TC}.tar.gz" ] ; then + # Clear existing binaries... + for tc_set in K3 K5 PW2 KHF ; do + for bin_set in lib bin ; do + for file in "${MRINSTALLER_BASEDIR}"/"${bin_set}"/"${tc_set}"/* ; do + [ -f "${file}" ] && rm -f "${file}" + done + done + done + tar -xvzf "${MRINSTALLER_BASEDIR}/data/mrpi-${BINARIES_TC}.tar.gz" -C "${MRINSTALLER_BASEDIR}" + # Clear data folder now + for file in "${MRINSTALLER_BASEDIR}"/data/mrpi-*.tar.gz ; do + [ -f "${file}" ] && rm -f "${file}" + done + fi + + # Check that our binary actually is available... + if [ ! -x "${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/fbink" ] ; then + print_bottom_centered "No FBInk binary, aborting" 1 + echo -e "\nCould not find a proper FBInk binary for the current arch (${BINARIES_TC}), aborting . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # We're good, set it up... + FBINK_BIN="${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/fbink" + + # And use it to pickup our device ID, and a few things we'll need to compute the ideal icon size... + eval "$(${FBINK_BIN} -e | tr ';' '\n' | grep -e viewHeight -e FONTH -e deviceId | tr '\n' ';')" + # And convert the deviceID to hex, as that's what KindleTool reports... + # FIXME: Switch to numeric comparisons when switching to KindleTool's metadata dump format! + if [ "${deviceId}" -gt 255 ] ; then + kmodel="$(printf "%03X" "${deviceId}")" + else + kmodel="$(printf "%02X" "${deviceId}")" + fi + + # Compute the ideal icon size, knowing that we'll print it in the center of the screen, + # and we need 8 rows of text on the bottom, plus two of padding... + ICON_SIZE="$(( viewHeight - (10 * FONTH * 2) ))" + # Double check that it's sane + is_integer "${ICON_SIZE}" || ICON_SIZE="450" + + return 0 +} + +## Display an icon in the middle of the screen +print_icon() +{ + # We need at least one arg + if [ $# -lt 1 ] ; then + logmsg "W" "print_icon" "" "not enough arguments passed to print_icon ($# while we need at least 1)" + return 1 + fi + + kh_icon="${1}" + + # Log it, too + logmsg "I" "print_icon" "" "${kh_icon}" + + # Which one did we want? + case "${kh_icon}" in + "OK" ) + kh_icon_cp="" # \uf633 + ;; + "FAIL" ) + kh_icon_cp="" # \uf071 + ;; + "BOMB" ) + kh_icon_cp="ﮏ" # \ufb8f + ;; + "WAIT" ) + kh_icon_cp="" # \uf252 + ;; + "PYTHON" ) + kh_icon_cp="" # \ue73c + ;; + "USBNET" ) + kh_icon_cp="" # \uf68c + ;; + "BRIDGE" ) + kh_icon_cp="" # \ue286 + ;; + "AMZ" ) + kh_icon_cp="" # \uf270 + ;; + "SAD" ) + kh_icon_cp="" # \uf119 + ;; + "LINUX" ) + kh_icon_cp="" # \uf17c + ;; + "HAPPY" ) + kh_icon_cp="" # \uf118 + ;; + "TOOLS" ) + kh_icon_cp="" # \uf425 + ;; + "KUAL" ) + kh_icon_cp="異" # \uf962 + ;; + "MRPI" ) + kh_icon_cp="" # \uf487 + ;; + * ) + logmsg "W" "print_icon" "" "requested an unknown icon (${kh_icon})" + return 1 + ;; + esac + + # This is why we needed a custom build: for TTF support ;). + # Patched font from https://nerdfonts.com/ + ${FBINK_BIN} -qMm -t regular="${MRINSTALLER_BASEDIR}/data/BigBlue_Terminal.ttf",px="${ICON_SIZE}" "${kh_icon_cp}" +} + +## Rotate the log +rotate_logs() +{ + # If it's over 1MB, compress & timestamp + if [ -f "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" ] ; then + log_size="$(stat -c %s "${MRINSTALLER_BASEDIR}/log/mrinstaller.log")" + + if [ "${log_size}" -gt $((1 * 1024 * 1024)) ] ; then + gzip "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + mv "${MRINSTALLER_BASEDIR}/log/mrinstaller.log.gz" "${MRINSTALLER_BASEDIR}/log/mrinstaller-$(date +%Y-%m-%d_%H-%M).log.gz" + fi + fi +} + +## Check that we have enough free space +enough_free_space() +{ + if [ "$(df -k /mnt/us | tail -n 1 | awk '{ print $4; }')" -lt "$((150 * 1024))" ] ; then + # Less than 150MB left, meep! + return 1 + else + # Good enough! + return 0 + fi +} + +## Check if our own tmpfs is mounted +is_mrpi_tmpfs_up() +{ + if grep -q "^tmpfs ${MRPI_TMPFS} tmpfs " /proc/mounts ; then + # Peachy :) + return 0 + fi + + # Huh, it's already up? + return 1 +} + +## Compute an estimate of the amount of available memory to resize our tmpfs +resize_mrpi_tmpfs() +{ + logmsg "I" "resize_mrpi_tmpfs" "" "checking available memory" + + # We'll resort to a few different methods, because depending on the age of the Linux kernel, + # we won't always have access to the easiest of them, which is relying on MemAvailable in /proc/meminfo... + # c.f., https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 + if grep -q 'MemAvailable' /proc/meminfo ; then + # We'll settle for 85% of available memory to leave a bit of breathing room + tmpfs_size="$(awk '/MemAvailable/ {printf "%d", $2 * 0.85}' /proc/meminfo)" + elif grep -q 'Inactive(file)' /proc/meminfo ; then + # Basically try to emulate the kernel's computation, c.f., https://unix.stackexchange.com/q/261247 + # Again, 85% of available memory + tmpfs_size="$(awk -v low="$(grep low /proc/zoneinfo | awk '{k+=$2}END{printf "%d", k}')" \ + '{a[$1]=$2} + END{ + printf "%d", (a["MemFree:"]+a["Active(file):"]+a["Inactive(file):"]+a["SReclaimable:"]-(12*low))*0.85; + }' /proc/meminfo)" + else + # Ye olde crap workaround of Free + Buffers + Cache... + # Take it with a grain of salt, and settle for 80% of that... + tmpfs_size="$(awk \ + '{a[$1]=$2} + END{ + printf "%d", (a["MemFree:"]+a["Buffers:"]+a["Cached:"])*0.80; + }' /proc/meminfo)" + fi + + # Make sure we end up with a sane-ish fallback in case all this failed... + # NOTE: Yeah, it definitely breaks on FW versions where awk is broken... >_<" + is_integer "${tmpfs_size}" || tmpfs_size="81920" + + # Log those computations + logmsg "I" "resize_mrpi_tmpfs" "" "can spare $((tmpfs_size / 1024))MB for mrpi tmpfs" + + # Check that we actually end up with enough free memory to be able to use it... + # NOTE: The most space-hungry package I have is the legacy variant of USBNet, which currently requires about 46MB. + # That said, we should be pretty safe: so far, I've always had *more* available RAM than the ceiling value, + # even on the K2, where the ceiling is at ~77MB, and I usually have around ~89MB of available memory to spare! + if [ "${tmpfs_size}" -lt "$(( 56 * 1024 ))" ] ; then + # If we can spare less than 56MB, abort! + logmsg "E" "resize_mrpi_tmpfs" "" "not enough available memory (< 56MB) for mrpi tmpfs!" + return 1 + fi + + # Compare that with our ceiling, 62.5% of the total memory + tmpfs_ceil="$(awk '/MemTotal/ {printf "%d", $2 * 0.625}' /proc/meminfo)" + # With a fallback... + is_integer "${tmpfs_ceil}" || tmpfs_ceil="81920" + + # If our computed size is smaller than the ceiling value, resize the tmpfs + if [ "${tmpfs_size}" -lt "${tmpfs_ceil}" ] ; then + if ! /bin/mount -t tmpfs tmpfs "${MRPI_TMPFS}" -o remount,defaults,size=${tmpfs_size}K,mode=1777,noatime ; then + logmsg "E" "resize_mrpi_tmpfs" "" "failed to remount mrpi tmpfs!" + return 1 + fi + + # Even if it appeared to work, double check... + if ! is_mrpi_tmpfs_up ; then + logmsg "E" "resize_mrpi_tmpfs" "" "mrpi tmpfs is still not mounted!" + return 1 + fi + + # Success! + logmsg "I" "resize_mrpi_tmpfs" "" "resized mrpi tmpfs down to $((tmpfs_size / 1024))MB" + fi + + return 0 +} + +## To make things faster, we'll try to do as much work in RAM as possible +## But since none of the existing tmpfs fit our needs, we'll create our own. +mount_mrpi_tmpfs() +{ + logmsg "I" "mount_mrpi_tmpfs" "" "trying to create mrpi tmpfs" + + # Sync first... + sync + + # Don't do anything if for some strange reason it's already up... + if is_mrpi_tmpfs_up ; then + logmsg "W" "mount_mrpi_tmpfs" "" "mrpi tmpfs is already mounted!" + return 0 + fi + + # Namely, the default ones tend to be small. So let's say we want one that's about 62.5% of the total RAM. + # That's usually close enough to the free RAM we get with the framework down, and should be more than enough ;). + tmpfs_size="$(awk '/MemTotal/ {printf "%d", $2 * 0.625}' /proc/meminfo)" + # Just in case the apocalypse hits, and our awk shenanigans fail, make sure we have sane defaults, even for 128MB of RAM + is_integer "${tmpfs_size}" || tmpfs_size="81920" + + # Create our mountpoint + mkdir -p "${MRPI_TMPFS}" + + # And mount it :) + if ! /bin/mount -t tmpfs tmpfs "${MRPI_TMPFS}" -o defaults,size=${tmpfs_size}K,mode=1777,noatime ; then + logmsg "E" "mount_mrpi_tmpfs" "" "failed to create mrpi tmpfs!" + return 1 + fi + + # Even if it appeared to work, double check... + if ! is_mrpi_tmpfs_up ; then + logmsg "E" "mount_mrpi_tmpfs" "" "mrpi tmpfs is still not mounted!" + return 1 + fi + + # Success! + logmsg "I" "mount_mrpi_tmpfs" "" "created $((tmpfs_size / 1024))MB mrpi tmpfs" + return 0 +} + +## And unmount it when we're done... +umount_mrpi_tmpfs() +{ + logmsg "I" "umount_mrpi_tmpfs" "" "trying to unmount mrpi tmpfs" + + # Sync first... + sync + + if ! /bin/umount "${MRPI_TMPFS}" ; then + logmsg "E" "umount_mrpi_tmpfs" "" "failed to unmount mrpi tmpfs!" + return 1 + fi + + # Even if it appeared to work, double check... + if is_mrpi_tmpfs_up ; then + logmsg "E" "umount_mrpi_tmpfs" "" "mrpi tmpfs is still mounted!" + return 1 + fi + + # Success! + logmsg "I" "umount_mrpi_tmpfs" "" "successfully unmounted mrpi tmpfs" + return 0 +} + +## Reimplement mntroot ourselves, because its checks aren't as robust as one would hope... +is_rootfs_ro() +{ + if awk '$4~/(^|,)ro($|,)/' /proc/mounts | grep -q '^/dev/root / ' ; then + # Peachy :) + return 0 + fi + + # Hu oh, it's rw... + return 1 +} + +is_rootfs_rw() +{ + if awk '$4~/(^|,)rw($|,)/' /proc/mounts | grep -q '^/dev/root / ' ; then + # Peachy :) + return 0 + fi + + # Hu oh, it's ro... + return 1 +} + +make_rootfs_rw() +{ + logmsg "I" "make_rootfs_rw" "" "trying to remount rootfs rw" + + # Sync first... + sync + + # Don't do anything if for some strange reason it's already rw... + if is_rootfs_rw ; then + logmsg "W" "make_rootfs_rw" "" "rootfs is already rw!" + return 0 + fi + + # Do eet! + if ! /bin/mount -o remount,rw / ; then + logmsg "E" "make_rootfs_rw" "" "failed to remount rootfs rw!" + return 1 + fi + + # Even if it appeared to work, double check... + if is_rootfs_ro ; then + logmsg "E" "make_rootfs_rw" "" "rootfs is still ro after a rw remount!" + return 1 + fi + + # Success! + logmsg "I" "make_rootfs_rw" "" "rootfs has been remounted rw" + return 0 +} + +make_rootfs_ro() +{ + logmsg "I" "make_rootfs_ro" "" "trying to remount rootfs ro" + + # Sync first... + sync + + # Don't do anything if for some strange reason it's already ro... + if is_rootfs_ro ; then + logmsg "W" "make_rootfs_ro" "" "rootfs is already ro!" + return 0 + fi + + # Do eet! + if ! /bin/mount -o remount,ro / ; then + logmsg "E" "make_rootfs_ro" "" "failed to remount rootfs ro!" + return 1 + fi + + # Even if it appeared to work, double check... + if is_rootfs_rw ; then + logmsg "E" "make_rootfs_ro" "" "rootfs is still rw after a ro remount!" + return 1 + fi + + # Success! + logmsg "I" "make_rootfs_ro" "" "rootfs has been remounted ro" + return 0 +} + +## Run a single package +run_package() +{ + # We need at one arg + if [ $# -lt 1 ] ; then + logmsg "W" "run_package" "" "not enough arguments passed to run_package ($# while we need at least 1)" + return 1 + fi + + # Clear our five lines... + print_bottom_centered "" 4 + print_bottom_centered "" 3 + print_bottom_centered "" 2 + print_bottom_centered "" 1 + print_bottom_centered "" 0 + + PKG_FILENAME="${1}" + + # Cleanup the name a bit for the screen + PKG_NAME="${PKG_FILENAME#[uU]pdate[-_]*}" + # Legacy devices have an older busybox version, with an ash build that sucks even more! [Can't use / substitutions] + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/uninstall/U/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/install/I/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/touch_pw/K5/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_and_up/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_zelda_rex_bellatrix/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_zelda_rex/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_zelda/W+Z/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario/WARIO/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_and_up/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4_koa3_pw5_ks/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4_koa3_pw5/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4_koa3/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2/W+Z/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3/WARIO+/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3/WARIO/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv/WARIO/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2/PW2/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/k2_dx_k3/LEGACY/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/koa2_koa3/ZELDA/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/koa2/ZELDA/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/zelda/ZELDA/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw4_kt4/REX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw4/REX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/rex/REX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5_kt5_ks/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5_kt5/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5_ks/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/kt5/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/scribe/BELLATRIX3/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/bellatrix/BELLATRIX/')" + PKG_NAME="${PKG_NAME%*.bin}" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/[-_]/ /g')" + + # Start by timestamping our logs... + echo -e "\n\n**** **** **** ****" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + echo -e "\n[$(date +'%F @ %T %z')] :: [MRPI r${MRPI_REV}] - Beginning the processing of package '${PKG_FILENAME}' (${PKG_NAME}) . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + + # Now that the framework is down and we actually have a decent amount of free RAM, try to resize out tmpfs accordingly + if ! resize_mrpi_tmpfs ; then + print_bottom_centered "Failed to resize MRPI tmpfs, waiting . . ." 1 + echo -e "\nFailed to resize MRPI tmpfs, waiting . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + # Try one final time... + if ! resize_mrpi_tmpfs ; then + print_icon "FAIL" + print_bottom_centered "Really failed to resize MRPI tmpfs, skipping" 1 + echo -e "\nReally failed to resize MRPI tmpfs, skipping ${PKG_NAME} . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + return 1 + fi + fi + + # Show a possibly relevant icon during processing + # First, make sure case won't bother us... + uc_pkg_name="$(echo "${PKG_NAME}" | tr '[:lower:]' '[:upper:]')" + case "${uc_pkg_name}" in + *PYTHON* ) + pkg_icon="PYTHON" + ;; + *USBNET* ) + pkg_icon="USBNET" + ;; + *BRIDGE* | *HOTFIX* ) + pkg_icon="BRIDGE" + ;; + *KUAL* ) + pkg_icon="KUAL" + ;; + *CRP* | *RP* ) + pkg_icon="TOOLS" + ;; + * ) + pkg_icon="WAIT" + ;; + esac + # And show it + print_icon "${pkg_icon}" + + # Check if it's valid... + print_bottom_centered "Checking ${PKG_NAME}" 4 + # Always re-compute the OTA number, in case something dared to mess with it... + compute_current_ota_version + # Save KindleTool's output (Tweak the IFS to make our life easier...) + BASE_IFS="${IFS}" + IFS='' + ktool_output="$(run_kindletool convert -i "${PKG_FILENAME}" 2>&1)" + mrpi_ret="$?" + # On the off chance that failed, abort + # NOTE: One sneaky possibility for this to fail would be an userstore mounted noexec, which might happen if the bridge is broken, as it was during the whole 5.10 debacle... + if [ ${mrpi_ret} -ne 0 ] ; then + IFS="${BASE_IFS}" + print_icon "FAIL" + print_bottom_centered "Failed to parse package, skipping" 1 + echo -e "\nFailed to parse package '${PKG_FILENAME}' (${PKG_NAME}) [return code: ${mrpi_ret}], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + echo -e "\nKindleTool output:\n${ktool_output}\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + # Check bundle type + PKG_BUNDLE_TYPE="$(echo "${ktool_output}" | sed -n -r 's/^(Bundle Type)([[:blank:]]*)(.*?)$/\3/p')" + case "${PKG_BUNDLE_TYPE}" in + "OTA V1" ) + PKG_PADDING_BYTE="$(echo "${ktool_output}" | sed -n -r 's/^(Padding Byte)([[:blank:]]*)([[:digit:]]*)( \()(.*?)(\))$/\5/p')" + PKG_DEVICE_CODE="$(echo "${ktool_output}" | sed -n -r '/^(Device)([[:blank:]]*)(.*?)$/p' | sed -n -r -e 's/^(Device)([[:blank:]]*)(.*?)(\()//' -e 's/((([[:xdigit:]GHJKLMNPQRSTUVWX]{3})( -> 0x)([[:xdigit:]]{3}))|((0x)([[:xdigit:]]{2})))(\))(.*?)$/\5\8/p')" + PKG_MIN_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Minimum OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + PKG_MAX_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Target OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + # Now that we're done with KindleTool's output, restore our original IFS value... + IFS="${BASE_IFS}" + + # Check padding byte + case "${PKG_PADDING_BYTE}" in + "0x13" | "0x00" ) + is_mr_package="true" + ;; + * ) + is_mr_package="false" + ;; + esac + # Reject non-MR packages + if [ "${is_mr_package}" = "false" ] ; then + print_icon "FAIL" + print_bottom_centered "Not an MR package, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not an MR package, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Check device code + if [ "${kmodel}" != "${PKG_DEVICE_CODE}" ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your device, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your device [${kmodel} vs. ${PKG_DEVICE_CODE}], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Version check... NOTE: Busybox (and Bash < 3) stores ints as int_32_t (i.e., signed 32bit), so we have to get creative to avoid overflows... >_<. We don't have access to bc, so rely on awk... + if [ "$(awk -v fw_build="${fw_build}" -v PKG_MIN_OTA="${PKG_MIN_OTA}" -v PKG_MAX_OTA="${PKG_MAX_OTA}" 'BEGIN { print (fw_build < PKG_MIN_OTA || fw_build >= PKG_MAX_OTA) }')" -ne 0 ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your FW version, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your FW version [!(${PKG_MIN_OTA} <= ${fw_build} < ${PKG_MAX_OTA})], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + ;; + "OTA V2" ) + PKG_CERT_NUM="$(echo "${ktool_output}" | sed -n -r 's/^(Cert number)([[:blank:]]*)(.*?)$/\3/p')" + PKG_DEVICES_CODES="$(echo "${ktool_output}" | sed -n -r '/^(Device)([[:blank:]]*)(.*?)$/p' | sed -n -r -e 's/^(Device)([[:blank:]]*)(.*?)(\()//' -e 's/((([[:xdigit:]GHJKLMNPQRSTUVWX]{3})( -> 0x)([[:xdigit:]]{3}))|((0x)([[:xdigit:]]{2})))(\))(.*?)$/\5\8/p')" + PKG_MIN_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Minimum OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + PKG_MAX_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Target OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + # Now that we're done with KindleTool's output, restore our original IFS value... + IFS="${BASE_IFS}" + + # Check signing cert to reject non-MR packages + if [ "${PKG_CERT_NUM}" -ne 0 ] ; then + print_icon "FAIL" + print_bottom_centered "Not an MR package, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not an MR package, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Check device codes + devcode_match="false" + for cur_devcode in ${PKG_DEVICES_CODES} ; do + if [ "${kmodel}" = "${cur_devcode}" ] ; then + devcode_match="true" + fi + done + if [ "${devcode_match}" = "false" ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your device, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your device [${kmodel} vs. $(echo "${PKG_DEVICES_CODES}" | tr -s '\\n' ' ')], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Version check... NOTE: Busybox (and Bash < 3) stores ints as int_32_t, so we have to get creative to avoid overflows... >_<. We don't have access to bc, so rely on awk... + if [ "$(awk -v fw_build="${fw_build}" -v PKG_MIN_OTA="${PKG_MIN_OTA}" -v PKG_MAX_OTA="${PKG_MAX_OTA}" 'BEGIN { print (fw_build < PKG_MIN_OTA || fw_build > PKG_MAX_OTA) }')" -ne 0 ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your FW version, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your FW version [!(${PKG_MIN_OTA} < ${fw_build} < ${PKG_MAX_OTA})] skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + ;; + * ) + IFS="${BASE_IFS}" + + print_icon "FAIL" + print_bottom_centered "Not an OTA package, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not an OTA package [${PKG_BUNDLE_TYPE}], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + ;; + esac + + # Start it up... + print_bottom_centered "* ${PKG_NAME} *" 4 + + # Clear workdir, and extract package in it + rm -rf "${MRPI_WORKDIR}" + if ! enough_free_space ; then + print_icon "FAIL" + print_bottom_centered "Not enough free space left, skipping" 1 + echo -e "\nNot enough space left to process ${PKG_NAME}, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + # NOTE: Using >> LOG 2>&1 will truncate the log before storing KindleTool's output, for some mysterious reason... + # But this works (as does using tee -a). + if ! run_kindletool extract "${PKG_FILENAME}" "${MRPI_WORKDIR}" 2>> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" ; then + # KindleTool handles the integrity checking for us, so let's check that this went fine... + print_icon "FAIL" + print_bottom_centered "Failed to extract package, skipping" 1 + echo -e "\nFailed to extract package '${PKG_FILENAME}' (${PKG_NAME}), skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + # We can then remove the package itself + rm -f "${PKG_FILENAME}" + + # Make the rootfs rw... + if ! make_rootfs_rw ; then + print_bottom_centered "Failed to remount rootfs RW, waiting . . ." 1 + echo -e "\nFailed to remount rootfs RW, waiting . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + # Try one final time... + if ! make_rootfs_rw ; then + print_icon "FAIL" + print_bottom_centered "Really failed to remount rootfs RW, skipping" 1 + echo -e "\nReally failed to remount rootfs RW, skipping ${PKG_NAME} . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + return 1 + fi + fi + + # Run the package scripts in alphabetical order, from inside our workdir... + cd "${MRPI_WORKDIR}" + RAN_SOMETHING="false" + FAILED_SOMETHING="false" + # NOTE: We only handle toplevel scripts + for pkg_script in *.sh *.ffs ; do + if [ -f "./${pkg_script}" ] ; then + RAN_SOMETHING="true" + print_bottom_centered "Running ${pkg_script} . . ." 3 + # Log what the script does... + echo -e "--\nRunning '${pkg_script}' for '${PKG_NAME}' (${PKG_FILENAME}) @ $(date -R)\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + # Abort at the first sign of trouble... + if check_is_touch_device ; then + /bin/sh -e "./${pkg_script}" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" 2>&1 + # Catch errors... + mrpi_ret="$?" + else + # NOTE: Unfortunately, on legacy devices, actually sourcing /etc/rc.d/functions will fail, so we can't use -e here... >_<" + /bin/sh "./${pkg_script}" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" 2>&1 + # On the off-chance it'd actually be useful then, keep catching errors... + mrpi_ret="$?" + fi + if [ ${mrpi_ret} -ne 0 ] ; then + FAILED_SOMETHING="true" + print_icon "FAIL" + print_bottom_centered "Package script failed (${mrpi_ret}), moving on . . . :(" 1 + echo -e "\nHu oh... Got return code ${mrpi_ret} . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + # Leave time to the user to read it... + sleep 10 + else + print_bottom_centered "Success. :)" 1 + echo -e "\nSuccess! :)\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + fi + fi + done + # Warn if no scripts were found + if [ "${RAN_SOMETHING}" = "false" ] ; then + print_icon "BOMB" + print_bottom_centered "No scripts were found, skipping" 1 + echo -e "\nNo scripts were found, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + else + # NOTE: While we show a FAIL icon ASAP when *any* script fails, + # we only show an OK checkmark at the end of *every* script, + # provided none of them failed. + if [ ${mrpi_ret} -eq 0 ] && [ "${FAILED_SOMETHING}" = "false" ] ; then + print_icon "OK" + fi + fi + # And get out of the staging directory once we're done. + cd "${MRPI_PKGDIR}" + + # Lock the rootfs down + if ! make_rootfs_ro ; then + print_bottom_centered "Failed to remount rootfs RO, waiting . . ." 2 + echo -e "\nFailed to remount rootfs RO, waiting . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 10 + # Try one final time... + if ! make_rootfs_ro ; then + print_icon "BOMB" + print_bottom_centered "Really failed to remount rootfs RO -_-" 2 + print_bottom_centered "A reboot would be recommended" 1 + echo -e "\nReally failed to remount rootfs RO -_-\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + fi + fi + + # Clean up behind us + rm -rf "${MRPI_WORKDIR}" + + return 0 +} + +## Go! +launch_installer() +{ + # Check for FBInk, and refresh binaries if need be... + if ! check_fbink ; then + print_bottom_centered "Couldn't setup binaries, aborting." 1 + return 1 + fi + + # Sleep a while to let KUAL die + print_bottom_centered "Hush, little baby . . ." 1 + sleep 5 + + # NOTE: Fugly FW 5.6.1 handling. Die *before* stopping the UI if we're not root, because we might not be able to bring it back up otherwise. + if [ "$(id -u)" -ne 0 ] ; then + print_bottom_centered "Unprivileged user, aborting." 1 + return 1 + fi + + # Let's do this! + print_icon "MRPI" + print_bottom_centered "Launching the MR installer . . ." 1 + + # Rotate the logs if need be + rotate_logs + + # Move to our package directory... + mkdir -p "${MRPI_PKGDIR}" + cd "${MRPI_PKGDIR}" + + # Loop over packages... + for pkg in *.bin ; do + # Check that we actually have some + if [ -f "${pkg}" ] ; then + # Try to build a list of packages, while honoring a modicum of dependency tree + case "${pkg}" in + *usbnet*install* ) + # Top priority + MR_PKGS_HEAD_LIST="${pkg} ${MR_PKGS_HEAD_LIST}" + ;; + *jailbreak*uninstall* ) + # Lowest priority + MR_PKGS_TAIL_LIST="${MR_PKGS_TAIL_LIST} ${pkg}" + ;; + *jailbreak*install* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + *mkk*install* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + *python*install* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + *_rp_*install* | *_rescue_pack* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + * ) + # Normal priority + MR_PKGS_LIST="${MR_PKGS_LIST} ${pkg}" + ;; + esac + else + # No packages were found, go away + print_bottom_centered "No MR packages found" 1 + return 1 + fi + done + + # Construct our final package list + MR_PKGS_LIST="${MR_PKGS_HEAD_LIST} ${MR_PKGS_LIST} ${MR_PKGS_TAIL_LIST}" + + # Try to setup our tmpfs + if ! mount_mrpi_tmpfs ; then + print_bottom_centered "Failed to create MRPI tmpfs, waiting . . ." 1 + sleep 5 + # Try one final time... + if ! mount_mrpi_tmpfs ; then + print_bottom_centered "Really failed to create MRPI tmpfs, aborting." 1 + sleep 5 + return 1 + fi + fi + mkdir -p "${MRPI_TMPDIR}" + + # Don't get killed! + trap "" TERM + + # Bring down most of the services + if check_is_touch_device ; then + # NOTE: We used to stop/start x (IIRC, to avoid a black screen), but, since FW 5.15.1, + # doing so apparently fails to restart it properly... + # (If need be, a milestone for 5.15.x would be the presence of + # /etc/upstart/filesystems_nh.conf or /etc/upstart/n_setup.conf or + # /etc/upstart/var_local_backup_and_restore). + # NOTE: This means X, awesome & blanket are *still* running (which matches the standard OTA behavior)... + stop lab126_gui + # Let's settle down a bit... + sleep 5 + # If AcXE is installed, stop it (it doesn't depend on the UI, and thus would still be up) + if [ -f "/etc/upstart/acxe.conf" ] ; then + # Check if it's up... + if [ "$(status acxe)" = "acxe start/running" ] ; then + if ! stop acxe ; then + # Shouldn't happen... + print_bottom_centered "Failed to stop AcXE -_-" 1 + sleep 2 + fi + fi + fi + else + # This is going to get ugly... Clear the screen + ${FBINK_BIN} -c + # If we're in USBNet mode, down it manually first, because we might need volumd to tear it down, which we won't have in single-user mode... + # See the comments in USBNetwork itself related to volumd for the details on why we can't really keep it up during an update (TL;DR: it breaks usbms exports)... + # NOTE: We need these shenanigans with custom services because we usually don't install the proper symlinks for the single-user runlevel... ;). + if [ -f "/etc/init.d/usbnet" ] ; then + # Do this unconditionally, the script is smart enough to figure out the rest ;). + /etc/init.d/usbnet stop + fi + # Switch to single-user + telinit 1 + # Reprint our message after the clear... + sleep 2 + print_icon "MRPI" + print_bottom_centered "Launching the MR installer . . ." 1 + # Wait for everything to go down... + sleep 20 + # Re-up syslog + /etc/init.d/syslog-ng start + + # And down most of the custom stuff... + # Start by listing everything that goes down when updating... + for service in /etc/rc3.d/K* ; do + UPDATE_RUNLEVEL_KILLS="${UPDATE_RUNLEVEL_KILLS} ${service##*/}" + done + # And everything that goes down in single-user mode... + for service in /etc/rc1.d/K* ; do + SINGLEU_RUNLEVEL_KILLS="${SINGLEU_RUNLEVEL_KILLS} ${service##*/}" + done + # Manually down anything that the updater runlevel downs, but not single-user... + for cur_service in ${UPDATE_RUNLEVEL_KILLS} ; do + is_custom="true" + for service in ${SINGLEU_RUNLEVEL_KILLS} ; do + if [ "${cur_service}" = "${service}" ] ; then + is_custom="false" + fi + done + # Is it *really* custom? + if [ "${is_custom}" = "true" ] ; then + # Don't store USBNet, we're handling it manually + if [ "$(echo "${cur_service}" | tail -c +4)" != "usbnet" ] ; then + # Store the list of custom services without their order prefix... + CUSTOM_SERVICES_LIST="${CUSTOM_SERVICES_LIST} $(echo "${cur_service}" | tail -c +4)" + fi + fi + done + + # And down them! + for service in ${CUSTOM_SERVICES_LIST} ; do + if [ -f "/etc/init.d/${service}" ] ; then + "/etc/init.d/${service}" stop + fi + done + + # let's wait a bit more... + sleep 7 + fi + + # Sync FS + sync + + # Blank the screen + ${FBINK_BIN} -c + + # Say hi (again) + print_icon "MRPI" + + # And install our packages in order, one by one... + for cur_pkg in ${MR_PKGS_LIST} ; do + if [ -f "${cur_pkg}" ] ; then + if ! run_package "${cur_pkg}" ; then + # Remove package in case of failure... + print_bottom_centered "Destroying package . . ." 1 + rm -f "${cur_pkg}" + # Don't leave a staging directory behind us, we might have failed without clearing it... + rm -rf "${MRPI_WORKDIR}" + # Try to avoid leaving a rw rootfs, we might have failed with it still rw... + if ! make_rootfs_ro ; then + print_bottom_centered "Failed to remount rootfs RO, waiting . . ." 2 + print_bottom_centered "" 1 + sleep 10 + # Try one final time... + if ! make_rootfs_ro ; then + print_bottom_centered "Really failed to remount rootfs RO -_-" 2 + print_bottom_centered "A reboot would be recommended" 1 + sleep 5 + fi + fi + sleep 2 + fi + else + # Should never happen... + print_bottom_centered "${cur_pkg} is not a file, skipping" 1 + fi + done + + # Sync FS + sync + + # Try to unmount our tmpfs + rm -rf "${MRPI_TMPDIR}" + if ! umount_mrpi_tmpfs ; then + print_bottom_centered "Failed to unmount MRPI tmpfs, waiting . . ." 1 + sleep 5 + # Try one final time... + if ! umount_mrpi_tmpfs ; then + print_bottom_centered "Really failed to unmount MRPI tmpfs -_-" 1 + sleep 5 + fi + fi + + # We're done! Sleep between print calls in order to avoid losing our last message... + print_icon "AMZ" + PRINT_SLEEP="true" + print_bottom_centered "" 4 + print_bottom_centered "" 3 + print_bottom_centered "" 2 + print_bottom_centered "Done, restarting UI . . ." 1 + print_bottom_centered "" 0 + sleep 2 + + # Bring the UI back up! + if check_is_touch_device ; then + # If we still have AcXE installed, restart it + if [ -f "/etc/upstart/acxe.conf" ] ; then + # Check if it's down... + if [ "$(status acxe)" = "acxe stop/waiting" ] ; then + if ! start acxe ; then + # Shouldn't happen... + print_bottom_centered "Failed to start AcXE -_-" 1 + sleep 2 + else + sleep 1 + fi + fi + fi + start lab126_gui + else + # Thankfully enough, we don't have to jump through any hoops this time ;). + telinit 5 + fi +} + +# Main +case "${1}" in + "launch_installer" ) + ${1} + ;; + * ) + print_bottom_centered "invalid action (${1})" 1 + ;; +esac + +return 0 diff --git a/dash/staging/mrpi/extensions/MRInstaller/config.xml b/dash/staging/mrpi/extensions/MRInstaller/config.xml new file mode 100755 index 0000000..373317c --- /dev/null +++ b/dash/staging/mrpi/extensions/MRInstaller/config.xml @@ -0,0 +1,12 @@ + + + + MR Installer + 1.6 + NiLuJe + MRInstaller + + + menu.json + + diff --git a/dash/staging/mrpi/extensions/MRInstaller/data/BigBlue_Terminal.ttf b/dash/staging/mrpi/extensions/MRInstaller/data/BigBlue_Terminal.ttf new file mode 100644 index 0000000..179ce23 Binary files /dev/null and b/dash/staging/mrpi/extensions/MRInstaller/data/BigBlue_Terminal.ttf differ diff --git a/dash/staging/mrpi/extensions/MRInstaller/data/icons.py b/dash/staging/mrpi/extensions/MRInstaller/data/icons.py new file mode 100755 index 0000000..6f888c0 --- /dev/null +++ b/dash/staging/mrpi/extensions/MRInstaller/data/icons.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python2 +""" +Barebones example of FBInk usage through Python's cFFI module +""" + +# To get a Py3k-like print function +from __future__ import print_function + +import sys +# Load the wrapper module, it's linked against FBInk, so the dynamic loader will take care of pulling in the actual FBInk library +from _fbink import ffi, lib as FBInk + +# Let's check which FBInk version we're using... +# NOTE: ffi.string() returns a bytes on Python 3, not a str, hence the extra decode +print("Loaded FBInk {}".format(ffi.string(FBInk.fbink_version()).decode("ascii"))) + +# And now we're good to go! Let's print "Hello World" in the center of the screen... +# Setup the config... +fbink_cfg = ffi.new("FBInkConfig *") +fbink_cfg.is_centered = False +fbink_cfg.is_halfway = True +fbink_cfg.is_cleared = True +fbink_cfg.is_verbose = True + +fbink_ot_cfg = ffi.new("FBInkOTConfig *") +fbink_ot_cfg.size_pt = 24 + +""" +# Open the FB... +fbfd = FBInk.fbink_open() +if fbfd == -1: + raise SystemExit("Failed to open the framebuffer, aborting . . .") + +# Initialize FBInk... +if FBInk.fbink_init(fbfd, fbink_cfg) < 0: + raise SystemExit("Failed to initialize FBInk, aborting . . .") + +# Do stuff! +if FBInk.fbink_print(fbfd, b"Hello World", fbink_cfg) < 0: + print("Failed to print that string!", file=sys.stderr) + +# And now we can wind things down... +if FBInk.fbink_close(fbfd) < 0: + raise SystemExit("Failed to close the framebuffer, aborting . . .") +""" + +# Or, the same but in a slightly more Pythonic approach ;). +fbfd = FBInk.fbink_open() +try: + FBInk.fbink_init(fbfd, fbink_cfg) + FBInk.fbink_add_ot_font("BigBlue_Terminal.ttf", FBInk.FNT_REGULAR) + string = u"Success \uf632 or \ufadf or \ufae0 or \ufc8f or \uf633 or \uf4a1" // \uf633 + string += u"\n" + string += u"Error \uf071 or \uf525 or \uf529 or \uf421 or \ufb8f" // \uf071 + string += u"\n" + string += u"Wait \uf252 or \ufa1e or \uf49b" // \uf252 + string += u"\n" + string += u"Python \ue73c or \ue235 or \uf81f or \ue606" // \ue73c + string += u"\n" + string += u"USBNet \ue795 or \uf68c or \ufcb5 or \uf489" // \uf68c + string += u"\n" + string += u"Bridge \uf270 or \uf52c or \ue286 or \uf5a6 or \ue214" // \ue286 (AMZ: \uf270) + string += u"\n" + string += u":( \uf119 or \uf6f7" // \uf119 + string += u"\n" + string += u"Linux \uf17c or \uf31a or \uf83c" // \uf17c + string += u"\n" + string += u":) \uf118 or \uf6f4" // \uf118 + string += u"\n" + string += u"Tools \ue20f or \ufbf6 or \uf992 or \ufab6 or \uf425" // \uf425 + string += u"\n" + string += u"MRPI \ufcdd or \ufcde or \uf8d5 or \uf962 or \ufac3 or \uf487 or \uf427" // \uf8d5 (KUAL: \uf962) + string = string.encode("utf-8") + # NOTE: On Python 3, cFFI maps char to bytes, not str + FBInk.fbink_print_ot(fbfd, string, fbink_ot_cfg, fbink_cfg, ffi.NULL) +finally: + FBInk.fbink_free_ot_fonts() + FBInk.fbink_close(fbfd) diff --git a/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-K3.tar.gz b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-K3.tar.gz new file mode 100644 index 0000000..a8b280b Binary files /dev/null and b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-K3.tar.gz differ diff --git a/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-K5.tar.gz b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-K5.tar.gz new file mode 100644 index 0000000..1e415ab Binary files /dev/null and b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-K5.tar.gz differ diff --git a/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-KHF.tar.gz b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-KHF.tar.gz new file mode 100644 index 0000000..5901599 Binary files /dev/null and b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-KHF.tar.gz differ diff --git a/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-PW2.tar.gz b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-PW2.tar.gz new file mode 100644 index 0000000..c71cd74 Binary files /dev/null and b/dash/staging/mrpi/extensions/MRInstaller/data/mrpi-PW2.tar.gz differ diff --git a/dash/staging/mrpi/extensions/MRInstaller/menu.json b/dash/staging/mrpi/extensions/MRInstaller/menu.json new file mode 100755 index 0000000..55a3403 --- /dev/null +++ b/dash/staging/mrpi/extensions/MRInstaller/menu.json @@ -0,0 +1,24 @@ +{ + "comment001": "MR Package Installer", + "comment002": "", + "comment003": "$Id: menu.json 11305 2014-12-23 14:56:54Z NiLuJe $", + "comment004": "", + "items": [ + { + "name": "Helper", + "priority": -998, + "items": [ + { + "name": "Install MR Packages", + "action": "./bin/mrinstaller.sh", + "params": "launch_installer", + "priority": -998, + "exitmenu": true, + "refresh": false, + "status": false, + "internal": "status Launching the MR Installer" + } + ] + } + ] +} diff --git a/dash/staging/post-jailbreak-root/dashboard/dash.sh b/dash/staging/post-jailbreak-root/dashboard/dash.sh new file mode 100755 index 0000000..a1eaa09 --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/dash.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +DASH_PNG="$DIR/dash.png" +FETCH_DASHBOARD_CMD="$DIR/local/fetch-dashboard.sh" +LOW_BATTERY_CMD="$DIR/local/low-battery.sh" + +REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"2,32 8-17 * * MON-FRI"} +FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} +SLEEP_SCREEN_INTERVAL=${SLEEP_SCREEN_INTERVAL:-3600} +RTC=/sys/devices/platform/mxc_rtc.0/wakeup_enable + +LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +LOW_BATTERY_THRESHOLD_PERCENT=${LOW_BATTERY_THRESHOLD_PERCENT:-10} + +num_refresh=0 + +init() { + if [ -z "$TIMEZONE" ] || [ -z "$REFRESH_SCHEDULE" ]; then + echo "Missing required configuration." + echo "Timezone: ${TIMEZONE:-(not set)}." + echo "Schedule: ${REFRESH_SCHEDULE:-(not set)}." + exit 1 + fi + + echo "Starting dashboard with $REFRESH_SCHEDULE refresh..." + + /etc/init.d/framework stop + initctl stop webreader >/dev/null 2>&1 + echo powersave >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + lipc-set-prop com.lab126.powerd preventScreenSaver 1 +} + +prepare_sleep() { + echo "Preparing sleep" + + /usr/sbin/eips -f -g "$DIR/sleeping.png" + + # Give screen time to refresh + sleep 2 + + # Ensure a full screen refresh is triggered after wake from sleep + num_refresh=$FULL_DISPLAY_REFRESH_RATE +} + +refresh_dashboard() { + echo "Refreshing dashboard" + "$DIR/wait-for-wifi.sh" "$WIFI_TEST_IP" + + "$FETCH_DASHBOARD_CMD" "$DASH_PNG" + fetch_status=$? + + if [ "$fetch_status" -ne 0 ]; then + echo "Not updating screen, fetch-dashboard returned $fetch_status" + return 1 + fi + + if [ "$num_refresh" -eq "$FULL_DISPLAY_REFRESH_RATE" ]; then + num_refresh=0 + + # trigger a full refresh once in every 4 refreshes, to keep the screen clean + echo "Full screen refresh" + /usr/sbin/eips -f -g "$DASH_PNG" + else + echo "Partial screen refresh" + /usr/sbin/eips -g "$DASH_PNG" + fi + + num_refresh=$((num_refresh + 1)) +} + +log_battery_stats() { + battery_level=$(gasgauge-info -c) + echo "$(date) Battery level: $battery_level." + + if [ "$LOW_BATTERY_REPORTING" = true ]; then + battery_level_numeric=${battery_level%?} + if [ "$battery_level_numeric" -le "$LOW_BATTERY_THRESHOLD_PERCENT" ]; then + "$LOW_BATTERY_CMD" "$battery_level_numeric" + fi + fi +} + +rtc_sleep() { + duration=$1 + + if [ "$DEBUG" = true ]; then + sleep "$duration" + else + # shellcheck disable=SC2039 + [ "$(cat "$RTC")" -eq 0 ] && echo -n "$duration" >"$RTC" + echo "mem" >/sys/power/state + fi +} + +main_loop() { + while true; do + log_battery_stats + + next_wakeup_secs=$("$DIR/next-wakeup" --schedule="$REFRESH_SCHEDULE" --timezone="$TIMEZONE") + + if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ]; then + action="sleep" + prepare_sleep + else + action="suspend" + refresh_dashboard + fi + + # take a bit of time before going to sleep, so this process can be aborted + sleep 10 + + echo "Going to $action, next wakeup in ${next_wakeup_secs}s" + + rtc_sleep "$next_wakeup_secs" + done +} + +init +main_loop diff --git a/dash/staging/post-jailbreak-root/dashboard/local/env.sh b/dash/staging/post-jailbreak-root/dashboard/local/env.sh new file mode 100644 index 0000000..2e74d96 --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/local/env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Export environment variables here +export WIFI_TEST_IP=${WIFI_TEST_IP:-1.1.1.1} +# 测试配置:全天每分钟刷新一次,便于验证图片拉取与屏幕刷新是否正常。 +export REFRESH_SCHEDULE=${REFRESH_SCHEDULE:-"* * * * *"} +export TIMEZONE=${TIMEZONE:-"Asia/Shanghai"} + +# By default, partial screen updates are used to update the screen, +# to prevent the screen from flashing. After a few partial updates, +# the screen will start to look a bit distorted (due to e-ink ghosting). +# 测试阶段强制每次都做一次全刷,避免首页残影和局部刷新的旧内容干扰验证。 +# 等图片尺寸与刷新逻辑确认无误后,再改回 4 之类的值以节省功耗。 +export FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0} + +# When the time until the next wakeup is greater or equal to this number, +# the dashboard will not be refreshed anymore, but instead show a +# 'kindle is sleeping' screen. This can be useful if your schedule only runs +# during the day, for example. +export SLEEP_SCREEN_INTERVAL=3600 + +export LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false} +export LOW_BATTERY_THRESHOLD_PERCENT=10 diff --git a/dash/staging/post-jailbreak-root/dashboard/local/fetch-dashboard.sh b/dash/staging/post-jailbreak-root/dashboard/local/fetch-dashboard.sh new file mode 100755 index 0000000..0733233 --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/local/fetch-dashboard.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +# Fetch a new dashboard image, make sure to output it to "$1". +# For example: +"$(dirname "$0")/../xh" -d -q -o "$1" get https://raw.githubusercontent.com/pascalw/kindle-dash/master/example/example.png diff --git a/dash/staging/post-jailbreak-root/dashboard/local/low-battery.sh b/dash/staging/post-jailbreak-root/dashboard/local/low-battery.sh new file mode 100644 index 0000000..dc70ced --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/local/low-battery.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +battery_level_percentage=$1 +last_battery_report_state="$(dirname "$0")/state/last_battery_report" + +previous_report_timestamp=$(cat "$last_battery_report_state" 2>/dev/null || echo '-1') +now=$(date +%s) + +# Implement desired logic here. The example below for example only reports low +# battery every 24 hours. + +if [ "$previous_report_timestamp" -eq -1 ] || + [ $((now - previous_report_timestamp)) -gt 86400 ]; then + # Replace this with for example an HTTP call via curl, or xh + echo "Reporting low battery: $battery_level_percentage%" + + echo "$now" >"$last_battery_report_state" +fi diff --git a/dash/staging/post-jailbreak-root/dashboard/next-wakeup b/dash/staging/post-jailbreak-root/dashboard/next-wakeup new file mode 100755 index 0000000..382d28d Binary files /dev/null and b/dash/staging/post-jailbreak-root/dashboard/next-wakeup differ diff --git a/dash/staging/post-jailbreak-root/dashboard/sleeping.png b/dash/staging/post-jailbreak-root/dashboard/sleeping.png new file mode 100644 index 0000000..5da94ac Binary files /dev/null and b/dash/staging/post-jailbreak-root/dashboard/sleeping.png differ diff --git a/dash/staging/post-jailbreak-root/dashboard/start.sh b/dash/staging/post-jailbreak-root/dashboard/start.sh new file mode 100755 index 0000000..f4f38c1 --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/start.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +DEBUG=${DEBUG:-false} +[ "$DEBUG" = true ] && set -x + +DIR="$(dirname "$0")" +ENV_FILE="$DIR/local/env.sh" +LOG_FILE="$DIR/logs/dash.log" + +mkdir -p "$(dirname "$LOG_FILE")" + +# shellcheck disable=SC1090 +[ -f "$ENV_FILE" ] && . "$ENV_FILE" + +if [ "$DEBUG" = true ]; then + "$DIR/dash.sh" +else + "$DIR/dash.sh" >>"$LOG_FILE" 2>&1 & +fi diff --git a/dash/staging/post-jailbreak-root/dashboard/stop.sh b/dash/staging/post-jailbreak-root/dashboard/stop.sh new file mode 100755 index 0000000..ecc884e --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/stop.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +pkill -f dash.sh diff --git a/dash/staging/post-jailbreak-root/dashboard/wait-for-wifi.sh b/dash/staging/post-jailbreak-root/dashboard/wait-for-wifi.sh new file mode 100755 index 0000000..7785fc6 --- /dev/null +++ b/dash/staging/post-jailbreak-root/dashboard/wait-for-wifi.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +test_ip=$1 + +if [ -z "$test_ip" ]; then + echo "No test ip specified" + exit 1 +fi + +wait_for_wifi() { + max_retry=30 + counter=0 + + ping -c 1 "$test_ip" >/dev/null 2>&1 + + # shellcheck disable=SC2181 + while [ $? -ne 0 ]; do + [ $counter -eq $max_retry ] && echo "Couldn't connect to Wi-Fi" && exit 1 + counter=$((counter + 1)) + + sleep 1 + ping -c 1 "$test_ip" >/dev/null 2>&1 + done +} + +wait_for_wifi +echo "Wi-Fi connected" diff --git a/dash/staging/post-jailbreak-root/dashboard/xh b/dash/staging/post-jailbreak-root/dashboard/xh new file mode 100755 index 0000000..b52ba80 Binary files /dev/null and b/dash/staging/post-jailbreak-root/dashboard/xh differ diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/CREDITS b/dash/staging/post-jailbreak-root/extensions/MRInstaller/CREDITS new file mode 100644 index 0000000..5507d8a --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/MRInstaller/CREDITS @@ -0,0 +1,27 @@ + kindletool: KindleTool, Copyright (C) 2011-2015 Yifan Lu, licensed under the GNU General Public License version 3+ (http://www.gnu.org/licenses/gpl.html). +(https://github.com/NiLuJe/KindleTool/) + + | + |-> libarchive, Copyright (C) Tim Kientzle, licensed under the New BSD License (http://www.opensource.org/licenses/bsd-license.php) + | (http://libarchive.github.com/) + | + |-> GMP, GNU MP Library, Copyright 1991-2013 Free Software Foundation, Inc., + | licensed under the GNU Lesser General Public License version 3+ (http://www.gnu.org/licenses/lgpl.html). + | (http://gmplib.org/) + | + `-> nettle, Copyright (C) 2001-2013 Niels Möller, + licensed under the GNU Lesser General Public License version 2.1+ (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html). + (http://www.lysator.liu.se/~nisse/nettle) + + libz: zlib, Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler, + Licensed under the zlib license (http://zlib.net/zlib_license.html) + (http://zlib.net/) + + fbink: FBInk (FrameBuffer eInker), Copyright (C) 2018-2019 NiLuJe , + Released under the GNU General Public License version 3+ (https://www.gnu.org/licenses/gpl.html) + (https://github.com/NiLuJe/FBInk) + + BigBlue_Terminal.ttf: BigBlue Terminal +, Copyright (C) VileR, , + Licensed under a Creative Commons Attribution-ShareAlike 4.0 International License (http://creativecommons.org/licenses/by-sa/4.0/) + (https://int10h.org/blog/2015/12/bigblue-terminal-oldschool-fixed-width-font/). + Patched with extra glyphs via https://github.com/ryanoasis/nerd-fonts/, see the WiKi for individual licenses. diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/bin/mrinstaller.sh b/dash/staging/post-jailbreak-root/extensions/MRInstaller/bin/mrinstaller.sh new file mode 100755 index 0000000..ad0da9d --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/MRInstaller/bin/mrinstaller.sh @@ -0,0 +1,1183 @@ +#!/bin/sh +## +# +# MR Package Installer +# +# $Id: mrinstaller.sh 19303 2023-11-06 17:06:17Z NiLuJe $ +# +# shellcheck disable=SC1090,SC3037,SC3043,SC2164 +# +## + +# Remember our current revision for logging purposes... +MRPI_REV="$( echo '$Revision: 19303 $' | cut -d ' ' -f 2 )" + +## Logging +# Pull some helper functions for logging +_FUNCTIONS=/etc/upstart/functions +if [ -f "${_FUNCTIONS}" ] ; then + . "${_FUNCTIONS}" +else + # legacy... + _FUNCTIONS=/etc/rc.d/functions + [ -f "${_FUNCTIONS}" ] && . "${_FUNCTIONS}" +fi + +# Are we on FW 5.x? +IS_K5="true" +if [ -f "/etc/rc.d/functions" ] && grep -q "EIPS" "/etc/rc.d/functions" ; then + IS_K5="false" +fi +# We'll also need to know our device ID... +kmodel="??" + +## Check if we're a K5 +check_is_touch_device() +{ + [ "${IS_K5}" = "true" ] && return 0 + + # We're not! + return 1 +} + +# Logging... +logmsg() +{ + if check_is_touch_device ; then + # Adapt the K5 logging calls to the simpler legacy syntax + f_log "${1}" "mr_installer" "${2}" "${3}" "${4}" + else + # Slightly tweaked version of msg() (from ${_FUNCTIONS}, where the constants are defined) + local _NVPAIRS + local _FREETEXT + local _MSG_SLLVL + local _MSG_SLNUM + + _MSG_LEVEL="${1}" + _MSG_COMP="${2}" + + { [ $# -ge 4 ] && _NVPAIRS="${3}" && shift ; } + + _FREETEXT="${3}" + + eval _MSG_SLLVL=\${MSG_SLLVL_$_MSG_LEVEL} + eval _MSG_SLNUM=\${MSG_SLNUM_$_MSG_LEVEL} + + local _CURLVL + + { [ -f "${MSG_CUR_LVL}" ] && _CURLVL=$(cat "${MSG_CUR_LVL}") ; } || _CURLVL=1 + + if [ "${_MSG_SLNUM}" -ge "${_CURLVL}" ] ; then + /usr/bin/logger -p local4."${_MSG_SLLVL}" -t "mr_installer" "${_MSG_LEVEL} def:${_MSG_COMP}:${_NVPAIRS}:${_FREETEXT}" + fi + + [ "${_MSG_LEVEL}" != "D" ] && echo "mr_installer: ${_MSG_LEVEL} def:${_MSG_COMP}:${_NVPAIRS}:${_FREETEXT}" + fi +} + +# From libotautils[5], adapted from libkh[5] +do_fbink_print() +{ + # We need at least two args + if [ $# -lt 2 ] ; then + logmsg "W" "do_fbink_print" "" "not enough arguments passed to do_fbink_print ($# while we need at least 2)" + return 1 + fi + + kh_string="${1}" + kh_y_shift_up="${2}" + + # Unlike eips, we need at least a single space to even try to print something ;). + if [ "${kh_string}" = "" ] ; then + kh_string=" " + fi + + # Check if we asked for a highlighted message... + if [ "${3}" = "h" ] ; then + fbink_extra_args="h" + else + fbink_extra_args="" + fi + + # NOTE: FBInk will handle the padding. FBInk's default font is square, not tall like eips, + # so we compensate by tweaking the baseline ;). + ${FBINK_BIN} -qpm${fbink_extra_args} -y $(( -4 - kh_y_shift_up )) "${kh_string}" +} + +print_bottom_centered() +{ + # We need at least two args + if [ $# -lt 2 ] ; then + logmsg "W" "print_bottom_centered" "" "not enough arguments passed to print_bottom_centered ($# while we need at least 2)" + return 1 + fi + + kh_string="${1}" + kh_y_shift_up="${2}" + + # Log it, too + logmsg "I" "print_bottom_centered" "" "${kh_string}" + + # Sleep a tiny bit to workaround the logic in the 'new' (K4+) eInk controllers that tries to bundle updates + if [ "${PRINT_SLEEP}" = "true" ] ; then + usleep 150000 # 150ms + fi + + do_fbink_print "${kh_string}" "${kh_y_shift_up}" +} + +## Check if arg is an int +is_integer() +{ + # Cheap trick ;) + [ "${1}" -eq "${1}" ] 2>/dev/null + return $? +} + +## Compute our current OTA version (NOTE: Pilfered from Helper's device_id.sh ;)) +fw_build="0" +compute_current_ota_version() +{ + fw_build_maj="$(awk '/Version:/ { print $NF }' /etc/version.txt | awk -F- '{ print $NF }')" + # NOTE: Apparently, awk is deficient (?!) on some FW versions... (I cannot reproduce this in a sandbox, FWIW) + # FIXME: This appears to be related to something in the env (an LD_PRELOAD?), as it behaves when run in a clean env... + if [ -z "${fw_build_maj}" ] ; then + fw_build_maj="$(sed -ne '/Version:/p' /etc/version.txt | sed 's/^.*-//')" + fi + fw_build_min="$(awk '/Version:/ { print $NF }' /etc/version.txt | awk -F- '{ print $1 }')" + if [ -z "${fw_build_min}" ] ; then + fw_build_min="$(sed -ne '/Version:/p' /etc/version.txt | sed -e 's/^.*Version: //' | cut -f1 -d '-')" + fi + # Legacy major versions used to have a leading zero, which is stripped from the complete build number. Except on really ancient builds, that (or an extra) 0 is always used as a separator between maj and min... + fw_build_maj_pp="${fw_build_maj#0}" + # That only leaves some weird diags build that handle this stuff in potentially even weirder ways to take care of... + if [ "${fw_build_maj}" -eq "${fw_build_min}" ] ; then + # Weird diags builds... (5.0.0) + fw_build="${fw_build_maj_pp}0???" + else + # Most common instance... maj#6 + 0 + min#3 or maj#5 + 0 + min#3 (potentially with a leading 0 stripped from maj#5) + if [ ${#fw_build_min} -eq 3 ] ; then + fw_build="${fw_build_maj_pp}0${fw_build_min}" + else + # Truly ancient builds... For instance, 2.5.6, which is maj#5 + min#4 (with a leading 0 stripped from maj#5) + fw_build="${fw_build_maj_pp}${fw_build_min}" + fi + fi +} + +# Our packages live in a specific directory +MRPI_PKGDIR="/mnt/us/mrpackages" +# We're using our own tmpfs +MRPI_TMPFS="/var/tmp/mrpi" +# We're working in a staging directory, in our tmpfs +MRPI_WORKDIR="${MRPI_TMPFS}/staging" +# We're making KindleTool use our own tmpfs as a temp directory +MRPI_TMPDIR="${MRPI_TMPFS}/tmpdir" + +## Call kindletool with the right environment setup +MRINSTALLER_BINDIR="$(dirname "$(realpath "${0}")")" +MRINSTALLER_BASEDIR="${MRINSTALLER_BINDIR%*/bin}" +run_kindletool() +{ + # Check that our binary actually is available... + if [ ! -x "${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/kindletool" ] ; then + print_bottom_centered "No KindleTool binary, aborting" 1 + echo -e "\nCould not find a proper KindleTool binary for the current arch (${BINARIES_TC}), aborting . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Pick up our own libz build... + env KT_WITH_UNKNOWN_DEVCODES="true" TMPDIR="${MRPI_TMPDIR}" LD_LIBRARY_PATH="${MRINSTALLER_BASEDIR}/lib/${BINARIES_TC}" "${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/kindletool" "$@" +} + +# Default to something that won't horribly blow up... +FBINK_BIN="true" +ICON_SIZE="450" +check_fbink() +{ + # Pick the right binary for our device... + # FIXME: That'll be easier for >= 5.16.3, just need to check for /lib/ld-linux-armhf.so.3 + # That'll of course require a new TC: armhf, glibc 2.20, kernel 4.1.15 (i.e., PW4+), tuned for whichever big cortex is on the MTK SoCs (A15?) + # And new packages, with a breakpoint to avoid footguns at OTA number 4110100057 (lowest 5.16.3 release) + MACHINE_ARCH="$(uname -m)" + if [ "${MACHINE_ARCH}" = "armv7l" ] ; then + if [ -e "/lib/ld-linux-armhf.so.3" ] ; then + # Very cheap hard float detection + BINARIES_TC="KHF" + elif grep -e '^Hardware' /proc/cpuinfo | grep -q -e 'i\.MX[[:space:]]\?[6-7]' ; then + # NOTE: Slightly crappy Wario/Rex & Zelda detection ;p + BINARIES_TC="PW2" + elif grep -e '^Hardware' /proc/cpuinfo | grep -q -e 'MT8110' ; then + # Similarly cheap Bellatrix detection + BINARIES_TC="PW2" + else + BINARIES_TC="K5" + fi + else + BINARIES_TC="K3" + fi + + # Check if we have a tarball of binaries to install... + if [ -f "${MRINSTALLER_BASEDIR}/data/mrpi-${BINARIES_TC}.tar.gz" ] ; then + # Clear existing binaries... + for tc_set in K3 K5 PW2 KHF ; do + for bin_set in lib bin ; do + for file in "${MRINSTALLER_BASEDIR}"/"${bin_set}"/"${tc_set}"/* ; do + [ -f "${file}" ] && rm -f "${file}" + done + done + done + tar -xvzf "${MRINSTALLER_BASEDIR}/data/mrpi-${BINARIES_TC}.tar.gz" -C "${MRINSTALLER_BASEDIR}" + # Clear data folder now + for file in "${MRINSTALLER_BASEDIR}"/data/mrpi-*.tar.gz ; do + [ -f "${file}" ] && rm -f "${file}" + done + fi + + # Check that our binary actually is available... + if [ ! -x "${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/fbink" ] ; then + print_bottom_centered "No FBInk binary, aborting" 1 + echo -e "\nCould not find a proper FBInk binary for the current arch (${BINARIES_TC}), aborting . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # We're good, set it up... + FBINK_BIN="${MRINSTALLER_BASEDIR}/bin/${BINARIES_TC}/fbink" + + # And use it to pickup our device ID, and a few things we'll need to compute the ideal icon size... + eval "$(${FBINK_BIN} -e | tr ';' '\n' | grep -e viewHeight -e FONTH -e deviceId | tr '\n' ';')" + # And convert the deviceID to hex, as that's what KindleTool reports... + # FIXME: Switch to numeric comparisons when switching to KindleTool's metadata dump format! + if [ "${deviceId}" -gt 255 ] ; then + kmodel="$(printf "%03X" "${deviceId}")" + else + kmodel="$(printf "%02X" "${deviceId}")" + fi + + # Compute the ideal icon size, knowing that we'll print it in the center of the screen, + # and we need 8 rows of text on the bottom, plus two of padding... + ICON_SIZE="$(( viewHeight - (10 * FONTH * 2) ))" + # Double check that it's sane + is_integer "${ICON_SIZE}" || ICON_SIZE="450" + + return 0 +} + +## Display an icon in the middle of the screen +print_icon() +{ + # We need at least one arg + if [ $# -lt 1 ] ; then + logmsg "W" "print_icon" "" "not enough arguments passed to print_icon ($# while we need at least 1)" + return 1 + fi + + kh_icon="${1}" + + # Log it, too + logmsg "I" "print_icon" "" "${kh_icon}" + + # Which one did we want? + case "${kh_icon}" in + "OK" ) + kh_icon_cp="" # \uf633 + ;; + "FAIL" ) + kh_icon_cp="" # \uf071 + ;; + "BOMB" ) + kh_icon_cp="ﮏ" # \ufb8f + ;; + "WAIT" ) + kh_icon_cp="" # \uf252 + ;; + "PYTHON" ) + kh_icon_cp="" # \ue73c + ;; + "USBNET" ) + kh_icon_cp="" # \uf68c + ;; + "BRIDGE" ) + kh_icon_cp="" # \ue286 + ;; + "AMZ" ) + kh_icon_cp="" # \uf270 + ;; + "SAD" ) + kh_icon_cp="" # \uf119 + ;; + "LINUX" ) + kh_icon_cp="" # \uf17c + ;; + "HAPPY" ) + kh_icon_cp="" # \uf118 + ;; + "TOOLS" ) + kh_icon_cp="" # \uf425 + ;; + "KUAL" ) + kh_icon_cp="異" # \uf962 + ;; + "MRPI" ) + kh_icon_cp="" # \uf487 + ;; + * ) + logmsg "W" "print_icon" "" "requested an unknown icon (${kh_icon})" + return 1 + ;; + esac + + # This is why we needed a custom build: for TTF support ;). + # Patched font from https://nerdfonts.com/ + ${FBINK_BIN} -qMm -t regular="${MRINSTALLER_BASEDIR}/data/BigBlue_Terminal.ttf",px="${ICON_SIZE}" "${kh_icon_cp}" +} + +## Rotate the log +rotate_logs() +{ + # If it's over 1MB, compress & timestamp + if [ -f "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" ] ; then + log_size="$(stat -c %s "${MRINSTALLER_BASEDIR}/log/mrinstaller.log")" + + if [ "${log_size}" -gt $((1 * 1024 * 1024)) ] ; then + gzip "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + mv "${MRINSTALLER_BASEDIR}/log/mrinstaller.log.gz" "${MRINSTALLER_BASEDIR}/log/mrinstaller-$(date +%Y-%m-%d_%H-%M).log.gz" + fi + fi +} + +## Check that we have enough free space +enough_free_space() +{ + if [ "$(df -k /mnt/us | tail -n 1 | awk '{ print $4; }')" -lt "$((150 * 1024))" ] ; then + # Less than 150MB left, meep! + return 1 + else + # Good enough! + return 0 + fi +} + +## Check if our own tmpfs is mounted +is_mrpi_tmpfs_up() +{ + if grep -q "^tmpfs ${MRPI_TMPFS} tmpfs " /proc/mounts ; then + # Peachy :) + return 0 + fi + + # Huh, it's already up? + return 1 +} + +## Compute an estimate of the amount of available memory to resize our tmpfs +resize_mrpi_tmpfs() +{ + logmsg "I" "resize_mrpi_tmpfs" "" "checking available memory" + + # We'll resort to a few different methods, because depending on the age of the Linux kernel, + # we won't always have access to the easiest of them, which is relying on MemAvailable in /proc/meminfo... + # c.f., https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 + if grep -q 'MemAvailable' /proc/meminfo ; then + # We'll settle for 85% of available memory to leave a bit of breathing room + tmpfs_size="$(awk '/MemAvailable/ {printf "%d", $2 * 0.85}' /proc/meminfo)" + elif grep -q 'Inactive(file)' /proc/meminfo ; then + # Basically try to emulate the kernel's computation, c.f., https://unix.stackexchange.com/q/261247 + # Again, 85% of available memory + tmpfs_size="$(awk -v low="$(grep low /proc/zoneinfo | awk '{k+=$2}END{printf "%d", k}')" \ + '{a[$1]=$2} + END{ + printf "%d", (a["MemFree:"]+a["Active(file):"]+a["Inactive(file):"]+a["SReclaimable:"]-(12*low))*0.85; + }' /proc/meminfo)" + else + # Ye olde crap workaround of Free + Buffers + Cache... + # Take it with a grain of salt, and settle for 80% of that... + tmpfs_size="$(awk \ + '{a[$1]=$2} + END{ + printf "%d", (a["MemFree:"]+a["Buffers:"]+a["Cached:"])*0.80; + }' /proc/meminfo)" + fi + + # Make sure we end up with a sane-ish fallback in case all this failed... + # NOTE: Yeah, it definitely breaks on FW versions where awk is broken... >_<" + is_integer "${tmpfs_size}" || tmpfs_size="81920" + + # Log those computations + logmsg "I" "resize_mrpi_tmpfs" "" "can spare $((tmpfs_size / 1024))MB for mrpi tmpfs" + + # Check that we actually end up with enough free memory to be able to use it... + # NOTE: The most space-hungry package I have is the legacy variant of USBNet, which currently requires about 46MB. + # That said, we should be pretty safe: so far, I've always had *more* available RAM than the ceiling value, + # even on the K2, where the ceiling is at ~77MB, and I usually have around ~89MB of available memory to spare! + if [ "${tmpfs_size}" -lt "$(( 56 * 1024 ))" ] ; then + # If we can spare less than 56MB, abort! + logmsg "E" "resize_mrpi_tmpfs" "" "not enough available memory (< 56MB) for mrpi tmpfs!" + return 1 + fi + + # Compare that with our ceiling, 62.5% of the total memory + tmpfs_ceil="$(awk '/MemTotal/ {printf "%d", $2 * 0.625}' /proc/meminfo)" + # With a fallback... + is_integer "${tmpfs_ceil}" || tmpfs_ceil="81920" + + # If our computed size is smaller than the ceiling value, resize the tmpfs + if [ "${tmpfs_size}" -lt "${tmpfs_ceil}" ] ; then + if ! /bin/mount -t tmpfs tmpfs "${MRPI_TMPFS}" -o remount,defaults,size=${tmpfs_size}K,mode=1777,noatime ; then + logmsg "E" "resize_mrpi_tmpfs" "" "failed to remount mrpi tmpfs!" + return 1 + fi + + # Even if it appeared to work, double check... + if ! is_mrpi_tmpfs_up ; then + logmsg "E" "resize_mrpi_tmpfs" "" "mrpi tmpfs is still not mounted!" + return 1 + fi + + # Success! + logmsg "I" "resize_mrpi_tmpfs" "" "resized mrpi tmpfs down to $((tmpfs_size / 1024))MB" + fi + + return 0 +} + +## To make things faster, we'll try to do as much work in RAM as possible +## But since none of the existing tmpfs fit our needs, we'll create our own. +mount_mrpi_tmpfs() +{ + logmsg "I" "mount_mrpi_tmpfs" "" "trying to create mrpi tmpfs" + + # Sync first... + sync + + # Don't do anything if for some strange reason it's already up... + if is_mrpi_tmpfs_up ; then + logmsg "W" "mount_mrpi_tmpfs" "" "mrpi tmpfs is already mounted!" + return 0 + fi + + # Namely, the default ones tend to be small. So let's say we want one that's about 62.5% of the total RAM. + # That's usually close enough to the free RAM we get with the framework down, and should be more than enough ;). + tmpfs_size="$(awk '/MemTotal/ {printf "%d", $2 * 0.625}' /proc/meminfo)" + # Just in case the apocalypse hits, and our awk shenanigans fail, make sure we have sane defaults, even for 128MB of RAM + is_integer "${tmpfs_size}" || tmpfs_size="81920" + + # Create our mountpoint + mkdir -p "${MRPI_TMPFS}" + + # And mount it :) + if ! /bin/mount -t tmpfs tmpfs "${MRPI_TMPFS}" -o defaults,size=${tmpfs_size}K,mode=1777,noatime ; then + logmsg "E" "mount_mrpi_tmpfs" "" "failed to create mrpi tmpfs!" + return 1 + fi + + # Even if it appeared to work, double check... + if ! is_mrpi_tmpfs_up ; then + logmsg "E" "mount_mrpi_tmpfs" "" "mrpi tmpfs is still not mounted!" + return 1 + fi + + # Success! + logmsg "I" "mount_mrpi_tmpfs" "" "created $((tmpfs_size / 1024))MB mrpi tmpfs" + return 0 +} + +## And unmount it when we're done... +umount_mrpi_tmpfs() +{ + logmsg "I" "umount_mrpi_tmpfs" "" "trying to unmount mrpi tmpfs" + + # Sync first... + sync + + if ! /bin/umount "${MRPI_TMPFS}" ; then + logmsg "E" "umount_mrpi_tmpfs" "" "failed to unmount mrpi tmpfs!" + return 1 + fi + + # Even if it appeared to work, double check... + if is_mrpi_tmpfs_up ; then + logmsg "E" "umount_mrpi_tmpfs" "" "mrpi tmpfs is still mounted!" + return 1 + fi + + # Success! + logmsg "I" "umount_mrpi_tmpfs" "" "successfully unmounted mrpi tmpfs" + return 0 +} + +## Reimplement mntroot ourselves, because its checks aren't as robust as one would hope... +is_rootfs_ro() +{ + if awk '$4~/(^|,)ro($|,)/' /proc/mounts | grep -q '^/dev/root / ' ; then + # Peachy :) + return 0 + fi + + # Hu oh, it's rw... + return 1 +} + +is_rootfs_rw() +{ + if awk '$4~/(^|,)rw($|,)/' /proc/mounts | grep -q '^/dev/root / ' ; then + # Peachy :) + return 0 + fi + + # Hu oh, it's ro... + return 1 +} + +make_rootfs_rw() +{ + logmsg "I" "make_rootfs_rw" "" "trying to remount rootfs rw" + + # Sync first... + sync + + # Don't do anything if for some strange reason it's already rw... + if is_rootfs_rw ; then + logmsg "W" "make_rootfs_rw" "" "rootfs is already rw!" + return 0 + fi + + # Do eet! + if ! /bin/mount -o remount,rw / ; then + logmsg "E" "make_rootfs_rw" "" "failed to remount rootfs rw!" + return 1 + fi + + # Even if it appeared to work, double check... + if is_rootfs_ro ; then + logmsg "E" "make_rootfs_rw" "" "rootfs is still ro after a rw remount!" + return 1 + fi + + # Success! + logmsg "I" "make_rootfs_rw" "" "rootfs has been remounted rw" + return 0 +} + +make_rootfs_ro() +{ + logmsg "I" "make_rootfs_ro" "" "trying to remount rootfs ro" + + # Sync first... + sync + + # Don't do anything if for some strange reason it's already ro... + if is_rootfs_ro ; then + logmsg "W" "make_rootfs_ro" "" "rootfs is already ro!" + return 0 + fi + + # Do eet! + if ! /bin/mount -o remount,ro / ; then + logmsg "E" "make_rootfs_ro" "" "failed to remount rootfs ro!" + return 1 + fi + + # Even if it appeared to work, double check... + if is_rootfs_rw ; then + logmsg "E" "make_rootfs_ro" "" "rootfs is still rw after a ro remount!" + return 1 + fi + + # Success! + logmsg "I" "make_rootfs_ro" "" "rootfs has been remounted ro" + return 0 +} + +## Run a single package +run_package() +{ + # We need at one arg + if [ $# -lt 1 ] ; then + logmsg "W" "run_package" "" "not enough arguments passed to run_package ($# while we need at least 1)" + return 1 + fi + + # Clear our five lines... + print_bottom_centered "" 4 + print_bottom_centered "" 3 + print_bottom_centered "" 2 + print_bottom_centered "" 1 + print_bottom_centered "" 0 + + PKG_FILENAME="${1}" + + # Cleanup the name a bit for the screen + PKG_NAME="${PKG_FILENAME#[uU]pdate[-_]*}" + # Legacy devices have an older busybox version, with an ash build that sucks even more! [Can't use / substitutions] + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/uninstall/U/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/install/I/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/touch_pw/K5/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_and_up/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_zelda_rex_bellatrix/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_zelda_rex/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario_zelda/W+Z/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/wario/WARIO/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_and_up/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4_koa3_pw5_ks/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4_koa3_pw5/W+Z+R+B/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4_koa3/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4_kt4/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2_pw4/W+Z+R/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3_koa2/W+Z/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3_koa_kt3/WARIO+/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv_pw3/WARIO/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2_kt2_kv/WARIO/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw2/PW2/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/k2_dx_k3/LEGACY/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/koa2_koa3/ZELDA/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/koa2/ZELDA/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/zelda/ZELDA/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw4_kt4/REX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw4/REX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/rex/REX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5_kt5_ks/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5_kt5/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5_ks/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/pw5/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/kt5/BELLATRIX/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/scribe/BELLATRIX3/')" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/bellatrix/BELLATRIX/')" + PKG_NAME="${PKG_NAME%*.bin}" + PKG_NAME="$(echo "${PKG_NAME}" | sed -e 's/[-_]/ /g')" + + # Start by timestamping our logs... + echo -e "\n\n**** **** **** ****" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + echo -e "\n[$(date +'%F @ %T %z')] :: [MRPI r${MRPI_REV}] - Beginning the processing of package '${PKG_FILENAME}' (${PKG_NAME}) . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + + # Now that the framework is down and we actually have a decent amount of free RAM, try to resize out tmpfs accordingly + if ! resize_mrpi_tmpfs ; then + print_bottom_centered "Failed to resize MRPI tmpfs, waiting . . ." 1 + echo -e "\nFailed to resize MRPI tmpfs, waiting . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + # Try one final time... + if ! resize_mrpi_tmpfs ; then + print_icon "FAIL" + print_bottom_centered "Really failed to resize MRPI tmpfs, skipping" 1 + echo -e "\nReally failed to resize MRPI tmpfs, skipping ${PKG_NAME} . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + return 1 + fi + fi + + # Show a possibly relevant icon during processing + # First, make sure case won't bother us... + uc_pkg_name="$(echo "${PKG_NAME}" | tr '[:lower:]' '[:upper:]')" + case "${uc_pkg_name}" in + *PYTHON* ) + pkg_icon="PYTHON" + ;; + *USBNET* ) + pkg_icon="USBNET" + ;; + *BRIDGE* | *HOTFIX* ) + pkg_icon="BRIDGE" + ;; + *KUAL* ) + pkg_icon="KUAL" + ;; + *CRP* | *RP* ) + pkg_icon="TOOLS" + ;; + * ) + pkg_icon="WAIT" + ;; + esac + # And show it + print_icon "${pkg_icon}" + + # Check if it's valid... + print_bottom_centered "Checking ${PKG_NAME}" 4 + # Always re-compute the OTA number, in case something dared to mess with it... + compute_current_ota_version + # Save KindleTool's output (Tweak the IFS to make our life easier...) + BASE_IFS="${IFS}" + IFS='' + ktool_output="$(run_kindletool convert -i "${PKG_FILENAME}" 2>&1)" + mrpi_ret="$?" + # On the off chance that failed, abort + # NOTE: One sneaky possibility for this to fail would be an userstore mounted noexec, which might happen if the bridge is broken, as it was during the whole 5.10 debacle... + if [ ${mrpi_ret} -ne 0 ] ; then + IFS="${BASE_IFS}" + print_icon "FAIL" + print_bottom_centered "Failed to parse package, skipping" 1 + echo -e "\nFailed to parse package '${PKG_FILENAME}' (${PKG_NAME}) [return code: ${mrpi_ret}], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + echo -e "\nKindleTool output:\n${ktool_output}\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + # Check bundle type + PKG_BUNDLE_TYPE="$(echo "${ktool_output}" | sed -n -r 's/^(Bundle Type)([[:blank:]]*)(.*?)$/\3/p')" + case "${PKG_BUNDLE_TYPE}" in + "OTA V1" ) + PKG_PADDING_BYTE="$(echo "${ktool_output}" | sed -n -r 's/^(Padding Byte)([[:blank:]]*)([[:digit:]]*)( \()(.*?)(\))$/\5/p')" + PKG_DEVICE_CODE="$(echo "${ktool_output}" | sed -n -r '/^(Device)([[:blank:]]*)(.*?)$/p' | sed -n -r -e 's/^(Device)([[:blank:]]*)(.*?)(\()//' -e 's/((([[:xdigit:]GHJKLMNPQRSTUVWX]{3})( -> 0x)([[:xdigit:]]{3}))|((0x)([[:xdigit:]]{2})))(\))(.*?)$/\5\8/p')" + PKG_MIN_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Minimum OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + PKG_MAX_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Target OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + # Now that we're done with KindleTool's output, restore our original IFS value... + IFS="${BASE_IFS}" + + # Check padding byte + case "${PKG_PADDING_BYTE}" in + "0x13" | "0x00" ) + is_mr_package="true" + ;; + * ) + is_mr_package="false" + ;; + esac + # Reject non-MR packages + if [ "${is_mr_package}" = "false" ] ; then + print_icon "FAIL" + print_bottom_centered "Not an MR package, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not an MR package, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Check device code + if [ "${kmodel}" != "${PKG_DEVICE_CODE}" ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your device, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your device [${kmodel} vs. ${PKG_DEVICE_CODE}], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Version check... NOTE: Busybox (and Bash < 3) stores ints as int_32_t (i.e., signed 32bit), so we have to get creative to avoid overflows... >_<. We don't have access to bc, so rely on awk... + if [ "$(awk -v fw_build="${fw_build}" -v PKG_MIN_OTA="${PKG_MIN_OTA}" -v PKG_MAX_OTA="${PKG_MAX_OTA}" 'BEGIN { print (fw_build < PKG_MIN_OTA || fw_build >= PKG_MAX_OTA) }')" -ne 0 ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your FW version, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your FW version [!(${PKG_MIN_OTA} <= ${fw_build} < ${PKG_MAX_OTA})], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + ;; + "OTA V2" ) + PKG_CERT_NUM="$(echo "${ktool_output}" | sed -n -r 's/^(Cert number)([[:blank:]]*)(.*?)$/\3/p')" + PKG_DEVICES_CODES="$(echo "${ktool_output}" | sed -n -r '/^(Device)([[:blank:]]*)(.*?)$/p' | sed -n -r -e 's/^(Device)([[:blank:]]*)(.*?)(\()//' -e 's/((([[:xdigit:]GHJKLMNPQRSTUVWX]{3})( -> 0x)([[:xdigit:]]{3}))|((0x)([[:xdigit:]]{2})))(\))(.*?)$/\5\8/p')" + PKG_MIN_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Minimum OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + PKG_MAX_OTA="$(echo "${ktool_output}" | sed -n -r 's/^(Target OTA)([[:blank:]]*)([[:digit:]]*)$/\3/p')" + # Now that we're done with KindleTool's output, restore our original IFS value... + IFS="${BASE_IFS}" + + # Check signing cert to reject non-MR packages + if [ "${PKG_CERT_NUM}" -ne 0 ] ; then + print_icon "FAIL" + print_bottom_centered "Not an MR package, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not an MR package, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Check device codes + devcode_match="false" + for cur_devcode in ${PKG_DEVICES_CODES} ; do + if [ "${kmodel}" = "${cur_devcode}" ] ; then + devcode_match="true" + fi + done + if [ "${devcode_match}" = "false" ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your device, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your device [${kmodel} vs. $(echo "${PKG_DEVICES_CODES}" | tr -s '\\n' ' ')], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + + # Version check... NOTE: Busybox (and Bash < 3) stores ints as int_32_t, so we have to get creative to avoid overflows... >_<. We don't have access to bc, so rely on awk... + if [ "$(awk -v fw_build="${fw_build}" -v PKG_MIN_OTA="${PKG_MIN_OTA}" -v PKG_MAX_OTA="${PKG_MAX_OTA}" 'BEGIN { print (fw_build < PKG_MIN_OTA || fw_build > PKG_MAX_OTA) }')" -ne 0 ] ; then + print_icon "FAIL" + print_bottom_centered "Not targeting your FW version, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not targeting your FW version [!(${PKG_MIN_OTA} < ${fw_build} < ${PKG_MAX_OTA})] skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + ;; + * ) + IFS="${BASE_IFS}" + + print_icon "FAIL" + print_bottom_centered "Not an OTA package, skipping" 1 + echo -e "\nPackage '${PKG_FILENAME}' (${PKG_NAME}) is not an OTA package [${PKG_BUNDLE_TYPE}], skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + ;; + esac + + # Start it up... + print_bottom_centered "* ${PKG_NAME} *" 4 + + # Clear workdir, and extract package in it + rm -rf "${MRPI_WORKDIR}" + if ! enough_free_space ; then + print_icon "FAIL" + print_bottom_centered "Not enough free space left, skipping" 1 + echo -e "\nNot enough space left to process ${PKG_NAME}, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + # NOTE: Using >> LOG 2>&1 will truncate the log before storing KindleTool's output, for some mysterious reason... + # But this works (as does using tee -a). + if ! run_kindletool extract "${PKG_FILENAME}" "${MRPI_WORKDIR}" 2>> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" ; then + # KindleTool handles the integrity checking for us, so let's check that this went fine... + print_icon "FAIL" + print_bottom_centered "Failed to extract package, skipping" 1 + echo -e "\nFailed to extract package '${PKG_FILENAME}' (${PKG_NAME}), skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + return 1 + fi + # We can then remove the package itself + rm -f "${PKG_FILENAME}" + + # Make the rootfs rw... + if ! make_rootfs_rw ; then + print_bottom_centered "Failed to remount rootfs RW, waiting . . ." 1 + echo -e "\nFailed to remount rootfs RW, waiting . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + # Try one final time... + if ! make_rootfs_rw ; then + print_icon "FAIL" + print_bottom_centered "Really failed to remount rootfs RW, skipping" 1 + echo -e "\nReally failed to remount rootfs RW, skipping ${PKG_NAME} . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + return 1 + fi + fi + + # Run the package scripts in alphabetical order, from inside our workdir... + cd "${MRPI_WORKDIR}" + RAN_SOMETHING="false" + FAILED_SOMETHING="false" + # NOTE: We only handle toplevel scripts + for pkg_script in *.sh *.ffs ; do + if [ -f "./${pkg_script}" ] ; then + RAN_SOMETHING="true" + print_bottom_centered "Running ${pkg_script} . . ." 3 + # Log what the script does... + echo -e "--\nRunning '${pkg_script}' for '${PKG_NAME}' (${PKG_FILENAME}) @ $(date -R)\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + # Abort at the first sign of trouble... + if check_is_touch_device ; then + /bin/sh -e "./${pkg_script}" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" 2>&1 + # Catch errors... + mrpi_ret="$?" + else + # NOTE: Unfortunately, on legacy devices, actually sourcing /etc/rc.d/functions will fail, so we can't use -e here... >_<" + /bin/sh "./${pkg_script}" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" 2>&1 + # On the off-chance it'd actually be useful then, keep catching errors... + mrpi_ret="$?" + fi + if [ ${mrpi_ret} -ne 0 ] ; then + FAILED_SOMETHING="true" + print_icon "FAIL" + print_bottom_centered "Package script failed (${mrpi_ret}), moving on . . . :(" 1 + echo -e "\nHu oh... Got return code ${mrpi_ret} . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + # Leave time to the user to read it... + sleep 10 + else + print_bottom_centered "Success. :)" 1 + echo -e "\nSuccess! :)\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + fi + fi + done + # Warn if no scripts were found + if [ "${RAN_SOMETHING}" = "false" ] ; then + print_icon "BOMB" + print_bottom_centered "No scripts were found, skipping" 1 + echo -e "\nNo scripts were found, skipping . . . :(\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + else + # NOTE: While we show a FAIL icon ASAP when *any* script fails, + # we only show an OK checkmark at the end of *every* script, + # provided none of them failed. + if [ ${mrpi_ret} -eq 0 ] && [ "${FAILED_SOMETHING}" = "false" ] ; then + print_icon "OK" + fi + fi + # And get out of the staging directory once we're done. + cd "${MRPI_PKGDIR}" + + # Lock the rootfs down + if ! make_rootfs_ro ; then + print_bottom_centered "Failed to remount rootfs RO, waiting . . ." 2 + echo -e "\nFailed to remount rootfs RO, waiting . . .\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 10 + # Try one final time... + if ! make_rootfs_ro ; then + print_icon "BOMB" + print_bottom_centered "Really failed to remount rootfs RO -_-" 2 + print_bottom_centered "A reboot would be recommended" 1 + echo -e "\nReally failed to remount rootfs RO -_-\n" >> "${MRINSTALLER_BASEDIR}/log/mrinstaller.log" + sleep 5 + fi + fi + + # Clean up behind us + rm -rf "${MRPI_WORKDIR}" + + return 0 +} + +## Go! +launch_installer() +{ + # Check for FBInk, and refresh binaries if need be... + if ! check_fbink ; then + print_bottom_centered "Couldn't setup binaries, aborting." 1 + return 1 + fi + + # Sleep a while to let KUAL die + print_bottom_centered "Hush, little baby . . ." 1 + sleep 5 + + # NOTE: Fugly FW 5.6.1 handling. Die *before* stopping the UI if we're not root, because we might not be able to bring it back up otherwise. + if [ "$(id -u)" -ne 0 ] ; then + print_bottom_centered "Unprivileged user, aborting." 1 + return 1 + fi + + # Let's do this! + print_icon "MRPI" + print_bottom_centered "Launching the MR installer . . ." 1 + + # Rotate the logs if need be + rotate_logs + + # Move to our package directory... + mkdir -p "${MRPI_PKGDIR}" + cd "${MRPI_PKGDIR}" + + # Loop over packages... + for pkg in *.bin ; do + # Check that we actually have some + if [ -f "${pkg}" ] ; then + # Try to build a list of packages, while honoring a modicum of dependency tree + case "${pkg}" in + *usbnet*install* ) + # Top priority + MR_PKGS_HEAD_LIST="${pkg} ${MR_PKGS_HEAD_LIST}" + ;; + *jailbreak*uninstall* ) + # Lowest priority + MR_PKGS_TAIL_LIST="${MR_PKGS_TAIL_LIST} ${pkg}" + ;; + *jailbreak*install* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + *mkk*install* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + *python*install* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + *_rp_*install* | *_rescue_pack* ) + # High priority + MR_PKGS_HEAD_LIST="${MR_PKGS_HEAD_LIST} ${pkg}" + ;; + * ) + # Normal priority + MR_PKGS_LIST="${MR_PKGS_LIST} ${pkg}" + ;; + esac + else + # No packages were found, go away + print_bottom_centered "No MR packages found" 1 + return 1 + fi + done + + # Construct our final package list + MR_PKGS_LIST="${MR_PKGS_HEAD_LIST} ${MR_PKGS_LIST} ${MR_PKGS_TAIL_LIST}" + + # Try to setup our tmpfs + if ! mount_mrpi_tmpfs ; then + print_bottom_centered "Failed to create MRPI tmpfs, waiting . . ." 1 + sleep 5 + # Try one final time... + if ! mount_mrpi_tmpfs ; then + print_bottom_centered "Really failed to create MRPI tmpfs, aborting." 1 + sleep 5 + return 1 + fi + fi + mkdir -p "${MRPI_TMPDIR}" + + # Don't get killed! + trap "" TERM + + # Bring down most of the services + if check_is_touch_device ; then + # NOTE: We used to stop/start x (IIRC, to avoid a black screen), but, since FW 5.15.1, + # doing so apparently fails to restart it properly... + # (If need be, a milestone for 5.15.x would be the presence of + # /etc/upstart/filesystems_nh.conf or /etc/upstart/n_setup.conf or + # /etc/upstart/var_local_backup_and_restore). + # NOTE: This means X, awesome & blanket are *still* running (which matches the standard OTA behavior)... + stop lab126_gui + # Let's settle down a bit... + sleep 5 + # If AcXE is installed, stop it (it doesn't depend on the UI, and thus would still be up) + if [ -f "/etc/upstart/acxe.conf" ] ; then + # Check if it's up... + if [ "$(status acxe)" = "acxe start/running" ] ; then + if ! stop acxe ; then + # Shouldn't happen... + print_bottom_centered "Failed to stop AcXE -_-" 1 + sleep 2 + fi + fi + fi + else + # This is going to get ugly... Clear the screen + ${FBINK_BIN} -c + # If we're in USBNet mode, down it manually first, because we might need volumd to tear it down, which we won't have in single-user mode... + # See the comments in USBNetwork itself related to volumd for the details on why we can't really keep it up during an update (TL;DR: it breaks usbms exports)... + # NOTE: We need these shenanigans with custom services because we usually don't install the proper symlinks for the single-user runlevel... ;). + if [ -f "/etc/init.d/usbnet" ] ; then + # Do this unconditionally, the script is smart enough to figure out the rest ;). + /etc/init.d/usbnet stop + fi + # Switch to single-user + telinit 1 + # Reprint our message after the clear... + sleep 2 + print_icon "MRPI" + print_bottom_centered "Launching the MR installer . . ." 1 + # Wait for everything to go down... + sleep 20 + # Re-up syslog + /etc/init.d/syslog-ng start + + # And down most of the custom stuff... + # Start by listing everything that goes down when updating... + for service in /etc/rc3.d/K* ; do + UPDATE_RUNLEVEL_KILLS="${UPDATE_RUNLEVEL_KILLS} ${service##*/}" + done + # And everything that goes down in single-user mode... + for service in /etc/rc1.d/K* ; do + SINGLEU_RUNLEVEL_KILLS="${SINGLEU_RUNLEVEL_KILLS} ${service##*/}" + done + # Manually down anything that the updater runlevel downs, but not single-user... + for cur_service in ${UPDATE_RUNLEVEL_KILLS} ; do + is_custom="true" + for service in ${SINGLEU_RUNLEVEL_KILLS} ; do + if [ "${cur_service}" = "${service}" ] ; then + is_custom="false" + fi + done + # Is it *really* custom? + if [ "${is_custom}" = "true" ] ; then + # Don't store USBNet, we're handling it manually + if [ "$(echo "${cur_service}" | tail -c +4)" != "usbnet" ] ; then + # Store the list of custom services without their order prefix... + CUSTOM_SERVICES_LIST="${CUSTOM_SERVICES_LIST} $(echo "${cur_service}" | tail -c +4)" + fi + fi + done + + # And down them! + for service in ${CUSTOM_SERVICES_LIST} ; do + if [ -f "/etc/init.d/${service}" ] ; then + "/etc/init.d/${service}" stop + fi + done + + # let's wait a bit more... + sleep 7 + fi + + # Sync FS + sync + + # Blank the screen + ${FBINK_BIN} -c + + # Say hi (again) + print_icon "MRPI" + + # And install our packages in order, one by one... + for cur_pkg in ${MR_PKGS_LIST} ; do + if [ -f "${cur_pkg}" ] ; then + if ! run_package "${cur_pkg}" ; then + # Remove package in case of failure... + print_bottom_centered "Destroying package . . ." 1 + rm -f "${cur_pkg}" + # Don't leave a staging directory behind us, we might have failed without clearing it... + rm -rf "${MRPI_WORKDIR}" + # Try to avoid leaving a rw rootfs, we might have failed with it still rw... + if ! make_rootfs_ro ; then + print_bottom_centered "Failed to remount rootfs RO, waiting . . ." 2 + print_bottom_centered "" 1 + sleep 10 + # Try one final time... + if ! make_rootfs_ro ; then + print_bottom_centered "Really failed to remount rootfs RO -_-" 2 + print_bottom_centered "A reboot would be recommended" 1 + sleep 5 + fi + fi + sleep 2 + fi + else + # Should never happen... + print_bottom_centered "${cur_pkg} is not a file, skipping" 1 + fi + done + + # Sync FS + sync + + # Try to unmount our tmpfs + rm -rf "${MRPI_TMPDIR}" + if ! umount_mrpi_tmpfs ; then + print_bottom_centered "Failed to unmount MRPI tmpfs, waiting . . ." 1 + sleep 5 + # Try one final time... + if ! umount_mrpi_tmpfs ; then + print_bottom_centered "Really failed to unmount MRPI tmpfs -_-" 1 + sleep 5 + fi + fi + + # We're done! Sleep between print calls in order to avoid losing our last message... + print_icon "AMZ" + PRINT_SLEEP="true" + print_bottom_centered "" 4 + print_bottom_centered "" 3 + print_bottom_centered "" 2 + print_bottom_centered "Done, restarting UI . . ." 1 + print_bottom_centered "" 0 + sleep 2 + + # Bring the UI back up! + if check_is_touch_device ; then + # If we still have AcXE installed, restart it + if [ -f "/etc/upstart/acxe.conf" ] ; then + # Check if it's down... + if [ "$(status acxe)" = "acxe stop/waiting" ] ; then + if ! start acxe ; then + # Shouldn't happen... + print_bottom_centered "Failed to start AcXE -_-" 1 + sleep 2 + else + sleep 1 + fi + fi + fi + start lab126_gui + else + # Thankfully enough, we don't have to jump through any hoops this time ;). + telinit 5 + fi +} + +# Main +case "${1}" in + "launch_installer" ) + ${1} + ;; + * ) + print_bottom_centered "invalid action (${1})" 1 + ;; +esac + +return 0 diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/config.xml b/dash/staging/post-jailbreak-root/extensions/MRInstaller/config.xml new file mode 100755 index 0000000..373317c --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/MRInstaller/config.xml @@ -0,0 +1,12 @@ + + + + MR Installer + 1.6 + NiLuJe + MRInstaller + + + menu.json + + diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/BigBlue_Terminal.ttf b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/BigBlue_Terminal.ttf new file mode 100644 index 0000000..179ce23 Binary files /dev/null and b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/BigBlue_Terminal.ttf differ diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/icons.py b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/icons.py new file mode 100755 index 0000000..6f888c0 --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/icons.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python2 +""" +Barebones example of FBInk usage through Python's cFFI module +""" + +# To get a Py3k-like print function +from __future__ import print_function + +import sys +# Load the wrapper module, it's linked against FBInk, so the dynamic loader will take care of pulling in the actual FBInk library +from _fbink import ffi, lib as FBInk + +# Let's check which FBInk version we're using... +# NOTE: ffi.string() returns a bytes on Python 3, not a str, hence the extra decode +print("Loaded FBInk {}".format(ffi.string(FBInk.fbink_version()).decode("ascii"))) + +# And now we're good to go! Let's print "Hello World" in the center of the screen... +# Setup the config... +fbink_cfg = ffi.new("FBInkConfig *") +fbink_cfg.is_centered = False +fbink_cfg.is_halfway = True +fbink_cfg.is_cleared = True +fbink_cfg.is_verbose = True + +fbink_ot_cfg = ffi.new("FBInkOTConfig *") +fbink_ot_cfg.size_pt = 24 + +""" +# Open the FB... +fbfd = FBInk.fbink_open() +if fbfd == -1: + raise SystemExit("Failed to open the framebuffer, aborting . . .") + +# Initialize FBInk... +if FBInk.fbink_init(fbfd, fbink_cfg) < 0: + raise SystemExit("Failed to initialize FBInk, aborting . . .") + +# Do stuff! +if FBInk.fbink_print(fbfd, b"Hello World", fbink_cfg) < 0: + print("Failed to print that string!", file=sys.stderr) + +# And now we can wind things down... +if FBInk.fbink_close(fbfd) < 0: + raise SystemExit("Failed to close the framebuffer, aborting . . .") +""" + +# Or, the same but in a slightly more Pythonic approach ;). +fbfd = FBInk.fbink_open() +try: + FBInk.fbink_init(fbfd, fbink_cfg) + FBInk.fbink_add_ot_font("BigBlue_Terminal.ttf", FBInk.FNT_REGULAR) + string = u"Success \uf632 or \ufadf or \ufae0 or \ufc8f or \uf633 or \uf4a1" // \uf633 + string += u"\n" + string += u"Error \uf071 or \uf525 or \uf529 or \uf421 or \ufb8f" // \uf071 + string += u"\n" + string += u"Wait \uf252 or \ufa1e or \uf49b" // \uf252 + string += u"\n" + string += u"Python \ue73c or \ue235 or \uf81f or \ue606" // \ue73c + string += u"\n" + string += u"USBNet \ue795 or \uf68c or \ufcb5 or \uf489" // \uf68c + string += u"\n" + string += u"Bridge \uf270 or \uf52c or \ue286 or \uf5a6 or \ue214" // \ue286 (AMZ: \uf270) + string += u"\n" + string += u":( \uf119 or \uf6f7" // \uf119 + string += u"\n" + string += u"Linux \uf17c or \uf31a or \uf83c" // \uf17c + string += u"\n" + string += u":) \uf118 or \uf6f4" // \uf118 + string += u"\n" + string += u"Tools \ue20f or \ufbf6 or \uf992 or \ufab6 or \uf425" // \uf425 + string += u"\n" + string += u"MRPI \ufcdd or \ufcde or \uf8d5 or \uf962 or \ufac3 or \uf487 or \uf427" // \uf8d5 (KUAL: \uf962) + string = string.encode("utf-8") + # NOTE: On Python 3, cFFI maps char to bytes, not str + FBInk.fbink_print_ot(fbfd, string, fbink_ot_cfg, fbink_cfg, ffi.NULL) +finally: + FBInk.fbink_free_ot_fonts() + FBInk.fbink_close(fbfd) diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-K3.tar.gz b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-K3.tar.gz new file mode 100644 index 0000000..a8b280b Binary files /dev/null and b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-K3.tar.gz differ diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-K5.tar.gz b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-K5.tar.gz new file mode 100644 index 0000000..1e415ab Binary files /dev/null and b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-K5.tar.gz differ diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-KHF.tar.gz b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-KHF.tar.gz new file mode 100644 index 0000000..5901599 Binary files /dev/null and b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-KHF.tar.gz differ diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-PW2.tar.gz b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-PW2.tar.gz new file mode 100644 index 0000000..c71cd74 Binary files /dev/null and b/dash/staging/post-jailbreak-root/extensions/MRInstaller/data/mrpi-PW2.tar.gz differ diff --git a/dash/staging/post-jailbreak-root/extensions/MRInstaller/menu.json b/dash/staging/post-jailbreak-root/extensions/MRInstaller/menu.json new file mode 100755 index 0000000..55a3403 --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/MRInstaller/menu.json @@ -0,0 +1,24 @@ +{ + "comment001": "MR Package Installer", + "comment002": "", + "comment003": "$Id: menu.json 11305 2014-12-23 14:56:54Z NiLuJe $", + "comment004": "", + "items": [ + { + "name": "Helper", + "priority": -998, + "items": [ + { + "name": "Install MR Packages", + "action": "./bin/mrinstaller.sh", + "params": "launch_installer", + "priority": -998, + "exitmenu": true, + "refresh": false, + "status": false, + "internal": "status Launching the MR Installer" + } + ] + } + ] +} diff --git a/dash/staging/post-jailbreak-root/extensions/kindle-dash/config.xml b/dash/staging/post-jailbreak-root/extensions/kindle-dash/config.xml new file mode 100644 index 0000000..da580b2 --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/kindle-dash/config.xml @@ -0,0 +1,10 @@ + + + + Kindle dashboard + pascalw-kindle-dash + + + menu.json + + \ No newline at end of file diff --git a/dash/staging/post-jailbreak-root/extensions/kindle-dash/menu.json b/dash/staging/post-jailbreak-root/extensions/kindle-dash/menu.json new file mode 100644 index 0000000..cb75b2d --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/kindle-dash/menu.json @@ -0,0 +1,5 @@ +{ + "items": [ + {"name": "Kindle Dashboard", "action": "/mnt/us/dashboard/start.sh"} + ] +} \ No newline at end of file diff --git a/dash/staging/post-jailbreak-root/extensions/renameotabin/bin/rename.sh b/dash/staging/post-jailbreak-root/extensions/renameotabin/bin/rename.sh new file mode 100644 index 0000000..925f8eb --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/renameotabin/bin/rename.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +mntroot rw +cd /usr/bin +mv otaupd otaupd.bck +mv otav3 otav3.bck +mntroot ro +reboot diff --git a/dash/staging/post-jailbreak-root/extensions/renameotabin/bin/restore.sh b/dash/staging/post-jailbreak-root/extensions/renameotabin/bin/restore.sh new file mode 100644 index 0000000..006163d --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/renameotabin/bin/restore.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +mntroot rw +cd /usr/bin +mv otaupd.bck otaupd +mv otav3.bck otav3 +mntroot ro +reboot diff --git a/dash/staging/post-jailbreak-root/extensions/renameotabin/config.xml b/dash/staging/post-jailbreak-root/extensions/renameotabin/config.xml new file mode 100644 index 0000000..0fcc4e5 --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/renameotabin/config.xml @@ -0,0 +1,8 @@ + + + + + + menu.json + + diff --git a/dash/staging/post-jailbreak-root/extensions/renameotabin/menu.json b/dash/staging/post-jailbreak-root/extensions/renameotabin/menu.json new file mode 100644 index 0000000..61a72a9 --- /dev/null +++ b/dash/staging/post-jailbreak-root/extensions/renameotabin/menu.json @@ -0,0 +1,22 @@ +{ + "items": [ + { + "name": "Rename OTA binaries", + "priority": -998, + "items": [ + { + "name": "Rename", + "priority": 1, + "action": "bin/rename.sh", + "exitmenu": true + }, + { + "name": "Restore", + "priority": 2, + "action": "bin/restore.sh", + "exitmenu": true + } + ] + } + ] +} diff --git a/dash/staging/post-jailbreak-root/mrpackages/Update_KUALBooklet_HDRepack.bin b/dash/staging/post-jailbreak-root/mrpackages/Update_KUALBooklet_HDRepack.bin new file mode 100644 index 0000000..bbb827e Binary files /dev/null and b/dash/staging/post-jailbreak-root/mrpackages/Update_KUALBooklet_HDRepack.bin differ diff --git a/dash/staging/post-jailbreak-root/mrpackages/Update_usbnet_0.22.N_install_pw2_and_up.bin b/dash/staging/post-jailbreak-root/mrpackages/Update_usbnet_0.22.N_install_pw2_and_up.bin new file mode 100644 index 0000000..50e0c42 Binary files /dev/null and b/dash/staging/post-jailbreak-root/mrpackages/Update_usbnet_0.22.N_install_pw2_and_up.bin differ diff --git a/dash/staging/usbnetwork/Update_usbnet_0.22.N_install_pw2_and_up.bin b/dash/staging/usbnetwork/Update_usbnet_0.22.N_install_pw2_and_up.bin new file mode 100644 index 0000000..50e0c42 Binary files /dev/null and b/dash/staging/usbnetwork/Update_usbnet_0.22.N_install_pw2_and_up.bin differ diff --git a/dash/staging/watchthis/KV-5.13.6/KV-5.13.6.zip b/dash/staging/watchthis/KV-5.13.6/KV-5.13.6.zip new file mode 100644 index 0000000..e208d38 Binary files /dev/null and b/dash/staging/watchthis/KV-5.13.6/KV-5.13.6.zip differ diff --git a/dash/staging/watchthis/KV-5.13.6/demo.json b/dash/staging/watchthis/KV-5.13.6/demo.json new file mode 100644 index 0000000..3c2a799 --- /dev/null +++ b/dash/staging/watchthis/KV-5.13.6/demo.json @@ -0,0 +1 @@ +{"type": "demo_activation_manifest", "version": 1, "locale": ["de_AT", "it_CH", "ro_RO", "mt_MT", "es_EC", "es_US", "pt_BR", "cs_CZ", "fr_LU", "es_UY", "es_MX", "sk_SK", "es_ES", "ar_OM", "es_VE", "nl_BE", "sq_AL", "sv_SE", "da_DK", "es_NI", "iw_IL", "ko_KR", "en_US", "en_MT", "el_GR", "it_IT", "pl_PL", "fr_BE", "be_BY", "en_AU", "tr_TR", "ja_JP", "de_DE", "es_SV", "ar_QA", "de_CH", "zh_HK", "ar_YE", "es_CO", "es_CL", "fr_CA", "es_CR", "en_SG", "fr_CH", "vi_VN", "fi_FI", "en_CA", "lv_LV", "uk_UA", "es_DO", "ga_IE", "ar_IQ", "sl_SI", "ar_AE", "pt_PT", "en_GY", "en_PH", "th_TH", "in_ID", "ca_ES", "hu_HU", "ar_SA", "ar_SD", "sr_BA", "ar_BH", "ar_JO", "nl_NL", "is_IS", "es_AR", "sr_RS", "en_IE", "hr_HR", "ar_KW", "de_LU", "lt_LT", "ar_SY", "en_IN", "es_BO", "no_NO", "en_ZA", "ru_RU", "ar_TN", "ar_LB", "hi_IN", "fr_FR", "el_CY", "ms_MY", "zh_TW", "ar_LY", "sr_CS", "es_GT", "en_NZ", "es_PE", "zh_SG", "es_PA", "ar_DZ", "bg_BG", "mk_MK", "ar_MA", "sr_ME", "en_GB", "es_HN", "et_EE", "es_PR", "ar_EG", "es_PY"], "files": [{"name": "KV-5.13.6.zip", "checksum": "f65287a2af835cfb60aef7b47e4ec968"}]} \ No newline at end of file diff --git a/dash/staging/watchthis/Update_hotfix_watchthis_custom.bin b/dash/staging/watchthis/Update_hotfix_watchthis_custom.bin new file mode 100644 index 0000000..691b1b5 Binary files /dev/null and b/dash/staging/watchthis/Update_hotfix_watchthis_custom.bin differ diff --git a/dash/staging/watchthis/extracted/watchthis-release/KV/KV-5.13.6/KV-5.13.6.zip b/dash/staging/watchthis/extracted/watchthis-release/KV/KV-5.13.6/KV-5.13.6.zip new file mode 100644 index 0000000..e208d38 Binary files /dev/null and b/dash/staging/watchthis/extracted/watchthis-release/KV/KV-5.13.6/KV-5.13.6.zip differ diff --git a/dash/staging/watchthis/extracted/watchthis-release/KV/KV-5.13.6/demo.json b/dash/staging/watchthis/extracted/watchthis-release/KV/KV-5.13.6/demo.json new file mode 100644 index 0000000..3c2a799 --- /dev/null +++ b/dash/staging/watchthis/extracted/watchthis-release/KV/KV-5.13.6/demo.json @@ -0,0 +1 @@ +{"type": "demo_activation_manifest", "version": 1, "locale": ["de_AT", "it_CH", "ro_RO", "mt_MT", "es_EC", "es_US", "pt_BR", "cs_CZ", "fr_LU", "es_UY", "es_MX", "sk_SK", "es_ES", "ar_OM", "es_VE", "nl_BE", "sq_AL", "sv_SE", "da_DK", "es_NI", "iw_IL", "ko_KR", "en_US", "en_MT", "el_GR", "it_IT", "pl_PL", "fr_BE", "be_BY", "en_AU", "tr_TR", "ja_JP", "de_DE", "es_SV", "ar_QA", "de_CH", "zh_HK", "ar_YE", "es_CO", "es_CL", "fr_CA", "es_CR", "en_SG", "fr_CH", "vi_VN", "fi_FI", "en_CA", "lv_LV", "uk_UA", "es_DO", "ga_IE", "ar_IQ", "sl_SI", "ar_AE", "pt_PT", "en_GY", "en_PH", "th_TH", "in_ID", "ca_ES", "hu_HU", "ar_SA", "ar_SD", "sr_BA", "ar_BH", "ar_JO", "nl_NL", "is_IS", "es_AR", "sr_RS", "en_IE", "hr_HR", "ar_KW", "de_LU", "lt_LT", "ar_SY", "en_IN", "es_BO", "no_NO", "en_ZA", "ru_RU", "ar_TN", "ar_LB", "hi_IN", "fr_FR", "el_CY", "ms_MY", "zh_TW", "ar_LY", "sr_CS", "es_GT", "en_NZ", "es_PE", "zh_SG", "es_PA", "ar_DZ", "bg_BG", "mk_MK", "ar_MA", "sr_ME", "en_GB", "es_HN", "et_EE", "es_PR", "ar_EG", "es_PY"], "files": [{"name": "KV-5.13.6.zip", "checksum": "f65287a2af835cfb60aef7b47e4ec968"}]} \ No newline at end of file diff --git a/dash/staging/watchthis/extracted/watchthis-release/Update_hotfix_watchthis_custom.bin b/dash/staging/watchthis/extracted/watchthis-release/Update_hotfix_watchthis_custom.bin new file mode 100644 index 0000000..691b1b5 Binary files /dev/null and b/dash/staging/watchthis/extracted/watchthis-release/Update_hotfix_watchthis_custom.bin differ diff --git a/dash/tmp-mega-tool/package-lock.json b/dash/tmp-mega-tool/package-lock.json new file mode 100644 index 0000000..927213d --- /dev/null +++ b/dash/tmp-mega-tool/package-lock.json @@ -0,0 +1,150 @@ +{ + "name": "tmp-mega-tool", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tmp-mega-tool", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "megajs": "^1.3.9" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/megajs": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/megajs/-/megajs-1.3.9.tgz", + "integrity": "sha512-91GGJbUfUu9z/KFORHcn4bugVILWcGahaoy07Q7M5GLzT6zOsrpusxkjEvEys9XCXbxntg0v+f2JN6sITrEkPQ==", + "license": "MIT", + "dependencies": { + "pumpify": "^2.0.1", + "stream-skip": "^1.0.3" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/stream-skip": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-skip/-/stream-skip-1.0.3.tgz", + "integrity": "sha512-2rB0uBiOnYSQwJxJ3wZLher+fz0yyXQxKuKnVTsidHmkqvC8rWZ2AbX50ZVdz7fsL6zkYkqaN/pPD0RldKIbpQ==", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/dash/tmp-mega-tool/package.json b/dash/tmp-mega-tool/package.json new file mode 100644 index 0000000..67daf84 --- /dev/null +++ b/dash/tmp-mega-tool/package.json @@ -0,0 +1,16 @@ +{ + "name": "tmp-mega-tool", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "megajs": "^1.3.9" + } +} diff --git a/dash/watchthis-jailbreak-r03.zip b/dash/watchthis-jailbreak-r03.zip new file mode 100644 index 0000000..008e124 Binary files /dev/null and b/dash/watchthis-jailbreak-r03.zip differ diff --git a/日知录.md b/日知录.md new file mode 100644 index 0000000..bd5ec88 --- /dev/null +++ b/日知录.md @@ -0,0 +1,137 @@ +--- +layout: post +title: 日知录 +subtitle: +description: +date: 2026-01-06 +author: 大童 +image: +showtoc: +tags: + - 读书 +URL: +categories: + - read +slug: rizhilu +--- + +> 为学大患在好名 + + + +# 日知录 +> 由于缺少安宁,文明或许将逐渐终结于一种新的野蛮状态。多头工作,这一为适应现代晚期信息爆炸的社会需求而必须掌握的技能,实际上是一种倒退,如同荒野求生中,需满散的多头注意力管理技术,以应对复杂野蛮环境,如今亦是。纷乱,应接不暇,对大脑刺激,进而坠入行动上的野蛮状态。永不安息的人必大行其道,超越以往任何时代。应对之法是,人应当对人性做出必要修正,在其中大量增加悠闲、冥想、瑜伽的成分。 -- 2026-03-14 +> 在不确定性的领域中沉浮多年,我见多了披着科学家外衣的江湖术士。在这些人里面,可以找到被随机性愚弄最深的人。 -- 2026-03-15 +> 如果把无限多的猴子放在打字机前面,让它们去乱敲,那么其中一只肯定会打出一字不差的《伊利亚特》叙事诗。 -- 2026-03-16 +> 对于米尔顿·弗里德曼来说,社会在灾难之后的休克状态是一个机遇,是重塑新自由主义社会的最佳时机。新自由主义政权因此采取了休克这种操控手段。休克抹去并清空了灵魂,使其无力防御,这样,灵魂就可以心甘情愿地向残暴的重新编程屈服。 -- 2026-03-17 +> 如果说,睡眠是身体放松的最高形式,那么深度无聊则是精神放松的终极状态。一味的忙碌不会产生新事物。 -- 2026-03-18 +> 多工作业不是人类新掌握的技能,以便适应现代晚期信息社会的需求。更确切地说,它代表了一种倒退。当动物身处野外捕猎区时,普遍存在多任务处理。这种注意力的管理技术是荒野求生的必备技能。 -- 2026-03-19 +> 由于缺少安宁,我们的文明将逐渐终结于一种新的野蛮状态。行动者,即那些永不安息的人如今大行其道,超越以往任何时代。因此,人们应当对人性做出必要的修正,在其中大量增加悠闲冥想的成分。 -- 2026-03-20 +> 立刻做出反应、回应每一个刺激冲动,这已经是一种疾病、一种倒退,也是疲劳、衰竭的征兆。尼采在这里表达的正是重新恢复沉思生活的必要性。 -- 2026-03-21 +> 灵魂只有在丰裕(Überfluss)中才会盛放。艺术不会源自“面包”和“水”,不论这水有多么“甘甜” -- 2026-03-22 +> 认知将是这盛开的思想的果实,仅仅靠困境和劳苦是无法形成思想的。 -- 2026-03-23 +> 道德如若进步,强制与痛苦必不可少。 -- 2026-03-24 +> 丰裕是一切美的前提。 -- 2026-03-25 +> 年少,口号,不知天高地厚,以为大地在你脚上,荷尔蒙武装起来的正义感,这些东西搅和起来,人就操蛋了起来。而这操蛋中最操蛋的一点,就是那貌似反叛精神中隐藏的谄媚情结以及羊群心态。 -- 2026-03-26 +> 这大约也是为什么很多评论家视他为二流作家的原因(毛姆)。他的小说里,技巧性、创新性的东西太少了,留给评论家去“诠释”、去“解密”去炫耀他们的理解力的东>西太少了。但对我来说,这恰恰是他的可爱之处。什么尤利西斯、普鲁斯特、卡夫卡之类的“大师”,我根本读不下去,也不想作若有所悟状。总觉得那些“实验性”小说写作里,作者的自我意识太强烈了,总是要从文字中伸出一只手来,使劲摇晃着一面旗帜,上面飘扬着两个大字“个性”,与其说我们在读一个故事,不如说在观赏一场行为艺术。 -- 2026-03-27 +> 人渴望被承认,也就是别人的目光,但是同时,当别人的目光围拢过来的时候,他又感到窒息,感到不自由。获得承认和追求自由之间,有一个多么辩证的关系。 -- 2026-03-28 +> 怀才不遇,逆水行舟,一个人就像一支队伍,对着自己的头脑和心灵招兵买马,不气馁,有召唤,爱自由。 -- 2026-03-29 +> 如果失败的代价过于沉重、难以承受,那么这件事成功的概率有多高根本无关紧要。 -- 2026-03-30 +> 幸运的傻瓜一点也不认为自己可能只是运气不错而已—依混为一谈定义,他们不知道自己属于这种人,他们的行为举止就像那些钱是他们该得的。一连串的成功给他们注入了不少血清素(serotonin,或者某种类似的物质),以至于自欺欺人,以为自己有能力击败市场—我们的内分泌系统并不知道自己的成功是不是得自运气。 -- 2026-03-31 +> 一个人的表现好坏会显露在外表上,就像动物的显性特征可以用来发出信号,因为赢家容易被人看到,在择偶时这是很有效率的方法。 -- 2026-04-01 +> 俄罗斯转盘的赢家,也很可能被家人、朋友和邻居当做模范对象。 -- 2026-04-02 +> 假设25岁的人一年玩一次俄罗斯转盘,他要活到50岁生日的机会十分渺茫。但是如果有很多人,比方说几千个25岁的年轻人都在玩这个游戏,那么应该会有少数人能够年过半百且极其富有,其他人则已成一堆墓冢。 -- 2026-04-03 +> 最近重读《伊利亚特》(Iliad)叙事诗,我的第一印象是,叙事诗人并没有以成败论英雄。英雄打胜仗或败仗,和他们本身的英勇行为完全无关;他们的命运完全取决于外部的力量,而这通常是命运之神的杰作。英雄之所以是英雄,是因为他们的行为十分英勇,而不是因为战场上的成败。 -- 2026-04-04 +> 每个人都会向长期的性质靠拢。 -- 2026-04-05 +> 老思想越陈越香,不同于新思想的粗糙低劣。 -- 2026-04-06 +> 神看到未来的事情,平凡人看到眼前的事情,聪明人看到即将发生的事情。 -- 2026-04-07 +> 我认为,看波德莱尔的诗,远比收看CNN新闻或听威尔一派胡言要愉快得多。 -- 2026-04-08 +> 苏格兰哲学家休谟在他的《人性论》(Treatise on Human Nature)中以下述方式讨论这个问题:我们看到的白天鹅数目再怎么多,也没办法据以推论所有的天鹅都是白的,但是只要看到一只黑天鹅,就足以推翻这个结论。这个问题后来以穆勒(John Stuart Mill)所谓的“黑天鹅问题”著称。 -- 2026-04-09 +> 厚实的银行户头的好处是,可以买到时间,思考和享受人生,免于成为某家公司的员工,变成只谈“工作伦理”的企业奴隶。 -- 2026-04-10 +> 人的天性很难变得更理性,或者不以社会地位低下为耻,至少就我们目前的DNA密码来说是如此。 -- 2026-04-11 +> 我们经历的现实只是所有可能出现的随机历史中的一个,我们却误将它当做最具代表性的,忘了还有其他可能性。简言之,存活者偏差是指“表现最好的最容易被看见”。为什么?因为输家并没有现身。 -- 2026-04-12 +> 即使是停住不动的时钟,一天也有两次正确。 -- 2026-04-13 +> 不管喜不喜欢,我们都是自身生物构造的俘虏。 -- 2026-04-14 +> 在基因上,我们仍和许久以前漫游在非洲大草原上的老祖先很接近。我们信念的形成充满着迷信,即使现今也不例外,或许必须说,于今尤烈。 -- 2026-04-15 +> 科学只是投机和有系统的推测。 -- 2026-04-16 +> 工作是一种必要的恶。我们接受工作,是因为它让我们能够去从事那些我们认为美好的事物。 -- 2026-04-17 +> 工作伦理的幌子之下演化出一种纪律伦理:不用在意尊严或荣誉,感受或目的——全力工作就好,日复一日,争分夺秒,即使你完全看不到努力的意义所在。 -- 2026-04-18 +> 为了产生效果,两者都需要劳工能够思考和计算。然而,思想是一把双刃剑,或者说,是原本严密的墙体中留下的一道危险缝隙,通过这道缝隙,麻烦的、难以预料的、无法估量的因素(如人们对有尊严的生活的热情或自主的冲动)会从之前的放逐中回归。 -- 2026-04-19 +> 在工作场所,工人的自治权是不被容忍的。工作伦理要求人们选择一种献身于劳动的生活,但这也就意味着没有选择、无法选择和禁止选择。 -- 2026-04-20 +> 圆形监狱的训练方式不适合培育消费者。那套体系擅长训练人们习惯例行的、单调的行为,并通过限制选择或完全取消选择巩固效果。然而,不因循守旧、持续进行选择恰恰是消费者的美德(实际上是"角色要求")。因此,圆形监狱式训练不仅在后工业化时代大幅减少,而且与消费者社会的需求背道而驰。它擅长培养的气质和生活态度,与理想的消费者大相径庭。 -- 2026-04-21 +> 理想情况下,消费者立刻得到满足——消费应该立刻带来满足感,没有时延,不需要旷日持久的技能学习和准备工作;而一旦消费行为完成,这种满足感就应该尽可能快地消失。 -- 2026-04-22 +> 他们的生活从吸引到吸引,从诱惑到诱惑,从吞下一个诱饵到寻找另一个诱饵,每一个新的吸引、诱惑和诱饵都不尽相同,似乎比之前的更加诱人。他们生活于这种轮回,就像他们的先辈,那些生产者,生活于一个传送带和下一个传送带之间。 -- 2026-04-23 +> 本来是市场选择了他们,并把他们培养成消费者,剥夺了他们不受诱惑的自由,但每次来到市场,消费者都觉得自己在掌控一切。他们可以评判、评论和选择,他们可以拒绝无限选择中的任何一个——除了"必须作出选择"之外。寻求自我认同,获取社会地位,以他人认为有意义的方式生活,这些都需要日复一日地到访消费市场。 -- 2026-04-24 +> 目前的全球趋势是"通过大幅减少产品和服务的寿命,以及提供不稳定的工作(临时的、灵活的、兼职的工作),将经济导向短周期和不确定的生产"。 -- 2026-04-25 +> "身份"这个词或许已经失去了效用,因为在日常生活中,它所掩饰的比揭露的更多。随着社会地位越来越得到关注,人们恐惧过于牢固的身份认同,害怕在必要时难以全身而退。对社会身份的渴望和恐惧,社会身份唤起的吸引和排斥,混合在一起,产生了一种持久、矛盾、困惑的复杂心态。 -- 2026-04-26 +> 消费品意味着消耗殆尽,时间性和短暂性是其内在特征,它们遍体都写满了死亡的悼词。 -- 2026-04-27 +> 一个人选择的自由度越大,自由行使的选择权越多,他在社会阶层中的地位就越高,获得的社会尊重和自尊就越多,距离"美好生活"的理想也越近。当然,财富和收入也很重要,否则消费选择就会受到限制或被完全剥夺,它们作为资本(用于赚取更多金钱的金钱)的用途即使没有被遗忘,也逐渐退居次席,让位于扩大消费选择的范围。 -- 2026-04-28 +> 消费者社会也是咨询和广告的天堂,是预言家、算命先生、贩卖魔法药水的商人和点金术士的沃土。 -- 2026-04-29 +> 对于合格的消费者来说,世界是一个充满可能性的巨型矩阵,包含着更强烈的感受和更深刻的体验。 -- 2026-04-30 +> 娱乐式工作是一种最令人羡慕的特权,那些有幸得到这种特权的人,一头扎进工作提供的强烈感官享受和令人兴奋的体验中。"工作狂"没有固定的工作时间,7×24小时地专注于工作的挑战。这些人并非过去的奴隶,而是当下幸运和成功的精英。 -- 2026-05-01 +> 韦伯笔下的"清教徒"把自己的工作生活作为道德的修行,作为对神圣戒律的践行,他们认为所有的工作在本质上都是道德问题。今天的精英同样自然而然地认为所有的工作在本质上都是美学问题。 -- 2026-05-02 +> 消费市场比弗洛伊德更有创造力,它唤起了弗洛伊德认为无法实现的幸福状态。秘诀在于:在欲望被安抚之前激发新的欲望,在因占有而感到厌倦、烦躁之前替换新的猎物。 -- 2026-05-03 +> 想要缓解无聊,就需要花钱。如果想一劳永逸地摆脱这个幽灵的纠缠,达到"幸福状态",就需要大量的金钱。欲望是免费的,但实现欲望,进而体验实现欲望的愉悦状态,需要资源。对抗无聊的药方不在医保范畴,金钱才是进入治疗无聊的场所(如商场、游乐园或健身中心)的通行证。 -- 2026-05-04 +> 杰里米·希布鲁克(Jeremy Seabrook)曾提醒过他的读者,当今社会依赖于"制造人为的、主观的不满足感",因为本质上"人们满足于自己拥有的东西才是最可怕的威胁"。于是,人们真正拥有的东西被淡化,被贬低,被较富裕的人锋芒毕露的奢侈消费所掩盖:"富人成为被普遍崇拜的对象"。 -- 2026-05-05 +> 公共福利的理念宣称,应该在任何时候都保证国家的所有公民"有权"过上体面的、有尊严的生活,即使他们对公共财富没有任何贡献。因此,公共福利允许(明示或暗示)把公民生活与"对社会的贡献"分离开来,生产贡献只应在职业范畴中讨论,由此削弱了工作伦理最神圣的、最不容置疑的前提。 -- 2026-05-06 +> 如果不是因为福利国家式的公共保险和资本主义经济需求之间的共鸣,很难想象多因素决定的福利国家,最初能够在一个资本主义主导的社会中获得广泛政治支持。此外,福利国家在长期的"劳动力再商品化"过程中也发挥了至关重要的作用。通过为贫困家庭的孩子提供良好的教育、适当的医疗服务、体面的住所和健康的营养品,它保证了资本主义工业可雇佣劳动力的稳定供给,这是任何公司或集团都无法做到的 -- 2026-05-07 +> 产品的营销必须宣扬(至少口头上)对差异化和选择的崇拜,福利国家则必须追求公民生存环境、需求和人权的平等,它们是彼此对立的。福利国家胜算不大,消费心理造成的压力占据了绝对优势,即使国家能够提供更好的服务,仍然会背负"丧失自由选择的权利"这个消费者社会获得豁免的基本缺陷——在业已皈依的、虔诚的、"获得新生的"消费者眼里,这个缺陷使得福利国家无可救药地声名扫地。 -- 2026-05-08 +> 底层阶级在当今富裕社会最重要的作用之一,就是吸纳恐惧和焦虑,过去强大的外部敌人扮演了这个角色,但他们已不复存在。底层阶级是内部的敌人,注定要取代外部敌人,成为保持社会健康的关键药物,成为源于个体不安全感的社会紧张的安全阀。 -- 2026-05-09 +> 当需要治疗的病症很大程度上是医源性的,即病症是治疗本身的副产品,当重组的对象大多数是过去重组的产物,是过去破坏性喧嚣的恶报时,幻觉就不再能够维系。除了装睡的人,大家都清楚,所有的创造都是创造性的破坏。所有的创造都会留下一些具有污染性且常常有毒性的残渣——"合理化"的废弃物。 -- 2026-05-10 +> 每一个已知的社会都对穷人持一种特有的矛盾态度,一方面是恐惧和反感,另一方面是怜悯和同情。这两种成分都不可或缺。前者允许在需要秩序维护的时候对穷人进行严厉的处理;后者强调了那些达不到标准的人的悲惨命运,由此让正常生活的人在遵守社会规范时遭遇的所有艰辛都变得微不足道。 -- 2026-05-11 +> 人类迷信的根源,是过度自信和绝望交织的矛盾心理。在顺境中,我们拒绝理性建议,而在逆境中,我们又急切地寻找各种预兆来获得确定性。 -- 2026-05-12 +> 卡夫卡曾写道,一部伟大的作品应该像一把斧子,可以劈开我们内心冰封的海洋。 -- 2026-05-13 +> 在中年之路的典型症状背后,有一个假设,即我们能通过寻找和联系外部世界中的新事物或新人而得到拯救。唉,对于溺水的中年水手来说,根本不存在这样的救援。在灵魂的波涛中,虽然周围有许多人,但我们需要靠自己的力量游泳。真理很简单,我们必须知道的必然来自内心。 -- 2026-05-14 +> 若将你内在的东西活出来,它们必能拯救你。若不把你内在的东西活出来,它们必将毁灭你。 -- 2026-05-15 +> 发生在我身上的事,并没有决定我是谁;我的选择才决定了我是谁。 -- 2026-05-16 +> 我们永远无法确定自己有多自由或坚定,但正如存在主义者所提醒的,我们必须像拥有自由一样去行动。这种行动恢复了一个人的尊严和目的,否则他只能继续做一个受害者。 -- 2026-05-17 +> 个体化的悖论在于,我们为亲密关系服务的最佳方式是充分发展自己,不需要依赖他人。同样,我们为社会服务的最佳方式是成为个体,为任何群体的健康发展提供辩证的一面。在社会这幅马赛克画面中,每一块碎片都因其独特的色彩而做出了最大的贡献。当我们有一些独特的东西,有最充实的自我时,我们对社会是最有用的。 -- 2026-05-18 +> 孤独不是当代才有的事物,对孤独的逃避也不是。17世纪哲学家帕斯卡尔(Blaise Pascal)在他的《思想录》中指出,宫廷小丑的发明是为了让国王摆脱孤独,因为尽管他是国王,但如果他反思自我,就会变得烦躁和焦虑。因此,帕斯卡尔认为,所有的现代文化都是一场尽兴的娱乐,让我们远离孤独,不去思考自我。类似的,尼采在100多年前写道:“当我们安静地独处时,我们害怕有人在耳边低语,所以我们讨厌静默,用社交生活来麻醉自己。” -- 2026-05-19 +> 冒着孤独的风险来实现与自己合一,我们称之为独处;如果一个人要在中年之路上幸存下来,这是必不可少的。 -- 2026-05-20 +> 在每天的某些时候,冒险彻底地面对自己,遵循一种安静的仪式,远离内外纷扰的交通,这是颇有好处的。当寂静开始言语时,一个人就获得了自己的陪伴,从孤独走向了独处,这是个体化的一个必要前提。 -- 2026-05-21 +> 中年之路提供了一个无与伦比的机会,让我们去问:“我的内在小孩喜欢什么?”是回去上音乐课;是上美术课,管他天资如何;还是重新发现游戏?我的一位朋友曾经采访过一些退休人员,他说,他从来没有听说过有人希望在办公室里花更多时间。没错,我们需要应对外在的职责和关系,但我们也必须为失落的孩子腾出时间 -- 2026-05-22 +> 但如果我们害怕自己的内心,害怕自己的激情,那么应该更害怕没有活过的生命 -- 2026-05-23 +> 天之道,利而不害,圣人之道,为而不争。 -- 2026-05-24 +> 充满性物化的社会中,很容易拿着社会给的尺子来测量自己,衡量自己是否“符合标准”或“被认为美”。测量和校准反馈的长期循环,这套心理机制又内化为自我认知和获取信心、归属与安全感的来源。 -- 2026-05-25 +> 凡事留不尽之意则机圆,凡物留不尽之意则用裕,凡情留不尽之意则味深,凡言留不尽之意则致远,凡兴留不尽之意则趣多,凡才留不尽之意则神满。 -- 2026-05-26 +> 对青天而惧,闻雷霆而不惊;履平地而恐,涉风波而不疑。 -- 2026-05-27 +> 凡事韬晦,不独益己,抑且益人;凡事表暴,不独损人,抑且损己 -- 2026-05-28 +> 三日不读书,则礼义不交,便觉面目可憎,语言无味。 -- 2026-05-29 +> 处心不可着,着则偏;作事不可尽,尽则穷。 -- 2026-05-30 +> 泛交则多费,多费则多营,多营则多求,多求则多辱。 -- 2026-05-31 +> 正以处心,廉以律已,忠以事君,恭以事长,信以接物,宽以待下,敬以洽事,此居官之七要也。 -- 2026-06-01 +> 惟书不问贵贱贫富老少,观书一卷,则增一卷之益;观书一日,则有一日之益。 -- 2026-06-02 +> 坦易其心胸,率真其笑语,疏野其礼数,简少其交游 -- 2026-06-03 +> 酒能乱性,佛家戒之;酒能养气,仙家饮之。余于无酒时学佛,有酒时学仙。 -- 2026-06-04 +> 才人之行多放,当以正敛之;正人之行多板,当以趣通之 -- 2026-06-05 +> 人人爱睡,知其味者甚鲜;睡则双眼一合,百事俱忘,肢体皆适,尘劳尽消,即黄梁南柯,特余事已耳。静修诗云:"书外论交睡最贤。"旨哉言也。 -- 2026-06-06 +> 宠辱不惊,肝木自宁;动静以敬,心火自定;饮食有节,脾土不泄;调息寡言,肺金自全;怡神寡欲,肾水自足。 -- 2026-06-07 +> 宠辱不惊,闲看庭前花开花落;去留无意,漫随天外云卷云舒。斗室中万虑都捐,说甚画栋飞云,珠帘卷雨;三杯后一真自得,谁知素弦横月,短笛吟风。 -- 2026-06-08 +> 随分款留,忘形笑语,不言是非,不侈荣利,闲谈古今,静玩山水,清茶好酒,以适幽趣,臭味之交,如斯而已。 -- 2026-06-09 +> 权轻势去,何妨张雀罗于门前;位高金多,自当效蛇行于郊外。盖炎凉世态,本是常情,故人所浩叹,惟宜付之冷笑耳。 -- 2026-06-10 +> 强项者未必为穷之路,屈膝者未必为通之媒。故铜头铁面,君子落得做个君子;奴颜婢膝,小人枉自做了小人。 -- 2026-06-11 +> 群居闭口,独坐防心。 -- 2026-06-12 +> 人生不得行胸怀,虽寿百岁,犹夭也。 -- 2026-06-13 +> 心珠宜当独朗 -- 2026-06-14 +> 胸中涤去数斗尘 -- 2026-06-15 +> 俗气入骨,即吞刀刮肠,饮灰洗胃,觉俗态之益呈 -- 2026-06-16 +> 读《春秋》,在人事上见天理;读《周易》,在天理上见人事。 -- 2026-06-17 +> 看透阴阳颠倒之行,惟此冷眼一只。 -- 2026-06-18 +> 点破无稽不根之论,只须冷语半言;看透阴阳颠倒之行,惟此冷眼一只。 -- 2026-06-19 +> 天台杰起,绕之以赤霞;赤城孤峙,覆之以莲花。 -- 2026-06-20 +> 武士无刀兵气,书生无寒酸气,女郎无脂粉气,山人无烟霞气,僧家无香火气,换出一番世界,便为世上不可少之人。 -- 2026-06-21 +> 蒲团布衲,难于少时存老去之禅心;玉剑角弓,贵于老时任少年之侠气。 -- 2026-06-22 +> 桃花马上,春衫少年侠气;贝叶斋中,夜衲老去禅心。 -- 2026-06-23 +> 读书倦时须看剑,英发之气不磨;作文苦际可歌诗,郁结之怀随畅。 -- 2026-06-24 +> 负心满天地,辜他一片热肠;变态自古今,悬此两只冷眼。 -- 2026-06-25 +> 杀得人者,方能生人。有恩者,必然有怨。若使不阴不阳,随世披靡,肉菩萨出世,于世何补?此生何用? -- 2026-06-26 +> 上马横槊,下马作赋,自是英雄本色;熟读《离骚》,痛饮浊酒,果然名士风流。 -- 2026-06-27 +> 尘情一破,便同鸡犬为仙,世法相拘,何异鹤鹅作阵。 -- 2026-06-28 +> 医俗病莫如书,赠酒狂莫如月。 -- 2026-06-29 +> 琴瑟简编,学者不可无,盖有业以居之,心就不放。 -- 2026-06-30 +> 不平等问题之所以棘手,是因为“平等”不是唯一的目标—我们要的不是“向下的平等”,不是把上面的人拉下来,而是把下面的人拉上去。平等必须和别的价值并存:自由、秩序、创新、责任⋯如何平衡才是最艰难的。历史一再展示,追求平等这件事,我们或许有一种方式把它做对,却有1000种方式把它做错。 -- 2026-07-01 +> 这里有一些箴言需要记住: (1)没有激情的生活,是没有深度的生活。 (2)激情是生命力的表现,虽然它对秩序、可预测性和理智来说是危险的。 (3)如果不冒险去过神性所要求的、激情所提供的广阔生活,就不能接近神性,接近原型的深处。 (4)寻找并追随自己的激情,有助于我们的个体化。 -- 2026-07-02 +> 对年轻人来说,另一个与自我有关的希望是对完美关系的期待。虽然我们看到周围许多不甚完美的关系,但还是倾向于认为自己在某种程度上更聪明,更有能力做出选择、避开陷阱。《古兰经》训诫:“你以为你将进入极乐世界,而不用像前人那样经受试炼了吗? -- 2026-07-03 +> 如果说古典主义唤起的是曾拥有过的东西,那么浪漫主义试图唤起的则是那些本就不曾有过的。浪漫最初的本质是失落,不仅那些想象之物不会被真正拥有,甚至连想象自身就已是破碎的。倘若古典是美轮美奂的花瓶,浪漫就是其碎片。(齐泽克) -- 2026-07-04 +> 在探索自我解放的路上,我们常常会误入对于“最完美的偶像”、“最真实的自我”或“最正确的真理”的执着中,将某种观念视为绝对性的法则,从而彼此排斥与相互倾轧。 -- 2026-07-05 +> 读书作文,安能累人?人自累于得失耳。 -- 2026-07-06