miijs 2.1.2 → 2.1.3

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.
Files changed (2) hide show
  1. package/index.js +1077 -407
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -23,98 +23,15 @@ const FFLShaderMaterial = require("ffl.js/FFLShaderMaterial.js");
23
23
  // Typedefs for intellisence
24
24
  /** @typedef {import('./types').WiiMii} WiiMii */
25
25
 
26
- //Tools
27
- function Uint8Cat(){
28
- var destLength = 0
29
- for(var i = 0;i < arguments.length;i++){
30
- destLength += arguments[i].length;
31
- }
32
- var dest = new Uint8Array(destLength);
33
- var index = 0;
34
- for(var i=0;i<arguments.length;i++){
35
- dest.set(arguments[i],index);
36
- index += arguments[i].length;
37
- }
38
- return dest;
39
- }
40
- async function downloadImage(url) {
41
- return new Promise((resolve, reject) => {
42
- httpsLib.get(url, (res) => {
43
- if (res.statusCode === 200) {
44
- const data = [];
45
- res.on('data', chunk => data.push(chunk));
46
- res.on('end', () => resolve(Buffer.concat(data)));
47
- res.on('error', reject);
48
- } else {
49
- res.resume();
50
- reject(new Error(`Request Failed With a Status Code: ${res.statusCode}`));
51
- }
52
- });
53
- });
54
- }
55
- function byteToString(int){
56
- var str = int.toString(16);
57
- if(str.length < 2)str = '0' + str;
58
- return str;
59
- }
60
-
61
- //If FFLResHigh.dat is in the same directory as Node.js is calling the library from, use it by default
62
- let _fflRes; // undefined initially
63
- function getFFLRes() {
64
- // If we've already tried loading, just return the result
65
- if (_fflRes !== undefined) return _fflRes;
66
- for (const path of [ "./FFLResHigh.dat", "./ffl/FFLResHigh.dat" ]) {
67
- if (fs.existsSync(path)) {
68
- // Convert Buffer to Uint8Array explicitly
69
- const buffer = fs.readFileSync(path);
70
- return _fflRes = new Uint8Array(buffer);
71
- }
72
- }
73
- // If no file found, mark as null
74
- return _fflRes = null;
75
- }
76
-
77
- //3DS QR Code (En|De)cryption
78
- var NONCE_OFFSET = 0xC;
79
- var NONCE_LENGTH = 8;
80
- var TAG_LENGTH = 0x10;
81
- var aes_key = new Uint8Array([0x59, 0xFC, 0x81, 0x7E, 0x64, 0x46, 0xEA, 0x61, 0x90, 0x34, 0x7B, 0x20, 0xE9, 0xBD, 0xCE, 0x52]);
82
- var pad = new Uint8Array([0,0,0,0]);
83
- function decodeAesCcm(data){
84
- var nonce = Uint8Cat(data.subarray(0,NONCE_LENGTH),pad);
85
- var ciphertext = data.subarray(NONCE_LENGTH,0x70);
86
- var plaintext = asmCrypto.AES_CCM.decrypt(ciphertext,aes_key,nonce,undefined,TAG_LENGTH);
87
- return Uint8Cat(plaintext.subarray(0,NONCE_OFFSET),data.subarray(0,NONCE_LENGTH),plaintext.subarray(NONCE_OFFSET,plaintext.length - 4));
88
- }
89
- function crcCalc(data){
90
- var crc = 0;
91
- for (var byteIndex = 0;byteIndex < data.length; byteIndex++){
92
- for (var bitIndex = 7; bitIndex >= 0; bitIndex--){
93
- crc = (((crc << 1) | ((data[byteIndex] >> bitIndex) & 0x1)) ^
94
- (((crc & 0x8000) != 0) ? 0x1021 : 0));
95
- }
96
- }
97
- for(var counter = 16; counter > 0; counter--){
98
- crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0));
99
- }
100
- return(crc & 0xFFFF);
101
- }
102
- function encodeAesCcm(data){
103
- var nonce = Uint8Cat(data.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),pad);
104
- var crcSrc = Uint8Cat(data,new Uint8Array([0,0]));
105
- var crc = crcCalc(crcSrc);
106
- var cfsd = Uint8Cat(crcSrc,new Uint8Array([crc >>> 8,crc & 0xff]));
107
- var plaintext = Uint8Cat(cfsd.subarray(0,NONCE_OFFSET),cfsd.subarray(NONCE_OFFSET + NONCE_LENGTH,cfsd.length),pad,pad);
108
- var ciphertext = asmCrypto.AES_CCM.encrypt(plaintext,aes_key,nonce,undefined,TAG_LENGTH);
109
- return Uint8Cat(cfsd.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),ciphertext.subarray(0,ciphertext.length - 24),ciphertext.subarray(ciphertext.length - TAG_LENGTH,ciphertext.length))
110
- }
111
-
112
26
  //Miscellaneous Tables
