discstation 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -114,7 +114,6 @@ Support by OS:
114
114
  ./install.sh
115
115
 
116
116
  # Arduino (ESP32 DevKit remote)
117
- arduino-cli lib install QRCode
118
117
  arduino-cli compile --upload -b esp32:esp32:esp32 -p /dev/ttyUSB0 arduino/DiscStation
119
118
 
120
119
  # macOS host
@@ -8,7 +8,7 @@
8
8
  #include <ESPmDNS.h>
9
9
  #include <Preferences.h>
10
10
  #include <ArduinoOTA.h>
11
- #include <QRCode.h>
11
+ #include "qrcode_lite.h"
12
12
 
13
13
  // Optional build-time Wi-Fi override for power users: copy secrets.h.example
14
14
  // to secrets.h (git-ignored) and set WIFI_SSID / WIFI_PASS / OTA_PASSWORD.
@@ -0,0 +1,872 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2017 Richard Moore
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+
25
+ /**
26
+ * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
27
+ * heavily inspired and compared against.
28
+ *
29
+ * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
30
+ */
31
+
32
+ #include "qrcode_lite.h"
33
+
34
+ #include <stdlib.h>
35
+ #include <string.h>
36
+
37
+ #pragma mark - Error Correction Lookup tables
38
+
39
+ #if LOCK_VERSION == 0
40
+
41
+ static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = {
42
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
43
+ { 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium
44
+ { 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low
45
+ { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High
46
+ { 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile
47
+ };
48
+
49
+ static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = {
50
+ // Version: (note that index 0 is for padding, and is set to an illegal value)
51
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
52
+ { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
53
+ { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
54
+ { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
55
+ { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
56
+ };
57
+
58
+ static const uint16_t NUM_RAW_DATA_MODULES[40] = {
59
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
60
+ 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523,
61
+ // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
62
+ 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587,
63
+ // 32, 33, 34, 35, 36, 37, 38, 39, 40
64
+ 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648
65
+ };
66
+
67
+ // @TODO: Put other LOCK_VERSIONS here
68
+ #elif LOCK_VERSION == 3
69
+
70
+ static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {
71
+ 26, 15, 44, 36
72
+ };
73
+
74
+ static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {
75
+ 1, 1, 2, 2
76
+ };
77
+
78
+ static const uint16_t NUM_RAW_DATA_MODULES = 567;
79
+
80
+ #else
81
+
82
+ #error Unsupported LOCK_VERSION (add it...)
83
+
84
+ #endif
85
+
86
+
87
+ static int max(int a, int b) {
88
+ if (a > b) { return a; }
89
+ return b;
90
+ }
91
+
92
+ /*
93
+ static int abs(int value) {
94
+ if (value < 0) { return -value; }
95
+ return value;
96
+ }
97
+ */
98
+
99
+
100
+ #pragma mark - Mode testing and conversion
101
+
102
+ static int8_t getAlphanumeric(char c) {
103
+
104
+ if (c >= '0' && c <= '9') { return (c - '0'); }
105
+ if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); }
106
+
107
+ switch (c) {
108
+ case ' ': return 36;
109
+ case '$': return 37;
110
+ case '%': return 38;
111
+ case '*': return 39;
112
+ case '+': return 40;
113
+ case '-': return 41;
114
+ case '.': return 42;
115
+ case '/': return 43;
116
+ case ':': return 44;
117
+ }
118
+
119
+ return -1;
120
+ }
121
+
122
+ static bool isAlphanumeric(const char *text, uint16_t length) {
123
+ while (length != 0) {
124
+ if (getAlphanumeric(text[--length]) == -1) { return false; }
125
+ }
126
+ return true;
127
+ }
128
+
129
+
130
+ static bool isNumeric(const char *text, uint16_t length) {
131
+ while (length != 0) {
132
+ char c = text[--length];
133
+ if (c < '0' || c > '9') { return false; }
134
+ }
135
+ return true;
136
+ }
137
+
138
+
139
+ #pragma mark - Counting
140
+
141
+ // We store the following tightly packed (less 8) in modeInfo
142
+ // <=9 <=26 <= 40
143
+ // NUMERIC ( 10, 12, 14);
144
+ // ALPHANUMERIC ( 9, 11, 13);
145
+ // BYTE ( 8, 16, 16);
146
+ static char getModeBits(uint8_t version, uint8_t mode) {
147
+ // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits
148
+ // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2))
149
+ unsigned int modeInfo = 0x7bbb80a;
150
+
151
+ #if LOCK_VERSION == 0 || LOCK_VERSION > 9
152
+ if (version > 9) { modeInfo >>= 9; }
153
+ #endif
154
+
155
+ #if LOCK_VERSION == 0 || LOCK_VERSION > 26
156
+ if (version > 26) { modeInfo >>= 9; }
157
+ #endif
158
+
159
+ char result = 8 + ((modeInfo >> (3 * mode)) & 0x07);
160
+ if (result == 15) { result = 16; }
161
+
162
+ return result;
163
+ }
164
+
165
+
166
+ #pragma mark - BitBucket
167
+
168
+ typedef struct BitBucket {
169
+ uint32_t bitOffsetOrWidth;
170
+ uint16_t capacityBytes;
171
+ uint8_t *data;
172
+ } BitBucket;
173
+
174
+ /*
175
+ void bb_dump(BitBucket *bitBuffer) {
176
+ printf("Buffer: ");
177
+ for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) {
178
+ printf("%02x", bitBuffer->data[i]);
179
+ if ((i % 4) == 3) { printf(" "); }
180
+ }
181
+ printf("\n");
182
+ }
183
+ */
184
+
185
+ static uint16_t bb_getGridSizeBytes(uint8_t size) {
186
+ return (((size * size) + 7) / 8);
187
+ }
188
+
189
+ static uint16_t bb_getBufferSizeBytes(uint32_t bits) {
190
+ return ((bits + 7) / 8);
191
+ }
192
+
193
+ static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) {
194
+ bitBuffer->bitOffsetOrWidth = 0;
195
+ bitBuffer->capacityBytes = capacityBytes;
196
+ bitBuffer->data = data;
197
+
198
+ memset(data, 0, bitBuffer->capacityBytes);
199
+ }
200
+
201
+ static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) {
202
+ bitGrid->bitOffsetOrWidth = size;
203
+ bitGrid->capacityBytes = bb_getGridSizeBytes(size);
204
+ bitGrid->data = data;
205
+
206
+ memset(data, 0, bitGrid->capacityBytes);
207
+ }
208
+
209
+ static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) {
210
+ uint32_t offset = bitBuffer->bitOffsetOrWidth;
211
+ for (int8_t i = length - 1; i >= 0; i--, offset++) {
212
+ bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
213
+ }
214
+ bitBuffer->bitOffsetOrWidth = offset;
215
+ }
216
+ /*
217
+ void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) {
218
+ for (int8_t i = length - 1; i >= 0; i--, offset++) {
219
+ bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
220
+ }
221
+ }
222
+ */
223
+ static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) {
224
+ uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
225
+ uint8_t mask = 1 << (7 - (offset & 0x07));
226
+ if (on) {
227
+ bitGrid->data[offset >> 3] |= mask;
228
+ } else {
229
+ bitGrid->data[offset >> 3] &= ~mask;
230
+ }
231
+ }
232
+
233
+ static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) {
234
+ uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
235
+ uint8_t mask = 1 << (7 - (offset & 0x07));
236
+ bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0);
237
+ if (on ^ invert) {
238
+ bitGrid->data[offset >> 3] |= mask;
239
+ } else {
240
+ bitGrid->data[offset >> 3] &= ~mask;
241
+ }
242
+ }
243
+
244
+ static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) {
245
+ uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
246
+ return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
247
+ }
248
+
249
+
250
+ #pragma mark - Drawing Patterns
251
+
252
+ // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
253
+ // properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
254
+ // This means it is possible to apply a mask, undo it, and try another mask. Note that a final
255
+ // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
256
+ static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) {
257
+ uint8_t size = modules->bitOffsetOrWidth;
258
+
259
+ for (uint8_t y = 0; y < size; y++) {
260
+ for (uint8_t x = 0; x < size; x++) {
261
+ if (bb_getBit(isFunction, x, y)) { continue; }
262
+
263
+ bool invert = 0;
264
+ switch (mask) {
265
+ case 0: invert = (x + y) % 2 == 0; break;
266
+ case 1: invert = y % 2 == 0; break;
267
+ case 2: invert = x % 3 == 0; break;
268
+ case 3: invert = (x + y) % 3 == 0; break;
269
+ case 4: invert = (x / 3 + y / 2) % 2 == 0; break;
270
+ case 5: invert = x * y % 2 + x * y % 3 == 0; break;
271
+ case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;
272
+ case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;
273
+ }
274
+ bb_invertBit(modules, x, y, invert);
275
+ }
276
+ }
277
+ }
278
+
279
+ static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) {
280
+ bb_setBit(modules, x, y, on);
281
+ bb_setBit(isFunction, x, y, true);
282
+ }
283
+
284
+ // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
285
+ static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
286
+ uint8_t size = modules->bitOffsetOrWidth;
287
+
288
+ for (int8_t i = -4; i <= 4; i++) {
289
+ for (int8_t j = -4; j <= 4; j++) {
290
+ uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm
291
+ int16_t xx = x + j, yy = y + i;
292
+ if (0 <= xx && xx < size && 0 <= yy && yy < size) {
293
+ setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4);
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ // Draws a 5*5 alignment pattern, with the center module at (x, y).
300
+ static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
301
+ for (int8_t i = -2; i <= 2; i++) {
302
+ for (int8_t j = -2; j <= 2; j++) {
303
+ setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1);
304
+ }
305
+ }
306
+ }
307
+
308
+ // Draws two copies of the format bits (with its own error correction code)
309
+ // based on the given mask and this object's error correction level field.
310
+ static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) {
311
+
312
+ uint8_t size = modules->bitOffsetOrWidth;
313
+
314
+ // Calculate error correction code and pack bits
315
+ uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3
316
+ uint32_t rem = data;
317
+ for (int i = 0; i < 10; i++) {
318
+ rem = (rem << 1) ^ ((rem >> 9) * 0x537);
319
+ }
320
+
321
+ data = data << 10 | rem;
322
+ data ^= 0x5412; // uint15
323
+
324
+ // Draw first copy
325
+ for (uint8_t i = 0; i <= 5; i++) {
326
+ setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0);
327
+ }
328
+
329
+ setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0);
330
+ setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0);
331
+ setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0);
332
+
333
+ for (int8_t i = 9; i < 15; i++) {
334
+ setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0);
335
+ }
336
+
337
+ // Draw second copy
338
+ for (int8_t i = 0; i <= 7; i++) {
339
+ setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0);
340
+ }
341
+
342
+ for (int8_t i = 8; i < 15; i++) {
343
+ setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0);
344
+ }
345
+
346
+ setFunctionModule(modules, isFunction, 8, size - 8, true);
347
+ }
348
+
349
+
350
+ // Draws two copies of the version bits (with its own error correction code),
351
+ // based on this object's version field (which only has an effect for 7 <= version <= 40).
352
+ static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) {
353
+
354
+ int8_t size = modules->bitOffsetOrWidth;
355
+
356
+ #if LOCK_VERSION != 0 && LOCK_VERSION < 7
357
+ return;
358
+
359
+ #else
360
+ if (version < 7) { return; }
361
+
362
+ // Calculate error correction code and pack bits
363
+ uint32_t rem = version; // version is uint6, in the range [7, 40]
364
+ for (uint8_t i = 0; i < 12; i++) {
365
+ rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
366
+ }
367
+
368
+ uint32_t data = version << 12 | rem; // uint18
369
+
370
+ // Draw two copies
371
+ for (uint8_t i = 0; i < 18; i++) {
372
+ bool bit = ((data >> i) & 1) != 0;
373
+ uint8_t a = size - 11 + i % 3, b = i / 3;
374
+ setFunctionModule(modules, isFunction, a, b, bit);
375
+ setFunctionModule(modules, isFunction, b, a, bit);
376
+ }
377
+
378
+ #endif
379
+ }
380
+
381
+ static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) {
382
+
383
+ uint8_t size = modules->bitOffsetOrWidth;
384
+
385
+ // Draw the horizontal and vertical timing patterns
386
+ for (uint8_t i = 0; i < size; i++) {
387
+ setFunctionModule(modules, isFunction, 6, i, i % 2 == 0);
388
+ setFunctionModule(modules, isFunction, i, 6, i % 2 == 0);
389
+ }
390
+
391
+ // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
392
+ drawFinderPattern(modules, isFunction, 3, 3);
393
+ drawFinderPattern(modules, isFunction, size - 4, 3);
394
+ drawFinderPattern(modules, isFunction, 3, size - 4);
395
+
396
+ #if LOCK_VERSION == 0 || LOCK_VERSION > 1
397
+
398
+ if (version > 1) {
399
+
400
+ // Draw the numerous alignment patterns
401
+
402
+ uint8_t alignCount = version / 7 + 2;
403
+ uint8_t step;
404
+ if (version != 32) {
405
+ step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2
406
+ } else { // C-C-C-Combo breaker!
407
+ step = 26;
408
+ }
409
+
410
+ uint8_t alignPositionIndex = alignCount - 1;
411
+ uint8_t alignPosition[alignCount];
412
+
413
+ alignPosition[0] = 6;
414
+
415
+ uint8_t size = version * 4 + 17;
416
+ for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) {
417
+ alignPosition[alignPositionIndex--] = pos;
418
+ }
419
+
420
+ for (uint8_t i = 0; i < alignCount; i++) {
421
+ for (uint8_t j = 0; j < alignCount; j++) {
422
+ if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) {
423
+ continue; // Skip the three finder corners
424
+ } else {
425
+ drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]);
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ #endif
432
+
433
+ // Draw configuration data
434
+ drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor
435
+ drawVersion(modules, isFunction, version);
436
+ }
437
+
438
+
439
+ // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
440
+ // data area of this QR Code symbol. Function modules need to be marked off before this is called.
441
+ static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) {
442
+
443
+ uint32_t bitLength = codewords->bitOffsetOrWidth;
444
+ uint8_t *data = codewords->data;
445
+
446
+ uint8_t size = modules->bitOffsetOrWidth;
447
+
448
+ // Bit index into the data
449
+ uint32_t i = 0;
450
+
451
+ // Do the funny zigzag scan
452
+ for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
453
+ if (right == 6) { right = 5; }
454
+
455
+ for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter
456
+ for (int j = 0; j < 2; j++) {
457
+ uint8_t x = right - j; // Actual x coordinate
458
+ bool upwards = ((right & 2) == 0) ^ (x < 6);
459
+ uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate
460
+ if (!bb_getBit(isFunction, x, y) && i < bitLength) {
461
+ bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0);
462
+ i++;
463
+ }
464
+ // If there are any remainder bits (0 to 7), they are already
465
+ // set to 0/false/white when the grid of modules was initialized
466
+ }
467
+ }
468
+ }
469
+ }
470
+
471
+
472
+
473
+ #pragma mark - Penalty Calculation
474
+
475
+ #define PENALTY_N1 3
476
+ #define PENALTY_N2 3
477
+ #define PENALTY_N3 40
478
+ #define PENALTY_N4 10
479
+
480
+ // Calculates and returns the penalty score based on state of this QR Code's current modules.
481
+ // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
482
+ // @TODO: This can be optimized by working with the bytes instead of bits.
483
+ static uint32_t getPenaltyScore(BitBucket *modules) {
484
+ uint32_t result = 0;
485
+
486
+ uint8_t size = modules->bitOffsetOrWidth;
487
+
488
+ // Adjacent modules in row having same color
489
+ for (uint8_t y = 0; y < size; y++) {
490
+
491
+ bool colorX = bb_getBit(modules, 0, y);
492
+ for (uint8_t x = 1, runX = 1; x < size; x++) {
493
+ bool cx = bb_getBit(modules, x, y);
494
+ if (cx != colorX) {
495
+ colorX = cx;
496
+ runX = 1;
497
+
498
+ } else {
499
+ runX++;
500
+ if (runX == 5) {
501
+ result += PENALTY_N1;
502
+ } else if (runX > 5) {
503
+ result++;
504
+ }
505
+ }
506
+ }
507
+ }
508
+
509
+ // Adjacent modules in column having same color
510
+ for (uint8_t x = 0; x < size; x++) {
511
+ bool colorY = bb_getBit(modules, x, 0);
512
+ for (uint8_t y = 1, runY = 1; y < size; y++) {
513
+ bool cy = bb_getBit(modules, x, y);
514
+ if (cy != colorY) {
515
+ colorY = cy;
516
+ runY = 1;
517
+ } else {
518
+ runY++;
519
+ if (runY == 5) {
520
+ result += PENALTY_N1;
521
+ } else if (runY > 5) {
522
+ result++;
523
+ }
524
+ }
525
+ }
526
+ }
527
+
528
+ uint16_t black = 0;
529
+ for (uint8_t y = 0; y < size; y++) {
530
+ uint16_t bitsRow = 0, bitsCol = 0;
531
+ for (uint8_t x = 0; x < size; x++) {
532
+ bool color = bb_getBit(modules, x, y);
533
+
534
+ // 2*2 blocks of modules having same color
535
+ if (x > 0 && y > 0) {
536
+ bool colorUL = bb_getBit(modules, x - 1, y - 1);
537
+ bool colorUR = bb_getBit(modules, x, y - 1);
538
+ bool colorL = bb_getBit(modules, x - 1, y);
539
+ if (color == colorUL && color == colorUR && color == colorL) {
540
+ result += PENALTY_N2;
541
+ }
542
+ }
543
+
544
+ // Finder-like pattern in rows and columns
545
+ bitsRow = ((bitsRow << 1) & 0x7FF) | color;
546
+ bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x);
547
+
548
+ // Needs 11 bits accumulated
549
+ if (x >= 10) {
550
+ if (bitsRow == 0x05D || bitsRow == 0x5D0) {
551
+ result += PENALTY_N3;
552
+ }
553
+ if (bitsCol == 0x05D || bitsCol == 0x5D0) {
554
+ result += PENALTY_N3;
555
+ }
556
+ }
557
+
558
+ // Balance of black and white modules
559
+ if (color) { black++; }
560
+ }
561
+ }
562
+
563
+ // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
564
+ uint16_t total = size * size;
565
+ for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) {
566
+ result += PENALTY_N4;
567
+ }
568
+
569
+ return result;
570
+ }
571
+
572
+
573
+ #pragma mark - Reed-Solomon Generator
574
+
575
+ static uint8_t rs_multiply(uint8_t x, uint8_t y) {
576
+ // Russian peasant multiplication
577
+ // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
578
+ uint16_t z = 0;
579
+ for (int8_t i = 7; i >= 0; i--) {
580
+ z = (z << 1) ^ ((z >> 7) * 0x11D);
581
+ z ^= ((y >> i) & 1) * x;
582
+ }
583
+ return z;
584
+ }
585
+
586
+ static void rs_init(uint8_t degree, uint8_t *coeff) {
587
+ memset(coeff, 0, degree);
588
+ coeff[degree - 1] = 1;
589
+
590
+ // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
591
+ // drop the highest term, and store the rest of the coefficients in order of descending powers.
592
+ // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
593
+ uint16_t root = 1;
594
+ for (uint8_t i = 0; i < degree; i++) {
595
+ // Multiply the current product by (x - r^i)
596
+ for (uint8_t j = 0; j < degree; j++) {
597
+ coeff[j] = rs_multiply(coeff[j], root);
598
+ if (j + 1 < degree) {
599
+ coeff[j] ^= coeff[j + 1];
600
+ }
601
+ }
602
+ root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D)
603
+ }
604
+ }
605
+
606
+ static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) {
607
+ // Compute the remainder by performing polynomial division
608
+
609
+ //for (uint8_t i = 0; i < degree; i++) { result[] = 0; }
610
+ //memset(result, 0, degree);
611
+
612
+ for (uint8_t i = 0; i < length; i++) {
613
+ uint8_t factor = data[i] ^ result[0];
614
+ for (uint8_t j = 1; j < degree; j++) {
615
+ result[(j - 1) * stride] = result[j * stride];
616
+ }
617
+ result[(degree - 1) * stride] = 0;
618
+
619
+ for (uint8_t j = 0; j < degree; j++) {
620
+ result[j * stride] ^= rs_multiply(coeff[j], factor);
621
+ }
622
+ }
623
+ }
624
+
625
+
626
+
627
+ #pragma mark - QrCode
628
+
629
+ static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) {
630
+ int8_t mode = MODE_BYTE;
631
+
632
+ if (isNumeric((char*)text, length)) {
633
+ mode = MODE_NUMERIC;
634
+ bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4);
635
+ bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC));
636
+
637
+ uint16_t accumData = 0;
638
+ uint8_t accumCount = 0;
639
+ for (uint16_t i = 0; i < length; i++) {
640
+ accumData = accumData * 10 + ((char)(text[i]) - '0');
641
+ accumCount++;
642
+ if (accumCount == 3) {
643
+ bb_appendBits(dataCodewords, accumData, 10);
644
+ accumData = 0;
645
+ accumCount = 0;
646
+ }
647
+ }
648
+
649
+ // 1 or 2 digits remaining
650
+ if (accumCount > 0) {
651
+ bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1);
652
+ }
653
+
654
+ } else if (isAlphanumeric((char*)text, length)) {
655
+ mode = MODE_ALPHANUMERIC;
656
+ bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4);
657
+ bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC));
658
+
659
+ uint16_t accumData = 0;
660
+ uint8_t accumCount = 0;
661
+ for (uint16_t i = 0; i < length; i++) {
662
+ accumData = accumData * 45 + getAlphanumeric((char)(text[i]));
663
+ accumCount++;
664
+ if (accumCount == 2) {
665
+ bb_appendBits(dataCodewords, accumData, 11);
666
+ accumData = 0;
667
+ accumCount = 0;
668
+ }
669
+ }
670
+
671
+ // 1 character remaining
672
+ if (accumCount > 0) {
673
+ bb_appendBits(dataCodewords, accumData, 6);
674
+ }
675
+
676
+ } else {
677
+ bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4);
678
+ bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE));
679
+ for (uint16_t i = 0; i < length; i++) {
680
+ bb_appendBits(dataCodewords, (char)(text[i]), 8);
681
+ }
682
+ }
683
+
684
+ //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode));
685
+
686
+ return mode;
687
+ }
688
+
689
+ static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) {
690
+
691
+ // See: http://www.thonky.com/qr-code-tutorial/structure-final-message
692
+
693
+ #if LOCK_VERSION == 0
694
+ uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1];
695
+ uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1];
696
+ uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
697
+ #else
698
+ uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc];
699
+ uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc];
700
+ uint16_t moduleCount = NUM_RAW_DATA_MODULES;
701
+ #endif
702
+
703
+ uint8_t blockEccLen = totalEcc / numBlocks;
704
+ uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks;
705
+ uint8_t shortBlockLen = moduleCount / 8 / numBlocks;
706
+
707
+ uint8_t shortDataBlockLen = shortBlockLen - blockEccLen;
708
+
709
+ uint8_t result[data->capacityBytes];
710
+ memset(result, 0, sizeof(result));
711
+
712
+ uint8_t coeff[blockEccLen];
713
+ rs_init(blockEccLen, coeff);
714
+
715
+ uint16_t offset = 0;
716
+ uint8_t *dataBytes = data->data;
717
+
718
+
719
+ // Interleave all short blocks
720
+ for (uint8_t i = 0; i < shortDataBlockLen; i++) {
721
+ uint16_t index = i;
722
+ uint8_t stride = shortDataBlockLen;
723
+ for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
724
+ result[offset++] = dataBytes[index];
725
+
726
+ #if LOCK_VERSION == 0 || LOCK_VERSION >= 5
727
+ if (blockNum == numShortBlocks) { stride++; }
728
+ #endif
729
+ index += stride;
730
+ }
731
+ }
732
+
733
+ // Version less than 5 only have short blocks
734
+ #if LOCK_VERSION == 0 || LOCK_VERSION >= 5
735
+ {
736
+ // Interleave long blocks
737
+ uint16_t index = shortDataBlockLen * (numShortBlocks + 1);
738
+ uint8_t stride = shortDataBlockLen;
739
+ for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) {
740
+ result[offset++] = dataBytes[index];
741
+
742
+ if (blockNum == 0) { stride++; }
743
+ index += stride;
744
+ }
745
+ }
746
+ #endif
747
+
748
+ // Add all ecc blocks, interleaved
749
+ uint8_t blockSize = shortDataBlockLen;
750
+ for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
751
+
752
+ #if LOCK_VERSION == 0 || LOCK_VERSION >= 5
753
+ if (blockNum == numShortBlocks) { blockSize++; }
754
+ #endif
755
+ rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks);
756
+ dataBytes += blockSize;
757
+ }
758
+
759
+ memcpy(data->data, result, data->capacityBytes);
760
+ data->bitOffsetOrWidth = moduleCount;
761
+ }
762
+
763
+ // We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits)
764
+ // The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc)
765
+ static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0);
766
+
767
+
768
+ #pragma mark - Public QRCode functions
769
+
770
+ uint16_t qrcode_getBufferSize(uint8_t version) {
771
+ return bb_getGridSizeBytes(4 * version + 17);
772
+ }
773
+
774
+ // @TODO: Return error if data is too big.
775
+ int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) {
776
+ uint8_t size = version * 4 + 17;
777
+ qrcode->version = version;
778
+ qrcode->size = size;
779
+ qrcode->ecc = ecc;
780
+ qrcode->modules = modules;
781
+
782
+ uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03;
783
+
784
+ #if LOCK_VERSION == 0
785
+ uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
786
+ uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1];
787
+ #else
788
+ version = LOCK_VERSION;
789
+ uint16_t moduleCount = NUM_RAW_DATA_MODULES;
790
+ uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits];
791
+ #endif
792
+
793
+ struct BitBucket codewords;
794
+ uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
795
+ bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));
796
+
797
+ // Place the data code words into the buffer
798
+ int8_t mode = encodeDataCodewords(&codewords, data, length, version);
799
+
800
+ if (mode < 0) { return -1; }
801
+ qrcode->mode = mode;
802
+
803
+ // Add terminator and pad up to a byte if applicable
804
+ uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth;
805
+ if (padding > 4) { padding = 4; }
806
+ bb_appendBits(&codewords, 0, padding);
807
+ bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8);
808
+
809
+ // Pad with alternate bytes until data capacity is reached
810
+ for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) {
811
+ bb_appendBits(&codewords, padByte, 8);
812
+ }
813
+
814
+ BitBucket modulesGrid;
815
+ bb_initGrid(&modulesGrid, modules, size);
816
+
817
+ BitBucket isFunctionGrid;
818
+ uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
819
+ bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
820
+
821
+ // Draw function patterns, draw all codewords, do masking
822
+ drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits);
823
+ performErrorCorrection(version, eccFormatBits, &codewords);
824
+ drawCodewords(&modulesGrid, &isFunctionGrid, &codewords);
825
+
826
+ // Find the best (lowest penalty) mask
827
+ uint8_t mask = 0;
828
+ int32_t minPenalty = INT32_MAX;
829
+ for (uint8_t i = 0; i < 8; i++) {
830
+ drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i);
831
+ applyMask(&modulesGrid, &isFunctionGrid, i);
832
+ int penalty = getPenaltyScore(&modulesGrid);
833
+ if (penalty < minPenalty) {
834
+ mask = i;
835
+ minPenalty = penalty;
836
+ }
837
+ applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR
838
+ }
839
+
840
+ qrcode->mask = mask;
841
+
842
+ // Overwrite old format bits
843
+ drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask);
844
+
845
+ // Apply the final choice of mask
846
+ applyMask(&modulesGrid, &isFunctionGrid, mask);
847
+
848
+ return 0;
849
+ }
850
+
851
+ int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) {
852
+ return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data));
853
+ }
854
+
855
+ bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) {
856
+ if (x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) {
857
+ return false;
858
+ }
859
+
860
+ uint32_t offset = y * qrcode->size + x;
861
+ return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
862
+ }
863
+
864
+ /*
865
+ uint8_t qrcode_getHexLength(QRCode *qrcode) {
866
+ return ((qrcode->size * qrcode->size) + 7) / 4;
867
+ }
868
+
869
+ void qrcode_getHex(QRCode *qrcode, char *result) {
870
+
871
+ }
872
+ */
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2017 Richard Moore
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+
25
+ /**
26
+ * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
27
+ * heavily inspired and compared against.
28
+ *
29
+ * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
30
+ */
31
+
32
+
33
+ #ifndef __QRCODE_H_
34
+ #define __QRCODE_H_
35
+
36
+ #ifndef __cplusplus
37
+ typedef unsigned char bool;
38
+ static const bool false = 0;
39
+ static const bool true = 1;
40
+ #endif
41
+
42
+ #include <stdint.h>
43
+
44
+
45
+ // QR Code Format Encoding
46
+ #define MODE_NUMERIC 0
47
+ #define MODE_ALPHANUMERIC 1
48
+ #define MODE_BYTE 2
49
+
50
+
51
+ // Error Correction Code Levels
52
+ #define ECC_LOW 0
53
+ #define ECC_MEDIUM 1
54
+ #define ECC_QUARTILE 2
55
+ #define ECC_HIGH 3
56
+
57
+
58
+ // If set to non-zero, this library can ONLY produce QR codes at that version
59
+ // This saves a lot of dynamic memory, as the codeword tables are skipped
60
+ #ifndef LOCK_VERSION
61
+ #define LOCK_VERSION 0
62
+ #endif
63
+
64
+
65
+ typedef struct QRCode {
66
+ uint8_t version;
67
+ uint8_t size;
68
+ uint8_t ecc;
69
+ uint8_t mode;
70
+ uint8_t mask;
71
+ uint8_t *modules;
72
+ } QRCode;
73
+
74
+
75
+ #ifdef __cplusplus
76
+ extern "C"{
77
+ #endif /* __cplusplus */
78
+
79
+
80
+
81
+ uint16_t qrcode_getBufferSize(uint8_t version);
82
+
83
+ int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data);
84
+ int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length);
85
+
86
+ bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);
87
+
88
+
89
+
90
+ #ifdef __cplusplus
91
+ }
92
+ #endif /* __cplusplus */
93
+
94
+
95
+ #endif /* __QRCODE_H_ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discstation",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "DiscStation optical-media appliance host — one cross-OS installer",
5
5
  "bin": {
6
6
  "discstation-setup": "scripts/setup.mjs",