@wodzik/cubecore 0.1.0
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/LICENSE +373 -0
- package/README.md +118 -0
- package/analyze/client.d.ts +18 -0
- package/analyze/client.js +42 -0
- package/analyze/index.d.ts +114 -0
- package/analyze/index.js +151 -0
- package/analyze/rotations.d.ts +3 -0
- package/analyze/rotations.js +15 -0
- package/analyze/roux.d.ts +67 -0
- package/analyze/roux.js +95 -0
- package/analyze/worker.d.ts +6 -0
- package/analyze/worker.js +31 -0
- package/analyze/zz.d.ts +47 -0
- package/analyze/zz.js +102 -0
- package/bld/index.d.ts +14 -0
- package/bld/index.js +14 -0
- package/bld/memo.d.ts +66 -0
- package/bld/memo.js +139 -0
- package/bld/scheme.d.ts +30 -0
- package/bld/scheme.js +35 -0
- package/bld/skin.d.ts +26 -0
- package/bld/skin.js +46 -0
- package/bld/tracker.d.ts +68 -0
- package/bld/tracker.js +145 -0
- package/bluetooth/clock.d.ts +22 -0
- package/bluetooth/clock.js +37 -0
- package/bluetooth/cubeSkins.d.ts +31 -0
- package/bluetooth/cubeSkins.js +32 -0
- package/bluetooth/gyro.d.ts +31 -0
- package/bluetooth/gyro.js +46 -0
- package/bluetooth/index.d.ts +5 -0
- package/bluetooth/index.js +5 -0
- package/bluetooth/session.d.ts +140 -0
- package/bluetooth/session.js +201 -0
- package/bluetooth/simulated.d.ts +52 -0
- package/bluetooth/simulated.js +80 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.d.ts +38 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.js +51 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.d.ts +35 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.js +70 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.d.ts +273 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.js +846 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.d.ts +38 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.js +340 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.js +118 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.d.ts +2 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.js +15 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.d.ts +18 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.js +152 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.d.ts +74 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.js +161 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.d.ts +7 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.js +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.d.ts +2 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.js +36 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/index.d.ts +4 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/index.js +4 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.d.ts +12 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.js +122 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.js +55 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.d.ts +21 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.js +47 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.js +24 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.js +57 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.js +67 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.d.ts +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.js +98 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.d.ts +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.js +128 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.d.ts +5 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.js +60 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.d.ts +2 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.js +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.d.ts +7 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.js +142 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.js +21 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.d.ts +32 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.js +1 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.d.ts +6 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.js +68 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.js +178 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.d.ts +16 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.js +141 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.d.ts +11 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.js +10 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.d.ts +26 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.js +8 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.js +265 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.js +345 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.d.ts +15 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.js +382 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.js +352 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.d.ts +57 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.js +243 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.js +454 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.d.ts +3 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.js +424 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.d.ts +78 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.js +1 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/utils.d.ts +45 -0
- package/bluetooth/vendor/smartcube-web-bluetooth/utils.js +150 -0
- package/cfop/caseData.d.ts +16 -0
- package/cfop/caseData.js +88 -0
- package/cfop/cases.d.ts +42 -0
- package/cfop/cases.js +116 -0
- package/cfop/f2l.d.ts +28 -0
- package/cfop/f2l.js +88 -0
- package/cfop/f2lData.d.ts +15 -0
- package/cfop/f2lData.js +49 -0
- package/cfop/index.d.ts +41 -0
- package/cfop/index.js +110 -0
- package/cfop/slots.d.ts +2 -0
- package/cfop/slots.js +1 -0
- package/core/arrows.d.ts +24 -0
- package/core/arrows.js +22 -0
- package/core/bytes.d.ts +27 -0
- package/core/bytes.js +145 -0
- package/core/centers.d.ts +32 -0
- package/core/centers.js +83 -0
- package/core/checks.d.ts +53 -0
- package/core/checks.js +85 -0
- package/core/cubies.d.ts +45 -0
- package/core/cubies.js +169 -0
- package/core/frames.d.ts +61 -0
- package/core/frames.js +126 -0
- package/core/geometry.d.ts +58 -0
- package/core/geometry.js +132 -0
- package/core/index.d.ts +19 -0
- package/core/index.js +19 -0
- package/core/masks.d.ts +32 -0
- package/core/masks.js +39 -0
- package/core/method.d.ts +35 -0
- package/core/method.js +30 -0
- package/core/moves.d.ts +48 -0
- package/core/moves.js +88 -0
- package/core/notation.d.ts +48 -0
- package/core/notation.js +226 -0
- package/core/physical.d.ts +53 -0
- package/core/physical.js +99 -0
- package/core/practice.d.ts +62 -0
- package/core/practice.js +87 -0
- package/core/sequence.d.ts +90 -0
- package/core/sequence.js +157 -0
- package/core/stages.d.ts +90 -0
- package/core/stages.js +21 -0
- package/core/state.d.ts +55 -0
- package/core/state.js +140 -0
- package/core/stateCodec.d.ts +12 -0
- package/core/stateCodec.js +51 -0
- package/core/stream.d.ts +45 -0
- package/core/stream.js +75 -0
- package/core/tracker.d.ts +62 -0
- package/core/tracker.js +121 -0
- package/element/alg.d.ts +45 -0
- package/element/alg.js +150 -0
- package/element/base.d.ts +2 -0
- package/element/base.js +3 -0
- package/element/bld.d.ts +82 -0
- package/element/bld.js +222 -0
- package/element/index.d.ts +8 -0
- package/element/index.js +21 -0
- package/element/model.d.ts +36 -0
- package/element/model.js +64 -0
- package/element/player.d.ts +196 -0
- package/element/player.js +680 -0
- package/element/practice.d.ts +75 -0
- package/element/practice.js +150 -0
- package/element/scramble.d.ts +50 -0
- package/element/scramble.js +107 -0
- package/element/sequence.d.ts +126 -0
- package/element/sequence.js +254 -0
- package/element/styles.d.ts +32 -0
- package/element/styles.js +196 -0
- package/image/cache.d.ts +21 -0
- package/image/cache.js +66 -0
- package/image/index.d.ts +2 -0
- package/image/index.js +2 -0
- package/image/png-node.d.ts +6 -0
- package/image/png-node.js +10 -0
- package/image/svg.d.ts +36 -0
- package/image/svg.js +215 -0
- package/lbl/index.d.ts +10 -0
- package/lbl/index.js +31 -0
- package/methods/index.d.ts +20 -0
- package/methods/index.js +33 -0
- package/package.json +133 -0
- package/petrus/index.d.ts +11 -0
- package/petrus/index.js +27 -0
- package/react/index.d.ts +176 -0
- package/react/index.js +228 -0
- package/render/arrows.d.ts +68 -0
- package/render/arrows.js +190 -0
- package/render/build/attachments.d.ts +35 -0
- package/render/build/attachments.js +94 -0
- package/render/build/models.d.ts +9 -0
- package/render/build/models.js +27 -0
- package/render/build/tiles.d.ts +48 -0
- package/render/build/tiles.js +194 -0
- package/render/gltf.d.ts +10 -0
- package/render/gltf.js +106 -0
- package/render/index.d.ts +12 -0
- package/render/index.js +12 -0
- package/render/layers.d.ts +21 -0
- package/render/layers.js +24 -0
- package/render/pictures.d.ts +46 -0
- package/render/pictures.js +80 -0
- package/render/pieceModels.d.ts +38 -0
- package/render/pieceModels.js +43 -0
- package/render/renderer.d.ts +165 -0
- package/render/renderer.js +647 -0
- package/render/tile.d.ts +114 -0
- package/render/tile.js +390 -0
- package/render/viewports.d.ts +25 -0
- package/render/viewports.js +26 -0
- package/roux/cmll.d.ts +35 -0
- package/roux/cmll.js +121 -0
- package/roux/cmllData.d.ts +12 -0
- package/roux/cmllData.js +49 -0
- package/roux/index.d.ts +52 -0
- package/roux/index.js +175 -0
- package/skin/attachments.d.ts +82 -0
- package/skin/attachments.js +110 -0
- package/skin/index.d.ts +3 -0
- package/skin/index.js +3 -0
- package/skin/shapes.d.ts +112 -0
- package/skin/shapes.js +147 -0
- package/skin/skin.d.ts +911 -0
- package/skin/skin.js +412 -0
- package/solve/client.d.ts +58 -0
- package/solve/client.js +56 -0
- package/solve/handle.d.ts +6 -0
- package/solve/handle.js +38 -0
- package/solve/index.d.ts +8 -0
- package/solve/index.js +7 -0
- package/solve/lse.d.ts +42 -0
- package/solve/lse.js +151 -0
- package/solve/pieces.d.ts +18 -0
- package/solve/pieces.js +24 -0
- package/solve/protocol.d.ts +70 -0
- package/solve/protocol.js +16 -0
- package/solve/scramble.d.ts +76 -0
- package/solve/scramble.js +187 -0
- package/solve/stage.d.ts +108 -0
- package/solve/stage.js +532 -0
- package/solve/twophase.d.ts +36 -0
- package/solve/twophase.js +229 -0
- package/solve/worker.d.ts +6 -0
- package/solve/worker.js +15 -0
- package/timeline/codec.d.ts +23 -0
- package/timeline/codec.js +84 -0
- package/timeline/index.d.ts +7 -0
- package/timeline/index.js +7 -0
- package/timeline/recording.d.ts +19 -0
- package/timeline/recording.js +20 -0
- package/timeline/replay.d.ts +68 -0
- package/timeline/replay.js +145 -0
- package/timeline/segments.d.ts +31 -0
- package/timeline/segments.js +39 -0
- package/timeline/share.d.ts +30 -0
- package/timeline/share.js +62 -0
- package/timeline/stats.d.ts +34 -0
- package/timeline/stats.js +56 -0
- package/timeline/timings.d.ts +30 -0
- package/timeline/timings.js +54 -0
- package/zz/index.d.ts +29 -0
- package/zz/index.js +68 -0
- package/zz/seeds.d.ts +9 -0
- package/zz/seeds.js +29 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-decrypt sanity checks for MAC candidate probing.
|
|
3
|
+
*/
|
|
4
|
+
/** Validates decrypted MoYu32 notification bitstream. */
|
|
5
|
+
export function isValidMoYu32DecryptedPacket(bytes) {
|
|
6
|
+
if (!bytes || bytes.length < 20) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
11
|
+
if (bytes[i] < 0 || bytes[i] > 255) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
let z = 0;
|
|
16
|
+
let f = 0;
|
|
17
|
+
for (let n = 0; n < Math.min(bytes.length, 20); n++) {
|
|
18
|
+
if (bytes[n] === 0) {
|
|
19
|
+
z++;
|
|
20
|
+
}
|
|
21
|
+
else if (bytes[n] === 255) {
|
|
22
|
+
f++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (z > 14 || f > 14 || new Set(bytes.slice(0, 20)).size > 18) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
let n = '';
|
|
29
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
30
|
+
n += (bytes[i] + 256).toString(2).slice(1);
|
|
31
|
+
}
|
|
32
|
+
const r = parseInt(n.slice(0, 8), 2);
|
|
33
|
+
return validateMoYu32BitBody(n, r);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function validateMoYu32BitBody(e, t) {
|
|
40
|
+
if (!e || e.length < 160) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const i = parseInt(e.slice(0, 8), 2);
|
|
45
|
+
if (i !== t ||
|
|
46
|
+
![161, 163, 164, 165, 171].includes(i) ||
|
|
47
|
+
(i === 164 && parseInt(e.slice(8, 16), 2) > 100)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (i === 161) {
|
|
51
|
+
for (let k = 0; k < 8; k++) {
|
|
52
|
+
const v = parseInt(e.slice(8 + 8 * k, 16 + 8 * k), 2);
|
|
53
|
+
if (v !== 0 && (v < 32 || v > 126)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (i === 165) {
|
|
59
|
+
let c = 0;
|
|
60
|
+
for (let j = 0; j < 5; j++) {
|
|
61
|
+
const v = parseInt(e.slice(96 + 5 * j, 101 + 5 * j), 2);
|
|
62
|
+
if (v <= 11) {
|
|
63
|
+
c++;
|
|
64
|
+
}
|
|
65
|
+
else if (v < 31) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (c === 0) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
let allZero = true;
|
|
73
|
+
let allMax = true;
|
|
74
|
+
for (let r = 0; r < c; r++) {
|
|
75
|
+
const v = parseInt(e.slice(8 + 16 * r, 24 + 16 * r), 2);
|
|
76
|
+
if (v !== 0) {
|
|
77
|
+
allZero = false;
|
|
78
|
+
}
|
|
79
|
+
if (v !== 65535) {
|
|
80
|
+
allMax = false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (allZero || allMax) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (i === 163) {
|
|
88
|
+
const body = e.slice(8, 152);
|
|
89
|
+
const zeros = (body.match(/0/g) || []).length;
|
|
90
|
+
const ones = (body.match(/1/g) || []).length;
|
|
91
|
+
if (zeros > 0.9 * body.length || ones > 0.9 * body.length) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Validates decrypted QiYi notification payload. */
|
|
102
|
+
export function isValidQiYiDecryptedPacket(e) {
|
|
103
|
+
if (!e || e.length < 7) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const t = e[0];
|
|
108
|
+
if (t === 254) {
|
|
109
|
+
const cmd = e[1];
|
|
110
|
+
const sub = e[2];
|
|
111
|
+
if (![2, 3].includes(sub) || cmd < 7 || cmd > 100) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const n = (((e[3] << 24) | (e[4] << 16) | (e[5] << 8) | e[6]) >>> 0);
|
|
115
|
+
if (n === 0 || n === 0xffffffff) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (t === 204 && e[1] === 16) {
|
|
121
|
+
if (e.length < 16) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const dv = new DataView(e.buffer, e.byteOffset);
|
|
125
|
+
const x = dv.getInt16(6, false);
|
|
126
|
+
const y = dv.getInt16(8, false);
|
|
127
|
+
const z = dv.getInt16(10, false);
|
|
128
|
+
const w = dv.getInt16(12, false);
|
|
129
|
+
if (Math.abs(x) > 2000 ||
|
|
130
|
+
Math.abs(y) > 2000 ||
|
|
131
|
+
Math.abs(z) > 2000 ||
|
|
132
|
+
Math.abs(w) > 2000) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="web-bluetooth" />
|
|
2
|
+
import type { SmartCubeProtocol } from '../protocol.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pick the protocol that best matches the GATT profile. Tie-break with `matchesDevice` (name).
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveProtocolByGatt(protocols: readonly SmartCubeProtocol[], serviceUuids: ReadonlySet<string>, device: BluetoothDevice): SmartCubeProtocol | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the protocol that best matches the GATT profile. Tie-break with `matchesDevice` (name).
|
|
3
|
+
*/
|
|
4
|
+
export function resolveProtocolByGatt(protocols, serviceUuids, device) {
|
|
5
|
+
const ranked = protocols.map((p) => ({
|
|
6
|
+
p,
|
|
7
|
+
score: p.gattAffinity(serviceUuids, device),
|
|
8
|
+
}));
|
|
9
|
+
const maxScore = ranked.reduce((m, r) => Math.max(m, r.score), -1);
|
|
10
|
+
if (maxScore > 0) {
|
|
11
|
+
const top = ranked.filter((r) => r.score === maxScore);
|
|
12
|
+
const preferred = top.find((r) => r.p.matchesDevice(device));
|
|
13
|
+
return (preferred ?? top[0]).p;
|
|
14
|
+
}
|
|
15
|
+
for (const p of protocols) {
|
|
16
|
+
if (p.matchesDevice(device)) {
|
|
17
|
+
return p;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/// <reference types="web-bluetooth" />
|
|
2
|
+
/** Context passed after GATT discovery so sessions can reuse known profile data. */
|
|
3
|
+
export interface AttachmentContext {
|
|
4
|
+
/** Primary service UUIDs (normalized 128-bit uppercase) seen on this connection. */
|
|
5
|
+
serviceUuids: ReadonlySet<string>;
|
|
6
|
+
/** Manufacturer data from a pre-connect advertisement pass, if any. */
|
|
7
|
+
advertisementManufacturerData?: BluetoothManufacturerData | null;
|
|
8
|
+
/** Longer advertisement / resolution attempts for MAC (QiYi / MoYu32). */
|
|
9
|
+
enableAddressSearch?: boolean;
|
|
10
|
+
onStatus?: (message: string) => void;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
export type DeviceSelectionMode = 'filtered' | 'any';
|
|
14
|
+
export interface ConnectSmartCubeOptions {
|
|
15
|
+
macAddressProvider?: import('../types.js').MacAddressProvider;
|
|
16
|
+
/** `filtered`: name/manufacturer filters. `any`: accept all BLE devices (GATT profile picks the driver). */
|
|
17
|
+
deviceSelection?: DeviceSelectionMode;
|
|
18
|
+
/**
|
|
19
|
+
* When set with `deviceSelection: 'filtered'`, adds exact-name and known-alias scan filters
|
|
20
|
+
* (helps reconnect when the cube uses a non-prefix-stable advertised name).
|
|
21
|
+
*/
|
|
22
|
+
deviceName?: string;
|
|
23
|
+
/** Aborts long-running steps (e.g. MAC candidate probing). */
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
/** Called with short status messages during resolution (e.g. MAC search). */
|
|
26
|
+
onStatus?: (message: string) => void;
|
|
27
|
+
/**
|
|
28
|
+
* When true, if advertisement and name hints fail for QiYi / MoYu32, try a bounded set of MAC candidates
|
|
29
|
+
* derived from the device name (slow; default false).
|
|
30
|
+
*/
|
|
31
|
+
enableAddressSearch?: boolean;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="web-bluetooth" />
|
|
2
|
+
import { now } from '../utils.js';
|
|
3
|
+
declare function findCharacteristic(characteristics: BluetoothRemoteGATTCharacteristic[], uuid: string): BluetoothRemoteGATTCharacteristic | null;
|
|
4
|
+
declare function waitForAdvertisements(device: BluetoothDevice, timeoutMs?: number): Promise<BluetoothManufacturerData | null>;
|
|
5
|
+
declare function extractMacFromManufacturerData(mfData: BluetoothManufacturerData | DataView | null, cicList: number[], reversedByteOrder?: boolean): string | null;
|
|
6
|
+
export { now, findCharacteristic, waitForAdvertisements, extractMacFromManufacturerData };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { now } from '../utils.js';
|
|
2
|
+
function toUuid128(uuid) {
|
|
3
|
+
if (/^[0-9A-Fa-f]{4}$/.exec(uuid)) {
|
|
4
|
+
uuid = "0000" + uuid + "-0000-1000-8000-00805F9B34FB";
|
|
5
|
+
}
|
|
6
|
+
return uuid.toUpperCase();
|
|
7
|
+
}
|
|
8
|
+
function findCharacteristic(characteristics, uuid) {
|
|
9
|
+
const targetUuid = toUuid128(uuid);
|
|
10
|
+
for (const chrct of characteristics) {
|
|
11
|
+
if (toUuid128(chrct.uuid) === targetUuid) {
|
|
12
|
+
return chrct;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
async function waitForAdvertisements(device, timeoutMs = 5000) {
|
|
18
|
+
if (typeof device.watchAdvertisements !== 'function') {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const abortController = new AbortController();
|
|
23
|
+
const onAdvEvent = (evt) => {
|
|
24
|
+
device.removeEventListener('advertisementreceived', onAdvEvent);
|
|
25
|
+
abortController.abort();
|
|
26
|
+
resolve(evt.manufacturerData);
|
|
27
|
+
};
|
|
28
|
+
const onAbort = () => {
|
|
29
|
+
device.removeEventListener('advertisementreceived', onAdvEvent);
|
|
30
|
+
abortController.abort();
|
|
31
|
+
resolve(null);
|
|
32
|
+
};
|
|
33
|
+
device.addEventListener('advertisementreceived', onAdvEvent);
|
|
34
|
+
device.watchAdvertisements({ signal: abortController.signal }).catch(onAbort);
|
|
35
|
+
setTimeout(onAbort, timeoutMs);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function extractMacFromManufacturerData(mfData, cicList, reversedByteOrder = true) {
|
|
39
|
+
if (!mfData)
|
|
40
|
+
return null;
|
|
41
|
+
let dataView;
|
|
42
|
+
if (mfData instanceof DataView) {
|
|
43
|
+
dataView = new DataView(mfData.buffer.slice(2));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
for (const id of cicList) {
|
|
47
|
+
if (mfData.has(id)) {
|
|
48
|
+
dataView = mfData.get(id);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!dataView || dataView.byteLength < 6)
|
|
54
|
+
return null;
|
|
55
|
+
const mac = [];
|
|
56
|
+
if (reversedByteOrder) {
|
|
57
|
+
for (let i = 5; i >= 0; i--) {
|
|
58
|
+
mac.push((dataView.getUint8(i) + 0x100).toString(16).slice(1));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
for (let i = dataView.byteLength - 1; i >= dataView.byteLength - 6; i--) {
|
|
63
|
+
mac.push((dataView.getUint8(i) + 0x100).toString(16).slice(1));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return mac.join(':');
|
|
67
|
+
}
|
|
68
|
+
export { now, findCharacteristic, waitForAdvertisements, extractMacFromManufacturerData };
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { filter, take, TimeoutError } from 'rxjs';
|
|
2
|
+
import { buildRequestDeviceOptions } from './attachment/build-picker-options.js';
|
|
3
|
+
import { collectPrimaryServiceUuids } from './attachment/gatt-snapshot.js';
|
|
4
|
+
import { resolveProtocolByGatt } from './attachment/profile-rank.js';
|
|
5
|
+
import { removeCachedMacForDevice, setCachedMacForDevice, waitForManufacturerData, } from './attachment/address-hints.js';
|
|
6
|
+
import { CubieCube } from './cubie-cube.js';
|
|
7
|
+
import { getRegisteredProtocols } from './protocol.js';
|
|
8
|
+
/**
|
|
9
|
+
* MoYu-style payloads always yield 54-char FACELETS from {U,F,R,B,L,D} even when AES decrypt is wrong;
|
|
10
|
+
* event-type checks are insufficient. A legal 3×3 state requires consistent sticker counts + geometry.
|
|
11
|
+
*/
|
|
12
|
+
function isMacCacheProofEvent(e) {
|
|
13
|
+
if (e.type !== 'FACELETS') {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return new CubieCube().fromFacelet(e.facelets) !== -1;
|
|
17
|
+
}
|
|
18
|
+
const MAC_VERIFY_TIMEOUT_MS = 10_000;
|
|
19
|
+
/**
|
|
20
|
+
* After we subscribe for MAC proof, ask the cube for a fresh report. Init often emits
|
|
21
|
+
* FACELETS before this subscription exists (Subject does not replay), so without this
|
|
22
|
+
* we can time out even with a correct MAC. Fire-and-forget so a stuck write cannot
|
|
23
|
+
* block verification or leave the UI on "Verifying…" indefinitely.
|
|
24
|
+
*/
|
|
25
|
+
function requestFreshStateForMacVerify(conn) {
|
|
26
|
+
const c = conn.capabilities;
|
|
27
|
+
const p = c.facelets
|
|
28
|
+
? conn.sendCommand({ type: 'REQUEST_FACELETS' })
|
|
29
|
+
: c.hardware
|
|
30
|
+
? conn.sendCommand({ type: 'REQUEST_HARDWARE' })
|
|
31
|
+
: c.battery
|
|
32
|
+
? conn.sendCommand({ type: 'REQUEST_BATTERY' })
|
|
33
|
+
: Promise.resolve();
|
|
34
|
+
p.catch(() => { });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Wait until we see decrypted/valid cube traffic so we do not persist a wrong MAC
|
|
38
|
+
* (GAN / MoYu32 / QiYi can complete GATT setup before crypto is proven).
|
|
39
|
+
*/
|
|
40
|
+
function waitForVerifiedCubeEvent(conn, timeoutMs, signal) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
let settled = false;
|
|
43
|
+
let timer;
|
|
44
|
+
let sub;
|
|
45
|
+
const cleanup = () => {
|
|
46
|
+
if (timer !== undefined) {
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
timer = undefined;
|
|
49
|
+
}
|
|
50
|
+
sub?.unsubscribe();
|
|
51
|
+
sub = undefined;
|
|
52
|
+
if (signal) {
|
|
53
|
+
signal.removeEventListener('abort', onAbort);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const finish = (action) => {
|
|
57
|
+
if (settled) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
settled = true;
|
|
61
|
+
cleanup();
|
|
62
|
+
action();
|
|
63
|
+
};
|
|
64
|
+
const onAbort = () => {
|
|
65
|
+
finish(() => reject(new DOMException('Aborted', 'AbortError')));
|
|
66
|
+
};
|
|
67
|
+
if (signal) {
|
|
68
|
+
if (signal.aborted) {
|
|
69
|
+
finish(() => reject(new DOMException('Aborted', 'AbortError')));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
73
|
+
}
|
|
74
|
+
timer = setTimeout(() => {
|
|
75
|
+
finish(() => reject(new TimeoutError({
|
|
76
|
+
seen: 0,
|
|
77
|
+
lastValue: null,
|
|
78
|
+
meta: null,
|
|
79
|
+
})));
|
|
80
|
+
}, timeoutMs);
|
|
81
|
+
sub = conn.events$.pipe(filter(isMacCacheProofEvent), take(1)).subscribe({
|
|
82
|
+
next: () => {
|
|
83
|
+
finish(() => resolve());
|
|
84
|
+
},
|
|
85
|
+
error: (err) => {
|
|
86
|
+
finish(() => reject(err));
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function isMacAddressProvider(x) {
|
|
92
|
+
return typeof x === 'function';
|
|
93
|
+
}
|
|
94
|
+
function normalizeOptions(arg) {
|
|
95
|
+
if (arg === undefined) {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
if (isMacAddressProvider(arg)) {
|
|
99
|
+
return { macAddressProvider: arg };
|
|
100
|
+
}
|
|
101
|
+
return arg;
|
|
102
|
+
}
|
|
103
|
+
export async function connectSmartCube(arg) {
|
|
104
|
+
const opts = normalizeOptions(arg);
|
|
105
|
+
const protocols = getRegisteredProtocols();
|
|
106
|
+
if (protocols.length === 0) {
|
|
107
|
+
throw new Error('No smartcube protocols registered');
|
|
108
|
+
}
|
|
109
|
+
const mode = opts.deviceSelection ?? 'filtered';
|
|
110
|
+
const requestOptions = buildRequestDeviceOptions(protocols, mode, {
|
|
111
|
+
deviceName: opts.deviceName,
|
|
112
|
+
});
|
|
113
|
+
opts.onStatus?.('Select your cube…');
|
|
114
|
+
const device = await navigator.bluetooth.requestDevice(requestOptions);
|
|
115
|
+
opts.onStatus?.('Reading advertisements…');
|
|
116
|
+
const advertisementManufacturerData = await waitForManufacturerData(device, opts.enableAddressSearch ? 8000 : 2500);
|
|
117
|
+
opts.onStatus?.('Connecting…');
|
|
118
|
+
const serviceUuids = await collectPrimaryServiceUuids(device);
|
|
119
|
+
const protocol = resolveProtocolByGatt(protocols, serviceUuids, device);
|
|
120
|
+
if (!protocol) {
|
|
121
|
+
try {
|
|
122
|
+
device.gatt?.disconnect();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
/* ignore */
|
|
126
|
+
}
|
|
127
|
+
throw new Error("Selected device doesn't match any registered smartcube protocol");
|
|
128
|
+
}
|
|
129
|
+
const context = {
|
|
130
|
+
serviceUuids,
|
|
131
|
+
advertisementManufacturerData,
|
|
132
|
+
enableAddressSearch: opts.enableAddressSearch === true,
|
|
133
|
+
onStatus: opts.onStatus,
|
|
134
|
+
signal: opts.signal,
|
|
135
|
+
};
|
|
136
|
+
let conn;
|
|
137
|
+
try {
|
|
138
|
+
conn = await protocol.connect(device, opts.macAddressProvider, context);
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
try {
|
|
142
|
+
device.gatt?.disconnect();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
/* ignore */
|
|
146
|
+
}
|
|
147
|
+
throw e;
|
|
148
|
+
}
|
|
149
|
+
if (conn.deviceMAC) {
|
|
150
|
+
opts.onStatus?.('Verifying connection…');
|
|
151
|
+
try {
|
|
152
|
+
const verifyPromise = waitForVerifiedCubeEvent(conn, MAC_VERIFY_TIMEOUT_MS, opts.signal);
|
|
153
|
+
requestFreshStateForMacVerify(conn);
|
|
154
|
+
await verifyPromise;
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
const aborted = e instanceof DOMException && e.name === 'AbortError';
|
|
158
|
+
if (!aborted) {
|
|
159
|
+
removeCachedMacForDevice(device);
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
device.gatt?.disconnect();
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
/* ignore */
|
|
166
|
+
}
|
|
167
|
+
if (aborted) {
|
|
168
|
+
throw e;
|
|
169
|
+
}
|
|
170
|
+
if (e instanceof TimeoutError) {
|
|
171
|
+
throw new Error('Timed out waiting for cube data. Check the Bluetooth MAC address and try again.');
|
|
172
|
+
}
|
|
173
|
+
throw e;
|
|
174
|
+
}
|
|
175
|
+
setCachedMacForDevice(device, conn.deviceMAC);
|
|
176
|
+
}
|
|
177
|
+
return conn;
|
|
178
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare const SOLVED_FACELET = "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB";
|
|
2
|
+
declare const cFacelet: number[][];
|
|
3
|
+
declare const eFacelet: number[][];
|
|
4
|
+
declare class CubieCube {
|
|
5
|
+
ca: number[];
|
|
6
|
+
ea: number[];
|
|
7
|
+
constructor();
|
|
8
|
+
init(ca: number[], ea: number[]): CubieCube;
|
|
9
|
+
static EdgeMult(a: CubieCube, b: CubieCube, prod: CubieCube): void;
|
|
10
|
+
static CornMult(a: CubieCube, b: CubieCube, prod: CubieCube): void;
|
|
11
|
+
static CubeMult(a: CubieCube, b: CubieCube, prod: CubieCube): void;
|
|
12
|
+
toFaceCube(customCFacelet?: number[][], customEFacelet?: number[][]): string;
|
|
13
|
+
fromFacelet(facelet: string, customCFacelet?: number[][], customEFacelet?: number[][]): CubieCube | -1;
|
|
14
|
+
static moveCube: CubieCube[];
|
|
15
|
+
}
|
|
16
|
+
export { CubieCube, SOLVED_FACELET, cFacelet, eFacelet };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const SOLVED_FACELET = "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB";
|
|
2
|
+
const cFacelet = [
|
|
3
|
+
[8, 9, 20], // URF
|
|
4
|
+
[6, 18, 38], // UFL
|
|
5
|
+
[0, 36, 47], // ULB
|
|
6
|
+
[2, 45, 11], // UBR
|
|
7
|
+
[29, 26, 15], // DFR
|
|
8
|
+
[27, 44, 24], // DLF
|
|
9
|
+
[33, 53, 42], // DBL
|
|
10
|
+
[35, 17, 51] // DRB
|
|
11
|
+
];
|
|
12
|
+
const eFacelet = [
|
|
13
|
+
[5, 10], // UR
|
|
14
|
+
[7, 19], // UF
|
|
15
|
+
[3, 37], // UL
|
|
16
|
+
[1, 46], // UB
|
|
17
|
+
[32, 16], // DR
|
|
18
|
+
[28, 25], // DF
|
|
19
|
+
[30, 43], // DL
|
|
20
|
+
[34, 52], // DB
|
|
21
|
+
[23, 12], // FR
|
|
22
|
+
[21, 41], // FL
|
|
23
|
+
[50, 39], // BL
|
|
24
|
+
[48, 14] // BR
|
|
25
|
+
];
|
|
26
|
+
class CubieCube {
|
|
27
|
+
ca;
|
|
28
|
+
ea;
|
|
29
|
+
constructor() {
|
|
30
|
+
this.ca = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
31
|
+
this.ea = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22];
|
|
32
|
+
}
|
|
33
|
+
init(ca, ea) {
|
|
34
|
+
this.ca = ca.slice();
|
|
35
|
+
this.ea = ea.slice();
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
static EdgeMult(a, b, prod) {
|
|
39
|
+
for (let ed = 0; ed < 12; ed++) {
|
|
40
|
+
prod.ea[ed] = a.ea[b.ea[ed] >> 1] ^ (b.ea[ed] & 1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
static CornMult(a, b, prod) {
|
|
44
|
+
for (let corn = 0; corn < 8; corn++) {
|
|
45
|
+
const ori = ((a.ca[b.ca[corn] & 7] >> 3) + (b.ca[corn] >> 3)) % 3;
|
|
46
|
+
prod.ca[corn] = (a.ca[b.ca[corn] & 7] & 7) | (ori << 3);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
static CubeMult(a, b, prod) {
|
|
50
|
+
CubieCube.CornMult(a, b, prod);
|
|
51
|
+
CubieCube.EdgeMult(a, b, prod);
|
|
52
|
+
}
|
|
53
|
+
toFaceCube(customCFacelet, customEFacelet) {
|
|
54
|
+
const cf = customCFacelet || cFacelet;
|
|
55
|
+
const ef = customEFacelet || eFacelet;
|
|
56
|
+
const f = [];
|
|
57
|
+
for (let i = 0; i < 54; i++) {
|
|
58
|
+
f[i] = i;
|
|
59
|
+
}
|
|
60
|
+
for (let c = 0; c < 8; c++) {
|
|
61
|
+
const j = this.ca[c] & 0x7;
|
|
62
|
+
const ori = this.ca[c] >> 3;
|
|
63
|
+
for (let n = 0; n < 3; n++) {
|
|
64
|
+
f[cf[c][(n + ori) % 3]] = cf[j][n];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (let e = 0; e < 12; e++) {
|
|
68
|
+
const j = this.ea[e] >> 1;
|
|
69
|
+
const ori = this.ea[e] & 1;
|
|
70
|
+
for (let n = 0; n < 2; n++) {
|
|
71
|
+
f[ef[e][(n + ori) % 2]] = ef[j][n];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const ts = "URFDLB";
|
|
75
|
+
return f.map(v => ts[~~(v / 9)]).join("");
|
|
76
|
+
}
|
|
77
|
+
fromFacelet(facelet, customCFacelet, customEFacelet) {
|
|
78
|
+
const cf = customCFacelet || cFacelet;
|
|
79
|
+
const ef = customEFacelet || eFacelet;
|
|
80
|
+
let count = 0;
|
|
81
|
+
const fArr = [];
|
|
82
|
+
const centers = facelet[4] + facelet[13] + facelet[22] + facelet[31] + facelet[40] + facelet[49];
|
|
83
|
+
for (let i = 0; i < 54; ++i) {
|
|
84
|
+
fArr[i] = centers.indexOf(facelet[i]);
|
|
85
|
+
if (fArr[i] == -1) {
|
|
86
|
+
return -1;
|
|
87
|
+
}
|
|
88
|
+
count += 1 << (fArr[i] << 2);
|
|
89
|
+
}
|
|
90
|
+
if (count != 0x999999) {
|
|
91
|
+
return -1;
|
|
92
|
+
}
|
|
93
|
+
for (let i = 0; i < 8; ++i) {
|
|
94
|
+
let ori;
|
|
95
|
+
for (ori = 0; ori < 3; ++ori) {
|
|
96
|
+
if (fArr[cf[i][ori]] == 0 || fArr[cf[i][ori]] == 3)
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
const col1 = fArr[cf[i][(ori + 1) % 3]];
|
|
100
|
+
const col2 = fArr[cf[i][(ori + 2) % 3]];
|
|
101
|
+
for (let j = 0; j < 8; ++j) {
|
|
102
|
+
if (col1 == ~~(cf[j][1] / 9) && col2 == ~~(cf[j][2] / 9)) {
|
|
103
|
+
this.ca[i] = j | ((ori % 3) << 3);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (let i = 0; i < 12; ++i) {
|
|
109
|
+
for (let j = 0; j < 12; ++j) {
|
|
110
|
+
if (fArr[ef[i][0]] == ~~(ef[j][0] / 9) && fArr[ef[i][1]] == ~~(ef[j][1] / 9)) {
|
|
111
|
+
this.ea[i] = j << 1;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
if (fArr[ef[i][0]] == ~~(ef[j][1] / 9) && fArr[ef[i][1]] == ~~(ef[j][0] / 9)) {
|
|
115
|
+
this.ea[i] = j << 1 | 1;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
static moveCube = (() => {
|
|
123
|
+
const mc = [];
|
|
124
|
+
for (let i = 0; i < 18; i++) {
|
|
125
|
+
mc[i] = new CubieCube();
|
|
126
|
+
}
|
|
127
|
+
mc[0].init([3, 0, 1, 2, 4, 5, 6, 7], [6, 0, 2, 4, 8, 10, 12, 14, 16, 18, 20, 22]); // U
|
|
128
|
+
mc[3].init([20, 1, 2, 8, 15, 5, 6, 19], [16, 2, 4, 6, 22, 10, 12, 14, 8, 18, 20, 0]); // R
|
|
129
|
+
mc[6].init([9, 21, 2, 3, 16, 12, 6, 7], [0, 19, 4, 6, 8, 17, 12, 14, 3, 11, 20, 22]); // F
|
|
130
|
+
mc[9].init([0, 1, 2, 3, 5, 6, 7, 4], [0, 2, 4, 6, 10, 12, 14, 8, 16, 18, 20, 22]); // D
|
|
131
|
+
mc[12].init([0, 10, 22, 3, 4, 17, 13, 7], [0, 2, 20, 6, 8, 10, 18, 14, 16, 4, 12, 22]); // L
|
|
132
|
+
mc[15].init([0, 1, 11, 23, 4, 5, 18, 14], [0, 2, 4, 23, 8, 10, 12, 21, 16, 18, 7, 15]); // B
|
|
133
|
+
for (let a = 0; a < 18; a += 3) {
|
|
134
|
+
for (let p = 0; p < 2; p++) {
|
|
135
|
+
CubieCube.CubeMult(mc[a + p], mc[a], mc[a + p + 1]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return mc;
|
|
139
|
+
})();
|
|
140
|
+
}
|
|
141
|
+
export { CubieCube, SOLVED_FACELET, cFacelet, eFacelet };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import './protocols/gan.js';
|
|
2
|
+
import './protocols/giiker.js';
|
|
3
|
+
import './protocols/gocube.js';
|
|
4
|
+
import './protocols/moyu-mhc.js';
|
|
5
|
+
import './protocols/moyu32.js';
|
|
6
|
+
import './protocols/qiyi.js';
|
|
7
|
+
export type { SmartCubeEvent, SmartCubeEventMessage, SmartCubeMoveEvent, SmartCubeFaceletsEvent, SmartCubeGyroEvent, SmartCubeBatteryEvent, SmartCubeProtocolInfo, SmartCubeHardwareEvent, SmartCubeDisconnectEvent, SmartCubeCommand, SmartCubeCapabilities, SmartCubeConnection } from './types.js';
|
|
8
|
+
export { connectSmartCube } from './connect.js';
|
|
9
|
+
export { getCachedMacForDevice, removeCachedMacForDevice } from './attachment/address-hints.js';
|
|
10
|
+
export type { SmartCubeProtocol, SmartCubeNameFilter, AttachmentContext, ConnectSmartCubeOptions, DeviceSelectionMode } from './protocol.js';
|
|
11
|
+
export { registerProtocol, getRegisteredProtocols } from './protocol.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Register all protocols (side-effect imports)
|
|
2
|
+
import './protocols/gan.js';
|
|
3
|
+
import './protocols/giiker.js';
|
|
4
|
+
import './protocols/gocube.js';
|
|
5
|
+
import './protocols/moyu-mhc.js';
|
|
6
|
+
import './protocols/moyu32.js';
|
|
7
|
+
import './protocols/qiyi.js';
|
|
8
|
+
export { connectSmartCube } from './connect.js';
|
|
9
|
+
export { getCachedMacForDevice, removeCachedMacForDevice } from './attachment/address-hints.js';
|
|
10
|
+
export { registerProtocol, getRegisteredProtocols } from './protocol.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// <reference types="web-bluetooth" />
|
|
2
|
+
import type { AttachmentContext } from './attachment/types.js';
|
|
3
|
+
import { SmartCubeConnection, MacAddressProvider } from './types.js';
|
|
4
|
+
/** Single OR branch for `requestDevice` (`namePrefix`, exact `name`, or both via multiple entries). */
|
|
5
|
+
export type SmartCubeNameFilter = {
|
|
6
|
+
namePrefix: string;
|
|
7
|
+
} | {
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
interface SmartCubeProtocol {
|
|
11
|
+
nameFilters: SmartCubeNameFilter[];
|
|
12
|
+
optionalServices: string[];
|
|
13
|
+
optionalManufacturerData?: number[];
|
|
14
|
+
matchesDevice(device: BluetoothDevice): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Higher scores win when choosing a driver from primary service UUIDs.
|
|
17
|
+
* Use 0 when this profile does not match the GATT snapshot.
|
|
18
|
+
*/
|
|
19
|
+
gattAffinity(serviceUuids: ReadonlySet<string>, device: BluetoothDevice): number;
|
|
20
|
+
connect(device: BluetoothDevice, macProvider?: MacAddressProvider, context?: AttachmentContext): Promise<SmartCubeConnection>;
|
|
21
|
+
}
|
|
22
|
+
declare function registerProtocol(protocol: SmartCubeProtocol): void;
|
|
23
|
+
declare function getRegisteredProtocols(): SmartCubeProtocol[];
|
|
24
|
+
export type { SmartCubeProtocol };
|
|
25
|
+
export { registerProtocol, getRegisteredProtocols };
|
|
26
|
+
export type { AttachmentContext, ConnectSmartCubeOptions, DeviceSelectionMode } from './attachment/types.js';
|