113
27
  const lookupTables = {
28
+ //Universals
114
29
  favCols: ["Red", "Orange", "Yellow", "Lime", "Green", "Blue", "Cyan", "Pink", "Purple", "Brown", "White", "Black"],
115
30
  skinCols: ["White", "Tanned White", "Darker White", "Tanned Darker", "Mostly Black", "Black"],
116
31
  hairCols: ["Black", "Brown", "Red", "Reddish Brown", "Grey", "Light Brown", "Dark Blonde", "Blonde"],
117
32
  eyeCols: ["Black", "Grey", "Brown", "Lime", "Blue", "Green"],
33
+
34
+ //Wii fields
118
35
  wiiFaceFeatures: ["None", "Blush", "Makeup and Blush", "Freckles", "Bags", "Wrinkles on Cheeks", "Wrinkles near Eyes", "Chin Wrinkle", "Makeup", "Stubble", "Wrinkles near Mouth", "Wrinkles"],
119
36
  wiiMouthColors: ["Peach", "Red", "Pink"],
120
37
  wiiGlassesCols: ["Grey", "Brown", "Red", "Blue", "Yellow", "White"],
@@ -131,183 +48,182 @@ const lookupTables = {
131
48
  "9": 9,
132
49
  "7": 10,
133
50
  },
134
-
135
51
  pages:{
136
52
  mouths: {
137
- '0': '1',
138
- '1': '1',
139
- '2': '2',
140
- '3': '2',
141
- '4': '2',
142
- '5': '1',
143
- '6': '1',
144
- '7': '2',
145
- '8': '1',
146
- '9': '2',
147
- '10': '1',
148
- '11': '2',
149
- '12': '2',
150
- '13': '1',
151
- '14': '2',
152
- '15': '2',
153
- '16': '1',
154
- '17': '2',
155
- '18': '2',
156
- '19': '1',
157
- '20': '2',
158
- '21': '1',
159
- '22': '1',
160
- '23': '1'
53
+ '0': 1,
54
+ '1': 1,
55
+ '2': 2,
56
+ '3': 2,
57
+ '4': 2,
58
+ '5': 1,
59
+ '6': 1,
60
+ '7': 2,
61
+ '8': 1,
62
+ '9': 2,
63
+ '10': 1,
64
+ '11': 2,
65
+ '12': 2,
66
+ '13': 1,
67
+ '14': 2,
68
+ '15': 2,
69
+ '16': 1,
70
+ '17': 2,
71
+ '18': 2,
72
+ '19': 1,
73
+ '20': 2,
74
+ '21': 1,
75
+ '22': 1,
76
+ '23': 1
161
77
  },
162
78
  eyebrows:{
163
- '0': '1',
164
- '1': '1',
165
- '2': '2',
166
- '3': '2',
167
- '4': '1',
168
- '5': '1',
169
- '6': '1',
170
- '7': '1',
171
- '8': '1',
172
- '9': '1',
173
- '10': '2',
174
- '11': '2',
175
- '12': '1',
176
- '13': '2',
177
- '14': '2',
178
- '15': '2',
179
- '16': '2',
180
- '17': '1',
181
- '18': '2',
182
- '19': '1',
183
- '20': '2',
184
- '21': '1',
185
- '22': '2',
186
- '23': '2'
79
+ '0': 1,
80
+ '1': 1,
81
+ '2': 2,
82
+ '3': 2,
83
+ '4': 1,
84
+ '5': 1,
85
+ '6': 1,
86
+ '7': 1,
87
+ '8': 1,
88
+ '9': 1,
89
+ '10': 2,
90
+ '11': 2,
91
+ '12': 1,
92
+ '13': 2,
93
+ '14': 2,
94
+ '15': 2,
95
+ '16': 2,
96
+ '17': 1,
97
+ '18': 2,
98
+ '19': 1,
99
+ '20': 2,
100
+ '21': 1,
101
+ '22': 2,
102
+ '23': 2
187
103
  },
188
104
  eyes:{
189
- '0': '1',
190
- '1': '1',
191
- '2': '1',
192
- '3': '4',
193
- '4': '1',
194
- '5': '3',
195
- '6': '3',
196
- '7': '4',
197
- '8': '1',
198
- '9': '2',
199
- '10': '4',
200
- '11': '2',
201
- '12': '2',
202
- '13': '3',
203
- '14': '4',
204
- '15': '1',
205
- '16': '1',
206
- '17': '1',
207
- '18': '3',
208
- '19': '2',
209
- '20': '1',
210
- '21': '2',
211
- '22': '4',
212
- '23': '2',
213
- '24': '3',
214
- '25': '2',
215
- '26': '1',
216
- '27': '1',
217
- '28': '3',
218
- '29': '4',
219
- '30': '3',
220
- '31': '3',
221
- '32': '2',
222
- '33': '2',
223
- '34': '2',
224
- '35': '2',
225
- '36': '3',
226
- '37': '3',
227
- '38': '4',
228
- '39': '1',
229
- '40': '2',
230
- '41': '3',
231
- '42': '4',
232
- '43': '4',
233
- '44': '4',
234
- '45': '4',
235
- '46': '3',
236
- '47': '4'
105
+ 0: 1,
106
+ 1: 1,
107
+ 2: 1,
108
+ 3: 4,
109
+ 4: 1,
110
+ 5: 3,
111
+ 6: 3,
112
+ 7: 4,
113
+ 8: 1,
114
+ 9: 2,
115
+ 10: 4,
116
+ 11: 2,
117
+ 12: 2,
118
+ 13: 3,
119
+ 14: 4,
120
+ 15: 1,
121
+ 16: 1,
122
+ 17: 1,
123
+ 18: 3,
124
+ 19: 2,
125
+ 20: 1,
126
+ 21: 2,
127
+ 22: 4,
128
+ 23: 2,
129
+ 24: 3,
130
+ 25: 2,
131
+ 26: 1,
132
+ 27: 1,
133
+ 28: 3,
134
+ 29: 4,
135
+ 30: 3,
136
+ 31: 3,
137
+ 32: 2,
138
+ 33: 2,
139
+ 34: 2,
140
+ 35: 2,
141
+ 36: 3,
142
+ 37: 3,
143
+ 38: 4,
144
+ 39: 1,
145
+ 40: 2,
146
+ 41: 3,
147
+ 42: 4,
148
+ 43: 4,
149
+ 44: 4,
150
+ 45: 4,
151
+ 46: 3,
152
+ 47: 4
237
153
  },
238
154
  hairs:{
239
- '0': '5',
240
- '1': '4',
241
- '2': '6',
242
- '3': '5',
243
- '4': '4',
244
- '5': '4',
245
- '6': '5',
246
- '7': '4',
247
- '8': '4',
248
- '9': '6',
249
- '10': '5',
250
- '11': '5',
251
- '12': '4',
252
- '13': '4',
253
- '14': '5',
254
- '15': '6',
255
- '16': '6',
256
- '17': '5',
257
- '18': '6',
258
- '19': '4',
259
- '20': '5',
260
- '21': '5',
261
- '22': '5',
262
- '23': '3',
263
- '24': '6',
264
- '25': '4',
265
- '26': '4',
266
- '27': '4',
267
- '28': '6',
268
- '29': '6',
269
- '30': '3',
270
- '31': '1',
271
- '32': '2',
272
- '33': '1',
273
- '34': '3',
274
- '35': '5',
275
- '36': '3',
276
- '37': '2',
277
- '38': '3',
278
- '39': '1',
279
- '40': '1',
280
- '41': '3',
281
- '42': '3',
282
- '43': '3',
283
- '44': '1',
284
- '45': '1',
285
- '46': '6',
286
- '47': '2',
287
- '48': '2',
288
- '49': '1',
289
- '50': '2',
290
- '51': '1',
291
- '52': '2',
292
- '53': '6',
293
- '54': '3',
294
- '55': '2',
295
- '56': '1',
296
- '57': '3',
297
- '58': '2',
298
- '59': '1',
299
- '60': '2',
300
- '61': '6',
301
- '62': '2',
302
- '63': '5',
303
- '64': '2',
304
- '65': '3',
305
- '66': '2',
306
- '67': '3',
307
- '68': '1',
308
- '69': '4',
309
- '70': '1',
310
- '71': '6'
155
+ '0': 5,
156
+ '1': 4,
157
+ '2': 6,
158
+ '3': 5,
159
+ '4': 4,
160
+ '5': 4,
161
+ '6': 5,
162
+ '7': 4,
163
+ '8': 4,
164
+ '9': 6,
165
+ '10': 5,
166
+ '11': 5,
167
+ '12': 4,
168
+ '13': 4,
169
+ '14': 5,
170
+ '15': 6,
171
+ '16': 6,
172
+ '17': 5,
173
+ '18': 6,
174
+ '19': 4,
175
+ '20': 5,
176
+ '21': 5,
177
+ '22': 5,
178
+ '23': 3,
179
+ '24': 6,
180
+ '25': 4,
181
+ '26': 4,
182
+ '27': 4,
183
+ '28': 6,
184
+ '29': 6,
185
+ '30': 3,
186
+ '31': 1,
187
+ '32': 2,
188
+ '33': 1,
189
+ '34': 3,
190
+ '35': 5,
191
+ '36': 3,
192
+ '37': 2,
193
+ '38': 3,
194
+ '39': 1,
195
+ '40': 1,
196
+ '41': 3,
197
+ '42': 3,
198
+ '43': 3,
199
+ '44': 1,
200
+ '45': 1,
201
+ '46': 6,
202
+ '47': 2,
203
+ '48': 2,
204
+ '49': 1,
205
+ '50': 2,
206
+ '51': 1,
207
+ '52': 2,
208
+ '53': 6,
209
+ '54': 3,
210
+ '55': 2,
211
+ '56': 1,
212
+ '57': 3,
213
+ '58': 2,
214
+ '59': 1,
215
+ '60': 2,
216
+ '61': 6,
217
+ '62': 2,
218
+ '63': 5,
219
+ '64': 2,
220
+ '65': 3,
221
+ '66': 2,
222
+ '67': 3,
223
+ '68': 1,
224
+ '69': 4,
225
+ '70': 1,
226
+ '71': 6
311
227
  }
312
228
  },
313
229
  types:{
@@ -488,6 +404,196 @@ const lookupTables = {
488
404
  "71": 8
489
405
  }
490
406
  },
407
+ wiiNoses:{
408
+ '0': 1,
409
+ '1': 10,
410
+ '2': 2,
411
+ '3': 3,
412
+ '4': 6,
413
+ '5': 0,
414
+ '6': 5,
415
+ '7': 4,
416
+ '8': 8,
417
+ '9': 9,
418
+ '10': 7,
419
+ '11': 11
420
+ },
421
+ mouthTable:{
422
+ '0': '113',
423
+ '1': '121',
424
+ '2': '231',
425
+ '3': '222',
426
+ '4': '232',
427
+ '5': '132',
428
+ '6': '124',
429
+ '7': '211',
430
+ '8': '123',
431
+ '9': '221',
432
+ '10': '133',
433
+ '11': '223',
434
+ '12': '234',
435
+ '13': '134',
436
+ '14': '224',
437
+ '15': '213',
438
+ '16': '114',
439
+ '17': '212',
440
+ '18': '214',
441
+ '19': '131',
442
+ '20': '233',
443
+ '21': '112',
444
+ '22': '122',
445
+ '23': '111'
446
+ },
447
+ eyebrowTable:{
448
+ '0': '121',
449
+ '1': '112',
450
+ '2': '231',
451
+ '3': '212',
452
+ '4': '134',
453
+ '5': '124',
454
+ '6': '111',
455
+ '7': '113',
456
+ '8': '133',
457
+ '9': '122',
458
+ '10': '221',
459
+ '11': '211',
460
+ '12': '131',
461
+ '13': '223',
462
+ '14': '222',
463
+ '15': '213',
464
+ '16': '224',
465
+ '17': '114',
466
+ '18': '214',
467
+ '19': '132',
468
+ '20': '232',
469
+ '21': '123',
470
+ '22': '233',
471
+ '23': '234'
472
+ },
473
+ eyeTable:{
474
+ '0': '131',
475
+ '1': '113',
476
+ '2': '111',
477
+ '3': '413',
478
+ '4': '121',
479
+ '5': '311',
480
+ '6': '332',
481
+ '7': '411',
482
+ '8': '112',
483
+ '9': '222',
484
+ '10': '414',
485
+ '11': '221',
486
+ '12': '232',
487
+ '13': '331',
488
+ '14': '424',
489
+ '15': '114',
490
+ '16': '133',
491
+ '17': '132',
492
+ '18': '314',
493
+ '19': '231',
494
+ '20': '134',
495
+ '21': '233',
496
+ '22': '433',
497
+ '23': '213',
498
+ '24': '313',
499
+ '25': '214',
500
+ '26': '123',
501
+ '27': '124',
502
+ '28': '324',
503
+ '29': '432',
504
+ '30': '323',
505
+ '31': '333',
506
+ '32': '212',
507
+ '33': '211',
508
+ '34': '223',
509
+ '35': '234',
510
+ '36': '312',
511
+ '37': '322',
512
+ '38': '431',
513
+ '39': '122',
514
+ '40': '224',
515
+ '41': '321',
516
+ '42': '412',
517
+ '43': '423',
518
+ '44': '421',
519
+ '45': '422',
520
+ '46': '334',
521
+ '47': '434'
522
+ },
523
+ hairTable:{
524
+ '0': '534',
525
+ '1': '413',
526
+ '2': '632',
527
+ '3': '521',
528
+ '4': '422',
529
+ '5': '433',
530
+ '6': '522',
531
+ '7': '434',
532
+ '8': '414',
533
+ '9': '612',
534
+ '10': '512',
535
+ '11': '513',
536
+ '12': '411',
537
+ '13': '421',
538
+ '14': '511',
539
+ '15': '624',
540
+ '16': '621',
541
+ '17': '533',
542
+ '18': '622',
543
+ '19': '423',
544
+ '20': '532',
545
+ '21': '524',
546
+ '22': '531',
547
+ '23': '312',
548
+ '24': '614',
549
+ '25': '432',
550
+ '26': '412',
551
+ '27': '424',
552
+ '28': '613',
553
+ '29': '634',
554
+ '30': '314',
555
+ '31': '134',
556
+ '32': '211',
557
+ '33': '111',
558
+ '34': '334',
559
+ '35': '514',
560
+ '36': '313',
561
+ '37': '231',
562
+ '38': '321',
563
+ '39': '122',
564
+ '40': '121',
565
+ '41': '323',
566
+ '42': '331',
567
+ '43': '311',
568
+ '44': '112',
569
+ '45': '113',
570
+ '46': '631',
571
+ '47': '221',
572
+ '48': '212',
573
+ '49': '123',
574
+ '50': '223',
575
+ '51': '131',
576
+ '52': '232',
577
+ '53': '623',
578
+ '54': '332',
579
+ '55': '233',
580
+ '56': '114',
581
+ '57': '324',
582
+ '58': '213',
583
+ '59': '133',
584
+ '60': '224',
585
+ '61': '611',
586
+ '62': '234',
587
+ '63': '523',
588
+ '64': '214',
589
+ '65': '333',
590
+ '66': '222',
591
+ '67': '322',
592
+ '68': '124',
593
+ '69': '431',
594
+ '70': '132',
595
+ '71': '633'
596
+ },
491
597
 
492
598
  // 3DS fields
493
599
  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"],
@@ -558,7 +664,6 @@ const lookupTables = {
558
664
  ]
559
665
  }
