miijs 1.0.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/asmCrypto.js +10538 -0
- package/crown.jpg +0 -0
- package/index.js +966 -0
- package/maddie.jpg +0 -0
- package/package.json +35 -0
package/index.js
ADDED
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { createCanvas, loadImage } = require('canvas');
|
|
3
|
+
const jsQR = require('jsqr');
|
|
4
|
+
var Jimp = require('jimp');
|
|
5
|
+
const QRCode = require('qrcode');
|
|
6
|
+
const httpsLib = require('https');
|
|
7
|
+
const asmCrypto=require("./asmCrypto.js");
|
|
8
|
+
function getKeyByValue(object, value) {
|
|
9
|
+
for (var key in object) {
|
|
10
|
+
if (object[key] === value) {
|
|
11
|
+
return key;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
var binary;
|
|
17
|
+
function getBinaryFromAddress(addr){//EG: 0x20
|
|
18
|
+
let byte = binary.readUInt8(addr);
|
|
19
|
+
let binaryString = '';
|
|
20
|
+
for (let i = 7; i >= 0; i--) {
|
|
21
|
+
binaryString += ((byte >> i) & 1) ? '1' : '0';
|
|
22
|
+
}
|
|
23
|
+
return binaryString;
|
|
24
|
+
}
|
|
25
|
+
var NONCE_OFFSET = 0xC;
|
|
26
|
+
var NONCE_LENGTH = 8;
|
|
27
|
+
var TAG_LENGTH = 0x10;
|
|
28
|
+
var aes_key = new Uint8Array([0x59, 0xFC, 0x81, 0x7E, 0x64, 0x46, 0xEA, 0x61, 0x90, 0x34, 0x7B, 0x20, 0xE9, 0xBD, 0xCE, 0x52]);
|
|
29
|
+
var pad = new Uint8Array([0,0,0,0]);
|
|
30
|
+
function Uint8Cat(){
|
|
31
|
+
var destLength = 0
|
|
32
|
+
for(var i = 0;i < arguments.length;i++)destLength += arguments[i].length;
|
|
33
|
+
var dest = new Uint8Array(destLength);
|
|
34
|
+
var index = 0;
|
|
35
|
+
for(i = 0;i < arguments.length;i++){
|
|
36
|
+
dest.set(arguments[i],index);
|
|
37
|
+
index += arguments[i].length;
|
|
38
|
+
}
|
|
39
|
+
return dest;
|
|
40
|
+
}
|
|
41
|
+
function decodeAesCcm(data){
|
|
42
|
+
var nonce = Uint8Cat(data.subarray(0,NONCE_LENGTH),pad);
|
|
43
|
+
var ciphertext = data.subarray(NONCE_LENGTH,0x70);
|
|
44
|
+
var plaintext = asmCrypto.AES_CCM.decrypt(ciphertext,aes_key,nonce,undefined,TAG_LENGTH);
|
|
45
|
+
return Uint8Cat(plaintext.subarray(0,NONCE_OFFSET),data.subarray(0,NONCE_LENGTH),plaintext.subarray(NONCE_OFFSET,plaintext.length - 4));
|
|
46
|
+
}
|
|
47
|
+
function crcCalc(data){
|
|
48
|
+
var crc = 0;
|
|
49
|
+
for (var byteIndex = 0;byteIndex < data.length; byteIndex++){
|
|
50
|
+
for (var bitIndex = 7; bitIndex >= 0; bitIndex--){
|
|
51
|
+
crc = (((crc << 1) | ((data[byteIndex] >> bitIndex) & 0x1)) ^
|
|
52
|
+
(((crc & 0x8000) != 0) ? 0x1021 : 0));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for(var counter = 16; counter > 0; counter--){
|
|
56
|
+
crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0));
|
|
57
|
+
}
|
|
58
|
+
return(crc & 0xFFFF);
|
|
59
|
+
}
|
|
60
|
+
function encodeAesCcm(data){
|
|
61
|
+
var nonce = Uint8Cat(data.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),pad);
|
|
62
|
+
var crcSrc = Uint8Cat(data,new Uint8Array([0,0]));
|
|
63
|
+
var crc = crcCalc(crcSrc);
|
|
64
|
+
var cfsd = Uint8Cat(crcSrc,new Uint8Array([crc >>> 8,crc & 0xff]));
|
|
65
|
+
var plaintext = Uint8Cat(cfsd.subarray(0,NONCE_OFFSET),cfsd.subarray(NONCE_OFFSET + NONCE_LENGTH,cfsd.length),pad,pad);
|
|
66
|
+
var ciphertext = asmCrypto.AES_CCM.encrypt(plaintext,aes_key,nonce,undefined,TAG_LENGTH);
|
|
67
|
+
return Uint8Cat(cfsd.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),ciphertext.subarray(0,ciphertext.length - 24),ciphertext.subarray(ciphertext.length - TAG_LENGTH,ciphertext.length))
|
|
68
|
+
}
|
|
69
|
+
function downloadImage(url, filepath) {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
httpsLib.get(url, (res) => {
|
|
72
|
+
if (res.statusCode === 200) {
|
|
73
|
+
res.pipe(fs.createWriteStream(filepath))
|
|
74
|
+
.on('error', reject)
|
|
75
|
+
.once('close', () => resolve(filepath));
|
|
76
|
+
} else {
|
|
77
|
+
// Consume response data to free up memory
|
|
78
|
+
res.resume();
|
|
79
|
+
reject(new Error(`Request Failed With a Status Code: ${res.statusCode}`));
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
var wiiCols=["Red","Orange","Yellow","Lime","Green","Blue","Light Blue","Pink","Purple","Brown","White","Black"];
|
|
86
|
+
var wiiFaceFeatures=["None","Blush","Makeup and Blush","Freckles","Bags","Wrinkles on Cheeks","Wrinkles near Eyes","Chin Wrinkle","Makeup","Stubble","Wrinkles near Mouth","Wrinkles"];
|
|
87
|
+
var wiiSkinColors=["White","Tanned White","Darker Skin","Tanned Darker","Mostly Black","Black"];
|
|
88
|
+
var wiiMouthColors=["Peach","Red","Pink"];
|
|
89
|
+
var wiiHairCols=["Black","Dark Brown","Mid Brown","Brown","Grey","Wooden Brown","Dark Blonde","Blonde"];
|
|
90
|
+
var wiiEyeCols=["Black","Grey","Brown","Lime","Blue","Green"];
|
|
91
|
+
var wiiGlassesCols=["Grey","Brown","Red","Blue","Yellow","White"];
|
|
92
|
+
var mouthTable={
|
|
93
|
+
'0': '113',
|
|
94
|
+
'1': '121',
|
|
95
|
+
'2': '231',
|
|
96
|
+
'3': '222',
|
|
97
|
+
'4': '232',
|
|
98
|
+
'5': '132',
|
|
99
|
+
'6': '124',
|
|
100
|
+
'7': '211',
|
|
101
|
+
'8': '123',
|
|
102
|
+
'9': '221',
|
|
103
|
+
'10': '133',
|
|
104
|
+
'11': '223',
|
|
105
|
+
'12': '234',
|
|
106
|
+
'13': '134',
|
|
107
|
+
'14': '224',
|
|
108
|
+
'15': '213',
|
|
109
|
+
'16': '114',
|
|
110
|
+
'17': '212',
|
|
111
|
+
'18': '214',
|
|
112
|
+
'19': '131',
|
|
113
|
+
'20': '233',
|
|
114
|
+
'21': '112',
|
|
115
|
+
'22': '122',
|
|
116
|
+
'23': '111'
|
|
117
|
+
};
|
|
118
|
+
var eyebrowTable={
|
|
119
|
+
'0': '121',
|
|
120
|
+
'1': '112',
|
|
121
|
+
'2': '231',
|
|
122
|
+
'3': '212',
|
|
123
|
+
'4': '134',
|
|
124
|
+
'5': '124',
|
|
125
|
+
'6': '111',
|
|
126
|
+
'7': '113',
|
|
127
|
+
'8': '133',
|
|
128
|
+
'9': '122',
|
|
129
|
+
'10': '221',
|
|
130
|
+
'11': '211',
|
|
131
|
+
'12': '131',
|
|
132
|
+
'13': '223',
|
|
133
|
+
'14': '222',
|
|
134
|
+
'15': '213',
|
|
135
|
+
'16': '224',
|
|
136
|
+
'17': '114',
|
|
137
|
+
'18': '214',
|
|
138
|
+
'19': '132',
|
|
139
|
+
'20': '232',
|
|
140
|
+
'21': '123',
|
|
141
|
+
'22': '233',
|
|
142
|
+
'23': '234'
|
|
143
|
+
};
|
|
144
|
+
var eyeTable={
|
|
145
|
+
'0': '131',
|
|
146
|
+
'1': '113',
|
|
147
|
+
'2': '111',
|
|
148
|
+
'3': '413',
|
|
149
|
+
'4': '121',
|
|
150
|
+
'5': '311',
|
|
151
|
+
'6': '332',
|
|
152
|
+
'7': '411',
|
|
153
|
+
'8': '112',
|
|
154
|
+
'9': '222',
|
|
155
|
+
'10': '414',
|
|
156
|
+
'11': '221',
|
|
157
|
+
'12': '232',
|
|
158
|
+
'13': '331',
|
|
159
|
+
'14': '424',
|
|
160
|
+
'15': '114',
|
|
161
|
+
'16': '133',
|
|
162
|
+
'17': '132',
|
|
163
|
+
'18': '314',
|
|
164
|
+
'19': '231',
|
|
165
|
+
'20': '134',
|
|
166
|
+
'21': '233',
|
|
167
|
+
'22': '433',
|
|
168
|
+
'23': '213',
|
|
169
|
+
'24': '313',
|
|
170
|
+
'25': '214',
|
|
171
|
+
'26': '123',
|
|
172
|
+
'27': '124',
|
|
173
|
+
'28': '324',
|
|
174
|
+
'29': '432',
|
|
175
|
+
'30': '323',
|
|
176
|
+
'31': '333',
|
|
177
|
+
'32': '212',
|
|
178
|
+
'33': '211',
|
|
179
|
+
'34': '223',
|
|
180
|
+
'35': '234',
|
|
181
|
+
'36': '312',
|
|
182
|
+
'37': '322',
|
|
183
|
+
'38': '431',
|
|
184
|
+
'39': '122',
|
|
185
|
+
'40': '224',
|
|
186
|
+
'41': '321',
|
|
187
|
+
'42': '412',
|
|
188
|
+
'43': '423',
|
|
189
|
+
'44': '421',
|
|
190
|
+
'45': '422',
|
|
191
|
+
'46': '334',
|
|
192
|
+
'47': '434'
|
|
193
|
+
};
|
|
194
|
+
var hairTable={
|
|
195
|
+
'0': '534',
|
|
196
|
+
'1': '413',
|
|
197
|
+
'2': '632',
|
|
198
|
+
'3': '521',
|
|
199
|
+
'4': '422',
|
|
200
|
+
'5': '433',
|
|
201
|
+
'6': '522',
|
|
202
|
+
'7': '434',
|
|
203
|
+
'8': '414',
|
|
204
|
+
'9': '612',
|
|
205
|
+
'10': '512',
|
|
206
|
+
'11': '513',
|
|
207
|
+
'12': '411',
|
|
208
|
+
'13': '421',
|
|
209
|
+
'14': '511',
|
|
210
|
+
'15': '624',
|
|
211
|
+
'16': '621',
|
|
212
|
+
'17': '533',
|
|
213
|
+
'18': '622',
|
|
214
|
+
'19': '423',
|
|
215
|
+
'20': '532',
|
|
216
|
+
'21': '524',
|
|
217
|
+
'22': '531',
|
|
218
|
+
'23': '312',
|
|
219
|
+
'24': '614',
|
|
220
|
+
'25': '432',
|
|
221
|
+
'26': '412',
|
|
222
|
+
'27': '424',
|
|
223
|
+
'28': '613',
|
|
224
|
+
'29': '634',
|
|
225
|
+
'30': '314',
|
|
226
|
+
'31': '134',
|
|
227
|
+
'32': '211',
|
|
228
|
+
'33': '111',
|
|
229
|
+
'34': '334',
|
|
230
|
+
'35': '514',
|
|
231
|
+
'36': '313',
|
|
232
|
+
'37': '231',
|
|
233
|
+
'38': '321',
|
|
234
|
+
'39': '122',
|
|
235
|
+
'40': '121',
|
|
236
|
+
'41': '323',
|
|
237
|
+
'42': '331',
|
|
238
|
+
'43': '311',
|
|
239
|
+
'44': '112',
|
|
240
|
+
'45': '113',
|
|
241
|
+
'46': '631',
|
|
242
|
+
'47': '221',
|
|
243
|
+
'48': '212',
|
|
244
|
+
'49': '123',
|
|
245
|
+
'50': '223',
|
|
246
|
+
'51': '131',
|
|
247
|
+
'52': '232',
|
|
248
|
+
'53': '623',
|
|
249
|
+
'54': '332',
|
|
250
|
+
'55': '233',
|
|
251
|
+
'56': '114',
|
|
252
|
+
'57': '324',
|
|
253
|
+
'58': '213',
|
|
254
|
+
'59': '133',
|
|
255
|
+
'60': '224',
|
|
256
|
+
'61': '611',
|
|
257
|
+
'62': '234',
|
|
258
|
+
'63': '523',
|
|
259
|
+
'64': '214',
|
|
260
|
+
'65': '333',
|
|
261
|
+
'66': '222',
|
|
262
|
+
'67': '322',
|
|
263
|
+
'68': '124',
|
|
264
|
+
'69': '431',
|
|
265
|
+
'70': '132',
|
|
266
|
+
'71': '633'
|
|
267
|
+
};
|
|
268
|
+
var cols3DS=["Red","Orange","Yellow","Lime","Green","Blue","Teal","Pink","Purple","Brown","White","Black"];
|
|
269
|
+
var skinCols3DS=["White","Tanned White","Darker White","Tanned Darker","Mostly Black","Black"];
|
|
270
|
+
var faceFeatures3DS=["None","Near Eye Creases","Cheek Creases","Far Eye Creases","Near Nose Creases","Giant Bags","Cleft Chin","Chin Crease","Sunken Eyes","Far Cheek Creases","Lines Near Eyes","Wrinkles"];
|
|
271
|
+
var makeups3DS=["None","Blush","Orange Blush","Blue Eyes","Blush 2","Orange Blush 2","Blue Eyes and Blush","Orange Eyes and Blush","Purple Eyes and Blush 2","Freckles","Beard Stubble","Beard and Mustache Stubble"];
|
|
272
|
+
var eyeCols3DS=["Black","Grey","Brown","Lime","Blue","Green"];
|
|
273
|
+
var hairCols3DS=["Black","Brown","Red","Reddish Brown","Grey","Light Brown","Dark Blonde","Blonde"];
|
|
274
|
+
var mouthCols3DS=["Orange","Red","Pink","Peach","Black"];
|
|
275
|
+
var glassesCols3DS=["Black","Brown","Red","Blue","Yellow","Grey"];
|
|
276
|
+
var tables={
|
|
277
|
+
faces: [
|
|
278
|
+
0x00,0x01,0x08,
|
|
279
|
+
0x02,0x03,0x09,
|
|
280
|
+
0x04,0x05,0x0a,
|
|
281
|
+
0x06,0x07,0x0b
|
|
282
|
+
],
|
|
283
|
+
hairs: [
|
|
284
|
+
[0x21,0x2f,0x28,
|
|
285
|
+
0x25,0x20,0x6b,
|
|
286
|
+
0x30,0x33,0x37,
|
|
287
|
+
0x46,0x2c,0x42],
|
|
288
|
+
[0x34,0x32,0x26,
|
|
289
|
+
0x31,0x2b,0x1f,
|
|
290
|
+
0x38,0x44,0x3e,
|
|
291
|
+
0x73,0x4c,0x77],
|
|
292
|
+
[0x40,0x51,0x74,
|
|
293
|
+
0x79,0x16,0x3a,
|
|
294
|
+
0x3c,0x57,0x7d,
|
|
295
|
+
0x75,0x49,0x4b],
|
|
296
|
+
[0x2a,0x59,0x39,
|
|
297
|
+
0x36,0x50,0x22,
|
|
298
|
+
0x17,0x56,0x58,
|
|
299
|
+
0x76,0x27,0x24],
|
|
300
|
+
[0x2d,0x43,0x3b,
|
|
301
|
+
0x41,0x29,0x1e,
|
|
302
|
+
0x0c,0x10,0x0a,
|
|
303
|
+
0x52,0x80,0x81],
|
|
304
|
+
[0x0e,0x5f,0x69,
|
|
305
|
+
0x64,0x06,0x14,
|
|
306
|
+
0x5d,0x66,0x1b,
|
|
307
|
+
0x04,0x11,0x6e],
|
|
308
|
+
[0x7b,0x08,0x6a,
|
|
309
|
+
0x48,0x03,0x15,
|
|
310
|
+
0x00,0x62,0x3f,
|
|
311
|
+
0x5a,0x0b,0x78],
|
|
312
|
+
[0x05,0x4a,0x6c,
|
|
313
|
+
0x5e,0x7c,0x19,
|
|
314
|
+
0x63,0x45,0x23,
|
|
315
|
+
0x0d,0x7a,0x71],
|
|
316
|
+
[0x35,0x18,0x55,
|
|
317
|
+
0x53,0x47,0x83,
|
|
318
|
+
0x60,0x65,0x1d,
|
|
319
|
+
0x07,0x0f,0x70],
|
|
320
|
+
[0x4f,0x01,0x6d,
|
|
321
|
+
0x7f,0x5b,0x1a,
|
|
322
|
+
0x3d,0x67,0x02,
|
|
323
|
+
0x4d,0x12,0x5c],
|
|
324
|
+
[0x54,0x09,0x13,
|
|
325
|
+
0x82,0x61,0x68,
|
|
326
|
+
0x2e,0x4e,0x1c,
|
|
327
|
+
0x72,0x7e,0x6f]
|
|
328
|
+
],
|
|
329
|
+
eyebrows: [
|
|
330
|
+
[0x06,0x00,0x0c,
|
|
331
|
+
0x01,0x09,0x13,
|
|
332
|
+
0x07,0x15,0x08,
|
|
333
|
+
0x11,0x05,0x04],
|
|
334
|
+
[0x0b,0x0a,0x02,
|
|
335
|
+
0x03,0x0e,0x14,
|
|
336
|
+
0x0f,0x0d,0x16,
|
|
337
|
+
0x12,0x10,0x17]
|
|
338
|
+
],
|
|
339
|
+
eyes: [
|
|
340
|
+
[0x02,0x04,0x00,
|
|
341
|
+
0x08,0x27,0x11,
|
|
342
|
+
0x01,0x1a,0x10,
|
|
343
|
+
0x0f,0x1b,0x14],
|
|
344
|
+
[0x21,0x0b,0x13,
|
|
345
|
+
0x20,0x09,0x0c,
|
|
346
|
+
0x17,0x22,0x15,
|
|
347
|
+
0x19,0x28,0x23],
|
|
348
|
+
[0x05,0x29,0x0d,
|
|
349
|
+
0x24,0x25,0x06,
|
|
350
|
+
0x18,0x1e,0x1f,
|
|
351
|
+
0x12,0x1c,0x2e],
|
|
352
|
+
[0x07,0x2c,0x26,
|
|
353
|
+
0x2a,0x2d,0x1d,
|
|
354
|
+
0x03,0x2b,0x16,
|
|
355
|
+
0x0a,0x0e,0x2f],
|
|
356
|
+
[0x30,0x31,0x32,
|
|
357
|
+
0x35,0x3b,0x38,
|
|
358
|
+
0x36,0x3a,0x39,
|
|
359
|
+
0x37,0x33,0x34]
|
|
360
|
+
],
|
|
361
|
+
noses: [
|
|
362
|
+
[0x01,0x0a,0x02,
|
|
363
|
+
0x03,0x06,0x00,
|
|
364
|
+
0x05,0x04,0x08,
|
|
365
|
+
0x09,0x07,0x0B],
|
|
366
|
+
[0x0d,0x0e,0x0c,
|
|
367
|
+
0x11,0x10,0x0f]
|
|
368
|
+
],
|
|
369
|
+
mouths: [
|
|
370
|
+
[0x17,0x01,0x13,
|
|
371
|
+
0x15,0x16,0x05,
|
|
372
|
+
0x00,0x08,0x0a,
|
|
373
|
+
0x10,0x06,0x0d],
|
|
374
|
+
[0x07,0x09,0x02,
|
|
375
|
+
0x11,0x03,0x04,
|
|
376
|
+
0x0f,0x0b,0x14,
|
|
377
|
+
0x12,0x0e,0x0c],
|
|
378
|
+
[0x1b,0x1e,0x18,
|
|
379
|
+
0x19,0x1d,0x1c,
|
|
380
|
+
0x1a,0x23,0x1f,
|
|
381
|
+
0x22,0x21,0x20]
|
|
382
|
+
]
|
|
383
|
+
};
|
|
384
|
+
function lookupTable(table,value,paginated){
|
|
385
|
+
if(paginated){
|
|
386
|
+
for(var i=0;i<tables[table].length;i++){
|
|
387
|
+
for(var j=0;j<tables[table][i].length;j++){
|
|
388
|
+
if(tables[table][i][j]===value){
|
|
389
|
+
return [i,j];
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
else{
|
|
395
|
+
for(var i=0;i<tables[table].length;i++){
|
|
396
|
+
if(tables[table][i]===value){
|
|
397
|
+
return i;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
module.exports={
|
|
404
|
+
readWiiBin:function(binPath){
|
|
405
|
+
var thisMii={
|
|
406
|
+
info:{},
|
|
407
|
+
face:{},
|
|
408
|
+
nose:{},
|
|
409
|
+
mouth:{},
|
|
410
|
+
mole:{},
|
|
411
|
+
hair:{},
|
|
412
|
+
eyebrows:{},
|
|
413
|
+
eyes:{},
|
|
414
|
+
glasses:{},
|
|
415
|
+
facialHair:{}
|
|
416
|
+
};
|
|
417
|
+
binary = fs.readFileSync(binPath);
|
|
418
|
+
var name="";
|
|
419
|
+
for(var i=0;i<10;i++){
|
|
420
|
+
name+=binary.slice(3+i*2, 4+i*2)+"";
|
|
421
|
+
}
|
|
422
|
+
thisMii.name=name.replaceAll("\x00","");
|
|
423
|
+
var cname="";
|
|
424
|
+
for(var i=0;i<10;i++){
|
|
425
|
+
cname+=binary.slice(55+i*2, 56+i*2)+"";
|
|
426
|
+
}
|
|
427
|
+
thisMii.creatorName=cname.replaceAll("\x00","");
|
|
428
|
+
thisMii.info.creatorName=thisMii.creatorName;
|
|
429
|
+
thisMii.info.name=thisMii.name;//Up to ten characters
|
|
430
|
+
thisMii.info.gender=getBinaryFromAddress(0x00)[1]==="1"?"Female":"Male";//0 for Male, 1 for Female
|
|
431
|
+
thisMii.info.miiId=parseInt(getBinaryFromAddress(0x18),2).toString(16)+parseInt(getBinaryFromAddress(0x19),2).toString(16)+parseInt(getBinaryFromAddress(0x1A),2).toString(16)+parseInt(getBinaryFromAddress(0x1B),2).toString(16);
|
|
432
|
+
thisMii.info.systemId=parseInt(getBinaryFromAddress(0x1C),2).toString(16)+parseInt(getBinaryFromAddress(0x1D),2).toString(16)+parseInt(getBinaryFromAddress(0x1E),2).toString(16)+parseInt(getBinaryFromAddress(0x1F),2).toString(16);
|
|
433
|
+
var temp=getBinaryFromAddress(0x20);
|
|
434
|
+
thisMii.face.shape=parseInt(temp.slice(0,3),2);//0-7
|
|
435
|
+
thisMii.face.col=wiiSkinColors[parseInt(temp.slice(3,6),2)];//0-5
|
|
436
|
+
temp=getBinaryFromAddress(0x21);
|
|
437
|
+
thisMii.face.feature=wiiFaceFeatures[parseInt(getBinaryFromAddress(0x20).slice(6,8)+temp.slice(0,2),2)];//0-11
|
|
438
|
+
thisMii.info.mingle=temp[5]==="1"?false:true;//0 for Mingle, 1 for Don't Mingle
|
|
439
|
+
temp=getBinaryFromAddress(0x2C);
|
|
440
|
+
thisMii.nose.type=parseInt(temp.slice(0,4),2);//Needs lookup table
|
|
441
|
+
thisMii.nose.size=parseInt(temp.slice(4,8),2);
|
|
442
|
+
thisMii.nose.vertPos=parseInt(getBinaryFromAddress(0x2D).slice(0,5),2);//From top to bottom, 0-18, default 9
|
|
443
|
+
temp=getBinaryFromAddress(0x2E);
|
|
444
|
+
thisMii.mouth.type=mouthTable[""+parseInt(temp.slice(0,5),2)];//0-23, Needs lookup table
|
|
445
|
+
thisMii.mouth.col=wiiMouthColors[parseInt(temp.slice(5,7),2)];//0-2, refer to mouthColors array
|
|
446
|
+
temp2=getBinaryFromAddress(0x2F);
|
|
447
|
+
thisMii.mouth.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-8, default 4
|
|
448
|
+
thisMii.mouth.yPos=parseInt(temp2.slice(3,8),2);//0-18, default 9, from top to bottom
|
|
449
|
+
temp=getBinaryFromAddress(0x00);
|
|
450
|
+
var temp2=getBinaryFromAddress(0x01);
|
|
451
|
+
thisMii.info.birthMonth=parseInt(temp.slice(2,6),2);
|
|
452
|
+
thisMii.info.birthday=parseInt(temp.slice(6,8)+temp2.slice(0,3),2);
|
|
453
|
+
thisMii.info.favColor=wiiCols[parseInt(temp2.slice(3,7),2)];//0-11, refer to cols array
|
|
454
|
+
thisMii.info.favorited=temp2[7]==="0"?false:true;
|
|
455
|
+
thisMii.info.height=parseInt(getBinaryFromAddress(0x16),2);//0-127
|
|
456
|
+
thisMii.info.weight=parseInt(getBinaryFromAddress(0x17),2);//0-127
|
|
457
|
+
thisMii.info.downloadedFromCheckMiiOut=getBinaryFromAddress(0x21)[7]==="0"?false:true;
|
|
458
|
+
temp=getBinaryFromAddress(0x34);
|
|
459
|
+
temp2=getBinaryFromAddress(0x35);
|
|
460
|
+
thisMii.mole.on=temp[0]==="0"?false:true;//0 for Off, 1 for On
|
|
461
|
+
thisMii.mole.size=parseInt(temp.slice(1,5),2);//0-8, default 4
|
|
462
|
+
thisMii.mole.xPos=parseInt(temp2.slice(2,7),2);//0-16, Default 2
|
|
463
|
+
thisMii.mole.yPos=parseInt(temp.slice(5,8)+temp2.slice(0,2),2);//Top to bottom
|
|
464
|
+
temp=getBinaryFromAddress(0x22);
|
|
465
|
+
temp2=getBinaryFromAddress(0x23);
|
|
466
|
+
thisMii.hair.type=hairTable[""+parseInt(temp.slice(0,7),2)];//0-71, Needs lookup table
|
|
467
|
+
thisMii.hair.col=wiiHairCols[parseInt(temp[7]+temp2.slice(0,2),2)];//0-7, refer to hairCols array
|
|
468
|
+
thisMii.hair.flipped=temp2[2]==="0"?false:true;
|
|
469
|
+
temp=getBinaryFromAddress(0x24);
|
|
470
|
+
temp2=getBinaryFromAddress(0x25);
|
|
471
|
+
thisMii.eyebrows.type=eyebrowTable[""+parseInt(temp.slice(0,5),2)];//0-23, Needs lookup table
|
|
472
|
+
thisMii.eyebrows.rotation=parseInt(temp.slice(6,8)+temp2.slice(0,2),2);//0-11, default varies based on eyebrow type
|
|
473
|
+
temp=getBinaryFromAddress(0x26);
|
|
474
|
+
temp2=getBinaryFromAddress(0x27);
|
|
475
|
+
thisMii.eyebrows.col=wiiHairCols[parseInt(temp.slice(0,3),2)];
|
|
476
|
+
thisMii.eyebrows.size=parseInt(temp.slice(3,7),2);//0-8, default 4
|
|
477
|
+
thisMii.eyebrows.yPos=parseInt(temp[7]+temp2.slice(0,4),2);//0-18, default 10
|
|
478
|
+
thisMii.eyebrows.distApart=parseInt(temp2.slice(4,8),2);//0-12, default 2
|
|
479
|
+
thisMii.eyes.type=eyeTable[parseInt(getBinaryFromAddress(0x28).slice(0,6),2)];//0-47, needs lookup table
|
|
480
|
+
temp=getBinaryFromAddress(0x29);
|
|
481
|
+
thisMii.eyes.rotation=parseInt(temp.slice(0,3),2);//0-7, default varies based on eye type
|
|
482
|
+
thisMii.eyes.yPos=parseInt(temp.slice(3,8),2);//0-18, default 12, top to bottom
|
|
483
|
+
temp=getBinaryFromAddress(0x2A);
|
|
484
|
+
thisMii.eyes.col=wiiEyeCols[parseInt(temp.slice(0,3),2)];//0-5
|
|
485
|
+
thisMii.eyes.size=parseInt(temp.slice(4,7),2);//0-7, default 4
|
|
486
|
+
temp2=getBinaryFromAddress(0x2B);
|
|
487
|
+
thisMii.eyes.distApart=parseInt(temp[7]+temp2.slice(0,3),2);//0-12, default 2
|
|
488
|
+
temp=getBinaryFromAddress(0x30);
|
|
489
|
+
thisMii.glasses.type=parseInt(temp.slice(0,4),2);//0-8
|
|
490
|
+
thisMii.glasses.col=wiiGlassesCols[parseInt(temp.slice(4,7),2)];//0-5
|
|
491
|
+
temp=getBinaryFromAddress(0x31);
|
|
492
|
+
thisMii.glasses.size=parseInt(temp.slice(0,3),2);//0-7, default 4
|
|
493
|
+
thisMii.glasses.yPos=parseInt(temp.slice(3,8),2);//0-20, default 10
|
|
494
|
+
temp=getBinaryFromAddress(0x32);
|
|
495
|
+
temp2=getBinaryFromAddress(0x33);
|
|
496
|
+
thisMii.facialHair.mustacheType=parseInt(temp.slice(0,2),2);//0-3
|
|
497
|
+
thisMii.facialHair.beardType=parseInt(temp.slice(2,4),2);//0-3
|
|
498
|
+
thisMii.facialHair.col=wiiHairCols[parseInt(temp.slice(4,7),2)];//0-7
|
|
499
|
+
thisMii.facialHair.mustacheSize=parseInt(temp[7]+temp2.slice(0,3),2);//0-30, default 20
|
|
500
|
+
thisMii.facialHair.mustacheYPos=parseInt(temp2.slice(3,8),2);//0-16, default 2
|
|
501
|
+
return thisMii;
|
|
502
|
+
},
|
|
503
|
+
read3DSQR:async function(qrPath){
|
|
504
|
+
function readMii(){
|
|
505
|
+
var miiJson={
|
|
506
|
+
info:{},
|
|
507
|
+
perms:{},
|
|
508
|
+
hair:{},
|
|
509
|
+
face:{},
|
|
510
|
+
eyes:{},
|
|
511
|
+
eyebrows:{},
|
|
512
|
+
nose:{},
|
|
513
|
+
mouth:{},
|
|
514
|
+
facialHair:{},
|
|
515
|
+
glasses:{},
|
|
516
|
+
mole:{}
|
|
517
|
+
};
|
|
518
|
+
binary=fs.readFileSync("./decryptedTemp.3dMii");
|
|
519
|
+
var temp=getBinaryFromAddress(0x18);
|
|
520
|
+
var temp2=getBinaryFromAddress(0x19);
|
|
521
|
+
miiJson.info.birthday=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
|
|
522
|
+
miiJson.info.birthMonth=parseInt(temp.slice(3,7),2);
|
|
523
|
+
var name="";
|
|
524
|
+
for(var i=0x1A;i<0x2E;i++){
|
|
525
|
+
name+=binary.slice(i,i+1);
|
|
526
|
+
}
|
|
527
|
+
miiJson.name=name.replaceAll("\x00","");
|
|
528
|
+
var cname="";
|
|
529
|
+
for(var i=0x48;i<0x5C;i++){
|
|
530
|
+
cname+=binary.slice(i,i+1);
|
|
531
|
+
}
|
|
532
|
+
miiJson.creatorName=cname.replaceAll("\x00","");
|
|
533
|
+
miiJson.info.name=miiJson.name;
|
|
534
|
+
miiJson.info.creatorName=miiJson.creatorName;
|
|
535
|
+
miiJson.info.height=parseInt(getBinaryFromAddress(0x2E),2);
|
|
536
|
+
miiJson.info.weight=parseInt(getBinaryFromAddress(0x2F),2);
|
|
537
|
+
miiJson.info.gender=temp[7]==="1"?"Female":"Male";
|
|
538
|
+
temp=getBinaryFromAddress(0x30);
|
|
539
|
+
miiJson.perms.sharing=temp[7]==="1"?false:true;
|
|
540
|
+
miiJson.info.favColor=cols3DS[parseInt(temp2.slice(2,6),2)];
|
|
541
|
+
miiJson.perms.copying=getBinaryFromAddress(0x01)[7]==="1"?true:false;
|
|
542
|
+
miiJson.hair.style=lookupTable("hairs",parseInt(getBinaryFromAddress(0x32),2),true);
|
|
543
|
+
miiJson.face.shape=lookupTable("faces",parseInt(temp.slice(3,7),2),false);
|
|
544
|
+
miiJson.face.col=skinCols3DS[parseInt(temp.slice(0,3),2)];
|
|
545
|
+
temp=getBinaryFromAddress(0x31);
|
|
546
|
+
miiJson.face.feature=faceFeatures3DS[parseInt(temp.slice(4,8),2)];
|
|
547
|
+
miiJson.face.makeup=makeups3DS[parseInt(temp.slice(0,4),2)];
|
|
548
|
+
temp=getBinaryFromAddress(0x34);
|
|
549
|
+
miiJson.eyes.type=lookupTable("eyes",parseInt(temp.slice(2,8),2),true);
|
|
550
|
+
temp2=getBinaryFromAddress(0x33);
|
|
551
|
+
miiJson.hair.col=hairCols3DS[parseInt(temp2.slice(5,8),2)];
|
|
552
|
+
miiJson.hair.flipped=temp2[4]==="0"?false:true;
|
|
553
|
+
miiJson.eyes.col=eyeCols3DS[parseInt(getBinaryFromAddress(0x35)[7]+temp.slice(0,2),2)];
|
|
554
|
+
temp=getBinaryFromAddress(0x35);
|
|
555
|
+
miiJson.eyes.size=parseInt(temp.slice(3,7),2);
|
|
556
|
+
miiJson.eyes.squash=parseInt(temp.slice(0,3),2);
|
|
557
|
+
temp=getBinaryFromAddress(0x36);
|
|
558
|
+
temp2=getBinaryFromAddress(0x37);
|
|
559
|
+
miiJson.eyes.rot=parseInt(temp.slice(3,8),2);
|
|
560
|
+
miiJson.eyes.distApart=parseInt(temp2[7]+temp.slice(0,3),2);
|
|
561
|
+
miiJson.eyes.yPos=parseInt(temp2.slice(2,7),2);
|
|
562
|
+
temp=getBinaryFromAddress(0x38);
|
|
563
|
+
miiJson.eyebrows.style=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true);
|
|
564
|
+
miiJson.eyebrows.col=hairCols3DS[parseInt(temp.slice(0,3),2)];
|
|
565
|
+
temp=getBinaryFromAddress(0x39);
|
|
566
|
+
miiJson.eyebrows.size=parseInt(temp.slice(4,8),2);
|
|
567
|
+
miiJson.eyebrows.squash=parseInt(temp.slice(1,4),2);
|
|
568
|
+
temp=getBinaryFromAddress(0x3A);
|
|
569
|
+
miiJson.eyebrows.rot=parseInt(temp.slice(4,8),2);
|
|
570
|
+
temp2=getBinaryFromAddress(0x3B);
|
|
571
|
+
miiJson.eyebrows.distApart=parseInt(temp2[7]+temp.slice(0,3),2);
|
|
572
|
+
miiJson.eyebrows.yPos=parseInt(temp2.slice(2,7),2)-3;
|
|
573
|
+
temp=getBinaryFromAddress(0x3C);
|
|
574
|
+
miiJson.nose.type=lookupTable("noses",parseInt(temp.slice(3,8),2),true);
|
|
575
|
+
temp2=getBinaryFromAddress(0x3D);
|
|
576
|
+
miiJson.nose.size=parseInt(temp2[7]+temp.slice(0,3),2);
|
|
577
|
+
miiJson.nose.yPos=parseInt(temp2.slice(2,7),2);
|
|
578
|
+
temp=getBinaryFromAddress(0x3E);
|
|
579
|
+
miiJson.mouth.type=lookupTable("mouths",parseInt(temp.slice(2,8),2),true);
|
|
580
|
+
temp2=getBinaryFromAddress(0x3F);
|
|
581
|
+
miiJson.mouth.col=mouthCols3DS[parseInt(temp2[7]+temp.slice(0,2),2)];
|
|
582
|
+
miiJson.mouth.size=parseInt(temp2.slice(3,7),2);
|
|
583
|
+
miiJson.mouth.squash=parseInt(temp2.slice(0,3),2);
|
|
584
|
+
temp=getBinaryFromAddress(0x40);
|
|
585
|
+
miiJson.mouth.yPos=parseInt(temp.slice(3,8),2);
|
|
586
|
+
miiJson.facialHair.mustacheType=parseInt(temp.slice(0,3),2);
|
|
587
|
+
temp=getBinaryFromAddress(0x42);
|
|
588
|
+
miiJson.facialHair.beardType=parseInt(temp.slice(5,8),2);
|
|
589
|
+
miiJson.facialHair.col=hairCols3DS[parseInt(temp.slice(2,5),2)];
|
|
590
|
+
temp2=getBinaryFromAddress(0x43);
|
|
591
|
+
miiJson.facialHair.mustacheSize=parseInt(temp2.slice(6,8)+temp.slice(0,2),2);
|
|
592
|
+
miiJson.facialHair.mustacheYPos=parseInt(temp2.slice(1,6),2);
|
|
593
|
+
temp=getBinaryFromAddress(0x44);
|
|
594
|
+
miiJson.glasses.type=parseInt(temp.slice(4,8),2);
|
|
595
|
+
miiJson.glasses.col=glassesCols3DS[parseInt(temp.slice(1,4),2)];
|
|
596
|
+
temp2=getBinaryFromAddress(0x45);
|
|
597
|
+
miiJson.glasses.size=parseInt(temp2.slice(5,8)+temp[0],2);
|
|
598
|
+
miiJson.glasses.yPos=parseInt(temp2.slice(1,5),2);
|
|
599
|
+
temp=getBinaryFromAddress(0x46);
|
|
600
|
+
miiJson.mole.on=temp[7]==="0"?false:true;
|
|
601
|
+
miiJson.mole.size=parseInt(temp.slice(3,7),2);
|
|
602
|
+
temp2=getBinaryFromAddress(0x47);
|
|
603
|
+
miiJson.mole.xPos=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
|
|
604
|
+
miiJson.mole.yPos=parseInt(temp2.slice(1,6),2);
|
|
605
|
+
fs.unlinkSync("./decryptedTemp.3dmii");
|
|
606
|
+
fs.unlinkSync("./encryptedTemp.3dmii");
|
|
607
|
+
return miiJson;
|
|
608
|
+
}
|
|
609
|
+
var data=fs.readFileSync(qrPath);
|
|
610
|
+
var img=await loadImage(data);
|
|
611
|
+
const canvas = createCanvas(img.width, img.height);
|
|
612
|
+
const ctx = canvas.getContext('2d');
|
|
613
|
+
ctx.drawImage(img, 0, 0);
|
|
614
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
615
|
+
const qrCode = jsQR(imageData.data, imageData.width, imageData.height);
|
|
616
|
+
|
|
617
|
+
if (qrCode) {
|
|
618
|
+
fs.writeFileSync("./encryptedTemp.3dMii",Buffer.from(qrCode.binaryData));
|
|
619
|
+
var data = decodeAesCcm(new Uint8Array(qrCode.binaryData));
|
|
620
|
+
fs.writeFileSync("./decryptedTemp.3dMii",Buffer.from(data));
|
|
621
|
+
return Promise.resolve(readMii());
|
|
622
|
+
} else {
|
|
623
|
+
console.error('Failed to decode QR code');
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
writeWiiBin:function(jsonIn,outPath){
|
|
627
|
+
var mii=jsonIn;
|
|
628
|
+
var miiBin="0";
|
|
629
|
+
miiBin+=mii.info.gender==="Male"?"0":"1";
|
|
630
|
+
miiBin+=mii.info.birthMonth.toString(2).padStart(4,"0");
|
|
631
|
+
miiBin+=mii.info.birthday.toString(2).padStart(5,"0");
|
|
632
|
+
miiBin+=wiiCols.indexOf(mii.info.favColor).toString(2).padStart(4,"0");
|
|
633
|
+
miiBin+=mii.info.favorited?1:0;
|
|
634
|
+
for(var i=0;i<10;i++){
|
|
635
|
+
miiBin+="00000000";
|
|
636
|
+
if(i<mii.name.length){
|
|
637
|
+
miiBin+=mii.name.charCodeAt(i).toString(2).padStart(8,"0");
|
|
638
|
+
}
|
|
639
|
+
else{
|
|
640
|
+
miiBin+="00000000";
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
miiBin+=mii.info.height.toString(2).padStart(8,"0");
|
|
644
|
+
miiBin+=mii.info.weight.toString(2).padStart(8,"0");
|
|
645
|
+
let miiId="";
|
|
646
|
+
switch(mii.info.type){
|
|
647
|
+
case "Special":
|
|
648
|
+
miiId="01000110";
|
|
649
|
+
break;
|
|
650
|
+
case "Foreign":
|
|
651
|
+
miiId="11000110";
|
|
652
|
+
break;
|
|
653
|
+
default:
|
|
654
|
+
miiId="10101001";
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
for(var i=0;i<3;i++){
|
|
658
|
+
miiId+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
|
|
659
|
+
}
|
|
660
|
+
miiBin+=miiId;
|
|
661
|
+
miiBin+="11111111".repeat(4);//System ID
|
|
662
|
+
miiBin+=mii.face.shape.toString(2).padStart(3,"0");
|
|
663
|
+
miiBin+=wiiSkinColors.indexOf(mii.face.col).toString(2).padStart(3,"0");
|
|
664
|
+
miiBin+=wiiFaceFeatures.indexOf(mii.face.feature).toString(2).padStart(4,"0");
|
|
665
|
+
miiBin+="000";
|
|
666
|
+
if(mii.info.mingle&&mii.info.type==="Special"){
|
|
667
|
+
console.error("A Special Mii cannot have Mingle on and still render on the Wii. Turned Mingle off in the output file.");
|
|
668
|
+
}
|
|
669
|
+
miiBin+=mii.info.mingle?"0":"1";
|
|
670
|
+
miiBin+="0";
|
|
671
|
+
miiBin+=mii.info.downloadedFromCheckMiiOut?"1":"0";
|
|
672
|
+
miiBin+=(+getKeyByValue(hairTable,mii.hair.type)).toString(2).padStart(7,"0");
|
|
673
|
+
miiBin+=wiiHairCols.indexOf(mii.hair.col).toString(2).padStart(3,"0");
|
|
674
|
+
miiBin+=mii.hair.flipped?"1":"0";
|
|
675
|
+
miiBin+="00000";
|
|
676
|
+
miiBin+=(+getKeyByValue(eyebrowTable,mii.eyebrows.type)).toString(2).padStart(5,"0");
|
|
677
|
+
miiBin+="0";
|
|
678
|
+
miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
|
|
679
|
+
miiBin+="000000";
|
|
680
|
+
miiBin+=wiiHairCols.indexOf(mii.eyebrows.col).toString(2).padStart(3,"0");
|
|
681
|
+
miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
|
|
682
|
+
miiBin+=mii.eyebrows.yPos.toString(2).padStart(5,"0");
|
|
683
|
+
miiBin+=mii.eyebrows.distApart.toString(2).padStart(4,"0");
|
|
684
|
+
miiBin+=(+getKeyByValue(eyeTable,mii.eyes.type)).toString(2).padStart(6,"0");
|
|
685
|
+
miiBin+="00";
|
|
686
|
+
miiBin+=mii.eyes.rotation.toString(2).padStart(3,"0");
|
|
687
|
+
miiBin+=mii.eyes.yPos.toString(2).padStart(5,"0");
|
|
688
|
+
miiBin+=wiiEyeCols.indexOf(mii.eyes.col).toString(2).padStart(3,"0");
|
|
689
|
+
miiBin+="0";
|
|
690
|
+
miiBin+=mii.eyes.size.toString(2).padStart(3,"0");
|
|
691
|
+
miiBin+=mii.eyes.distApart.toString(2).padStart(4,"0");
|
|
692
|
+
miiBin+="00000";
|
|
693
|
+
miiBin+=mii.nose.type.toString(2).padStart(4,"0");
|
|
694
|
+
miiBin+=mii.nose.size.toString(2).padStart(4,"0");
|
|
695
|
+
miiBin+=mii.nose.vertPos.toString(2).padStart(5,"0");
|
|
696
|
+
miiBin+="000";
|
|
697
|
+
miiBin+=(+getKeyByValue(mouthTable,mii.mouth.type)).toString(2).padStart(5,"0");
|
|
698
|
+
miiBin+=wiiMouthColors.indexOf(mii.mouth.col).toString(2).padStart(2,"0");
|
|
699
|
+
miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
|
|
700
|
+
miiBin+=mii.mouth.yPos.toString(2).padStart(5,"0");
|
|
701
|
+
miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
|
|
702
|
+
miiBin+=wiiGlassesCols.indexOf(mii.glasses.col).toString(2).padStart(3,"0");
|
|
703
|
+
miiBin+="0";
|
|
704
|
+
miiBin+=mii.glasses.size.toString(2).padStart(3,"0");
|
|
705
|
+
miiBin+=mii.glasses.yPos.toString(2).padStart(5,"0");
|
|
706
|
+
miiBin+=mii.facialHair.mustacheType.toString(2).padStart(2,"0");
|
|
707
|
+
miiBin+=mii.facialHair.beardType.toString(2).padStart(2,"0");
|
|
708
|
+
miiBin+=wiiHairCols.indexOf(mii.facialHair.col).toString(2).padStart(3,"0");
|
|
709
|
+
miiBin+=mii.facialHair.mustacheSize.toString(2).padStart(4,"0");
|
|
710
|
+
miiBin+=mii.facialHair.mustacheYPos.toString(2).padStart(5,"0");
|
|
711
|
+
miiBin+=mii.mole.on?"1":"0";
|
|
712
|
+
miiBin+=mii.mole.size.toString(2).padStart(4,"0");
|
|
713
|
+
miiBin+=mii.mole.yPos.toString(2).padStart(5,"0");
|
|
714
|
+
miiBin+=mii.mole.xPos.toString(2).padStart(5,"0");
|
|
715
|
+
miiBin+="0";
|
|
716
|
+
for(var i=0;i<10;i++){
|
|
717
|
+
miiBin+="00000000";
|
|
718
|
+
if(i<mii.creatorName.length){
|
|
719
|
+
miiBin+=mii.creatorName.charCodeAt(i).toString(2).padStart(8,"0");
|
|
720
|
+
}
|
|
721
|
+
else{
|
|
722
|
+
miiBin+="00000000";
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
//Writing based on miiBin
|
|
727
|
+
var toWrite=miiBin.match(/.{1,8}/g);
|
|
728
|
+
var buffers=[];
|
|
729
|
+
for(var i=0;i<toWrite.length;i++){
|
|
730
|
+
buffers.push(parseInt(toWrite[i],2));
|
|
731
|
+
}
|
|
732
|
+
const buffer = Buffer.from(buffers);
|
|
733
|
+
fs.writeFileSync(outPath, buffer);
|
|
734
|
+
},
|
|
735
|
+
write3DSQR:function(jsonIn,outPath){
|
|
736
|
+
var mii=jsonIn;
|
|
737
|
+
function makeMiiBinary(mii){
|
|
738
|
+
if(mii.perms.sharing&&mii.info.type==="Special"){
|
|
739
|
+
mii.perms.sharing=false;
|
|
740
|
+
console.log("Cannot have Sharing enabled for Special Miis. Disabled Sharing.");
|
|
741
|
+
}
|
|
742
|
+
var miiBin="00000011";
|
|
743
|
+
miiBin+="0000000";
|
|
744
|
+
miiBin+=mii.perms.copying?"1":"0";
|
|
745
|
+
miiBin+="00000000";
|
|
746
|
+
miiBin+="00110000";
|
|
747
|
+
miiBin+="1000101011010010000001101000011100011000110001100100011001100110010101100111111110111100000001110101110001000101011101100000001110100100010000000000000000000000".slice(0,8*8);
|
|
748
|
+
miiBin+=mii.info.type==="Special"?"0":"1";
|
|
749
|
+
miiBin+="0000000";
|
|
750
|
+
miiBin+="0111111110111100000001110101110001000101011101100000001110100100010000000000000000000000";
|
|
751
|
+
miiBin+=mii.info.birthday.toString(2).padStart(5,"0").slice(2,5);
|
|
752
|
+
miiBin+=mii.info.birthMonth.toString(2).padStart(4,"0");
|
|
753
|
+
miiBin+=mii.info.gender==="Male"?"0":"1";
|
|
754
|
+
miiBin+="00";
|
|
755
|
+
miiBin+=cols3DS.indexOf(mii.info.favColor).toString(2).padStart(4,"0");
|
|
756
|
+
miiBin+=mii.info.birthday.toString(2).padStart(5,"0").slice(0,2);
|
|
757
|
+
for(var i=0;i<10;i++){
|
|
758
|
+
if(i<mii.name.length){
|
|
759
|
+
miiBin+=mii.name.charCodeAt(i).toString(2).padStart(8,"0");
|
|
760
|
+
}
|
|
761
|
+
else{
|
|
762
|
+
miiBin+="00000000";
|
|
763
|
+
}
|
|
764
|
+
miiBin+="00000000";
|
|
765
|
+
}
|
|
766
|
+
miiBin+=mii.info.height.toString(2).padStart(8,"0");
|
|
767
|
+
miiBin+=mii.info.weight.toString(2).padStart(8,"0");
|
|
768
|
+
miiBin+=skinCols3DS.indexOf(mii.face.col).toString(2).padStart(3,"0");
|
|
769
|
+
miiBin+=tables.faces[mii.face.shape].toString(2).padStart(4,"0");
|
|
770
|
+
miiBin+=mii.perms.sharing?"0":"1";
|
|
771
|
+
miiBin+=makeups3DS.indexOf(mii.face.makeup).toString(2).padStart(4,"0");
|
|
772
|
+
miiBin+=faceFeatures3DS.indexOf(mii.face.feature).toString(2).padStart(4,"0");
|
|
773
|
+
miiBin+=tables.hairs[mii.hair.style[0]][mii.hair.style[1]].toString(2).padStart(8,"0");
|
|
774
|
+
miiBin+="0000";
|
|
775
|
+
miiBin+=mii.hair.flipped?"1":"0";
|
|
776
|
+
miiBin+=hairCols3DS.indexOf(mii.hair.col).toString(2).padStart(3,"0");
|
|
777
|
+
miiBin+=eyeCols3DS.indexOf(mii.eyes.col).toString(2).padStart(3,"0").slice(1,3);
|
|
778
|
+
miiBin+=tables.eyes[mii.eyes.type[0]][mii.eyes.type[1]].toString(2).padStart(6,"0");
|
|
779
|
+
miiBin+=mii.eyes.squash.toString(2).padStart(3,"0");
|
|
780
|
+
miiBin+=mii.eyes.size.toString(2).padStart(4,"0");
|
|
781
|
+
miiBin+=eyeCols3DS.indexOf(mii.eyes.col).toString(2).padStart(3,"0")[0];
|
|
782
|
+
miiBin+=mii.eyes.distApart.toString(2).padStart(4,"0").slice(1,4);
|
|
783
|
+
miiBin+=mii.eyes.rot.toString(2).padStart(5,"0");
|
|
784
|
+
miiBin+="00";
|
|
785
|
+
miiBin+=mii.eyes.yPos.toString(2).padStart(5,"0");
|
|
786
|
+
miiBin+=mii.eyes.distApart.toString(2).padStart(4,"0")[0];
|
|
787
|
+
miiBin+=hairCols3DS.indexOf(mii.eyebrows.col).toString(2).padStart(3,"0");
|
|
788
|
+
miiBin+=tables.eyebrows[mii.eyebrows.style[0]][mii.eyebrows.style[1]].toString(2).padStart(5,"0");
|
|
789
|
+
miiBin+="0";
|
|
790
|
+
miiBin+=mii.eyebrows.squash.toString(2).padStart(3,"0");
|
|
791
|
+
miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
|
|
792
|
+
miiBin+=mii.eyebrows.distApart.toString(2).padStart(4,"0").slice(1,4);
|
|
793
|
+
miiBin+="0";
|
|
794
|
+
miiBin+=mii.eyebrows.rot.toString(2).padStart(4,"0");
|
|
795
|
+
miiBin+="00";
|
|
796
|
+
miiBin+=(mii.eyebrows.yPos+3).toString(2).padStart(5,"0");
|
|
797
|
+
miiBin+=mii.eyebrows.distApart.toString(2).padStart(4,"0")[0];
|
|
798
|
+
miiBin+=mii.nose.size.toString(2).padStart(4,"0").slice(1,4);
|
|
799
|
+
miiBin+=tables.noses[mii.nose.type[0]][mii.nose.type[1]].toString(2).padStart(5,"0");
|
|
800
|
+
miiBin+="00";
|
|
801
|
+
miiBin+=mii.nose.yPos.toString(2).padStart(5,"0");
|
|
802
|
+
miiBin+=mii.nose.size.toString(2).padStart(4,"0")[0];
|
|
803
|
+
miiBin+=mouthCols3DS.indexOf(mii.mouth.col).toString(2).padStart(3,"0").slice(1,3);
|
|
804
|
+
miiBin+=tables.mouths[mii.mouth.type[0]][mii.mouth.type[1]].toString(2).padStart(6,"0");
|
|
805
|
+
miiBin+=mii.mouth.squash.toString(2).padStart(3,"0");
|
|
806
|
+
miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
|
|
807
|
+
miiBin+=mouthCols3DS.indexOf(mii.mouth.col).toString(2).padStart(3,"0")[0];
|
|
808
|
+
miiBin+=mii.facialHair.mustacheType.toString(2).padStart(3,"0");
|
|
809
|
+
miiBin+=mii.mouth.yPos.toString(2).padStart(5,"0");
|
|
810
|
+
miiBin+="00000000";
|
|
811
|
+
miiBin+=mii.facialHair.mustacheSize.toString(2).padStart(4,"0").slice(2,4);
|
|
812
|
+
miiBin+=hairCols3DS.indexOf(mii.facialHair.col).toString(2).padStart(3,"0");
|
|
813
|
+
miiBin+=mii.facialHair.beardType.toString(2).padStart(3,"0");
|
|
814
|
+
miiBin+="0";
|
|
815
|
+
miiBin+=mii.facialHair.mustacheYPos.toString(2).padStart(5,"0");
|
|
816
|
+
miiBin+=mii.facialHair.mustacheSize.toString(2).padStart(4,"0").slice(0,2);
|
|
817
|
+
miiBin+=mii.glasses.size.toString(2).padStart(4,"0")[3];
|
|
818
|
+
miiBin+=glassesCols3DS.indexOf(mii.glasses.col).toString(2).padStart(3,"0");
|
|
819
|
+
miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
|
|
820
|
+
miiBin+="0";
|
|
821
|
+
miiBin+=mii.glasses.yPos.toString(2).padStart(4,"0");
|
|
822
|
+
miiBin+=mii.glasses.size.toString(2).padStart(4,"0").slice(0,3);
|
|
823
|
+
miiBin+=mii.mole.xPos.toString(2).padStart(5,"0").slice(2,5);
|
|
824
|
+
miiBin+=mii.mole.size.toString(2).padStart(4,"0");
|
|
825
|
+
miiBin+=mii.mole.on?"1":"0";
|
|
826
|
+
miiBin+="0";
|
|
827
|
+
miiBin+=mii.mole.yPos.toString(2).padStart(5,"0");
|
|
828
|
+
miiBin+=mii.mole.xPos.toString(2).padStart(5,"0").slice(0,2);
|
|
829
|
+
for(var i=0;i<10;i++){
|
|
830
|
+
if(i<mii.creatorName.length){
|
|
831
|
+
miiBin+=mii.creatorName.charCodeAt(i).toString(2).padStart(8,"0");
|
|
832
|
+
}
|
|
833
|
+
else{
|
|
834
|
+
miiBin+="00000000";
|
|
835
|
+
}
|
|
836
|
+
miiBin+="00000000";
|
|
837
|
+
}
|
|
838
|
+
//Writing based on miiBin
|
|
839
|
+
var toWrite=miiBin.match(/.{1,8}/g);
|
|
840
|
+
var buffers=[];
|
|
841
|
+
for(var i=0;i<toWrite.length;i++){
|
|
842
|
+
buffers.push(parseInt(toWrite[i],2));
|
|
843
|
+
}
|
|
844
|
+
const buffer = Buffer.from(buffers);
|
|
845
|
+
fs.writeFileSync(outPath, buffer);
|
|
846
|
+
}
|
|
847
|
+
makeMiiBinary(mii);
|
|
848
|
+
var encryptedData = Buffer.from(encodeAesCcm(new Uint8Array(fs.readFileSync(outPath))));
|
|
849
|
+
fs.writeFileSync(outPath,encryptedData);
|
|
850
|
+
QRCode.toFile('./'+mii.name+'Output.png', [{ data: fs.readFileSync(outPath), mode: 'byte' }], {type: 'png'}, function (err) {
|
|
851
|
+
if (err) throw err;
|
|
852
|
+
});
|
|
853
|
+
var studioMii=new Uint8Array([0x08, 0x00, 0x40, 0x03, 0x08, 0x04, 0x04, 0x02, 0x02, 0x0c, 0x03, 0x01, 0x06, 0x04, 0x06, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x0a, 0x01, 0x00, 0x21, 0x40, 0x04, 0x00, 0x02, 0x14, 0x03, 0x13, 0x04, 0x17, 0x0d, 0x04, 0x00, 0x0a, 0x04, 0x01, 0x09]);
|
|
854
|
+
//miiPreview.src = 'https://studio.mii.nintendo.com/miis/image.png?data=' + mii.previewData + "&width=270&type=face";
|
|
855
|
+
function encodeStudio(studio) {
|
|
856
|
+
function byteToString(int){
|
|
857
|
+
var str = int.toString(16);
|
|
858
|
+
if(str.length < 2)str = '0' + str;
|
|
859
|
+
return str;
|
|
860
|
+
}
|
|
861
|
+
var n = 0;
|
|
862
|
+
var eo;
|
|
863
|
+
var dest = byteToString(n);
|
|
864
|
+
for (var i = 0; i < studio.length; i++) {
|
|
865
|
+
eo = (7 + (studio[i] ^ n)) & 0xFF;
|
|
866
|
+
n = eo;
|
|
867
|
+
dest += byteToString(eo);
|
|
868
|
+
}
|
|
869
|
+
return dest;
|
|
870
|
+
}
|
|
871
|
+
studioMii[0x16] = mii.info.gender==="Male"?0:1;
|
|
872
|
+
studioMii[0x15] = cols3DS.indexOf(mii.info.favColor);
|
|
873
|
+
studioMii[0x1E] = mii.info.height;
|
|
874
|
+
studioMii[2] = mii.info.weight;
|
|
875
|
+
studioMii[0x13] = tables.faces[mii.face.shape];
|
|
876
|
+
studioMii[0x11] = skinCols3DS.indexOf(mii.face.col);
|
|
877
|
+
studioMii[0x14] = faceFeatures3DS.indexOf(mii.face.feature);
|
|
878
|
+
studioMii[0x12] = makeups3DS.indexOf(mii.face.makeup);
|
|
879
|
+
studioMii[0x1D] = tables.hairs[mii.hair.style[0]][mii.hair.style[1]];
|
|
880
|
+
studioMii[0x1B] = hairCols3DS.indexOf(mii.hair.col);
|
|
881
|
+
if (!studioMii[0x1B]) studioMii[0x1B] = 8;
|
|
882
|
+
studioMii[0x1C] = mii.hair.flipped?1:0;
|
|
883
|
+
studioMii[7] = tables.eyes[mii.eyes.type[0]][mii.eyes.type[1]];
|
|
884
|
+
studioMii[4] = eyeCols3DS.indexOf(mii.eyes.col) + 8;
|
|
885
|
+
studioMii[6] = mii.eyes.size;
|
|
886
|
+
studioMii[3] = mii.eyes.squash;
|
|
887
|
+
studioMii[5] = mii.eyes.rot;
|
|
888
|
+
studioMii[8] = mii.eyes.distApart;
|
|
889
|
+
studioMii[9] = mii.eyes.yPos;
|
|
890
|
+
studioMii[0xE] = tables.eyebrows[mii.eyebrows.style[0]][mii.eyebrows.style[1]];
|
|
891
|
+
studioMii[0xB] = hairCols3DS.indexOf(mii.eyebrows.col);
|
|
892
|
+
if (!studioMii[0xB]) studioMii[0xB] = 8;
|
|
893
|
+
studioMii[0xD] = mii.eyebrows.size;
|
|
894
|
+
studioMii[0xA] = mii.eyebrows.squash;
|
|
895
|
+
studioMii[0xC] = mii.eyebrows.rot;
|
|
896
|
+
studioMii[0xF] = mii.eyebrows.distApart;
|
|
897
|
+
studioMii[0x10] = mii.eyebrows.yPos+3;
|
|
898
|
+
studioMii[0x2C] = tables.noses[mii.nose.type[0]][mii.nose.type[1]];
|
|
899
|
+
studioMii[0x2B] = mii.nose.size;
|
|
900
|
+
studioMii[0x2D] = mii.nose.yPos;
|
|
901
|
+
studioMii[0x26] = tables.mouths[mii.mouth.type[0]][mii.mouth.type[1]];
|
|
902
|
+
studioMii[0x24] = mouthCols3DS.indexOf(mii.mouth.col);
|
|
903
|
+
if (studioMii[0x24] < 4) {
|
|
904
|
+
studioMii[0x24] += 19;
|
|
905
|
+
} else {
|
|
906
|
+
studioMii[0x24] = 0;
|
|
907
|
+
}
|
|
908
|
+
studioMii[0x25] = mii.mouth.size;
|
|
909
|
+
studioMii[0x23] = mii.mouth.squash;
|
|
910
|
+
studioMii[0x27] = mii.mouth.yPos;
|
|
911
|
+
studioMii[0x29] = mii.facialHair.mustacheType;
|
|
912
|
+
studioMii[1] = mii.facialHair.beardType;
|
|
913
|
+
studioMii[0] = mii.facialHair.col;
|
|
914
|
+
if (!studioMii[0]) studioMii[0] = 8;
|
|
915
|
+
studioMii[0x28] = mii.facialHair.mustacheSize;
|
|
916
|
+
studioMii[0x2A] = mii.facialHair.mustacheYPos;
|
|
917
|
+
studioMii[0x19] = mii.glasses.type;
|
|
918
|
+
studioMii[0x17] = mii.glasses.col;
|
|
919
|
+
if (!studioMii[0x17]) {
|
|
920
|
+
studioMii[0x17] = 8;
|
|
921
|
+
} else if (studioMii[0x17] < 6) {
|
|
922
|
+
studioMii[0x17] += 13;
|
|
923
|
+
} else {
|
|
924
|
+
studioMii[0x17] = 0;
|
|
925
|
+
}
|
|
926
|
+
studioMii[0x18] = mii.glasses.size;
|
|
927
|
+
studioMii[0x1A] = mii.glasses.yPos;
|
|
928
|
+
studioMii[0x20] = mii.mole.on?1:0;
|
|
929
|
+
studioMii[0x1F] = mii.mole.size;
|
|
930
|
+
studioMii[0x21] = mii.mole.xPos;
|
|
931
|
+
studioMii[0x22] = mii.mole.yPos;
|
|
932
|
+
downloadImage('https://studio.mii.nintendo.com/miis/image.png?data=' + encodeStudio(studioMii) + "&width=270&type=face","./temp.png").then(d=>{
|
|
933
|
+
Jimp.read(mii.name+'Output.png', (err, fir_img) => {
|
|
934
|
+
if(err) {
|
|
935
|
+
console.log(err);
|
|
936
|
+
} else {
|
|
937
|
+
Jimp.read('temp.png', (err, sec_img) => {
|
|
938
|
+
if(err) {
|
|
939
|
+
console.log(err);
|
|
940
|
+
} else {
|
|
941
|
+
fir_img.resize(424, 424);
|
|
942
|
+
sec_img.resize(130,130);
|
|
943
|
+
const canvas = new Jimp(sec_img.bitmap.width, sec_img.bitmap.height, 0xFFFFFFFF);
|
|
944
|
+
canvas.composite(sec_img, 0, 0);
|
|
945
|
+
fir_img.blit(canvas, 212-130/2,212-130/2);
|
|
946
|
+
Jimp.loadFont(Jimp.FONT_SANS_16_BLACK).then(font => {
|
|
947
|
+
fir_img.print(font, 0, 50, {
|
|
948
|
+
text: mii.name,
|
|
949
|
+
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
|
|
950
|
+
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
|
|
951
|
+
}, 424, 424);
|
|
952
|
+
Jimp.read('./node_modules/miijs/crown.jpg',(err,thi_img)=>{
|
|
953
|
+
thi_img.resize(40,20);
|
|
954
|
+
if(mii.info.type==="Special") fir_img.blit(thi_img,232,150);
|
|
955
|
+
fir_img.write(outPath);
|
|
956
|
+
fs.unlinkSync("./temp.png");
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
})
|
|
961
|
+
}
|
|
962
|
+
fs.unlinkSync(mii.name+"Output.png");
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
}
|