560
666
  };
561
-
562
667
  var convTables={
563
668
  face3DSToWii:[0,1,2,2,3,1,4,5,4,6,7,6],
564
669
  features3DSToWii:["0","6",5,6,"6",4,7,7,8,10,"6",11],//If typeof===String, choose a makeup in that field's place - there is no suitable replacement. Read the discrepancies in the README for more information.
@@ -735,6 +840,18 @@ var convTables={
735
840
  "9",5,2,
736
841
  3,7,8,
737
842
  "10",9,11
843
+ ],
844
+ formatTo:[
845
+ [0,1,2],
846
+ [3,4,5],
847
+ [6,7,8],
848
+ [9,10,11]
849
+ ],
850
+ formatFrom:[
851
+ "11","21","31",
852
+ "12","22","32",
853
+ "13","23","33",
854
+ "14","24","34"
738
855
  ]
739
856
  };
740
857
  const kidNames={
@@ -1027,6 +1144,126 @@ const kidNames={
1027
1144
  ]
1028
1145
  };
1029
1146
 
1147
+ //Tools
1148
+ function Uint8Cat(){
1149
+ var destLength = 0
1150
+ for(var i = 0;i < arguments.length;i++){
1151
+ destLength += arguments[i].length;
1152
+ }
1153
+ var dest = new Uint8Array(destLength);
1154
+ var index = 0;
1155
+ for(var i=0;i<arguments.length;i++){
1156
+ dest.set(arguments[i],index);
1157
+ index += arguments[i].length;
1158
+ }
1159
+ return dest;
1160
+ }
1161
+ async function downloadImage(url) {
1162
+ return new Promise((resolve, reject) => {
1163
+ httpsLib.get(url, (res) => {
1164
+ if (res.statusCode === 200) {
1165
+ const data = [];
1166
+ res.on('data', chunk => data.push(chunk));
1167
+ res.on('end', () => resolve(Buffer.concat(data)));
1168
+ res.on('error', reject);
1169
+ } else {
1170
+ res.resume();
1171
+ reject(new Error(`Request Failed With a Status Code: ${res.statusCode}`));
1172
+ }
1173
+ });
1174
+ });
1175
+ }
1176
+ function byteToString(int){
1177
+ var str = int.toString(16);
1178
+ if(str.length < 2)str = '0' + str;
1179
+ return str;
1180
+ }
1181
+ function getBinaryFromAddress(addr, bin){
1182
+ let byte = bin.readUInt8(addr);
1183
+ let binaryString = '';
1184
+ for (let i = 7; i >= 0; i--) {
1185
+ binaryString += ((byte >> i) & 1) ? '1' : '0';
1186
+ }
1187
+ return binaryString;
1188
+ }
1189
+ function getKeyByValue(object, value) {
1190
+ for (var key in object) {
1191
+ if (object[key] === value) {
1192
+ return key;
1193
+ }
1194
+ }
1195
+ }
1196
+ function lookupTable(table,value,paginated){
1197
+ if(paginated){
1198
+ for(var i=0;i<lookupTables[table].values.length;i++){
1199
+ for(var j=0;j<lookupTables[table].values[i].length;j++){
1200
+ if(lookupTables[table].values[i][j]===value){
1201
+ return [i,j];
1202
+ }
1203
+ }
1204
+ }
1205
+ }
1206
+ else{
1207
+ for(var i=0;i<lookupTables[table].values.length;i++){
1208
+ if(lookupTables[table].values[i]===value){
1209
+ return i;
1210
+ }
1211
+ }
1212
+ }
1213
+ return undefined;
1214
+ }
1215
+
1216
+ //If FFLResHigh.dat is in the same directory as Node.js is calling the library from, use it by default
1217
+ let _fflRes; // undefined initially
1218
+ function getFFLRes() {
1219
+ // If we've already tried loading, just return the result
1220
+ if (_fflRes !== undefined) return _fflRes;
1221
+ for (const path of [ "./FFLResHigh.dat", "./ffl/FFLResHigh.dat" ]) {
1222
+ if (fs.existsSync(path)) {
1223
+ // Convert Buffer to Uint8Array explicitly
1224
+ const buffer = fs.readFileSync(path);
1225
+ return _fflRes = new Uint8Array(buffer);
1226
+ }
1227
+ }
1228
+ // If no file found, mark as null
1229
+ return _fflRes = null;
1230
+ }
1231
+
1232
+ //3DS QR Code (En|De)cryption
1233
+ var NONCE_OFFSET = 0xC;
1234
+ var NONCE_LENGTH = 8;
1235
+ var TAG_LENGTH = 0x10;
1236
+ var aes_key = new Uint8Array([0x59, 0xFC, 0x81, 0x7E, 0x64, 0x46, 0xEA, 0x61, 0x90, 0x34, 0x7B, 0x20, 0xE9, 0xBD, 0xCE, 0x52]);
1237
+ var pad = new Uint8Array([0,0,0,0]);
1238
+ function decodeAesCcm(data){
1239
+ var nonce = Uint8Cat(data.subarray(0,NONCE_LENGTH),pad);
1240
+ var ciphertext = data.subarray(NONCE_LENGTH,0x70);
1241
+ var plaintext = asmCrypto.AES_CCM.decrypt(ciphertext,aes_key,nonce,undefined,TAG_LENGTH);
1242
+ return Uint8Cat(plaintext.subarray(0,NONCE_OFFSET),data.subarray(0,NONCE_LENGTH),plaintext.subarray(NONCE_OFFSET,plaintext.length - 4));
1243
+ }
1244
+ function crcCalc(data){
1245
+ var crc = 0;
1246
+ for (var byteIndex = 0;byteIndex < data.length; byteIndex++){
1247
+ for (var bitIndex = 7; bitIndex >= 0; bitIndex--){
1248
+ crc = (((crc << 1) | ((data[byteIndex] >> bitIndex) & 0x1)) ^
1249
+ (((crc & 0x8000) != 0) ? 0x1021 : 0));
1250
+ }
1251
+ }
1252
+ for(var counter = 16; counter > 0; counter--){
1253
+ crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0));
1254
+ }
1255
+ return(crc & 0xFFFF);
1256
+ }
1257
+ function encodeAesCcm(data){
1258
+ var nonce = Uint8Cat(data.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),pad);
1259
+ var crcSrc = Uint8Cat(data,new Uint8Array([0,0]));
1260
+ var crc = crcCalc(crcSrc);
1261
+ var cfsd = Uint8Cat(crcSrc,new Uint8Array([crc >>> 8,crc & 0xff]));
1262
+ var plaintext = Uint8Cat(cfsd.subarray(0,NONCE_OFFSET),cfsd.subarray(NONCE_OFFSET + NONCE_LENGTH,cfsd.length),pad,pad);
1263
+ var ciphertext = asmCrypto.AES_CCM.encrypt(plaintext,aes_key,nonce,undefined,TAG_LENGTH);
1264
+ return Uint8Cat(cfsd.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),ciphertext.subarray(0,ciphertext.length - 24),ciphertext.subarray(ciphertext.length - TAG_LENGTH,ciphertext.length))
1265
+ }
1266
+
1030
1267
  //Defaults
1031
1268
  const defaultInstrs={
1032
1269
  wii:{
@@ -1510,7 +1747,30 @@ const encoders = {
1510
1747
  }
1511
1748
  return undefined;
1512
1749
  }
1513
- },
1750
+ },
1751
+ encodingTable: (decodedValue, field, tables) => {
1752
+ const table = getNestedProperty(tables, field.encodingTable);
1753
+ console.log(table);
1754
+ if (!table) return "ERROR: could not find requested encoding table";
1755
+
1756
+ if (table.indexLookup){
1757
+ if (table.paginated) {
1758
+ if (!Array.isArray(decodedValue) || decodedValue.length !== 2) {
1759
+ return undefined;
1760
+ }
1761
+ const [page, index] = decodedValue;
1762
+ if (page >= 0 && page < table.values.length && index >= 0 && index < table.values[page].length) {
1763
+ return table.values[page][index];
1764
+ }
1765
+ return undefined;
1766
+ } else {
1767
+ return "ERROR";
1768
+ }
1769
+ }
1770
+ else{
1771
+ return "ERROR";
1772
+ }
1773
+ },
1514
1774
  color: (value, field, T) => {
1515
1775
  const arr = T[field.colorArray];
1516
1776
  return arr?.indexOf(value) ?? value;
@@ -1690,7 +1950,13 @@ function jsonToMiiBuffer(miiData, schema, lookupTables = {}, totalBytes = 74) {
1690
1950
  let raw = getNestedProperty(miiData, fieldPath);
1691
1951
 
1692
1952
  // Run encoders
1693
- if (fieldDef.decoder && typeof encoders !== 'undefined' && encoders[fieldDef.decoder]) {
1953
+ if (fieldDef.encodingTable && fieldDef.decoder && typeof encoders !== 'undefined' && encoders[fieldDef.decoder]){
1954
+ if(fieldDef.encodingTable==="NONE"){
1955
+ continue;
1956
+ }
1957
+ raw = encoders.encodingTable([raw,getNestedProperty(miiData,fieldDef.secondaryParameter)], fieldDef, lookupTables);
1958
+ }
1959
+ else if (fieldDef.decoder && typeof encoders !== 'undefined' && encoders[fieldDef.decoder]) {
1694
1960
  raw = encoders[fieldDef.decoder](raw, fieldDef, lookupTables);
1695
1961
  }
1696
1962
 
@@ -1725,7 +1991,7 @@ const WII_MII_SCHEMA = {
1725
1991
  'face.type': { byteOffset: 0x20, bitOffset: 0, bitLength: 3, decoder: 'number' },
1726
1992
  'face.color': { byteOffset: 0x20, bitOffset: 3, bitLength: 3, decoder: 'number' },
1727
1993
  'face.feature': { byteOffset: 0x20, bitOffset: 6, bitLength: 4, decoder: 'number' },
1728
- 'hair.page': { byteOffset: 0x22, bitOffset: 0, bitLength: 7, decoder: 'lookup', lookupTable: 'hairs' },
1994
+ 'hair.page': { byteOffset: 0x22, bitOffset: 0, bitLength: 7, decoder: 'lookup', lookupTable: 'pages.hairs' },//Refuse, marked for destruction.
1729
1995
  'hair.type': { byteOffset: 0x22, bitOffset: 0, bitLength: 7, decoder: 'lookup', lookupTable: 'types.hairs' },
1730
1996
  'hair.color': { byteOffset: 0x22, bitOffset: 7, bitLength: 3, decoder: 'number' },
1731
1997
  'hair.flipped': { byteOffset: 0x23, bitOffset: 2, bitLength: 1, decoder: 'boolean' },
@@ -1736,8 +2002,8 @@ const WII_MII_SCHEMA = {
1736
2002
  'eyebrows.size': { byteOffset: 0x26, bitOffset: 3, bitLength: 4, decoder: 'number' },
1737
2003
  'eyebrows.yPosition': { byteOffset: 0x26, bitOffset: 7, bitLength: 5, decoder: 'number', offset: -3 },
1738
2004
  'eyebrows.distanceApart': { byteOffset: 0x27, bitOffset: 4, bitLength: 4, decoder: 'number' },
1739
- 'eyes.page': { byteOffset: 0x28, bitOffset: 0, bitLength: 6, decoder: 'lookup', lookupTable: 'pages.eyes' },
1740
- 'eyes.type': { byteOffset: 0x28, bitOffset: 0, bitLength: 6, decoder: 'lookup', lookupTable: 'types.eyes' },
2005
+ 'eyes.page': { byteOffset: 0x28, bitOffset: 0, bitLength: 6, decoder: 'lookup', lookupTable: 'pages.eyes', encodingTable: 'eyes', secondaryParameter: 'eyes.type' },//Refuse, marked for destruction.
2006
+ 'eyes.type': { byteOffset: 0x28, bitOffset: 0, bitLength: 6, decoder: 'lookup', lookupTable: 'types.eyes', encodingTable:'NONE' },
1741
2007
  'eyes.rotation': { byteOffset: 0x29, bitOffset: 0, bitLength: 3, decoder: 'number' },
1742
2008
  'eyes.yPosition': { byteOffset: 0x29, bitOffset: 3, bitLength: 5, decoder: 'number' },
1743
2009
  'eyes.color': { byteOffset: 0x2A, bitOffset: 0, bitLength: 3, decoder: 'number' },
@@ -1746,7 +2012,7 @@ const WII_MII_SCHEMA = {
1746
2012
  'nose.type': { byteOffset: 0x2C, bitOffset: 0, bitLength: 4, decoder: 'lookup', lookupTable: 'wiiNoses' },
1747
2013
  'nose.size': { byteOffset: 0x2C, bitOffset: 4, bitLength: 4, decoder: 'number' },
1748
2014
  'nose.yPosition': { byteOffset: 0x2D, bitOffset: 0, bitLength: 5, decoder: 'number' },
1749
- 'mouth.page': { byteOffset: 0x2E, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'pages.mouths' },
2015
+ 'mouth.page': { byteOffset: 0x2E, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'pages.mouths' },//Refuse, marked for destruction.
1750
2016
  'mouth.type': { byteOffset: 0x2E, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'types.mouths' },
1751
2017
  'mouth.color': { byteOffset: 0x2E, bitOffset: 5, bitLength: 2, decoder: 'number' },
1752
2018
  'mouth.size': { byteOffset: 0x2E, bitOffset: 7, bitLength: 4, decoder: 'number' },
@@ -1930,8 +2196,8 @@ function convertMii(jsonIn,typeTo){
1930
2196
  miiTo.console="wii";
1931
2197
  }
1932
2198
  else if(typeFrom==="wii"){
1933
- miiTo.perms.sharing=mii.info.mingle;
1934
- miiTo.perms.copying=mii.info.mingle;
2199
+ miiTo.perms.sharing=mii.general.mingle;
2200
+ miiTo.perms.copying=mii.general.mingle;
1935
2201
  miiTo.hair.style=convTables.hairWiiTo3DS[mii.hair.page][mii.hair.type];
1936
2202
  miiTo.face.shape=convTables.faceWiiTo3DS[mii.face.shape];
1937
2203
  miiTo.face.makeup=0;
@@ -2026,10 +2292,121 @@ async function readWiiBin(binOrPath) {
2026
2292
  } else {
2027
2293
  data = Buffer.from(binOrPath);
2028
2294
  }
2295
+ var thisMii={
2296
+ general:{},
2297
+ perms:{},
2298
+ meta:{},
2299
+ face:{},
2300
+ nose:{},
2301
+ mouth:{},
2302
+ mole:{},
2303
+ hair:{},
2304
+ eyebrows:{},
2305
+ eyes:{},
2306
+ glasses:{},
2307
+ beard:{
2308
+ mustache:{}
2309
+ }
2310
+ };
2029
2311
 
2030
- const thisMii = miiBufferToJson(data, WII_MII_SCHEMA, lookupTables, true);
2031
- thisMii.console = 'wii';
2312
+ const get = address => getBinaryFromAddress(address, data);
2032
2313
 
2314
+ var name="";
2315
+ for(var i=0;i<10;i++){
2316
+ name+=data.slice(3+i*2, 4+i*2)+"";
2317
+ }
2318
+ thisMii.meta.name=name.replaceAll("\x00","");
2319
+ var cname="";
2320
+ for(var i=0;i<10;i++){
2321
+ cname+=data.slice(55+i*2, 56+i*2)+"";
2322
+ }
2323
+ thisMii.meta.creatorName=cname.replaceAll("\x00","");
2324
+ thisMii.general.gender=+get(0x00)[1];//0 for Male, 1 for Female
2325
+ thisMii.meta.miiId=parseInt(get(0x18),2).toString(16)+parseInt(get(0x19),2).toString(16)+parseInt(get(0x1A),2).toString(16)+parseInt(get(0x1B),2).toString(16);
2326
+ switch(thisMii.meta.miiId.slice(0,3)){
2327
+ case "010":
2328
+ thisMii.meta.type="Special";
2329
+ break;
2330
+ case "110":
2331
+ thisMii.meta.type="Foreign";
2332
+ break;
2333
+ default:
2334
+ thisMii.meta.type="Default";
2335
+ break;
2336
+ }
2337
+ thisMii.meta.systemId=parseInt(get(0x1C),2).toString(16)+parseInt(get(0x1D),2).toString(16)+parseInt(get(0x1E),2).toString(16)+parseInt(get(0x1F),2).toString(16);
2338
+ var temp=get(0x20);
2339
+ thisMii.face.type=parseInt(temp.slice(0,3),2);//0-7
2340
+ thisMii.face.color=parseInt(temp.slice(3,6),2);//0-5
2341
+ temp=get(0x21);
2342
+ thisMii.face.feature=parseInt(get(0x20).slice(6,8)+temp.slice(0,2),2);//0-11
2343
+ thisMii.perms.mingle=temp[5]==="0";//0 for Mingle, 1 for Don't Mingle
2344
+ temp=get(0x2C);
2345
+ thisMii.nose.type=+getKeyByValue(lookupTables.wiiNoses,parseInt(temp.slice(0,4),2));
2346
+ thisMii.nose.size=parseInt(temp.slice(4,8),2);
2347
+ thisMii.nose.yPosition=parseInt(get(0x2D).slice(0,5),2);//From top to bottom, 0-18, default 9
2348
+ temp=get(0x2E);
2349
+ thisMii.mouth.page=+lookupTables.mouthTable[""+parseInt(temp.slice(0,5),2)][0]-1;
2350
+ thisMii.mouth.type=convTables.formatTo[lookupTables.mouthTable[""+parseInt(temp.slice(0,5),2)][2]-1][lookupTables.mouthTable[""+parseInt(temp.slice(0,5),2)][1]-1];//0-23, Needs lookup table
2351
+ thisMii.mouth.color=parseInt(temp.slice(5,7),2);//0-2, refer to mouthColors array
2352
+ temp2=get(0x2F);
2353
+ thisMii.mouth.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-8, default 4
2354
+ thisMii.mouth.yPosition=parseInt(temp2.slice(3,8),2);//0-18, default 9, from top to bottom
2355
+ temp=get(0x00);
2356
+ var temp2=get(0x01);
2357
+ thisMii.general.birthMonth=parseInt(temp.slice(2,6),2);
2358
+ thisMii.general.birthday=parseInt(temp.slice(6,8)+temp2.slice(0,3),2);
2359
+ thisMii.general.favoriteColor=parseInt(temp2.slice(3,7),2);//0-11, refer to cols array
2360
+ thisMii.general.height=parseInt(get(0x16),2);//0-127
2361
+ thisMii.general.weight=parseInt(get(0x17),2);//0-127
2362
+ thisMii.perms.fromCheckMiiOut=get(0x21)[7]==="0"?false:true;
2363
+ temp=get(0x34);
2364
+ temp2=get(0x35);
2365
+ thisMii.mole.on=temp[0]==="0"?false:true;//0 for Off, 1 for On
2366
+ thisMii.mole.size=parseInt(temp.slice(1,5),2);//0-8, default 4
2367
+ thisMii.mole.xPosition=parseInt(temp2.slice(2,7),2);//0-16, Default 2
2368
+ thisMii.mole.yPosition=parseInt(temp.slice(5,8)+temp2.slice(0,2),2);//Top to bottom
2369
+ temp=get(0x22);
2370
+ temp2=get(0x23);
2371
+ thisMii.hair.page=+lookupTables.hairTable[""+parseInt(temp.slice(0,7),2)][0]-1;
2372
+ thisMii.hair.type=+convTables.formatTo[lookupTables.hairTable[""+parseInt(temp.slice(0,7),2)][2]-1][lookupTables.hairTable[""+parseInt(temp.slice(0,7),2)][1]-1];//0-71, Needs lookup table
2373
+ thisMii.hair.color=parseInt(temp[7]+temp2.slice(0,2),2);//0-7, refer to hairCols array
2374
+ thisMii.hair.flipped=temp2[2]==="0"?false:true;
2375
+ temp=get(0x24);
2376
+ temp2=get(0x25);
2377
+ thisMii.eyebrows.page=+lookupTables.eyebrowTable[""+parseInt(temp.slice(0,5),2)][0]-1;
2378
+ thisMii.eyebrows.type=convTables.formatTo[lookupTables.eyebrowTable[""+parseInt(temp.slice(0,5),2)][2]-1][lookupTables.eyebrowTable[""+parseInt(temp.slice(0,5),2)][1]-1];//0-23, Needs lookup table
2379
+ thisMii.eyebrows.rotation=parseInt(temp.slice(6,8)+temp2.slice(0,2),2);//0-11, default varies based on eyebrow type
2380
+ temp=get(0x26);
2381
+ temp2=get(0x27);
2382
+ thisMii.eyebrows.color=parseInt(temp.slice(0,3),2);
2383
+ thisMii.eyebrows.size=parseInt(temp.slice(3,7),2);//0-8, default 4
2384
+ thisMii.eyebrows.yPosition=(parseInt(temp[7]+temp2.slice(0,4),2))-3;//0-15, default 10
2385
+ thisMii.eyebrows.distanceApart=parseInt(temp2.slice(4,8),2);//0-12, default 2
2386
+ thisMii.eyes.page=+lookupTables.eyeTable[parseInt(get(0x28).slice(0,6),2)][0]-1;//0-47, needs lookup table
2387
+ thisMii.eyes.type=convTables.formatTo[lookupTables.eyeTable[parseInt(get(0x28).slice(0,6),2)][2]-1][lookupTables.eyeTable[parseInt(get(0x28).slice(0,6),2)][1]-1];//0-47, needs lookup table
2388
+ temp=get(0x29);
2389
+ thisMii.eyes.rotation=parseInt(temp.slice(0,3),2);//0-7, default varies based on eye type
2390
+ thisMii.eyes.yPosition=parseInt(temp.slice(3,8),2);//0-18, default 12, top to bottom
2391
+ temp=get(0x2A);
2392
+ thisMii.eyes.color=parseInt(temp.slice(0,3),2);//0-5
2393
+ thisMii.eyes.size=parseInt(temp.slice(4,7),2);//0-7, default 4
2394
+ temp2=get(0x2B);
2395
+ thisMii.eyes.distanceApart=parseInt(temp[7]+temp2.slice(0,3),2);//0-12, default 2
2396
+ temp=get(0x30);
2397
+ thisMii.glasses.type=parseInt(temp.slice(0,4),2);//0-8
2398
+ thisMii.glasses.color=parseInt(temp.slice(4,7),2);//0-5
2399
+ temp=get(0x31);
2400
+ thisMii.glasses.size=parseInt(temp.slice(0,3),2);//0-7, default 4
2401
+ thisMii.glasses.yPosition=parseInt(temp.slice(3,8),2);//0-20, default 10
2402
+ temp=get(0x32);
2403
+ temp2=get(0x33);
2404
+ thisMii.beard.mustache.type=parseInt(temp.slice(0,2),2);//0-3
2405
+ thisMii.beard.type=parseInt(temp.slice(2,4),2);//0-3
2406
+ thisMii.beard.color=parseInt(temp.slice(4,7),2);//0-7
2407
+ thisMii.beard.mustache.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-30, default 20
2408
+ thisMii.beard.mustache.yPosition=parseInt(temp2.slice(3,8),2);//0-16, default 2
2409
+ thisMii.console="Wii";
2033
2410
  return thisMii;
2034
2411
  }
2035
2412
  async function read3DSQR(binOrPath,returnDecryptedBin) {
@@ -2041,7 +2418,11 @@ async function read3DSQR(binOrPath,returnDecryptedBin) {
2041
2418
  const ctx = canvas.getContext('2d');
2042
2419
  ctx.drawImage(img, 0, 0);
2043
2420
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
2044
- qrCode = jsQR(imageData.data, imageData.width, imageData.height).binaryData;
2421
+ qrCode = jsQR(imageData.data, imageData.width, imageData.height)?.binaryData;
2422
+ if(!qrCode){
2423
+ console.error("Failed to read QR Code.");
2424
+ return;
2425
+ }
2045
2426
  }
2046
2427
  else {
2047
2428
  var d = binOrPath.match(/(0|1){1,8}/g);
@@ -2055,11 +2436,130 @@ async function read3DSQR(binOrPath,returnDecryptedBin) {
2055
2436
  if(returnDecryptedBin){
2056
2437
  return data;
2057
2438
  }
2058
- const miiJson = miiBufferToJson(data, THREEDS_MII_SCHEMA, lookupTables, false);
2059
- miiJson.console = '3ds';
2439
+ const miiJson = {
2440
+ general:{},
2441
+ perms:{},
2442
+ meta:{},
2443
+ face:{},
2444
+ nose:{},
2445
+ mouth:{},
2446
+ mole:{},
2447
+ hair:{},
2448
+ eyebrows:{},
2449
+ eyes:{},
2450
+ glasses:{},
2451
+ beard:{
2452
+ mustache:{}
2453
+ }
2454
+ };
2455
+ const get = address => getBinaryFromAddress(address, data);
2456
+ var temp=get(0x18);
2457
+ var temp2=get(0x19);
2458
+ miiJson.general.birthday=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
2459
+ miiJson.general.birthMonth=parseInt(temp.slice(3,7),2);
2460
+ //Handle UTF-16 Names
2461
+ var name = "";
2462
+ for (var i = 0x1A; i < 0x2E; i += 2) {
2463
+ let lo = data[i];
2464
+ let hi = data[i + 1];
2465
+ if (lo === 0x00 && hi === 0x00) {
2466
+ break;
2467
+ }
2468
+ let codeUnit = (hi << 8) | lo;
2469
+ name += String.fromCharCode(codeUnit);
2470
+ }
2471
+ miiJson.meta.name = name.replace(/\u0000/g, "");
2472
+ var cname = "";
2473
+ for (var i = 0x48; i < 0x5C; i += 2) {
2474
+ let lo = data[i];
2475
+ let hi = data[i + 1];
2476
+ if (lo === 0x00 && hi === 0x00) {
2477
+ break;
2478
+ }
2479
+ let codeUnit = (hi << 8) | lo;
2480
+ cname += String.fromCharCode(codeUnit);
2481
+ }
2482
+ miiJson.meta.creatorName = cname.replace(/\u0000/g, "");
2483
+ miiJson.general.height=parseInt(get(0x2E),2);
2484
+ miiJson.general.weight=parseInt(get(0x2F),2);
2485
+ miiJson.general.gender=+temp[7];
2486
+ temp=get(0x30);
2487
+ miiJson.perms.sharing=temp[7]==="1"?false:true;
2488
+ miiJson.general.favoriteColor=parseInt(temp2.slice(2,6),2);
2489
+ miiJson.perms.copying=get(0x01)[7]==="1"?true:false;
2490
+ miiJson.hair.page=lookupTable("hairs",parseInt(get(0x32),2),true)[0];
2491
+ miiJson.hair.type=lookupTable("hairs",parseInt(get(0x32),2),true)[1];
2492
+ miiJson.face.type=lookupTable("faces",parseInt(temp.slice(3,7),2),false);
2493
+ miiJson.face.color=parseInt(temp.slice(0,3),2);
2494
+ temp=get(0x31);
2495
+ miiJson.face.feature=parseInt(temp.slice(4,8),2);
2496
+ miiJson.face.makeup=parseInt(temp.slice(0,4),2);
2497
+ temp=get(0x34);
2498
+ miiJson.eyes.page=lookupTable("eyes",parseInt(temp.slice(2,8),2),true)[0];
2499
+ miiJson.eyes.type=lookupTable("eyes",parseInt(temp.slice(2,8),2),true)[1];
2500
+ temp2=get(0x33);
2501
+ miiJson.hair.color=parseInt(temp2.slice(5,8),2);
2502
+ miiJson.hair.flipped=temp2[4]==="0"?false:true;
2503
+ miiJson.eyes.color=parseInt(get(0x35)[7]+temp.slice(0,2),2);
2504
+ temp=get(0x35);
2505
+ miiJson.eyes.size=parseInt(temp.slice(3,7),2);
2506
+ miiJson.eyes.squash=parseInt(temp.slice(0,3),2);
2507
+ temp=get(0x36);
2508
+ temp2=get(0x37);
2509
+ miiJson.eyes.rotation=parseInt(temp.slice(3,8),2);
2510
+ miiJson.eyes.distanceApart=parseInt(temp2[7]+temp.slice(0,3),2);
2511
+ miiJson.eyes.yPosition=parseInt(temp2.slice(2,7),2);
2512
+ temp=get(0x38);
2513
+ miiJson.eyebrows.page=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true)[0];
2514
+ miiJson.eyebrows.type=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true)[1];
2515
+ miiJson.eyebrows.color=parseInt(temp.slice(0,3),2);
2516
+ temp=get(0x39);
2517
+ miiJson.eyebrows.size=parseInt(temp.slice(4,8),2);
2518
+ miiJson.eyebrows.squash=parseInt(temp.slice(1,4),2);
2519
+ temp=get(0x3A);
2520
+ miiJson.eyebrows.rotation=parseInt(temp.slice(4,8),2);
2521
+ temp2=get(0x3B);
2522
+ miiJson.eyebrows.distanceApart=parseInt(temp2[7]+temp.slice(0,3),2);
2523
+ miiJson.eyebrows.yPosition=parseInt(temp2.slice(2,7),2)-3;
2524
+ temp=get(0x3C);
2525
+ miiJson.nose.page=lookupTable("noses",parseInt(temp.slice(3,8),2),true)[0];
2526
+ miiJson.nose.type=lookupTable("noses",parseInt(temp.slice(3,8),2),true)[1];
2527
+ temp2=get(0x3D);
2528
+ miiJson.nose.size=parseInt(temp2[7]+temp.slice(0,3),2);
2529
+ miiJson.nose.yPosition=parseInt(temp2.slice(2,7),2);
2530
+ temp=get(0x3E);
2531
+ miiJson.mouth.page=lookupTable("mouths",parseInt(temp.slice(2,8),2),true)[0];
2532
+ miiJson.mouth.type=lookupTable("mouths",parseInt(temp.slice(2,8),2),true)[1];
2533
+ temp2=get(0x3F);
2534
+ miiJson.mouth.color=parseInt(temp2[7]+temp.slice(0,2),2);
2535
+ miiJson.mouth.size=parseInt(temp2.slice(3,7),2);
2536
+ miiJson.mouth.squash=parseInt(temp2.slice(0,3),2);
2537
+ temp=get(0x40);
2538
+ miiJson.mouth.yPosition=parseInt(temp.slice(3,8),2);
2539
+ miiJson.beard.mustache.type=parseInt(temp.slice(0,3),2);
2540
+ temp=get(0x42);
2541
+ miiJson.beard.type=parseInt(temp.slice(5,8),2);
2542
+ miiJson.beard.color=parseInt(temp.slice(2,5),2);
2543
+ temp2=get(0x43);
2544
+ miiJson.beard.mustache.size=parseInt(temp2.slice(6,8)+temp.slice(0,2),2);
2545
+ miiJson.beard.mustache.yPosition=parseInt(temp2.slice(1,6),2);
2546
+ temp=get(0x44);
2547
+ miiJson.glasses.type=parseInt(temp.slice(4,8),2);
2548
+ miiJson.glasses.color=parseInt(temp.slice(1,4),2);
2549
+ temp2=get(0x45);
2550
+ miiJson.glasses.size=parseInt(temp2.slice(5,8)+temp[0],2);
2551
+ miiJson.glasses.yPosition=parseInt(temp2.slice(0,5),2);
2552
+ temp=get(0x46);
2553
+ miiJson.mole.on=temp[7]==="0"?false:true;
2554
+ miiJson.mole.size=parseInt(temp.slice(3,7),2);
2555
+ temp2=get(0x47);
2556
+ miiJson.mole.xPosition=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
2557
+ miiJson.mole.yPosition=parseInt(temp2.slice(1,6),2);
2558
+ miiJson.meta.type="Default";//qk, Make this actually retrieve MiiID, SystemID, and Mii type
2559
+ miiJson.console="3DS";
2060
2560
  return miiJson;
2061
2561
  } else {
2062
- console.error('Failed to decode QR code');
2562
+ console.error('Failed to read Mii.');
2063
2563
  }
2064
2564
  }
2065
2565
  async function renderMiiWithStudio(jsonIn){
@@ -2188,160 +2688,330 @@ async function writeWiiBin(jsonIn, outPath) {
2188
2688
  if (jsonIn.console?.toLowerCase() !== "wii") {
2189
2689
  convertMii(jsonIn);
2190
2690
  }
2191
- const miiBuffer = jsonToMiiBuffer(jsonIn, WII_MII_SCHEMA, lookupTables, 74);
2691
+ var mii=jsonIn;
2692
+ var miiBin="0";
2693
+ miiBin+=mii.general.gender;
2694
+ miiBin+=mii.general.birthMonth.toString(2).padStart(4,"0");
2695
+ miiBin+=mii.general.birthday.toString(2).padStart(5,"0");
2696
+ miiBin+=mii.general.favoriteColor.toString(2).padStart(4,"0");
2697
+ miiBin+='0';
2698
+ for(var i=0;i<10;i++){
2699
+ if(i<mii.meta.name.length){
2700
+ miiBin+=mii.meta.name.charCodeAt(i).toString(2).padStart(16,"0");
2701
+ }
2702
+ else{
2703
+ miiBin+="0000000000000000";
2704
+ }
2705
+ }
2706
+ miiBin+=mii.general.height.toString(2).padStart(8,"0");
2707
+ miiBin+=mii.general.weight.toString(2).padStart(8,"0");
2708
+ let miiId="";
2709
+ switch(mii.meta.type){
2710
+ case "Special":
2711
+ miiId="01000110";
2712
+ break;
2713
+ case "Foreign":
2714
+ miiId="11000110";
2715
+ break;
2716
+ default:
2717
+ miiId="10001001";
2718
+ break;
2719
+ }
2720
+ for(var i=0;i<3;i++){
2721
+ miiId+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
2722
+ }
2723
+ miiBin+=miiId;
2724
+ miiBin+="11111111".repeat(4);//System ID
2725
+ miiBin+=mii.face.type.toString(2).padStart(3,"0");
2726
+ miiBin+=mii.face.color.toString(2).padStart(3,"0");
2727
+ miiBin+=mii.face.feature.toString(2).padStart(4,"0");
2728
+ miiBin+="000";
2729
+ if(mii.perms.mingle&&mii.meta.type.toLowerCase()==="special"){
2730
+ mii.perms.mingle=false;
2731
+ console.warn("A Special Mii cannot have Mingle on and still render on the Wii. Turned Mingle off in the output.");
2732
+ }
2733
+ miiBin+=mii.perms.mingle?"0":"1";
2734
+ miiBin+="0";
2735
+ miiBin+=mii.perms.fromCheckMiiOut?"1":"0";
2736
+ miiBin+=(+getKeyByValue(lookupTables.hairTable,`${mii.hair.page+1}${convTables.formatFrom[mii.hair.type]}`)).toString(2).padStart(7,"0");
2737
+ miiBin+=mii.hair.color.toString(2).padStart(3,"0");
2738
+ miiBin+=mii.hair.flipped?"1":"0";
2739
+ miiBin+="00000";
2740
+ miiBin+=(+getKeyByValue(lookupTables.eyebrowTable,`${mii.eyebrows.page+1}${convTables.formatFrom[mii.eyebrows.type]}`)).toString(2).padStart(5,"0");
2741
+ miiBin+="0";
2742
+ miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
2743
+ miiBin+="000000";
2744
+ miiBin+=mii.eyebrows.color.toString(2).padStart(3,"0");
2745
+ miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
2746
+ miiBin+=(mii.eyebrows.yPosition+3).toString(2).padStart(5,"0");
2747
+ miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0");
2748
+ miiBin+=(+getKeyByValue(lookupTables.eyeTable,`${mii.eyes.page+1}${convTables.formatFrom[mii.eyes.type]}`)).toString(2).padStart(6,"0");
2749
+ miiBin+="00";
2750
+ miiBin+=mii.eyes.rotation.toString(2).padStart(3,"0");
2751
+ miiBin+=mii.eyes.yPosition.toString(2).padStart(5,"0");
2752
+ miiBin+=mii.eyes.color.toString(2).padStart(3,"0");
2753
+ miiBin+="0";
2754
+ miiBin+=mii.eyes.size.toString(2).padStart(3,"0");
2755
+ miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0");
2756
+ miiBin+="00000";
2757
+ miiBin+=lookupTables.wiiNoses[mii.nose.type].toString(2).padStart(4,"0");
2758
+ miiBin+=mii.nose.size.toString(2).padStart(4,"0");
2759
+ miiBin+=mii.nose.yPosition.toString(2).padStart(5,"0");
2760
+ miiBin+="000";
2761
+ miiBin+=(+getKeyByValue(lookupTables.mouthTable,`${mii.mouth.page+1}${convTables.formatFrom[mii.mouth.type]}`)).toString(2).padStart(5,"0");
2762
+ miiBin+=mii.mouth.color.toString(2).padStart(2,"0");
2763
+ miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
2764
+ miiBin+=mii.mouth.yPosition.toString(2).padStart(5,"0");
2765
+ miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
2766
+ miiBin+=mii.glasses.color.toString(2).padStart(3,"0");
2767
+ miiBin+="0";
2768
+ miiBin+=mii.glasses.size.toString(2).padStart(3,"0");
2769
+ miiBin+=mii.glasses.yPosition.toString(2).padStart(5,"0");
2770
+ miiBin+=mii.beard.mustache.type.toString(2).padStart(2,"0");
2771
+ miiBin+=mii.beard.type.toString(2).padStart(2,"0");
2772
+ miiBin+=mii.beard.color.toString(2).padStart(3,"0");
2773
+ miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0");
2774
+ miiBin+=mii.beard.mustache.yPosition.toString(2).padStart(5,"0");
2775
+ miiBin+=mii.mole.on?"1":"0";
2776
+ miiBin+=mii.mole.size.toString(2).padStart(4,"0");
2777
+ miiBin+=mii.mole.yPosition.toString(2).padStart(5,"0");
2778
+ miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0");
2779
+ miiBin+="0";
2780
+ for(var i=0;i<10;i++){
2781
+ if(i<mii.meta.creatorName.length){
2782
+ miiBin+=mii.meta.creatorName.charCodeAt(i).toString(2).padStart(16,"0");
2783
+ }
2784
+ else{
2785
+ miiBin+="0000000000000000";
2786
+ }
2787
+ }
2788
+
2789
+ //Writing based on miiBin
2790
+ var toWrite=miiBin.match(/.{1,8}/g);
2791
+ var buffers=[];
2792
+ for(var i=0;i<toWrite.length;i++){
2793
+ buffers.push(parseInt(toWrite[i],2));
2794
+ }
2795
+ toWrite=Buffer.from(buffers);
2192
2796
  if(outPath){
2193
- await fs.promises.writeFile(outPath, miiBuffer);
2797
+ await fs.promises.writeFile(outPath, toWrite);
2194
2798
  }
2195
2799
  else{
2196
- return miiBuffer;
2800
+ return toWrite;
2197
2801
  }
2198
2802
  }
2199
2803
  async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2804
+ //Convert the Mii if it isn't in 3DS format
2200
2805
  if (!["3ds", "wii u"].includes(miiJson.console?.toLowerCase())) {
2201
2806
  miiJson = convertMii(miiJson);
2202
2807
  }
2203
- return new Promise(async (resolve, reject) => {
2204
- const miiBinary = jsonToMiiBuffer(miiJson, THREEDS_MII_SCHEMA, lookupTables, 74);
2205
- var encryptedData = Buffer.from(encodeAesCcm(new Uint8Array(miiBinary)));
2206
-
2207
- const options = {
2208
- width: 300,
2209
- height: 300,
2210
- data: encryptedData.toString("latin1"),
2211
- image: "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", // 1x1 gif
2212
- dotsOptions: {
2213
- color: "#000000",
2214
- type: "square"
2215
- },
2216
- backgroundOptions: {
2217
- color: "#ffffff",
2218
- },
2219
- imageOptions: {
2220
- crossOrigin: "anonymous",
2221
- imageSize: 0.4 // Changes how large center area is
2222
- }
2223
- }
2224
- const qrCodeImage = new QRCodeStyling({
2225
- jsdom: JSDOM,
2226
- nodeCanvas,
2227
- ...options
2228
- });
2229
- const qrBuffer = Buffer.from(await qrCodeImage.getRawData("png"))
2230
-
2231
- 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]);
2232
- studioMii[0x16] = miiJson.info.gender === "Male" ? 0 : 1;
2233
- studioMii[0x15] = lookupTables.favCols.indexOf(miiJson.info.favColor);
2234
- studioMii[0x1E] = miiJson.info.height;
2235
- studioMii[2] = miiJson.info.weight;
2236
- studioMii[0x13] = lookupTables.faces.values[miiJson.face.shape];
2237
- studioMii[0x11] = lookupTables.skinCols.indexOf(miiJson.face.col);
2238
- studioMii[0x14] = lookupTables.faceFeatures3DS.indexOf(miiJson.face.feature);
2239
- studioMii[0x12] = lookupTables.makeups3DS.indexOf(miiJson.face.makeup);
2240
- studioMii[0x1D] = lookupTables.hairs.values[miiJson.hair.style[0]][miiJson.hair.style[1]];
2241
- studioMii[0x1B] = lookupTables.hairCols.indexOf(miiJson.hair.col);
2242
- if (!studioMii[0x1B]) studioMii[0x1B] = 8;
2243
- studioMii[0x1C] = miiJson.hair.flipped ? 1 : 0;
2244
- studioMii[7] = lookupTables.eyes.values[miiJson.eyes.type[0]][miiJson.eyes.type[1]];
2245
- studioMii[4] = lookupTables.eyeCols.indexOf(miiJson.eyes.col) + 8;
2246
- studioMii[6] = miiJson.eyes.size;
2247
- studioMii[3] = miiJson.eyes.squash;
2248
- studioMii[5] = miiJson.eyes.rot;
2249
- studioMii[8] = miiJson.eyes.distApart;
2250
- studioMii[9] = miiJson.eyes.yPos;
2251
- studioMii[0xE] = lookupTables.eyebrows.values[miiJson.eyebrows.style[0]][miiJson.eyebrows.style[1]];
2252
- studioMii[0xB] = lookupTables.hairCols.indexOf(miiJson.eyebrows.col);
2253
- if (!studioMii[0xB]) studioMii[0xB] = 8;
2254
- studioMii[0xD] = miiJson.eyebrows.size;
2255
- studioMii[0xA] = miiJson.eyebrows.squash;
2256
- studioMii[0xC] = miiJson.eyebrows.rot;
2257
- studioMii[0xF] = miiJson.eyebrows.distApart;
2258
- studioMii[0x10] = miiJson.eyebrows.yPos + 3;
2259
- studioMii[0x2C] = lookupTables.noses.values[miiJson.nose.type[0]][miiJson.nose.type[1]];
2260
- studioMii[0x2B] = miiJson.nose.size;
2261
- studioMii[0x2D] = miiJson.nose.yPos;
2262
- studioMii[0x26] = lookupTables.mouths.values[miiJson.mouth.type[0]][miiJson.mouth.type[1]];
2263
- studioMii[0x24] = lookupTables.mouthCols3DS.indexOf(miiJson.mouth.col);
2264
- if (studioMii[0x24] < 4) {
2265
- studioMii[0x24] += 19;
2266
- } else {
2267
- studioMii[0x24] = 0;
2808
+
2809
+ //Make the binary
2810
+ var mii=miiJson;
2811
+ var miiBin = "00000011";
2812
+ //If Special Miis are being used improperly, fix it and warn the user
2813
+ if(mii.meta.type.toLowerCase()==="special"&&(mii.console.toLowerCase()==="wii u"||mii.console.toLowerCase()==="wiiu")){
2814
+ mii.meta.type="Default";
2815
+ console.warn("Wii Us do not work with Special Miis. Reverted to Default Mii.");
2816
+ }
2817
+ if(mii.perms.sharing&&mii.meta.type==="Special"){
2818
+ mii.perms.sharing=false;
2819
+ console.warn("Cannot have Sharing enabled for Special Miis. Disabled Sharing in the output.");
2820
+ }
2821
+ miiBin+="0000000";
2822
+ miiBin+=mii.perms.copying?"1":"0";
2823
+ miiBin+="00000000";
2824
+ miiBin+="00110000";
2825
+ miiBin+="1000101011010010000001101000011100011000110001100100011001100110010101100111111110111100000001110101110001000101011101100000001110100100010000000000000000000000".slice(0,8*8);
2826
+ miiBin+=mii.meta.type==="Special"?"0":"1";
2827
+ miiBin+="0000000";
2828
+ for(var i=0;i<3;i++){
2829
+ miiBin+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
2830
+ }
2831
+ miiBin+="0000000001000101011101100000001110100100010000000000000000000000";
2832
+ miiBin+=mii.general.birthday.toString(2).padStart(5,"0").slice(2,5);
2833
+ miiBin+=mii.general.birthMonth.toString(2).padStart(4,"0");
2834
+ miiBin+=mii.general.gender;
2835
+ miiBin+="00";
2836
+ miiBin+=mii.general.favoriteColor.toString(2).padStart(4,"0");
2837
+ miiBin+=mii.general.birthday.toString(2).padStart(5,"0").slice(0,2);
2838
+ for (var i = 0; i < 10; i++) {
2839
+ if (i < mii.meta.name.length) {
2840
+ let code = mii.meta.name.charCodeAt(i);
2841
+ miiBin += (code & 0xFF).toString(2).padStart(8, "0");
2842
+ miiBin += ((code >> 8) & 0xFF).toString(2).padStart(8, "0");
2268
2843
  }
2269
- studioMii[0x25] = miiJson.mouth.size;
2270
- studioMii[0x23] = miiJson.mouth.squash;
2271
- studioMii[0x27] = miiJson.mouth.yPos;
2272
- studioMii[0x29] = miiJson.facialHair.mustacheType;
2273
- studioMii[1] = miiJson.facialHair.beardType;
2274
- studioMii[0] = lookupTables.hairCols.indexOf(miiJson.facialHair.col);
2275
- if (!studioMii[0]) studioMii[0] = 8;
2276
- studioMii[0x28] = miiJson.facialHair.mustacheSize;
2277
- studioMii[0x2A] = miiJson.facialHair.mustacheYPos;
2278
- studioMii[0x19] = miiJson.glasses.type;
2279
- studioMii[0x17] = miiJson.glasses.col;
2280
- if (!studioMii[0x17]) {
2281
- studioMii[0x17] = 8;
2282
- } else if (studioMii[0x17] < 6) {
2283
- studioMii[0x17] += 13;
2284
- } else {
2285
- studioMii[0x17] = 0;
2844
+ else {
2845
+ miiBin += "0000000000000000";
2286
2846
  }
2287
- studioMii[0x18] = miiJson.glasses.size;
2288
- studioMii[0x1A] = miiJson.glasses.yPos;
2289
- studioMii[0x20] = miiJson.mole.on ? 1 : 0;
2290
- studioMii[0x1F] = miiJson.mole.size;
2291
- studioMii[0x21] = miiJson.mole.xPos;
2292
- studioMii[0x22] = miiJson.mole.yPos;
2293
- let miiPNGBuf = null;
2294
- let renderedWithStudio = fflRes === null || fflRes === undefined;
2295
- if (renderedWithStudio) {
2296
- miiPNGBuf = await renderMiiWithStudio(miiJson);
2847
+ }
2848
+ miiBin+=mii.general.height.toString(2).padStart(8,"0");
2849
+ miiBin+=mii.general.weight.toString(2).padStart(8,"0");
2850
+ miiBin+=mii.face.color.toString(2).padStart(3,"0");
2851
+ miiBin+=lookupTables.faces.values[mii.face.type].toString(2).padStart(4,"0");
2852
+ miiBin+=mii.perms.sharing?"0":"1";
2853
+ miiBin+=mii.face.makeup.toString(2).padStart(4,"0");
2854
+ miiBin+=mii.face.feature.toString(2).padStart(4,"0");
2855
+ miiBin+=lookupTables.hairs.values[mii.hair.page][mii.hair.type].toString(2).padStart(8,"0");
2856
+ miiBin+="0000";
2857
+ miiBin+=mii.hair.flipped?"1":"0";
2858
+ miiBin+=mii.hair.color.toString(2).padStart(3,"0");
2859
+ miiBin+=mii.eyes.color.toString(2).padStart(3,"0").slice(1,3);
2860
+ miiBin+=lookupTables.eyes.values[mii.eyes.page][mii.eyes.type].toString(2).padStart(6,"0");
2861
+ miiBin+=mii.eyes.squash.toString(2).padStart(3,"0");
2862
+ miiBin+=mii.eyes.size.toString(2).padStart(4,"0");
2863
+ miiBin+=mii.eyes.color.toString(2).padStart(3,"0")[0];
2864
+ miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0").slice(1,4);
2865
+ miiBin+=mii.eyes.rotation.toString(2).padStart(5,"0");
2866
+ miiBin+="00";
2867
+ miiBin+=mii.eyes.yPosition.toString(2).padStart(5,"0");
2868
+ miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0")[0];
2869
+ miiBin+=mii.eyebrows.color.toString(2).padStart(3,"0");
2870
+ miiBin+=lookupTables.eyebrows.values[mii.eyebrows.page][mii.eyebrows.type].toString(2).padStart(5,"0");
2871
+ miiBin+="0";
2872
+ miiBin+=mii.eyebrows.squash.toString(2).padStart(3,"0");
2873
+ miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
2874
+ miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0").slice(1,4);
2875
+ miiBin+="0";
2876
+ miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
2877
+ miiBin+="00";
2878
+ miiBin+=(mii.eyebrows.yPosition+3).toString(2).padStart(5,"0");
2879
+ miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0")[0];
2880
+ miiBin+=mii.nose.size.toString(2).padStart(4,"0").slice(1,4);
2881
+ miiBin+=lookupTables.noses.values[mii.nose.page][mii.nose.type].toString(2).padStart(5,"0");
2882
+ miiBin+="00";
2883
+ miiBin+=mii.nose.yPosition.toString(2).padStart(5,"0");
2884
+ miiBin+=mii.nose.size.toString(2).padStart(4,"0")[0];
2885
+ miiBin+=mii.mouth.color.toString(2).padStart(3,"0").slice(1,3);
2886
+ miiBin+=lookupTables.mouths.values[mii.mouth.page][mii.mouth.type].toString(2).padStart(6,"0");
2887
+ miiBin+=mii.mouth.squash.toString(2).padStart(3,"0");
2888
+ miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
2889
+ miiBin+=mii.mouth.color.toString(2).padStart(3,"0")[0];
2890
+ miiBin+=mii.beard.mustache.type.toString(2).padStart(3,"0");
2891
+ miiBin+=mii.mouth.yPosition.toString(2).padStart(5,"0");
2892
+ miiBin+="00000000";
2893
+ miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0").slice(2,4);
2894
+ miiBin+=mii.beard.color.toString(2).padStart(3,"0");
2895
+ miiBin+=mii.beard.type.toString(2).padStart(3,"0");
2896
+ miiBin+="0";
2897
+ miiBin+=mii.beard.mustache.yPosition.toString(2).padStart(5,"0");
2898
+ miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0").slice(0,2);
2899
+ miiBin+=mii.glasses.size.toString(2).padStart(4,"0")[3];
2900
+ miiBin+=mii.glasses.color.toString(2).padStart(3,"0");
2901
+ miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
2902
+ miiBin+="0";
2903
+ miiBin+=mii.glasses.yPosition.toString(2).padStart(4,"0");
2904
+ miiBin+=mii.glasses.size.toString(2).padStart(4,"0").slice(0,3);
2905
+ miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0").slice(2,5);
2906
+ miiBin+=mii.mole.size.toString(2).padStart(4,"0");
2907
+ miiBin+=mii.mole.on?"1":"0";
2908
+ miiBin+="0";
2909
+ miiBin+=mii.mole.yPosition.toString(2).padStart(5,"0");
2910
+ miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0").slice(0,2);
2911
+ for (var i = 0; i < 10; i++) {
2912
+ if (i < mii.meta.creatorName.length) {
2913
+ let code = mii.meta.creatorName.charCodeAt(i);
2914
+ miiBin += (code & 0xFF).toString(2).padStart(8, "0");
2915
+ miiBin += ((code >> 8) & 0xFF).toString(2).padStart(8, "0");
2297
2916
  }
2298
2917
  else {
2299
- miiPNGBuf = await renderMii(miiJson, fflRes);
2918
+ miiBin += "0000000000000000";
2300
2919
  }
2301
- const main_img = await Jimp.read(qrBuffer);
2302
- main_img.resize(424, 424, Jimp.RESIZE_NEAREST_NEIGHBOR); // Don't anti-alias the QR code
2920
+ }
2921
+ //Writing based on the binary
2922
+ var toWrite=miiBin.match(/.{1,8}/g);
2923
+ var buffers=[];
2924
+ for(var i=0;i<toWrite.length;i++){
2925
+ buffers.push(parseInt(toWrite[i],2));
2926
+ }
2927
+ const buffer = Buffer.from(buffers);
2928
+ var encryptedData = Buffer.from(encodeAesCcm(new Uint8Array(buffer)));
2929
+
2930
+ //Prepare a QR code
2931
+ const options = {
2932
+ width: 300,
2933
+ height: 300,
2934
+ data: encryptedData.toString("latin1"),
2935
+ image: "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", // 1x1 gif
2936
+ dotsOptions: {
2937
+ color: "#000000",
2938
+ type: "square"
2939
+ },
2940
+ backgroundOptions: {
2941
+ color: "#ffffff",
2942
+ },
2943
+ imageOptions: {
2944
+ crossOrigin: "anonymous",
2945
+ imageSize: 0.4 // Changes how large center area is
2946
+ },
2947
+ qrOptions:{
2948
+ errorCorrectionLevel:'H'
2949
+ }
2950
+ }
2951
+ const qrCodeImage = new QRCodeStyling({
2952
+ jsdom: JSDOM,
2953
+ nodeCanvas,
2954
+ ...options
2955
+ });
2956
+ const qrBuffer = Buffer.from(await qrCodeImage.getRawData("png"))
2303
2957
 
2304
- let miiSize, miiZoomFactor, miiYOffset;
2305
- if (renderedWithStudio) {
2306
- miiSize = 100;
2307
- miiZoomFactor = 1;
2308
- miiYOffset = -15;
2958
+ let miiPNGBuf = null;
2959
+ let renderedWithStudio = fflRes === null || fflRes === undefined;
2960
+ if (renderedWithStudio) {
2961
+ miiPNGBuf = await renderMiiWithStudio(miiJson);
2962
+ }
2963
+ else {
2964
+ miiPNGBuf = await renderMii(miiJson, fflRes);
2965
+ }
2966
+ const main_img = await Jimp.read(qrBuffer);
2967
+ main_img.resize(424, 424, Jimp.RESIZE_NEAREST_NEIGHBOR); // Don't anti-alias the QR code
2309
2968
 
2310
- } else {
2311
- miiSize = 100;
2312
- miiZoomFactor = 1.25;
2313
- miiYOffset = -5;
2314
- }
2315
- const mii_img = await Jimp.read(miiPNGBuf);
2316
- mii_img.resize(miiSize * miiZoomFactor, miiSize * miiZoomFactor, Jimp.RESIZE_BICUBIC);
2317
- mii_img.crop(
2318
- (miiSize * miiZoomFactor - 100) / 2,
2319
- (miiSize * miiZoomFactor - 100) / 2,
2320
- miiSize,
2321
- miiSize
2322
- );
2323
-
2324
- const canvas = new Jimp(mii_img.bitmap.width, mii_img.bitmap.height, 0xFFFFFFFF);
2325
- canvas.composite(mii_img, 0, miiYOffset);
2326
- main_img.blit(canvas, 212 - 100 / 2, 212 - 100 / 2);
2327
- const font = await Jimp.loadFont(Jimp.FONT_SANS_16_BLACK)
2328
-
2329
- main_img.print(font, 0, 55, {
2330
- text: miiJson.name,
2331
- alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
2332
- alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
2333
- }, 424, 395);
2334
-
2335
- if (miiJson.info.type === "Special") {
2336
- const crown_img = await Jimp.read(path.join(__dirname, 'crown.jpg'));
2337
- crown_img.resize(40, 20);
2338
- main_img.blit(crown_img, 225, 160);
2339
- }
2969
+ let miiSize, miiZoomFactor, miiYOffset;
2970
+ if (renderedWithStudio) {
2971
+ miiSize = 100;
2972
+ miiZoomFactor = 1;
2973
+ miiYOffset = -15;
2974
+
2975
+ } else {
2976
+ miiSize = 100;
2977
+ miiZoomFactor = 1.25;
2978
+ miiYOffset = -5;
2979
+ }
2980
+ const mii_img = await Jimp.read(miiPNGBuf);
2981
+ mii_img.resize(miiSize * miiZoomFactor, miiSize * miiZoomFactor, Jimp.RESIZE_BICUBIC);
2982
+ mii_img.crop(
2983
+ (miiSize * miiZoomFactor - 100) / 2,
2984
+ (miiSize * miiZoomFactor - 100) / 2,
2985
+ miiSize,
2986
+ miiSize
2987
+ );
2988
+
2989
+ const canvas = new Jimp(mii_img.bitmap.width, mii_img.bitmap.height, 0xFFFFFFFF);
2990
+ canvas.composite(mii_img, 0, miiYOffset);
2991
+ main_img.blit(canvas, 212 - 100 / 2, 212 - 100 / 2);
2992
+ const font = await Jimp.loadFont(Jimp.FONT_SANS_16_BLACK)
2993
+
2994
+ main_img.print(font, 0, 55, {
2995
+ text: miiJson.name,
2996
+ alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
2997
+ alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
2998
+ }, 424, 395);
2999
+
3000
+ if (miiJson.meta.type === "Special") {
3001
+ const crown_img = await Jimp.read(path.join(__dirname, 'crown.jpg'));
3002
+ crown_img.resize(40, 20);
3003
+ main_img.blit(crown_img, 225, 160);
3004
+ }
2340
3005
 
2341
- main_img.write(outPath, (err, img) =>
2342
- resolve(img)
2343
- );
2344
- })
3006
+ // Get the buffer
3007
+ const imageBuffer = await main_img.getBufferAsync(Jimp.MIME_PNG);
3008
+
3009
+ // Optionally write to file if outPath is provided
3010
+ if (outPath) {
3011
+ await main_img.writeAsync(outPath);
3012
+ }
3013
+
3014
+ return imageBuffer;
2345
3015
  }
2346
3016
  function make3DSChild(dad,mom,options={}){
2347
3017
  if(!["3ds","wii u"].includes(dad.console?.toLowerCase())){