miijs 2.2.1 → 2.3.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/index.js CHANGED
@@ -5,18 +5,18 @@ const { createCanvas, loadImage, ImageData } = nodeCanvas;
5
5
  const jsQR = require('jsqr');
6
6
  const Jimp = require('jimp');
7
7
  const THREE = require('three');
8
- var GLTFLoader=null;
8
+ var GLTFLoader = null;
9
9
  const QRCodeStyling = require("qr-code-styling");
10
10
  const { JSDOM } = require("jsdom");
11
11
  const httpsLib = require('https');
12
- const asmCrypto=require("./asmCrypto.js");
13
- const path=require("path");
12
+ const asmCrypto = require("./asmCrypto.js");
13
+ const path = require("path");
14
14
  const createGL = require('gl');
15
15
 
16
16
  const {
17
- createCharModel, initCharModelTextures,
18
- initializeFFL, exitFFL, parseHexOrB64ToUint8Array,
19
- setIsWebGL1State, getCameraForViewType, ViewType
17
+ createCharModel, initCharModelTextures,
18
+ initializeFFL, exitFFL, parseHexOrB64ToUint8Array,
19
+ setIsWebGL1State, getCameraForViewType, ViewType
20
20
  } = require("./fflWrapper.js");
21
21
  const ModuleFFL = require("ffl.js/examples/ffl-emscripten-single-file.js");
22
22
  const FFLShaderMaterial = require("ffl.js/FFLShaderMaterial.js");
@@ -49,7 +49,7 @@ const lookupTables = {
49
49
  "9": 9,
50
50
  "7": 10,
51
51
  },
52
- pages:{
52
+ pages: {
53
53
  mouths: {
54
54
  '0': 1,
55
55
  '1': 1,
@@ -76,7 +76,7 @@ const lookupTables = {
76
76
  '22': 1,
77
77
  '23': 1
78
78
  },
79
- eyebrows:{
79
+ eyebrows: {
80
80
  '0': 1,
81
81
  '1': 1,
82
82
  '2': 2,
@@ -102,7 +102,7 @@ const lookupTables = {
102
102
  '22': 2,
103
103
  '23': 2
104
104
  },
105
- eyes:{
105
+ eyes: {
106
106
  0: 1,
107
107
  1: 1,
108
108
  2: 1,
@@ -152,7 +152,7 @@ const lookupTables = {
152
152
  46: 3,
153
153
  47: 4
154
154
  },
155
- hairs:{
155
+ hairs: {
156
156
  '0': 5,
157
157
  '1': 4,
158
158
  '2': 6,
@@ -227,7 +227,7 @@ const lookupTables = {
227
227
  '71': 6
228
228
  }
229
229
  },
230
- types:{
230
+ types: {
231
231
  "mouths": {
232
232
  "0": 6,
233
233
  "1": 1,
@@ -405,7 +405,7 @@ const lookupTables = {
405
405
  "71": 8
406
406
  }
407
407
  },
408
- wiiNoses:{
408
+ wiiNoses: {
409
409
  '0': 1,
410
410
  '1': 10,
411
411
  '2': 2,
@@ -419,7 +419,7 @@ const lookupTables = {
419
419
  '10': 7,
420
420
  '11': 11
421
421
  },
422
- mouthTable:{
422
+ mouthTable: {
423
423
  '0': '113',
424
424
  '1': '121',
425
425
  '2': '231',
@@ -445,7 +445,7 @@ const lookupTables = {
445
445
  '22': '122',
446
446
  '23': '111'
447
447
  },
448
- eyebrowTable:{
448
+ eyebrowTable: {
449
449
  '0': '121',
450
450
  '1': '112',
451
451
  '2': '231',
@@ -471,7 +471,7 @@ const lookupTables = {
471
471
  '22': '233',
472
472
  '23': '234'
473
473
  },
474
- eyeTable:{
474
+ eyeTable: {
475
475
  '0': '131',
476
476
  '1': '113',
477
477
  '2': '111',
@@ -521,7 +521,7 @@ const lookupTables = {
521
521
  '46': '334',
522
522
  '47': '434'
523
523
  },
524
- hairTable:{
524
+ hairTable: {
525
525
  '0': '534',
526
526
  '1': '413',
527
527
  '2': '632',
@@ -665,198 +665,198 @@ const lookupTables = {
665
665
  ]
666
666
  }
667
667
  };
668
- var convTables={
669
- face3DSToWii:[0,1,2,2,3,1,4,5,4,6,7,6],
670
- 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.
671
- makeup3DSToWii:[0,1,1,2,1,1,2,2,2,3,9,9],
672
- nose3DSToWii:[
673
- [0,1,2,3,4,5,6,7,8,9,10,11],
674
- [0,3,4,6,9,2]
668
+ var convTables = {
669
+ face3DSToWii: [0, 1, 2, 2, 3, 1, 4, 5, 4, 6, 7, 6],
670
+ 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.
671
+ makeup3DSToWii: [0, 1, 1, 2, 1, 1, 2, 2, 2, 3, 9, 9],
672
+ nose3DSToWii: [
673
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
674
+ [0, 3, 4, 6, 9, 2]
675
675
  ],
676
- mouth3DSToWii:[
677
- ["111","121","131","112","122","132","113","123","133","114","124","134"],
678
- ["211","221","231","212","222","232","213","223","233","214","224","234"],
679
- ["121","214","134","123","121","112","124","133","221","224","121","232"]
676
+ mouth3DSToWii: [
677
+ ["111", "121", "131", "112", "122", "132", "113", "123", "133", "114", "124", "134"],
678
+ ["211", "221", "231", "212", "222", "232", "213", "223", "233", "214", "224", "234"],
679
+ ["121", "214", "134", "123", "121", "112", "124", "133", "221", "224", "121", "232"]
680
680
  ],
681
- hair3DSToWii:[
681
+ hair3DSToWii: [
682
682
  [
683
- "111","221","121",
684
- "231","211","121",
685
- "212","131","233",
686
- "132","112","222"
683
+ "111", "221", "121",
684
+ "231", "211", "121",
685
+ "212", "131", "233",
686
+ "132", "112", "222"
687
687
  ],
688
688
  [
689
- "232","223","321",
690
- "123","311","134",
691
- "114","124","234",
692
- "114","134","234"
689
+ "232", "223", "321",
690
+ "123", "311", "134",
691
+ "114", "124", "234",
692
+ "114", "134", "234"
693
693
  ],
694
694
  [
695
- "214","523","433",
696
- "214","531","512",
697
- "523","433","134",
698
- "414","523","134"
695
+ "214", "523", "433",
696
+ "214", "531", "512",
697
+ "523", "433", "134",
698
+ "414", "523", "134"
699
699
  ],
700
700
  [
701
- "331","333","324",
702
- "332","333","334",
703
- "312","322","322",
704
- "113","122","313"
701
+ "331", "333", "324",
702
+ "332", "333", "334",
703
+ "312", "322", "322",
704
+ "113", "122", "313"
705
705
  ],
706
706
  [
707
- "113","322","133",
708
- "333","323","314",
709
- "411","621","521",
710
- "424","424","424"
707
+ "113", "322", "133",
708
+ "333", "323", "314",
709
+ "411", "621", "521",
710
+ "424", "424", "424"
711
711
  ],
712
712
  [
713
- "511","411","411",
714
- "422","522","523",
715
- "534","523","434",
716
- "422","533","424"
713
+ "511", "411", "411",
714
+ "422", "522", "523",
715
+ "534", "523", "434",
716
+ "422", "533", "424"
717
717
  ],
718
718
  [
719
- "511","531","534",
720
- "623","521","524",
721
- "534","523","523",
722
- "424","513","523"
719
+ "511", "531", "534",
720
+ "623", "521", "524",
721
+ "534", "523", "523",
722
+ "424", "513", "523"
723
723
  ],
724
724
  [
725
- "411","523","512",
726
- "513","432","432",
727
- "621","431","514",
728
- "421","432","514"
725
+ "411", "523", "512",
726
+ "513", "432", "432",
727
+ "621", "431", "514",
728
+ "421", "432", "514"
729
729
  ],
730
730
  [
731
- "623","614","633",
732
- "633","633","624",
733
- "434","633","634",
734
- "624","624","634"
731
+ "623", "614", "633",
732
+ "633", "633", "624",
733
+ "434", "633", "634",
734
+ "624", "624", "634"
735
735
  ],
736
736
  [
737
- "634","413","412",
738
- "413","413","412",
739
- "611","622","632",
740
- "611","622","632"
737
+ "634", "413", "412",
738
+ "413", "413", "412",
739
+ "611", "622", "632",
740
+ "611", "622", "632"
741
741
  ],
742
742
  [
743
- "423","632","423",
744
- "612","612","613",
745
- "631","631","613",
746
- "631","631","613"
743
+ "423", "632", "423",
744
+ "612", "612", "613",
745
+ "631", "631", "613",
746
+ "631", "631", "613"
747
747
  ]
748
748
  ],
749
- eyebrows3DSToWii:[
749
+ eyebrows3DSToWii: [
750
750
  [
751
- "111","121","131",
752
- "112","122","132",
753
- "113","123","133",
754
- "114","124","134"
751
+ "111", "121", "131",
752
+ "112", "122", "132",
753
+ "113", "123", "133",
754
+ "114", "124", "134"
755
755
  ],
756
756
  [
757
- "211","221","231",
758
- "212","222","232",
759
- "213","223","233",
760
- "214","224","234"
757
+ "211", "221", "231",
758
+ "212", "222", "232",
759
+ "213", "223", "233",
760
+ "214", "224", "234"
761
761
  ]
762
762
  ],
763
- eyes3DSToWii:[
763
+ eyes3DSToWii: [
764
764
  [
765
- "111","121","131",
766
- "112","122","132",
767
- "113","123","133",
768
- "114","124","134"
765
+ "111", "121", "131",
766
+ "112", "122", "132",
767
+ "113", "123", "133",
768
+ "114", "124", "134"
769
769
  ],
770
770
  [
771
- "211","221","231",
772
- "212","222","232",
773
- "213","223","233",
774
- "214","224","234"
771
+ "211", "221", "231",
772
+ "212", "222", "232",
773
+ "213", "223", "233",
774
+ "214", "224", "234"
775
775
  ],
776
776
  [
777
- "311","321","331",
778
- "312","322","332",
779
- "313","323","333",
780
- "314","324","334"
777
+ "311", "321", "331",
778
+ "312", "322", "332",
779
+ "313", "323", "333",
780
+ "314", "324", "334"
781
781
  ],
782
782
  [
783
- "411","421","431",
784
- "412","422","432",
785
- "413","423","433",
786
- "414","424","434"
783
+ "411", "421", "431",
784
+ "412", "422", "432",
785
+ "413", "423", "433",
786
+ "414", "424", "434"
787
787
  ],
788
788
  [
789
- "322","322","312",
790
- "224","224","431",
791
- "224","224","111",
792
- "121","411","431"
789
+ "322", "322", "312",
790
+ "224", "224", "431",
791
+ "224", "224", "111",
792
+ "121", "411", "431"
793
793
  ]
794
794
  ],
795
- hairWiiTo3DS:[
795
+ hairWiiTo3DS: [
796
796
  [
797
- [0,0],[0,2],[0,7],
798
- [0,10],[3,10],[0,9],
799
- [4,0],[1,3],[3,8],
800
- [1,6],[1,7],[1,5]
797
+ [0, 0], [0, 2], [0, 7],
798
+ [0, 10], [3, 10], [0, 9],
799
+ [4, 0], [1, 3], [3, 8],
800
+ [1, 6], [1, 7], [1, 5]
801
801
  ],
802
802
  [
803
- [0,4],[0,1],[0,3],
804
- [0,6],[0,11],[1,0],
805
- [2,5],[1,1],[0,8],
806
- [2,0],[2,6],[1,8]
803
+ [0, 4], [0, 1], [0, 3],
804
+ [0, 6], [0, 11], [1, 0],
805
+ [2, 5], [1, 1], [0, 8],
806
+ [2, 0], [2, 6], [1, 8]
807
807
  ],
808
808
  [
809
- [1,4],[1,2],[3,0],
810
- [4,0],[3,6],[3,3],
811
- [3,11],[4,4],[4,3],
812
- [4,5],[3,2],[3,5]
809
+ [1, 4], [1, 2], [3, 0],
810
+ [4, 0], [3, 6], [3, 3],
811
+ [3, 11], [4, 4], [4, 3],
812
+ [4, 5], [3, 2], [3, 5]
813
813
  ],
814
814
  [
815
- [4,6],[7,9],[7,7],
816
- [9,5],[5,9],[7,5],
817
- [9,1],[10,2],[7,0],
818
- [6,1],[5,8],[8,9]
815
+ [4, 6], [7, 9], [7, 7],
816
+ [9, 5], [5, 9], [7, 5],
817
+ [9, 1], [10, 2], [7, 0],
818
+ [6, 1], [5, 8], [8, 9]
819
819
  ],
820
820
  [
821
- [5,0],[6,4],[2,4],
822
- [4,8],[5,4],[5,5],
823
- [6,10],[6,8],[5,10],
824
- [7,8],[6,5],[6,6]
821
+ [5, 0], [6, 4], [2, 4],
822
+ [4, 8], [5, 4], [5, 5],
823
+ [6, 10], [6, 8], [5, 10],
824
+ [7, 8], [6, 5], [6, 6]
825
825
  ],
826
826
  [
827
- [9,6],[4,7],[10,6],
828
- [10,1],[9,10],[9,8],
829
- [10,8],[8,1],[2,0],
830
- [9,1],[8,9],[8,8]
827
+ [9, 6], [4, 7], [10, 6],
828
+ [10, 1], [9, 10], [9, 8],
829
+ [10, 8], [8, 1], [2, 0],
830
+ [9, 1], [8, 9], [8, 8]
831
831
  ]
832
832
  ],
833
- faceWiiTo3DS:[
834
- 0,1,
835
- 3,4,
836
- 6,7,
837
- 9,10
833
+ faceWiiTo3DS: [
834
+ 0, 1,
835
+ 3, 4,
836
+ 6, 7,
837
+ 9, 10
838
838
  ],
839
- featureWiiTo3DS:[
840
- 0,"1","6",
841
- "9",5,2,
842
- 3,7,8,
843
- "10",9,11
839
+ featureWiiTo3DS: [
840
+ 0, "1", "6",
841
+ "9", 5, 2,
842
+ 3, 7, 8,
843
+ "10", 9, 11
844
844
  ],
845
- formatTo:[
846
- [0,1,2],
847
- [3,4,5],
848
- [6,7,8],
849
- [9,10,11]
845
+ formatTo: [
846
+ [0, 1, 2],
847
+ [3, 4, 5],
848
+ [6, 7, 8],
849
+ [9, 10, 11]
850
850
  ],
851
- formatFrom:[
852
- "11","21","31",
853
- "12","22","32",
854
- "13","23","33",
855
- "14","24","34"
851
+ formatFrom: [
852
+ "11", "21", "31",
853
+ "12", "22", "32",
854
+ "13", "23", "33",
855
+ "14", "24", "34"
856
856
  ]
857
857
  };
858
- const kidNames={
859
- "Male":[
858
+ const kidNames = {
859
+ "Male": [
860
860
  "Aaron",
861
861
  "Adam",
862
862
  "Adrian",
@@ -1007,7 +1007,7 @@ const kidNames={
1007
1007
  "Sam",
1008
1008
  "Taylor"
1009
1009
  ],
1010
- "Female":[
1010
+ "Female": [
1011
1011
  "Aaliyah",
1012
1012
  "Abigail",
1013
1013
  "Addison",
@@ -1146,15 +1146,15 @@ const kidNames={
1146
1146
  };
1147
1147
 
1148
1148
  //Tools
1149
- function Uint8Cat(){
1149
+ function Uint8Cat() {
1150
1150
  var destLength = 0
1151
- for(var i = 0;i < arguments.length;i++){
1151
+ for (var i = 0; i < arguments.length; i++) {
1152
1152
  destLength += arguments[i].length;
1153
1153
  }
1154
1154
  var dest = new Uint8Array(destLength);
1155
1155
  var index = 0;
1156
- for(var i=0;i<arguments.length;i++){
1157
- dest.set(arguments[i],index);
1156
+ for (var i = 0; i < arguments.length; i++) {
1157
+ dest.set(arguments[i], index);
1158
1158
  index += arguments[i].length;
1159
1159
  }
1160
1160
  return dest;
@@ -1174,12 +1174,12 @@ async function downloadImage(url) {
1174
1174
  });
1175
1175
  });
1176
1176
  }
1177
- function byteToString(int){
1177
+ function byteToString(int) {
1178
1178
  var str = int.toString(16);
1179
- if(str.length < 2)str = '0' + str;
1179
+ if (str.length < 2) str = '0' + str;
1180
1180
  return str;
1181
1181
  }
1182
- function getBinaryFromAddress(addr, bin){
1182
+ function getBinaryFromAddress(addr, bin) {
1183
1183
  let byte = bin.readUInt8(addr);
1184
1184
  let binaryString = '';
1185
1185
  for (let i = 7; i >= 0; i--) {
@@ -1189,37 +1189,73 @@ function getBinaryFromAddress(addr, bin){
1189
1189
  }
1190
1190
  function getKeyByValue(object, value) {
1191
1191
  for (var key in object) {
1192
- if (object[key] === value) {
1193
- return key;
1194
- }
1192
+ if (object[key] === value) {
1193
+ return key;
1194
+ }
1195
+ }
1196
+ }
1197
+ function lookupTable(table, value, paginated) {
1198
+ if (paginated) {
1199
+ for (var i = 0; i < lookupTables[table].values.length; i++) {
1200
+ for (var j = 0; j < lookupTables[table].values[i].length; j++) {
1201
+ if (lookupTables[table].values[i][j] === value) {
1202
+ return [i, j];
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+ else {
1208
+ for (var i = 0; i < lookupTables[table].values.length; i++) {
1209
+ if (lookupTables[table].values[i] === value) {
1210
+ return i;
1211
+ }
1212
+ }
1195
1213
  }
1214
+ return undefined;
1215
+ }
1216
+ function hexToBytes(hex) {
1217
+ const cleaned = hex.replace(/[\s:_-]/g, "").replace(/^0x/i, "");
1218
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(cleaned)) throw new Error("Invalid hex string");
1219
+ const out = new Uint8Array(cleaned.length / 2);
1220
+ for (let i = 0; i < cleaned.length; i += 2) out[i / 2] = parseInt(cleaned.slice(i, i + 2), 16);
1221
+ return out;
1196
1222
  }
1197
- function lookupTable(table,value,paginated){
1198
- if(paginated){
1199
- for(var i=0;i<lookupTables[table].values.length;i++){
1200
- for(var j=0;j<lookupTables[table].values[i].length;j++){
1201
- if(lookupTables[table].values[i][j]===value){
1202
- return [i,j];
1223
+ function toRawStudioBytes(any) {
1224
+ if (typeof any === "string") {
1225
+ const looksHex = /^(?:0x)?(?:[\s:_-]*[0-9a-fA-F]{2})+[\s:_-]*$/.test(any);
1226
+ if (looksHex) {
1227
+ const bytes = hexToBytes(any);
1228
+ if (bytes.length > 40 && bytes[0] === 0) return decodeStudio(bytes);
1229
+ return bytes;
1203
1230
  }
1204
- }
1231
+ return decodeStudio(any);
1205
1232
  }
1206
- }
1207
- else{
1208
- for(var i=0;i<lookupTables[table].values.length;i++){
1209
- if(lookupTables[table].values[i]===value){
1210
- return i;
1211
- }
1233
+ if (any instanceof Uint8Array || (typeof Buffer !== "undefined" && Buffer.isBuffer?.(any))) {
1234
+ const bytes = any instanceof Uint8Array ? any : new Uint8Array(any);
1235
+ if (bytes.length > 40 && bytes[0] === 0) return decodeStudio(bytes);
1236
+ return bytes;
1212
1237
  }
1213
- }
1214
- return undefined;
1238
+ throw new Error("Unsupported input type");
1215
1239
  }
1240
+ const find1D = (arr, value) => {
1241
+ const idx = arr.indexOf(value);
1242
+ return idx >= 0 ? idx : 0;
1243
+ };
1244
+ const findPageType = (table2D, id) => {
1245
+ for (let page = 0; page < table2D.length; page++) {
1246
+ const type = table2D[page].indexOf(id);
1247
+ if (type >= 0) return { page, type };
1248
+ }
1249
+ return { page: 0, type: 0 };
1250
+ };
1251
+ const clamp = (x, lo, hi) => Math.max(lo, Math.min(hi, x));
1216
1252
 
1217
1253
  //If FFLResHigh.dat is in the same directory as Node.js is calling the library from, use it by default
1218
- let _fflRes; // undefined initially
1254
+ let _fflRes;
1219
1255
  function getFFLRes() {
1220
1256
  // If we've already tried loading, just return the result
1221
1257
  if (_fflRes !== undefined) return _fflRes;
1222
- for (const path of [ "./FFLResHigh.dat", "./ffl/FFLResHigh.dat" ]) {
1258
+ for (const path of ["./FFLResHigh.dat", "./ffl/FFLResHigh.dat", "./AFLResHigh.dat", "./afl/AFLResHigh.dat", "./ffl/AFLResHigh.dat"]) {
1223
1259
  if (fs.existsSync(path)) {
1224
1260
  // Convert Buffer to Uint8Array explicitly
1225
1261
  const buffer = fs.readFileSync(path);
@@ -1235,40 +1271,40 @@ var NONCE_OFFSET = 0xC;
1235
1271
  var NONCE_LENGTH = 8;
1236
1272
  var TAG_LENGTH = 0x10;
1237
1273
  var aes_key = new Uint8Array([0x59, 0xFC, 0x81, 0x7E, 0x64, 0x46, 0xEA, 0x61, 0x90, 0x34, 0x7B, 0x20, 0xE9, 0xBD, 0xCE, 0x52]);
1238
- var pad = new Uint8Array([0,0,0,0]);
1239
- function decodeAesCcm(data){
1240
- var nonce = Uint8Cat(data.subarray(0,NONCE_LENGTH),pad);
1241
- var ciphertext = data.subarray(NONCE_LENGTH,0x70);
1242
- var plaintext = asmCrypto.AES_CCM.decrypt(ciphertext,aes_key,nonce,undefined,TAG_LENGTH);
1243
- return Uint8Cat(plaintext.subarray(0,NONCE_OFFSET),data.subarray(0,NONCE_LENGTH),plaintext.subarray(NONCE_OFFSET,plaintext.length - 4));
1274
+ var pad = new Uint8Array([0, 0, 0, 0]);
1275
+ function decodeAesCcm(data) {
1276
+ var nonce = Uint8Cat(data.subarray(0, NONCE_LENGTH), pad);
1277
+ var ciphertext = data.subarray(NONCE_LENGTH, 0x70);
1278
+ var plaintext = asmCrypto.AES_CCM.decrypt(ciphertext, aes_key, nonce, undefined, TAG_LENGTH);
1279
+ return Uint8Cat(plaintext.subarray(0, NONCE_OFFSET), data.subarray(0, NONCE_LENGTH), plaintext.subarray(NONCE_OFFSET, plaintext.length - 4));
1244
1280
  }
1245
- function crcCalc(data){
1281
+ function crcCalc(data) {
1246
1282
  var crc = 0;
1247
- for (var byteIndex = 0;byteIndex < data.length; byteIndex++){
1248
- for (var bitIndex = 7; bitIndex >= 0; bitIndex--){
1283
+ for (var byteIndex = 0; byteIndex < data.length; byteIndex++) {
1284
+ for (var bitIndex = 7; bitIndex >= 0; bitIndex--) {
1249
1285
  crc = (((crc << 1) | ((data[byteIndex] >> bitIndex) & 0x1)) ^
1250
- (((crc & 0x8000) != 0) ? 0x1021 : 0));
1286
+ (((crc & 0x8000) != 0) ? 0x1021 : 0));
1251
1287
  }
1252
1288
  }
1253
- for(var counter = 16; counter > 0; counter--){
1289
+ for (var counter = 16; counter > 0; counter--) {
1254
1290
  crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0));
1255
1291
  }
1256
- return(crc & 0xFFFF);
1292
+ return (crc & 0xFFFF);
1257
1293
  }
1258
- function encodeAesCcm(data){
1259
- var nonce = Uint8Cat(data.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),pad);
1260
- var crcSrc = Uint8Cat(data,new Uint8Array([0,0]));
1294
+ function encodeAesCcm(data) {
1295
+ var nonce = Uint8Cat(data.subarray(NONCE_OFFSET, NONCE_OFFSET + NONCE_LENGTH), pad);
1296
+ var crcSrc = Uint8Cat(data, new Uint8Array([0, 0]));
1261
1297
  var crc = crcCalc(crcSrc);
1262
- var cfsd = Uint8Cat(crcSrc,new Uint8Array([crc >>> 8,crc & 0xff]));
1263
- var plaintext = Uint8Cat(cfsd.subarray(0,NONCE_OFFSET),cfsd.subarray(NONCE_OFFSET + NONCE_LENGTH,cfsd.length),pad,pad);
1264
- var ciphertext = asmCrypto.AES_CCM.encrypt(plaintext,aes_key,nonce,undefined,TAG_LENGTH);
1265
- return Uint8Cat(cfsd.subarray(NONCE_OFFSET,NONCE_OFFSET + NONCE_LENGTH),ciphertext.subarray(0,ciphertext.length - 24),ciphertext.subarray(ciphertext.length - TAG_LENGTH,ciphertext.length))
1298
+ var cfsd = Uint8Cat(crcSrc, new Uint8Array([crc >>> 8, crc & 0xff]));
1299
+ var plaintext = Uint8Cat(cfsd.subarray(0, NONCE_OFFSET), cfsd.subarray(NONCE_OFFSET + NONCE_LENGTH, cfsd.length), pad, pad);
1300
+ var ciphertext = asmCrypto.AES_CCM.encrypt(plaintext, aes_key, nonce, undefined, TAG_LENGTH);
1301
+ return Uint8Cat(cfsd.subarray(NONCE_OFFSET, NONCE_OFFSET + NONCE_LENGTH), ciphertext.subarray(0, ciphertext.length - 24), ciphertext.subarray(ciphertext.length - TAG_LENGTH, ciphertext.length))
1266
1302
  }
1267
1303
 
1268
1304
  //Defaults
1269
- const defaultInstrs={
1270
- wii:{
1271
- male:{
1305
+ const defaultInstrs = {
1306
+ wii: {
1307
+ male: {
1272
1308
  "col": "On the info page (first tab), set the Favorite Color to Red (1 from the left, top row).",
1273
1309
  "heightWeight": "On the build page (second tab), set the height to 50%, and the weight to 50%.",
1274
1310
  "faceShape": "On the face page (third tab), set the shape to the one 1 from the top, in the left column.",
@@ -1310,7 +1346,7 @@ const defaultInstrs={
1310
1346
  "beard": "On the beard page (within the ninth tab), set the beard to the one on the top-left.",
1311
1347
  "beardCol": "On the mustache OR beard pages (within the ninth tab), set the color to the one 1 from the left, on the top row."
1312
1348
  },
1313
- female:{
1349
+ female: {
1314
1350
  "col": "On the info page (first tab), set the Favorite Color to Red (1 from the left, top row).",
1315
1351
  "heightWeight": "On the build page (second tab), set the height to 50%, and the weight to 50%.",
1316
1352
  "faceShape": "On the face page (third tab), set the shape to the one 1 from the top, in the left column.",
@@ -1353,8 +1389,8 @@ const defaultInstrs={
1353
1389
  "beardCol": "On the mustache OR beard pages (within the ninth tab), set the color to the one 1 from the left, on the top row."
1354
1390
  }
1355
1391
  },
1356
- "3ds":{
1357
- "male":{
1392
+ "3ds": {
1393
+ "male": {
1358
1394
  "faceShape": "On the face page (first tab), set the face shape to the one 1 from the top, and 1 from the left.",
1359
1395
  "skinCol": "On the face page (first tab), set the color to the one 1 from the top.",
1360
1396
  "makeup": "On the face page's makeup tab, set the makeup to \"None\" (the one 1 from the top, and 1 from the left).",
@@ -1400,7 +1436,7 @@ const defaultInstrs={
1400
1436
  "heightWeight": "On the build page (eighth tab), set the height to 50%, and the weight to 50%.",
1401
1437
  "col": "On the info page (after pressing \"Next\"), set the Favorite Color to Red (1 from the left, top row)."
1402
1438
  },
1403
- "female":{
1439
+ "female": {
1404
1440
  "faceShape": "On the face page (first tab), set the face shape to the one 1 from the top, and 1 from the left.",
1405
1441
  "skinCol": "On the face page (first tab), set the color to the one 1 from the top.",
1406
1442
  "makeup": "On the face page's makeup tab, set the makeup to \"None\" (the one 1 from the top, and 1 from the left).",
@@ -1449,10 +1485,10 @@ const defaultInstrs={
1449
1485
  }
1450
1486
  };
1451
1487
 
1452
- const defaultMii={
1453
- "male":{
1488
+ const defaultMii = {
1489
+ "male": {
1454
1490
  "general": {
1455
- "type":3,
1491
+ "type": 3,
1456
1492
  "birthday": 17,
1457
1493
  "birthMonth": 4,
1458
1494
  "height": 0,
@@ -1460,10 +1496,10 @@ const defaultMii={
1460
1496
  "gender": 1,
1461
1497
  "favoriteColor": 7
1462
1498
  },
1463
- "meta":{
1499
+ "meta": {
1464
1500
  "name": "Madison",
1465
1501
  "creatorName": "",
1466
- "console":"3ds"
1502
+ "console": "3ds"
1467
1503
  },
1468
1504
  "perms": {
1469
1505
  "sharing": false,
@@ -1472,8 +1508,8 @@ const defaultMii={
1472
1508
  "mingle": true
1473
1509
  },
1474
1510
  "hair": {
1475
- "page":0,
1476
- "type":7,
1511
+ "page": 0,
1512
+ "type": 7,
1477
1513
  "color": 7,
1478
1514
  "flipped": false
1479
1515
  },
@@ -1484,7 +1520,7 @@ const defaultMii={
1484
1520
  "makeup": 0
1485
1521
  },
1486
1522
  "eyes": {
1487
- "page":0,
1523
+ "page": 0,
1488
1524
  "type": 9,
1489
1525
  "col": 4,
1490
1526
  "size": 1,
@@ -1494,9 +1530,9 @@ const defaultMii={
1494
1530
  "yPosition": 11
1495
1531
  },
1496
1532
  "eyebrows": {
1497
- "page":0,
1498
- "type":5,
1499
- "color":7,
1533
+ "page": 0,
1534
+ "type": 5,
1535
+ "color": 7,
1500
1536
  "size": 2,
1501
1537
  "squash": 4,
1502
1538
  "rotation": 4,
@@ -1504,21 +1540,21 @@ const defaultMii={
1504
1540
  "yPosition": 6
1505
1541
  },
1506
1542
  "nose": {
1507
- "page":1,
1508
- "type":0,
1543
+ "page": 1,
1544
+ "type": 0,
1509
1545
  "size": 0,
1510
1546
  "yPosition": 5
1511
1547
  },
1512
1548
  "mouth": {
1513
- "page":1,
1514
- "type":6,
1549
+ "page": 1,
1550
+ "type": 6,
1515
1551
  "color": 0,
1516
1552
  "size": 2,
1517
1553
  "squash": 3,
1518
1554
  "yPosition": 10
1519
1555
  },
1520
1556
  "beard": {
1521
- "mustache":{
1557
+ "mustache": {
1522
1558
  "type": 0,
1523
1559
  "size": 4,
1524
1560
  "yPosition": 10
@@ -1528,7 +1564,7 @@ const defaultMii={
1528
1564
  },
1529
1565
  "glasses": {
1530
1566
  "type": 0,
1531
- "color":0,
1567
+ "color": 0,
1532
1568
  "size": 4,
1533
1569
  "yPosition": 10
1534
1570
  },
@@ -1537,13 +1573,11 @@ const defaultMii={
1537
1573
  "size": 4,
1538
1574
  "xPosition": 2,
1539
1575
  "yPosition": 20
1540
- },
1541
- "name": "",
1542
- "creatorName": ""
1576
+ }
1543
1577
  },
1544
- "female":{
1578
+ "female": {
1545
1579
  "general": {
1546
- "type":3,
1580
+ "type": 3,
1547
1581
  "birthday": 17,
1548
1582
  "birthMonth": 4,
1549
1583
  "height": 0,
@@ -1551,10 +1585,10 @@ const defaultMii={
1551
1585
  "gender": 1,
1552
1586
  "favoriteColor": 7
1553
1587
  },
1554
- "meta":{
1588
+ "meta": {
1555
1589
  "name": "Madison",
1556
1590
  "creatorName": "",
1557
- "console":"3ds"
1591
+ "console": "3ds"
1558
1592
  },
1559
1593
  "perms": {
1560
1594
  "sharing": false,
@@ -1563,8 +1597,8 @@ const defaultMii={
1563
1597
  "mingle": true
1564
1598
  },
1565
1599
  "hair": {
1566
- "page":0,
1567
- "type":7,
1600
+ "page": 0,
1601
+ "type": 7,
1568
1602
  "color": 7,
1569
1603
  "flipped": false
1570
1604
  },
@@ -1575,7 +1609,7 @@ const defaultMii={
1575
1609
  "makeup": 0
1576
1610
  },
1577
1611
  "eyes": {
1578
- "page":0,
1612
+ "page": 0,
1579
1613
  "type": 9,
1580
1614
  "col": 4,
1581
1615
  "size": 1,
@@ -1585,9 +1619,9 @@ const defaultMii={
1585
1619
  "yPosition": 11
1586
1620
  },
1587
1621
  "eyebrows": {
1588
- "page":0,
1589
- "type":5,
1590
- "color":7,
1622
+ "page": 0,
1623
+ "type": 5,
1624
+ "color": 7,
1591
1625
  "size": 2,
1592
1626
  "squash": 4,
1593
1627
  "rotation": 4,
@@ -1595,21 +1629,21 @@ const defaultMii={
1595
1629
  "yPosition": 6
1596
1630
  },
1597
1631
  "nose": {
1598
- "page":1,
1599
- "type":0,
1632
+ "page": 1,
1633
+ "type": 0,
1600
1634
  "size": 0,
1601
1635
  "yPosition": 5
1602
1636
  },
1603
1637
  "mouth": {
1604
- "page":1,
1605
- "type":6,
1638
+ "page": 1,
1639
+ "type": 6,
1606
1640
  "color": 0,
1607
1641
  "size": 2,
1608
1642
  "squash": 3,
1609
1643
  "yPosition": 10
1610
1644
  },
1611
1645
  "beard": {
1612
- "mustache":{
1646
+ "mustache": {
1613
1647
  "type": 0,
1614
1648
  "size": 4,
1615
1649
  "yPosition": 10
@@ -1619,7 +1653,7 @@ const defaultMii={
1619
1653
  },
1620
1654
  "glasses": {
1621
1655
  "type": 0,
1622
- "color":0,
1656
+ "color": 0,
1623
1657
  "size": 4,
1624
1658
  "yPosition": 10
1625
1659
  },
@@ -1628,523 +1662,8 @@ const defaultMii={
1628
1662
  "size": 4,
1629
1663
  "xPosition": 2,
1630
1664
  "yPosition": 20
1631
- },
1632
- "name": "",
1633
- "creatorName": ""
1634
- }
1635
- };
1636
-
1637
- // Mii binary helpers
1638
- const decoders = {
1639
- number: (value, field) => value + (field.offset || 0),
1640
- boolean: (value, field) => field.invert ? value === 0 : value === 1,
1641
- enum: (value, field) => field.values[value],
1642
- lookup: (value, field, tables) => {
1643
- const table = getNestedProperty(tables, field.lookupTable)
1644
- if (!table) return "ERROR: could not find requested lookup table";
1645
-
1646
- if (table.indexLookup) {
1647
- if (table.paginated) {
1648
- // Handle paginated (2D array) lookup
1649
- for (let page = 0; page < table.values.length; page++) {
1650
- for (let index = 0; index < table.values[page].length; index++) {
1651
- if (table.values[page][index] === value) {
1652
- return [page, index];
1653
- }
1654
- }
1655
- }
1656
- return undefined;
1657
- } else {
1658
- // Handle non-paginated index lookup
1659
- return table.values.indexOf(value);
1660
- }
1661
- } else if (Array.isArray(table)) {
1662
- return table[value];
1663
- } else {
1664
- return table[value.toString()];
1665
- }
1666
- },
1667
- lookupPage: (value, field, tables, type) => {
1668
- const table = getNestedProperty(tables, field.lookupTable)
1669
- if (!table) return "ERROR: could not find requested lookup table";
1670
-
1671
- if (table.indexLookup) {
1672
- if (table.paginated) {
1673
- // Handle paginated (2D array) lookup
1674
- for (let page = 0; page < table.values.length; page++) {
1675
- for (let index = 0; index < table.values[page].length; index++) {
1676
- if (table.values[page][index] === value) {
1677
- return [page, index][0];
1678
- }
1679
- }
1680
- }
1681
- return undefined;
1682
- } else {
1683
- // Handle non-paginated index lookup
1684
- return table.values.indexOf(value);
1685
- }
1686
- } else if (Array.isArray(table)) {
1687
- return table[value];
1688
- } else {
1689
- return table[value.toString()];
1690
- }
1691
- },
1692
- lookupType: (value, field, tables, type) => {
1693
- const table = getNestedProperty(tables, field.lookupTable)
1694
- if (!table) return "ERROR: could not find requested lookup table";
1695
-
1696
- if (table.indexLookup) {
1697
- if (table.paginated) {
1698
- // Handle paginated (2D array) lookup
1699
- for (let page = 0; page < table.values.length; page++) {
1700
- for (let index = 0; index < table.values[page].length; index++) {
1701
- if (table.values[page][index] === value) {
1702
- return [page, index][1];
1703
- }
1704
- }
1705
- }
1706
- return undefined;
1707
- } else {
1708
- // Handle non-paginated index lookup
1709
- return table.values.indexOf(value);
1710
- }
1711
- } else if (Array.isArray(table)) {
1712
- return table[value];
1713
- } else {
1714
- return table[value.toString()];
1715
- }
1716
- },
1717
- color: (value, field, tables) => tables[field.colorArray]?.[value] || value
1718
- };
1719
-
1720
- const encoders = {
1721
- number: (value, field) => value - (field.offset || 0),
1722
- boolean: (value, field) => field.invert ? (value ? 0 : 1) : (value ? 1 : 0),
1723
- enum: (value, field) => field.values.indexOf(value),
1724
- lookup: (decodedValue, field, tables) => {
1725
- const table = getNestedProperty(tables, field.lookupTable)
1726
- if (!table) return "ERROR: could not find requested lookup table";
1727
-
1728
- if (table.indexLookup){
1729
- if (table.paginated) {
1730
- if (!Array.isArray(decodedValue) || decodedValue.length !== 2) {
1731
- return undefined;
1732
- }
1733
- const [page, index] = decodedValue;
1734
- if (page >= 0 && page < table.values.length && index >= 0 && index < table.values[page].length) {
1735
- return table.values[page][index];
1736
- }
1737
- return undefined;
1738
- } else {
1739
- return table.values[decodedValue];
1740
- }
1741
- } else if (Array.isArray(table)) {
1742
- const index = table.indexOf(decodedValue);
1743
- return index !== -1 ? index : undefined;
1744
- } else {
1745
- // Handle object lookup
1746
- for (const [key, val] of Object.entries(table)) {
1747
- if (val === decodedValue) return parseInt(key);
1748
- }
1749
- return undefined;
1750
- }
1751
- },
1752
- encodingTable: (decodedValue, field, tables) => {
1753
- const table = getNestedProperty(tables, field.encodingTable);
1754
- console.log(table);
1755
- if (!table) return "ERROR: could not find requested encoding table";
1756
-
1757
- if (table.indexLookup){
1758
- if (table.paginated) {
1759
- if (!Array.isArray(decodedValue) || decodedValue.length !== 2) {
1760
- return undefined;
1761
- }
1762
- const [page, index] = decodedValue;
1763
- if (page >= 0 && page < table.values.length && index >= 0 && index < table.values[page].length) {
1764
- return table.values[page][index];
1765
- }
1766
- return undefined;
1767
- } else {
1768
- return "ERROR";
1769
- }
1770
- }
1771
- else{
1772
- return "ERROR";
1773
- }
1774
- },
1775
- color: (value, field, T) => {
1776
- const arr = T[field.colorArray];
1777
- return arr?.indexOf(value) ?? value;
1778
- },
1779
- };
1780
-
1781
- // Decoding system
1782
- function decodeString(data, field) {
1783
- let result = "";
1784
- const maxLength = field.maxLength || 10;
1785
-
1786
- for (let i = 0; i < maxLength; i++) {
1787
- const charOffset = field.byteOffset + (i * 2);
1788
- if (charOffset + 1 < data.length) {
1789
- const char1 = data[charOffset];
1790
- const char2 = data[charOffset + 1];
1791
- if (char1 === 0 && char2 === 0) break;
1792
- result += String.fromCharCode(field.endianness == "little" ? char1 : char2);
1793
- }
1794
- }
1795
- return result.replace(/\x00/g, "");
1796
- }
1797
-
1798
- function encodeString(str, field) {
1799
- const result = [];
1800
- const maxLength = field.maxLength || 10;
1801
-
1802
- for (let i = 0; i < maxLength; i++) {
1803
- const code = i < str.length ? str.charCodeAt(i) : 0;
1804
-
1805
- if (field.endianness == "little") {
1806
- result.push(code); // Low byte
1807
- result.push(0); // High byte
1808
- } else {
1809
- result.push(0); // High byte
1810
- result.push(code); // Low byte
1811
- }
1812
- }
1813
- return result;
1814
- }
1815
-
1816
- function extractMultiBits(data, bitSpecs, isBigEndian = true) {
1817
- let result = 0;
1818
- let totalBitsProcessed = 0;
1819
-
1820
- // Process bit specs in order (they should be ordered from most significant to least significant)
1821
- for (const spec of bitSpecs) {
1822
- const bits = extractBits(data, spec.byteOffset, spec.bitOffset, spec.bitLength, isBigEndian);
1823
- result = (result << spec.bitLength) | bits;
1824
- totalBitsProcessed += spec.bitLength;
1825
- }
1826
-
1827
- return result;
1828
- }
1829
-
1830
- function setMultiBits(buffer, bitSpecs, value) {
1831
- let remainingValue = value;
1832
-
1833
- // Process specs in reverse order (from least significant to most significant)
1834
- for (let i = bitSpecs.length - 1; i >= 0; i--) {
1835
- const spec = bitSpecs[i];
1836
- const mask = (1 << spec.bitLength) - 1;
1837
- const bitsToSet = remainingValue & mask;
1838
-
1839
- setBits(buffer, spec.byteOffset, spec.bitOffset, spec.bitLength, bitsToSet);
1840
- remainingValue >>>= spec.bitLength;
1841
- }
1842
- }
1843
-
1844
- function extractBits(data, byteOffset, bitOffset, bitLength, isBigEndian = true) {
1845
- const totalBitOffset = byteOffset * 8 + bitOffset;
1846
- const startByte = Math.floor(totalBitOffset / 8);
1847
- const endByte = Math.floor((totalBitOffset + bitLength - 1) / 8);
1848
-
1849
- let value = 0;
1850
-
1851
- if (isBigEndian) {
1852
- // Big endian: process bytes left to right (original behavior)
1853
- for (let i = startByte; i <= endByte; i++) {
1854
- if (i < data.length) {
1855
- value = (value << 8) | data[i];
1856
- }
1857
- }
1858
- } else {
1859
- // Little endian: process bytes right to left
1860
- for (let i = endByte; i >= startByte; i--) {
1861
- if (i < data.length) {
1862
- value = (value << 8) | data[i];
1863
- }
1864
- }
1865
- }
1866
-
1867
- const rightShift = (endByte - startByte + 1) * 8 - (totalBitOffset % 8) - bitLength;
1868
- value >>>= rightShift;
1869
-
1870
- const mask = (1 << bitLength) - 1;
1871
- return value & mask;
1872
- }
1873
-
1874
- function setBits(buffer, byteOffset, bitOffset, bitLength, value) {
1875
- // Calculate the absolute bit position from the start
1876
- const absoluteBitPos = byteOffset * 8 + bitOffset;
1877
-
1878
- // Process each bit of the value
1879
- for (let i = 0; i < bitLength; i++) {
1880
- const currentBitPos = absoluteBitPos + i;
1881
- const currentByteIndex = Math.floor(currentBitPos / 8);
1882
- const currentBitInByte = currentBitPos % 8;
1883
-
1884
- // Extract the bit from the value (MSB first)
1885
- const bitValue = (value >> (bitLength - 1 - i)) & 1;
1886
-
1887
- // Create mask for this specific bit position
1888
- const mask = 1 << (7 - currentBitInByte);
1889
-
1890
- if (bitValue) {
1891
- // Set the bit
1892
- buffer[currentByteIndex] |= mask;
1893
- } else {
1894
- // Clear the bit
1895
- buffer[currentByteIndex] &= ~mask;
1896
- }
1897
- }
1898
- }
1899
-
1900
- function setNestedProperty(obj, path, value) {
1901
- // Allows to reference nested properties in extraction schemas with "name.subcategory.suboption"
1902
- const keys = path.split('.');
1903
- let current = obj;
1904
-
1905
- for (let i = 0; i < keys.length - 1; i++) {
1906
- if (!(keys[i] in current)) {
1907
- current[keys[i]] = {};
1908
- }
1909
- current = current[keys[i]];
1910
- }
1911
-
1912
- current[keys[keys.length - 1]] = value;
1913
- }
1914
-
1915
- function getNestedProperty(obj, path) {
1916
- // See `setNestedProperty` comment
1917
- return path.split('.').reduce((current, key) => current?.[key], obj);
1918
- }
1919
-
1920
- function miiBufferToJson(data, schema, lookupTables = {}, isBigEndian = true) {
1921
- const result = {};
1922
-
1923
- for (const [fieldPath, fieldDef] of Object.entries(schema)) {
1924
- let value;
1925
-
1926
- if (fieldDef.type === 'string') {
1927
- value = decodeString(data, fieldDef);
1928
- } else if (fieldDef.bitSpecs) {
1929
- // Handle multi-byte fields with non-contiguous bits
1930
- value = extractMultiBits(data, fieldDef.bitSpecs, isBigEndian);
1931
- } else {
1932
- // Handle standard contiguous bit fields
1933
- value = extractBits(data, fieldDef.byteOffset, fieldDef.bitOffset, fieldDef.bitLength, isBigEndian);
1934
- }
1935
-
1936
- // Apply decoder
1937
- if (fieldDef.decoder && typeof decoders !== 'undefined' && decoders[fieldDef.decoder]) {
1938
- value = decoders[fieldDef.decoder](value, fieldDef, lookupTables);
1939
- }
1940
-
1941
- setNestedProperty(result, fieldPath, value);
1942
- }
1943
-
1944
- return result;
1945
- }
1946
-
1947
- function jsonToMiiBuffer(miiData, schema, lookupTables = {}, totalBytes = 74) {
1948
- const buffer = new Array(totalBytes).fill(0);
1949
-
1950
- for (const [fieldPath, fieldDef] of Object.entries(schema)) {
1951
- let raw = getNestedProperty(miiData, fieldPath);
1952
-
1953
- // Run encoders
1954
- if (fieldDef.encodingTable && fieldDef.decoder && typeof encoders !== 'undefined' && encoders[fieldDef.decoder]){
1955
- if(fieldDef.encodingTable==="NONE"){
1956
- continue;
1957
- }
1958
- raw = encoders.encodingTable([raw,getNestedProperty(miiData,fieldDef.secondaryParameter)], fieldDef, lookupTables);
1959
- }
1960
- else if (fieldDef.decoder && typeof encoders !== 'undefined' && encoders[fieldDef.decoder]) {
1961
- raw = encoders[fieldDef.decoder](raw, fieldDef, lookupTables);
1962
- }
1963
-
1964
- if (fieldDef.type === 'string') {
1965
- const bytes = encodeString(raw, fieldDef);
1966
- for (let i = 0; i < bytes.length; i++) {
1967
- buffer[fieldDef.byteOffset + i] = bytes[i];
1968
- }
1969
- } else if (fieldDef.bitSpecs) {
1970
- // Handle multi-byte fields with non-contiguous bits
1971
- setMultiBits(buffer, fieldDef.bitSpecs, raw);
1972
- } else {
1973
- // Handle standard contiguous bit fields
1974
- setBits(buffer, fieldDef.byteOffset, fieldDef.bitOffset, fieldDef.bitLength, raw);
1975
1665
  }
1976
1666
  }
1977
-
1978
- return Buffer.from(buffer);
1979
- }
1980
-
1981
- const WII_MII_SCHEMA = {
1982
- 'general.gender': { byteOffset: 0x00, bitOffset: 1, bitLength: 1, decoder: 'number' },
1983
- 'general.birthMonth': { byteOffset: 0x00, bitOffset: 2, bitLength: 4, decoder: 'number' },
1984
- 'general.birthday': { byteOffset: 0x00, bitOffset: 6, bitLength: 5, decoder: 'number' },
1985
- 'general.favoriteColor': { byteOffset: 0x01, bitOffset: 3, bitLength: 4, decoder: 'number' },
1986
- 'meta.name': { type: 'string', byteOffset: 0x03, maxLength: 10, endianness: "little" },
1987
- 'meta.creatorName': { type: 'string', byteOffset: 0x37, maxLength: 10, endianness: "little" },
1988
- 'general.height': { byteOffset: 0x16, bitOffset: 0, bitLength: 8, decoder: 'number' },
1989
- 'general.weight': { byteOffset: 0x17, bitOffset: 0, bitLength: 8, decoder: 'number' },
1990
- 'perms.mingle': { byteOffset: 0x21, bitOffset: 5, bitLength: 1, decoder: 'boolean', invert: true },
1991
- 'perms.fromCheckMiiOut': { byteOffset: 0x21, bitOffset: 7, bitLength: 1, decoder: 'boolean' },
1992
- 'face.type': { byteOffset: 0x20, bitOffset: 0, bitLength: 3, decoder: 'number' },
1993
- 'face.color': { byteOffset: 0x20, bitOffset: 3, bitLength: 3, decoder: 'number' },
1994
- 'face.feature': { byteOffset: 0x20, bitOffset: 6, bitLength: 4, decoder: 'number' },
1995
- 'hair.page': { byteOffset: 0x22, bitOffset: 0, bitLength: 7, decoder: 'lookup', lookupTable: 'pages.hairs' },//Refuse, marked for destruction.
1996
- 'hair.type': { byteOffset: 0x22, bitOffset: 0, bitLength: 7, decoder: 'lookup', lookupTable: 'types.hairs' },
1997
- 'hair.color': { byteOffset: 0x22, bitOffset: 7, bitLength: 3, decoder: 'number' },
1998
- 'hair.flipped': { byteOffset: 0x23, bitOffset: 2, bitLength: 1, decoder: 'boolean' },
1999
- 'eyebrows.page': { byteOffset: 0x24, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'pages.eyebrows' },
2000
- 'eyebrows.type': { byteOffset: 0x24, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'types.eyebrows' },
2001
- 'eyebrows.rotation': { byteOffset: 0x24, bitOffset: 6, bitLength: 4, decoder: 'number' },
2002
- 'eyebrows.color': { byteOffset: 0x26, bitOffset: 0, bitLength: 3, decoder: 'number' },
2003
- 'eyebrows.size': { byteOffset: 0x26, bitOffset: 3, bitLength: 4, decoder: 'number' },
2004
- 'eyebrows.yPosition': { byteOffset: 0x26, bitOffset: 7, bitLength: 5, decoder: 'number', offset: -3 },
2005
- 'eyebrows.distanceApart': { byteOffset: 0x27, bitOffset: 4, bitLength: 4, decoder: 'number' },
2006
- 'eyes.page': { byteOffset: 0x28, bitOffset: 0, bitLength: 6, decoder: 'lookup', lookupTable: 'pages.eyes', encodingTable: 'eyes', secondaryParameter: 'eyes.type' },//Refuse, marked for destruction.
2007
- 'eyes.type': { byteOffset: 0x28, bitOffset: 0, bitLength: 6, decoder: 'lookup', lookupTable: 'types.eyes', encodingTable:'NONE' },
2008
- 'eyes.rotation': { byteOffset: 0x29, bitOffset: 0, bitLength: 3, decoder: 'number' },
2009
- 'eyes.yPosition': { byteOffset: 0x29, bitOffset: 3, bitLength: 5, decoder: 'number' },
2010
- 'eyes.color': { byteOffset: 0x2A, bitOffset: 0, bitLength: 3, decoder: 'number' },
2011
- 'eyes.size': { byteOffset: 0x2A, bitOffset: 4, bitLength: 3 },
2012
- 'eyes.distanceApart': { byteOffset: 0x2A, bitOffset: 7, bitLength: 4, decoder: 'number' },
2013
- 'nose.type': { byteOffset: 0x2C, bitOffset: 0, bitLength: 4, decoder: 'lookup', lookupTable: 'wiiNoses' },
2014
- 'nose.size': { byteOffset: 0x2C, bitOffset: 4, bitLength: 4, decoder: 'number' },
2015
- 'nose.yPosition': { byteOffset: 0x2D, bitOffset: 0, bitLength: 5, decoder: 'number' },
2016
- 'mouth.page': { byteOffset: 0x2E, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'pages.mouths' },//Refuse, marked for destruction.
2017
- 'mouth.type': { byteOffset: 0x2E, bitOffset: 0, bitLength: 5, decoder: 'lookup', lookupTable: 'types.mouths' },
2018
- 'mouth.color': { byteOffset: 0x2E, bitOffset: 5, bitLength: 2, decoder: 'number' },
2019
- 'mouth.size': { byteOffset: 0x2E, bitOffset: 7, bitLength: 4, decoder: 'number' },
2020
- 'mouth.yPosition': { byteOffset: 0x2F, bitOffset: 3, bitLength: 5, decoder: 'number' },
2021
- 'glasses.type': { byteOffset: 0x30, bitOffset: 0, bitLength: 4, decoder: 'number' },
2022
- 'glasses.color': { byteOffset: 0x30, bitOffset: 4, bitLength: 3, decoder: 'number' },
2023
- 'glasses.size': { byteOffset: 0x31, bitOffset: 0, bitLength: 3, decoder: 'number' },
2024
- 'glasses.yPosition': { byteOffset: 0x31, bitOffset: 3, bitLength: 5, decoder: 'number' },
2025
- 'beard.mustache.type': { byteOffset: 0x32, bitOffset: 0, bitLength: 2, decoder: 'number' },
2026
- 'beard.type': { byteOffset: 0x32, bitOffset: 2, bitLength: 2, decoder: 'number' },
2027
- 'beard.color': { byteOffset: 0x32, bitOffset: 4, bitLength: 3, decoder: 'number' },
2028
- 'beard.mustache.size': { byteOffset: 0x32, bitOffset: 7, bitLength: 4, decoder: 'number' },
2029
- 'beard.mustache.yPosition': { byteOffset: 0x33, bitOffset: 3, bitLength: 5, decoder: 'number' },
2030
- 'mole.on': { byteOffset: 0x34, bitOffset: 0, bitLength: 1, decoder: 'boolean' },
2031
- 'mole.size': { byteOffset: 0x34, bitOffset: 1, bitLength: 4, decoder: 'number' },
2032
- 'mole.yPosition': { byteOffset: 0x34, bitOffset: 5, bitLength: 5, decoder: 'number' },
2033
- 'mole.xPosition': { byteOffset: 0x35, bitOffset: 2, bitLength: 5, decoder: 'number' }
2034
- };
2035
-
2036
- const THREEDS_MII_SCHEMA = {
2037
- 'general.birthday': {
2038
- bitSpecs: [
2039
- { byteOffset: 0x19, bitOffset: 6, bitLength: 2 },
2040
- { byteOffset: 0x18, bitOffset: 0, bitLength: 3 }
2041
- ],
2042
- decoder: 'number'
2043
- },
2044
- 'general.birthMonth': { byteOffset: 0x18, bitOffset: 3, bitLength: 4, decoder: 'number' },
2045
- 'general.gender': { byteOffset: 0x18, bitOffset: 7, bitLength: 1, decoder: 'number' },
2046
- 'general.favoriteColor': { byteOffset: 0x19, bitOffset: 2, bitLength: 4, decoder: 'number' },
2047
- 'meta.name': { type: 'string', byteOffset: 0x1A, maxLength: 10, endianness: "little" },
2048
- 'meta.creatorName': { type: 'string', byteOffset: 0x48, maxLength: 10, endianness: "little" },
2049
- 'general.height': { byteOffset: 0x2E, bitOffset: 0, bitLength: 8, decoder: 'number' },
2050
- 'general.weight': { byteOffset: 0x2F, bitOffset: 0, bitLength: 8, decoder: 'number' },
2051
- 'perms.sharing': { byteOffset: 0x30, bitOffset: 7, bitLength: 1, decoder: 'boolean', invert: true },
2052
- 'perms.copying': { byteOffset: 0x01, bitOffset: 7, bitLength: 1, decoder: 'boolean' },
2053
- 'face.type': { byteOffset: 0x30, bitOffset: 3, bitLength: 4, decoder: 'lookup', lookupTable: 'faces' },
2054
- 'face.color': { byteOffset: 0x30, bitOffset: 0, bitLength: 3, decoder: 'number' },
2055
- 'face.feature': { byteOffset: 0x31, bitOffset: 4, bitLength: 4, decoder: 'number' },
2056
- 'face.makeup': { byteOffset: 0x31, bitOffset: 0, bitLength: 4, decoder: 'number' },
2057
- 'hair.page': { byteOffset: 0x32, bitOffset: 0, bitLength: 8, decoder: 'lookupPage', lookupTable: 'hairs' },//qk
2058
- 'hair.type': { byteOffset: 0x32, bitOffset: 0, bitLength: 8, decoder: 'lookupType', lookupTable: 'hairs' },//qk
2059
- 'hair.color': { byteOffset: 0x33, bitOffset: 5, bitLength: 3, decoder: 'number' },
2060
- 'hair.flipped': { byteOffset: 0x33, bitOffset: 4, bitLength: 1, decoder: 'boolean' },
2061
- 'eyes.page': { byteOffset: 0x34, bitOffset: 2, bitLength: 6, decoder: 'lookupPage', lookupTable: 'eyes' },//qk
2062
- 'eyes.type': { byteOffset: 0x34, bitOffset: 2, bitLength: 6, decoder: 'lookupType', lookupTable: 'eyes' },//qk
2063
- 'eyes.color': {
2064
- bitSpecs: [
2065
- { byteOffset: 0x35, bitOffset: 7, bitLength: 1 },
2066
- { byteOffset: 0x34, bitOffset: 0, bitLength: 2 }
2067
- ],
2068
- decoder: 'number'
2069
- },
2070
- 'eyes.size': { byteOffset: 0x35, bitOffset: 3, bitLength: 4, decoder: 'number' },
2071
- 'eyes.squash': { byteOffset: 0x35, bitOffset: 0, bitLength: 3, decoder: 'number' },
2072
- 'eyes.rotation': { byteOffset: 0x36, bitOffset: 3, bitLength: 5, decoder: 'number' },
2073
- 'eyes.distanceApart': {
2074
- bitSpecs: [
2075
- { byteOffset: 0x37, bitOffset: 7, bitLength: 1 },
2076
- { byteOffset: 0x36, bitOffset: 0, bitLength: 3 }
2077
- ],
2078
- decoder: 'number'
2079
- },
2080
- 'eyes.yPosition': { byteOffset: 0x37, bitOffset: 2, bitLength: 5, decoder: 'number' },
2081
- 'eyebrows.page': { byteOffset: 0x38, bitOffset: 3, bitLength: 5, decoder: 'lookupPage', lookupTable: 'eyebrows' },//qk
2082
- 'eyebrows.type': { byteOffset: 0x38, bitOffset: 3, bitLength: 5, decoder: 'lookupType', lookupTable: 'eyebrows' },//qk
2083
- 'eyebrows.color': { byteOffset: 0x38, bitOffset: 0, bitLength: 3, decoder: 'number' },
2084
- 'eyebrows.size': { byteOffset: 0x39, bitOffset: 4, bitLength: 4, decoder: 'number' },
2085
- 'eyebrows.squash': { byteOffset: 0x39, bitOffset: 1, bitLength: 3, decoder: 'number' },
2086
- 'eyebrows.rotation': { byteOffset: 0x3A, bitOffset: 4, bitLength: 4, decoder: 'number' },
2087
- 'eyebrows.distanceApart': {
2088
- bitSpecs: [
2089
- { byteOffset: 0x3B, bitOffset: 7, bitLength: 1 },
2090
- { byteOffset: 0x3A, bitOffset: 0, bitLength: 3 }
2091
- ],
2092
- decoder: 'number'
2093
- },
2094
- 'eyebrows.yPosition': { byteOffset: 0x3B, bitOffset: 2, bitLength: 5, decoder: 'number', offset: -3 },
2095
- 'nose.page': { byteOffset: 0x3C, bitOffset: 3, bitLength: 5, decoder: 'lookupPage', lookupTable: 'noses' },//qk
2096
- 'nose.type': { byteOffset: 0x3C, bitOffset: 3, bitLength: 5, decoder: 'lookupType', lookupTable: 'noses' },//qk
2097
- 'nose.size': {
2098
- bitSpecs: [
2099
- { byteOffset: 0x3D, bitOffset: 7, bitLength: 1 },
2100
- { byteOffset: 0x3C, bitOffset: 0, bitLength: 3 }
2101
- ],
2102
- decoder: 'number'
2103
- },
2104
- 'nose.yPosition': { byteOffset: 0x3D, bitOffset: 2, bitLength: 5, decoder: 'number' },
2105
- 'mouth.page': { byteOffset: 0x3E, bitOffset: 2, bitLength: 6, decoder: 'lookupPage', lookupTable: 'mouths' },//qk
2106
- 'mouth.type': { byteOffset: 0x3E, bitOffset: 2, bitLength: 6, decoder: 'lookupType', lookupTable: 'mouths' },//qk
2107
- 'mouth.color': {
2108
- bitSpecs: [
2109
- { byteOffset: 0x3F, bitOffset: 7, bitLength: 1 },
2110
- { byteOffset: 0x3E, bitOffset: 0, bitLength: 2 }
2111
- ],
2112
- decoder: 'number'
2113
- },
2114
- 'mouth.size': { byteOffset: 0x3F, bitOffset: 3, bitLength: 4, decoder: 'number' },
2115
- 'mouth.squash': { byteOffset: 0x3F, bitOffset: 0, bitLength: 3, decoder: 'number' },
2116
- 'mouth.yPosition': { byteOffset: 0x40, bitOffset: 3, bitLength: 5, decoder: 'number' },
2117
- 'beard.mustache.type': { byteOffset: 0x40, bitOffset: 0, bitLength: 3, decoder: 'number' },
2118
- 'beard.type': { byteOffset: 0x42, bitOffset: 5, bitLength: 3, decoder: 'number' },
2119
- 'beard.color': { byteOffset: 0x42, bitOffset: 2, bitLength: 3, decoder: 'number' },
2120
- 'beard.mustache.size': {
2121
- bitSpecs: [
2122
- { byteOffset: 0x43, bitOffset: 6, bitLength: 2 },
2123
- { byteOffset: 0x42, bitOffset: 0, bitLength: 2 }
2124
- ],
2125
- decoder: 'number'
2126
- },
2127
- 'beard.mustache.yPosition': { byteOffset: 0x43, bitOffset: 1, bitLength: 5, decoder: 'number' },
2128
- 'glasses.type': { byteOffset: 0x44, bitOffset: 4, bitLength: 4, decoder: 'number' },
2129
- 'glasses.color': { byteOffset: 0x44, bitOffset: 1, bitLength: 3, decoder: 'number' },
2130
- 'glasses.size': {
2131
- bitSpecs: [
2132
- { byteOffset: 0x45, bitOffset: 5, bitLength: 3 },
2133
- { byteOffset: 0x44, bitOffset: 0, bitLength: 1 }
2134
- ],
2135
- decoder: 'number'
2136
- },
2137
- 'glasses.yPosition': { byteOffset: 0x45, bitOffset: 0, bitLength: 5, decoder: 'number' },
2138
- 'mole.on': { byteOffset: 0x46, bitOffset: 7, bitLength: 1, decoder: 'boolean' },
2139
- 'mole.size': { byteOffset: 0x46, bitOffset: 3, bitLength: 4, decoder: 'number' },
2140
- 'mole.xPosition': {
2141
- bitSpecs: [
2142
- { byteOffset: 0x47, bitOffset: 6, bitLength: 2 },
2143
- { byteOffset: 0x46, bitOffset: 0, bitLength: 3 }
2144
- ],
2145
- decoder: 'number'
2146
- },
2147
- 'mole.yPosition': { byteOffset: 0x47, bitOffset: 1, bitLength: 5, decoder: 'number' },
2148
1667
  };
2149
1668
 
2150
1669
  //Functions for working with the Miis
@@ -2159,61 +1678,87 @@ function encodeStudio(mii) {
2159
1678
  }
2160
1679
  return dest;
2161
1680
  }
2162
- function convertMii(jsonIn,typeTo){
2163
- typeFrom=jsonIn.console?.toLowerCase();
2164
- if(typeFrom==null||typeTo===typeFrom){
1681
+ function decodeStudio(encoded) {
1682
+ let bytes;
1683
+ if (encoded instanceof Uint8Array) {
1684
+ bytes = Array.from(encoded);
1685
+ }
1686
+ else if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(encoded)) {
1687
+ bytes = Array.from(encoded.values());
1688
+ }
1689
+ else if (typeof encoded === "string") {
1690
+ bytes = new Array(encoded.length);
1691
+ for (let i = 0; i < encoded.length; i++) bytes[i] = encoded.charCodeAt(i) & 0xFF;
1692
+ }
1693
+ else {
1694
+ throw new Error("decodeStudio: unsupported input type");
1695
+ }
1696
+ if (bytes.length < 2) return new Uint8Array(0);
1697
+
1698
+ let n = 0;
1699
+ const out = new Uint8Array(bytes.length - 1);
1700
+ for (let i = 1; i < bytes.length; i++) {
1701
+ const eo = bytes[i] & 0xFF;
1702
+ out[i - 1] = ((((eo - 7) & 0xFF) ^ n) & 0xFF);
1703
+ n = eo;
1704
+ }
1705
+ return out;
1706
+ }
1707
+ function convertMii(jsonIn, typeTo) {
1708
+ typeFrom = jsonIn.console?.toLowerCase();
1709
+ if (typeFrom == null || typeTo === typeFrom) {
2165
1710
  return jsonIn;
2166
1711
  }
2167
- let mii=jsonIn;
2168
- var miiTo=structuredClone(mii);
2169
- if(["wii u","3ds"].includes(typeFrom)){
2170
- miiTo.perms.mingle=mii.perms.sharing;
2171
- miiTo.perms.fromCheckMiiOut=false;
2172
- miiTo.face.type=convTables.face3DSToWii[mii.face.type];
1712
+ let mii = jsonIn;
1713
+ var miiTo = structuredClone(mii);
1714
+ if (["wii u", "3ds"].includes(typeFrom)) {
1715
+ miiTo.perms.mingle = mii.perms.sharing;
1716
+ miiTo.perms.fromCheckMiiOut = false;
1717
+ miiTo.face.type = convTables.face3DSToWii[mii.face.type];
2173
1718
  //We prioritize Facial Features here because the Wii supports more of those than they do Makeup types, and is more likely to apply. The 3DS has two separate fields, so you can have makeup and wrinkles applied at the same time. The Wii only has one that covers both.
2174
- if(typeof(convTables.features3DSToWii[mii.face.feature])==='string'){
2175
- miiTo.face.feature=convTables.makeup3DSToWii[mii.face.makeup];
1719
+ if (typeof (convTables.features3DSToWii[mii.face.feature]) === 'string') {
1720
+ miiTo.face.feature = convTables.makeup3DSToWii[mii.face.makeup];
2176
1721
  }
2177
- else{
2178
- miiTo.face.feature=convTables.features3DSToWii[mii.face.feature];
1722
+ else {
1723
+ miiTo.face.feature = convTables.features3DSToWii[mii.face.feature];
2179
1724
  }
2180
- miiTo.nose.type=convTables.nose3DSToWii[mii.nose.page][mii.nose.type];
2181
- miiTo.mouth.type=convTables.mouth3DSToWii[mii.mouth.page][mii.mouth.type];
2182
- miiTo.mouth.color=mii.mouth.color>2?0:mii.mouth.color;
2183
- miiTo.hair.type=convTables.hair3DSToWii[mii.hair.page][mii.hair.type];
2184
- miiTo.eyebrows.type=convTables.eyebrows3DSToWii[mii.eyebrows.page][mii.eyebrows.type];
2185
- miiTo.eyes.type=convTables.eyes3DSToWii[mii.eyes.page][mii.eyes.type];
2186
- miiTo.glasses.color=mii.glasses.color;
2187
- if(miiTo.beard.mustache.type===4){
2188
- miiTo.beard.mustache.type=2;
1725
+ miiTo.nose.type = convTables.nose3DSToWii[mii.nose.page][mii.nose.type];
1726
+ miiTo.mouth.type = convTables.mouth3DSToWii[mii.mouth.page][mii.mouth.type];
1727
+ miiTo.mouth.color = mii.mouth.color > 2 ? 0 : mii.mouth.color;
1728
+ miiTo.hair.type = convTables.hair3DSToWii[mii.hair.page][mii.hair.type];
1729
+ miiTo.eyebrows.type = convTables.eyebrows3DSToWii[mii.eyebrows.page][mii.eyebrows.type];
1730
+ miiTo.eyes.type = convTables.eyes3DSToWii[mii.eyes.page][mii.eyes.type];
1731
+ miiTo.glasses.color = mii.glasses.color;
1732
+ if (miiTo.beard.mustache.type === 4) {
1733
+ miiTo.beard.mustache.type = 2;
2189
1734
  }
2190
- else if(miiTo.beard.mustache.type===5){
2191
- miiTo.beard.mustache.type=0;
2192
- miiTo.beard.type=1;
1735
+ else if (miiTo.beard.mustache.type === 5) {
1736
+ miiTo.beard.mustache.type = 0;
1737
+ miiTo.beard.type = 1;
2193
1738
  }
2194
- if(mii.beard.type>3){
2195
- mii.beard.type=3;
1739
+ if (mii.beard.type > 3) {
1740
+ mii.beard.type = 3;
2196
1741
  }
2197
- miiTo.console="wii";
2198
- }
2199
- else if(typeFrom==="wii"){
2200
- miiTo.perms.sharing=mii.general.mingle;
2201
- miiTo.perms.copying=mii.general.mingle;
2202
- miiTo.hair.style=convTables.hairWiiTo3DS[mii.hair.page][mii.hair.type];
2203
- miiTo.face.shape=convTables.faceWiiTo3DS[mii.face.shape];
2204
- miiTo.face.makeup=0;
2205
- miiTo.face.feature=0;
2206
- if(typeof(convTables.featureWiiTo3DS[mii.face.feature])==='string'){
2207
- miiTo.face.makeup=makeups3DS[+convTables.featureWiiTo3DS[mii.face.feature]];
1742
+ miiTo.console = "wii";
1743
+ }
1744
+ else if (typeFrom === "wii") {
1745
+ miiTo.perms.sharing = mii.general.mingle;
1746
+ miiTo.perms.copying = mii.general.mingle;
1747
+ miiTo.hair.style = convTables.hairWiiTo3DS[mii.hair.page][mii.hair.type];
1748
+ miiTo.face.shape = convTables.faceWiiTo3DS[mii.face.shape];
1749
+ miiTo.face.makeup = 0;
1750
+ miiTo.face.feature = 0;
1751
+ if (typeof (convTables.featureWiiTo3DS[mii.face.feature]) === 'string') {
1752
+ miiTo.face.makeup = makeups3DS[+convTables.featureWiiTo3DS[mii.face.feature]];
2208
1753
  }
2209
- else{
2210
- miiTo.face.feature=faceFeatures3DS[convTables.featureWiiTo3DS[mii.face.feature]];
1754
+ else {
1755
+ miiTo.face.feature = faceFeatures3DS[convTables.featureWiiTo3DS[mii.face.feature]];
2211
1756
  }
2212
- miiTo.eyes.squash=3;
2213
- miiTo.eyebrows.squash=3;
2214
- miiTo.mouth.color=mii.mouth.color;
2215
- miiTo.mouth.squash=3;
2216
- miiTo.console="3ds";
1757
+ miiTo.eyes.squash = 3;
1758
+ miiTo.eyebrows.squash = 3;
1759
+ miiTo.mouth.color = mii.mouth.color;
1760
+ miiTo.mouth.squash = 3;
1761
+ miiTo.console = "3ds";
2217
1762
  }
2218
1763
  return miiTo;
2219
1764
  }
@@ -2286,133 +1831,384 @@ function convertMiiToStudio(jsonIn) {
2286
1831
  studioMii[0x22] = mii.mole.yPosition;
2287
1832
  return encodeStudio(studioMii);
2288
1833
  }
1834
+ function convertStudioToMii(input) {
1835
+ const s = toRawStudioBytes(input);
1836
+ const mii = {
1837
+ general: {
1838
+ gender: s[0x16],
1839
+ favoriteColor: s[0x15],
1840
+ height: s[0x1E],
1841
+ weight: s[0x02]
1842
+ },
1843
+
1844
+ face: {
1845
+ type: find1D(lookupTables.faces.values, s[0x13]),
1846
+ color: s[0x11],
1847
+ feature: s[0x14],
1848
+ makeup: s[0x12]
1849
+ },
1850
+
1851
+ hair: (() => {
1852
+ const { page, type } = findPageType(lookupTables.hairs.values, s[0x1D]);
1853
+ const colorStored = s[0x1B];
1854
+ return {
1855
+ page, type,
1856
+ color: (colorStored === 8) ? 0 : colorStored,
1857
+ flipped: !!s[0x1C]
1858
+ };
1859
+ })(),
1860
+
1861
+ eyes: (() => {
1862
+ const { page, type } = findPageType(lookupTables.eyes.values, s[0x07]);
1863
+ return {
1864
+ page, type,
1865
+ color: (s[0x04] | 0) - 8,
1866
+ size: s[0x06],
1867
+ squash: s[0x03],
1868
+ rotation: s[0x05],
1869
+ distanceApart: s[0x08],
1870
+ yPosition: s[0x09]
1871
+ };
1872
+ })(),
1873
+
1874
+ eyebrows: (() => {
1875
+ const { page, type } = findPageType(lookupTables.eyebrows.values, s[0x0E]);
1876
+ const colorStored = s[0x0B];
1877
+ return {
1878
+ page, type,
1879
+ color: (colorStored === 8) ? 0 : colorStored,
1880
+ size: s[0x0D],
1881
+ squash: s[0x0A],
1882
+ rotation: s[0x0C],
1883
+ distanceApart: s[0x0F],
1884
+ yPosition: (s[0x10] | 0) - 3
1885
+ };
1886
+ })(),
1887
+
1888
+ nose: (() => {
1889
+ const { page, type } = findPageType(lookupTables.noses.values, s[0x2C]);
1890
+ return { page, type, size: s[0x2B], yPosition: s[0x2D] };
1891
+ })(),
1892
+
1893
+ mouth: (() => {
1894
+ const { page, type } = findPageType(lookupTables.mouths.values, s[0x26]);
1895
+ const stored = s[0x24];
1896
+ const color = (stored >= 19 && stored <= 22) ? (stored - 19) : 4;
1897
+ return {
1898
+ page, type, color,
1899
+ size: s[0x25],
1900
+ squash: s[0x23],
1901
+ yPosition: s[0x27]
1902
+ };
1903
+ })(),
1904
+
1905
+ beard: (() => {
1906
+ const color = (s[0x00] === 8) ? 0 : s[0x00];
1907
+ return {
1908
+ color,
1909
+ type: s[0x01],
1910
+ mustache: { type: s[0x29], size: s[0x28], yPosition: s[0x2A] }
1911
+ };
1912
+ })(),
1913
+
1914
+ glasses: (() => {
1915
+ const stored = s[0x17];
1916
+ let color;
1917
+ if (stored === 8) color = 0;
1918
+ else if (stored >= 14 && stored <= 18) color = stored - 13;
1919
+ else if (stored === 0) color = 6;
1920
+ else color = 0;
1921
+ return {
1922
+ type: s[0x19],
1923
+ color,
1924
+ size: s[0x18],
1925
+ yPosition: s[0x1A]
1926
+ };
1927
+ })(),
1928
+
1929
+ mole: {
1930
+ on: !!s[0x20],
1931
+ size: s[0x1F],
1932
+ xPosition: s[0x21],
1933
+ yPosition: s[0x22]
1934
+ },
1935
+
1936
+ //The rest is unprovided by Studio dumps and is hardcoded
1937
+ meta: {
1938
+ name: "Studio Mii",
1939
+ creatorName: "StudioUser",
1940
+ console: "3DS"
1941
+ },
1942
+
1943
+ perms: {
1944
+ sharing: true,
1945
+ copying: true
1946
+ },
1947
+
1948
+ console: "3DS"
1949
+ };
1950
+
1951
+ return mii;
1952
+ }
1953
+
2289
1954
  async function readWiiBin(binOrPath) {
2290
1955
  let data;
2291
- if (/[^01]/ig.test(binOrPath)) {
1956
+ if (Buffer.isBuffer(binOrPath)) {
1957
+ data = binOrPath;
1958
+ }
1959
+ else if (/[^01]/ig.test(binOrPath)) {
2292
1960
  data = await fs.promises.readFile(binOrPath);
2293
- } else {
1961
+ }
1962
+ else {
2294
1963
  data = Buffer.from(binOrPath);
2295
1964
  }
2296
- var thisMii={
2297
- general:{},
2298
- perms:{},
2299
- meta:{},
2300
- face:{},
2301
- nose:{},
2302
- mouth:{},
2303
- mole:{},
2304
- hair:{},
2305
- eyebrows:{},
2306
- eyes:{},
2307
- glasses:{},
2308
- beard:{
2309
- mustache:{}
1965
+ var thisMii = {
1966
+ general: {},
1967
+ perms: {},
1968
+ meta: {},
1969
+ face: {},
1970
+ nose: {},
1971
+ mouth: {},
1972
+ mole: {},
1973
+ hair: {},
1974
+ eyebrows: {},
1975
+ eyes: {},
1976
+ glasses: {},
1977
+ beard: {
1978
+ mustache: {}
2310
1979
  }
2311
1980
  };
2312
1981
 
2313
1982
  const get = address => getBinaryFromAddress(address, data);
2314
1983
 
2315
- var name="";
2316
- for(var i=0;i<10;i++){
2317
- name+=data.slice(3+i*2, 4+i*2)+"";
1984
+ var name = "";
1985
+ for (var i = 0; i < 10; i++) {
1986
+ name += data.slice(3 + i * 2, 4 + i * 2) + "";
2318
1987
  }
2319
- thisMii.meta.name=name.replaceAll("\x00","");
2320
- var cname="";
2321
- for(var i=0;i<10;i++){
2322
- cname+=data.slice(55+i*2, 56+i*2)+"";
1988
+ thisMii.meta.name = name.replaceAll("\x00", "");
1989
+ var cname = "";
1990
+ for (var i = 0; i < 10; i++) {
1991
+ cname += data.slice(55 + i * 2, 56 + i * 2) + "";
2323
1992
  }
2324
- thisMii.meta.creatorName=cname.replaceAll("\x00","");
2325
- thisMii.general.gender=+get(0x00)[1];//0 for Male, 1 for Female
2326
- 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);
2327
- switch(thisMii.meta.miiId.slice(0,3)){
1993
+ thisMii.meta.creatorName = cname.replaceAll("\x00", "");
1994
+ thisMii.general.gender = +get(0x00)[1];//0 for Male, 1 for Female
1995
+ 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);
1996
+ switch (thisMii.meta.miiId.slice(0, 3)) {
2328
1997
  case "010":
2329
- thisMii.meta.type="Special";
2330
- break;
1998
+ thisMii.meta.type = "Special";
1999
+ break;
2331
2000
  case "110":
2332
- thisMii.meta.type="Foreign";
2333
- break;
2001
+ thisMii.meta.type = "Foreign";
2002
+ break;
2334
2003
  default:
2335
- thisMii.meta.type="Default";
2336
- break;
2337
- }
2338
- 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);
2339
- var temp=get(0x20);
2340
- thisMii.face.type=parseInt(temp.slice(0,3),2);//0-7
2341
- thisMii.face.color=parseInt(temp.slice(3,6),2);//0-5
2342
- temp=get(0x21);
2343
- thisMii.face.feature=parseInt(get(0x20).slice(6,8)+temp.slice(0,2),2);//0-11
2344
- thisMii.perms.mingle=temp[5]==="0";//0 for Mingle, 1 for Don't Mingle
2345
- temp=get(0x2C);
2346
- thisMii.nose.type=+getKeyByValue(lookupTables.wiiNoses,parseInt(temp.slice(0,4),2));
2347
- thisMii.nose.size=parseInt(temp.slice(4,8),2);
2348
- thisMii.nose.yPosition=parseInt(get(0x2D).slice(0,5),2);//From top to bottom, 0-18, default 9
2349
- temp=get(0x2E);
2350
- thisMii.mouth.page=+lookupTables.mouthTable[""+parseInt(temp.slice(0,5),2)][0]-1;
2351
- 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
2352
- thisMii.mouth.color=parseInt(temp.slice(5,7),2);//0-2, refer to mouthColors array
2353
- temp2=get(0x2F);
2354
- thisMii.mouth.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-8, default 4
2355
- thisMii.mouth.yPosition=parseInt(temp2.slice(3,8),2);//0-18, default 9, from top to bottom
2356
- temp=get(0x00);
2357
- var temp2=get(0x01);
2358
- thisMii.general.birthMonth=parseInt(temp.slice(2,6),2);
2359
- thisMii.general.birthday=parseInt(temp.slice(6,8)+temp2.slice(0,3),2);
2360
- thisMii.general.favoriteColor=parseInt(temp2.slice(3,7),2);//0-11, refer to cols array
2361
- thisMii.general.height=parseInt(get(0x16),2);//0-127
2362
- thisMii.general.weight=parseInt(get(0x17),2);//0-127
2363
- thisMii.perms.fromCheckMiiOut=get(0x21)[7]==="0"?false:true;
2364
- temp=get(0x34);
2365
- temp2=get(0x35);
2366
- thisMii.mole.on=temp[0]==="0"?false:true;//0 for Off, 1 for On
2367
- thisMii.mole.size=parseInt(temp.slice(1,5),2);//0-8, default 4
2368
- thisMii.mole.xPosition=parseInt(temp2.slice(2,7),2);//0-16, Default 2
2369
- thisMii.mole.yPosition=parseInt(temp.slice(5,8)+temp2.slice(0,2),2);//Top to bottom
2370
- temp=get(0x22);
2371
- temp2=get(0x23);
2372
- thisMii.hair.page=+lookupTables.hairTable[""+parseInt(temp.slice(0,7),2)][0]-1;
2373
- 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
2374
- thisMii.hair.color=parseInt(temp[7]+temp2.slice(0,2),2);//0-7, refer to hairCols array
2375
- thisMii.hair.flipped=temp2[2]==="0"?false:true;
2376
- temp=get(0x24);
2377
- temp2=get(0x25);
2378
- thisMii.eyebrows.page=+lookupTables.eyebrowTable[""+parseInt(temp.slice(0,5),2)][0]-1;
2379
- 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
2380
- thisMii.eyebrows.rotation=parseInt(temp.slice(6,8)+temp2.slice(0,2),2);//0-11, default varies based on eyebrow type
2381
- temp=get(0x26);
2382
- temp2=get(0x27);
2383
- thisMii.eyebrows.color=parseInt(temp.slice(0,3),2);
2384
- thisMii.eyebrows.size=parseInt(temp.slice(3,7),2);//0-8, default 4
2385
- thisMii.eyebrows.yPosition=(parseInt(temp[7]+temp2.slice(0,4),2))-3;//0-15, default 10
2386
- thisMii.eyebrows.distanceApart=parseInt(temp2.slice(4,8),2);//0-12, default 2
2387
- thisMii.eyes.page=+lookupTables.eyeTable[parseInt(get(0x28).slice(0,6),2)][0]-1;//0-47, needs lookup table
2388
- 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
2389
- temp=get(0x29);
2390
- thisMii.eyes.rotation=parseInt(temp.slice(0,3),2);//0-7, default varies based on eye type
2391
- thisMii.eyes.yPosition=parseInt(temp.slice(3,8),2);//0-18, default 12, top to bottom
2392
- temp=get(0x2A);
2393
- thisMii.eyes.color=parseInt(temp.slice(0,3),2);//0-5
2394
- thisMii.eyes.size=parseInt(temp.slice(4,7),2);//0-7, default 4
2395
- temp2=get(0x2B);
2396
- thisMii.eyes.distanceApart=parseInt(temp[7]+temp2.slice(0,3),2);//0-12, default 2
2397
- temp=get(0x30);
2398
- thisMii.glasses.type=parseInt(temp.slice(0,4),2);//0-8
2399
- thisMii.glasses.color=parseInt(temp.slice(4,7),2);//0-5
2400
- temp=get(0x31);
2401
- thisMii.glasses.size=parseInt(temp.slice(0,3),2);//0-7, default 4
2402
- thisMii.glasses.yPosition=parseInt(temp.slice(3,8),2);//0-20, default 10
2403
- temp=get(0x32);
2404
- temp2=get(0x33);
2405
- thisMii.beard.mustache.type=parseInt(temp.slice(0,2),2);//0-3
2406
- thisMii.beard.type=parseInt(temp.slice(2,4),2);//0-3
2407
- thisMii.beard.color=parseInt(temp.slice(4,7),2);//0-7
2408
- thisMii.beard.mustache.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-30, default 20
2409
- thisMii.beard.mustache.yPosition=parseInt(temp2.slice(3,8),2);//0-16, default 2
2410
- thisMii.console="Wii";
2004
+ thisMii.meta.type = "Default";
2005
+ break;
2006
+ }
2007
+ 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);
2008
+ var temp = get(0x20);
2009
+ thisMii.face.type = parseInt(temp.slice(0, 3), 2);//0-7
2010
+ thisMii.face.color = parseInt(temp.slice(3, 6), 2);//0-5
2011
+ temp = get(0x21);
2012
+ thisMii.face.feature = parseInt(get(0x20).slice(6, 8) + temp.slice(0, 2), 2);//0-11
2013
+ thisMii.perms.mingle = temp[5] === "0";//0 for Mingle, 1 for Don't Mingle
2014
+ temp = get(0x2C);
2015
+ thisMii.nose.type = +getKeyByValue(lookupTables.wiiNoses, parseInt(temp.slice(0, 4), 2));
2016
+ thisMii.nose.size = parseInt(temp.slice(4, 8), 2);
2017
+ thisMii.nose.yPosition = parseInt(get(0x2D).slice(0, 5), 2);//From top to bottom, 0-18, default 9
2018
+ temp = get(0x2E);
2019
+ thisMii.mouth.page = +lookupTables.mouthTable["" + parseInt(temp.slice(0, 5), 2)][0] - 1;
2020
+ 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
2021
+ thisMii.mouth.color = parseInt(temp.slice(5, 7), 2);//0-2, refer to mouthColors array
2022
+ temp2 = get(0x2F);
2023
+ thisMii.mouth.size = parseInt(temp[7] + temp2.slice(0, 3), 2);//0-8, default 4
2024
+ thisMii.mouth.yPosition = parseInt(temp2.slice(3, 8), 2);//0-18, default 9, from top to bottom
2025
+ temp = get(0x00);
2026
+ var temp2 = get(0x01);
2027
+ thisMii.general.birthMonth = parseInt(temp.slice(2, 6), 2);
2028
+ thisMii.general.birthday = parseInt(temp.slice(6, 8) + temp2.slice(0, 3), 2);
2029
+ thisMii.general.favoriteColor = parseInt(temp2.slice(3, 7), 2);//0-11, refer to cols array
2030
+ thisMii.general.height = parseInt(get(0x16), 2);//0-127
2031
+ thisMii.general.weight = parseInt(get(0x17), 2);//0-127
2032
+ thisMii.perms.fromCheckMiiOut = get(0x21)[7] === "0" ? false : true;
2033
+ temp = get(0x34);
2034
+ temp2 = get(0x35);
2035
+ thisMii.mole.on = temp[0] === "0" ? false : true;//0 for Off, 1 for On
2036
+ thisMii.mole.size = parseInt(temp.slice(1, 5), 2);//0-8, default 4
2037
+ thisMii.mole.xPosition = parseInt(temp2.slice(2, 7), 2);//0-16, Default 2
2038
+ thisMii.mole.yPosition = parseInt(temp.slice(5, 8) + temp2.slice(0, 2), 2);//Top to bottom
2039
+ temp = get(0x22);
2040
+ temp2 = get(0x23);
2041
+ thisMii.hair.page = +lookupTables.hairTable["" + parseInt(temp.slice(0, 7), 2)][0] - 1;
2042
+ 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
2043
+ thisMii.hair.color = parseInt(temp[7] + temp2.slice(0, 2), 2);//0-7, refer to hairCols array
2044
+ thisMii.hair.flipped = temp2[2] === "0" ? false : true;
2045
+ temp = get(0x24);
2046
+ temp2 = get(0x25);
2047
+ thisMii.eyebrows.page = +lookupTables.eyebrowTable["" + parseInt(temp.slice(0, 5), 2)][0] - 1;
2048
+ 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
2049
+ thisMii.eyebrows.rotation = parseInt(temp.slice(6, 8) + temp2.slice(0, 2), 2);//0-11, default varies based on eyebrow type
2050
+ temp = get(0x26);
2051
+ temp2 = get(0x27);
2052
+ thisMii.eyebrows.color = parseInt(temp.slice(0, 3), 2);
2053
+ thisMii.eyebrows.size = parseInt(temp.slice(3, 7), 2);//0-8, default 4
2054
+ thisMii.eyebrows.yPosition = (parseInt(temp[7] + temp2.slice(0, 4), 2)) - 3;//0-15, default 10
2055
+ thisMii.eyebrows.distanceApart = parseInt(temp2.slice(4, 8), 2);//0-12, default 2
2056
+ thisMii.eyes.page = +lookupTables.eyeTable[parseInt(get(0x28).slice(0, 6), 2)][0] - 1;//0-47, needs lookup table
2057
+ 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
2058
+ temp = get(0x29);
2059
+ thisMii.eyes.rotation = parseInt(temp.slice(0, 3), 2);//0-7, default varies based on eye type
2060
+ thisMii.eyes.yPosition = parseInt(temp.slice(3, 8), 2);//0-18, default 12, top to bottom
2061
+ temp = get(0x2A);
2062
+ thisMii.eyes.color = parseInt(temp.slice(0, 3), 2);//0-5
2063
+ thisMii.eyes.size = parseInt(temp.slice(4, 7), 2);//0-7, default 4
2064
+ temp2 = get(0x2B);
2065
+ thisMii.eyes.distanceApart = parseInt(temp[7] + temp2.slice(0, 3), 2);//0-12, default 2
2066
+ temp = get(0x30);
2067
+ thisMii.glasses.type = parseInt(temp.slice(0, 4), 2);//0-8
2068
+ thisMii.glasses.color = parseInt(temp.slice(4, 7), 2);//0-5
2069
+ temp = get(0x31);
2070
+ thisMii.glasses.size = parseInt(temp.slice(0, 3), 2);//0-7, default 4
2071
+ thisMii.glasses.yPosition = parseInt(temp.slice(3, 8), 2);//0-20, default 10
2072
+ temp = get(0x32);
2073
+ temp2 = get(0x33);
2074
+ thisMii.beard.mustache.type = parseInt(temp.slice(0, 2), 2);//0-3
2075
+ thisMii.beard.type = parseInt(temp.slice(2, 4), 2);//0-3
2076
+ thisMii.beard.color = parseInt(temp.slice(4, 7), 2);//0-7
2077
+ thisMii.beard.mustache.size = parseInt(temp[7] + temp2.slice(0, 3), 2);//0-30, default 20
2078
+ thisMii.beard.mustache.yPosition = parseInt(temp2.slice(3, 8), 2);//0-16, default 2
2079
+ thisMii.console = "Wii";
2411
2080
  return thisMii;
2412
2081
  }
2413
- async function read3DSQR(binOrPath,returnDecryptedBin) {
2082
+ function decode3DSMii(data) {
2083
+ const miiJson = {
2084
+ general: {},
2085
+ perms: {},
2086
+ meta: {},
2087
+ face: {},
2088
+ nose: {},
2089
+ mouth: {},
2090
+ mole: {},
2091
+ hair: {},
2092
+ eyebrows: {},
2093
+ eyes: {},
2094
+ glasses: {},
2095
+ beard: {
2096
+ mustache: {}
2097
+ }
2098
+ };
2099
+ const get = address => getBinaryFromAddress(address, data);
2100
+ var temp = get(0x18);
2101
+ var temp2 = get(0x19);
2102
+ miiJson.general.birthday = parseInt(temp2.slice(6, 8) + temp.slice(0, 3), 2);
2103
+ miiJson.general.birthMonth = parseInt(temp.slice(3, 7), 2);
2104
+ //Handle UTF-16 Names
2105
+ var name = "";
2106
+ for (var i = 0x1A; i < 0x2E; i += 2) {
2107
+ let lo = data[i];
2108
+ let hi = data[i + 1];
2109
+ if (lo === 0x00 && hi === 0x00) {
2110
+ break;
2111
+ }
2112
+ let codeUnit = (hi << 8) | lo;
2113
+ name += String.fromCharCode(codeUnit);
2114
+ }
2115
+ miiJson.meta.name = name.replace(/\u0000/g, "");
2116
+ var cname = "";
2117
+ for (var i = 0x48; i < 0x5C; i += 2) {
2118
+ let lo = data[i];
2119
+ let hi = data[i + 1];
2120
+ if (lo === 0x00 && hi === 0x00) {
2121
+ break;
2122
+ }
2123
+ let codeUnit = (hi << 8) | lo;
2124
+ cname += String.fromCharCode(codeUnit);
2125
+ }
2126
+ miiJson.meta.creatorName = cname.replace(/\u0000/g, "");
2127
+ miiJson.general.height = parseInt(get(0x2E), 2);
2128
+ miiJson.general.weight = parseInt(get(0x2F), 2);
2129
+ miiJson.general.gender = +temp[7];
2130
+ temp = get(0x30);
2131
+ miiJson.perms.sharing = temp[7] === "1" ? false : true;
2132
+ miiJson.general.favoriteColor = parseInt(temp2.slice(2, 6), 2);
2133
+ miiJson.perms.copying = get(0x01)[7] === "1" ? true : false;
2134
+ miiJson.hair.page = lookupTable("hairs", parseInt(get(0x32), 2), true)[0];
2135
+ miiJson.hair.type = lookupTable("hairs", parseInt(get(0x32), 2), true)[1];
2136
+ miiJson.face.type = lookupTable("faces", parseInt(temp.slice(3, 7), 2), false);
2137
+ miiJson.face.color = parseInt(temp.slice(0, 3), 2);
2138
+ temp = get(0x31);
2139
+ miiJson.face.feature = parseInt(temp.slice(4, 8), 2);
2140
+ miiJson.face.makeup = parseInt(temp.slice(0, 4), 2);
2141
+ temp = get(0x34);
2142
+ miiJson.eyes.page = lookupTable("eyes", parseInt(temp.slice(2, 8), 2), true)[0];
2143
+ miiJson.eyes.type = lookupTable("eyes", parseInt(temp.slice(2, 8), 2), true)[1];
2144
+ temp2 = get(0x33);
2145
+ miiJson.hair.color = parseInt(temp2.slice(5, 8), 2);
2146
+ miiJson.hair.flipped = temp2[4] === "0" ? false : true;
2147
+ miiJson.eyes.color = parseInt(get(0x35)[7] + temp.slice(0, 2), 2);
2148
+ temp = get(0x35);
2149
+ miiJson.eyes.size = parseInt(temp.slice(3, 7), 2);
2150
+ miiJson.eyes.squash = parseInt(temp.slice(0, 3), 2);
2151
+ temp = get(0x36);
2152
+ temp2 = get(0x37);
2153
+ miiJson.eyes.rotation = parseInt(temp.slice(3, 8), 2);
2154
+ miiJson.eyes.distanceApart = parseInt(temp2[7] + temp.slice(0, 3), 2);
2155
+ miiJson.eyes.yPosition = parseInt(temp2.slice(2, 7), 2);
2156
+ temp = get(0x38);
2157
+ miiJson.eyebrows.page = lookupTable("eyebrows", parseInt(temp.slice(3, 8), 2), true)[0];
2158
+ miiJson.eyebrows.type = lookupTable("eyebrows", parseInt(temp.slice(3, 8), 2), true)[1];
2159
+ miiJson.eyebrows.color = parseInt(temp.slice(0, 3), 2);
2160
+ temp = get(0x39);
2161
+ miiJson.eyebrows.size = parseInt(temp.slice(4, 8), 2);
2162
+ miiJson.eyebrows.squash = parseInt(temp.slice(1, 4), 2);
2163
+ temp = get(0x3A);
2164
+ miiJson.eyebrows.rotation = parseInt(temp.slice(4, 8), 2);
2165
+ temp2 = get(0x3B);
2166
+ miiJson.eyebrows.distanceApart = parseInt(temp2[7] + temp.slice(0, 3), 2);
2167
+ miiJson.eyebrows.yPosition = parseInt(temp2.slice(2, 7), 2) - 3;
2168
+ temp = get(0x3C);
2169
+ miiJson.nose.page = lookupTable("noses", parseInt(temp.slice(3, 8), 2), true)[0];
2170
+ miiJson.nose.type = lookupTable("noses", parseInt(temp.slice(3, 8), 2), true)[1];
2171
+ temp2 = get(0x3D);
2172
+ miiJson.nose.size = parseInt(temp2[7] + temp.slice(0, 3), 2);
2173
+ miiJson.nose.yPosition = parseInt(temp2.slice(2, 7), 2);
2174
+ temp = get(0x3E);
2175
+ miiJson.mouth.page = lookupTable("mouths", parseInt(temp.slice(2, 8), 2), true)[0];
2176
+ miiJson.mouth.type = lookupTable("mouths", parseInt(temp.slice(2, 8), 2), true)[1];
2177
+ temp2 = get(0x3F);
2178
+ miiJson.mouth.color = parseInt(temp2[7] + temp.slice(0, 2), 2);
2179
+ miiJson.mouth.size = parseInt(temp2.slice(3, 7), 2);
2180
+ miiJson.mouth.squash = parseInt(temp2.slice(0, 3), 2);
2181
+ temp = get(0x40);
2182
+ miiJson.mouth.yPosition = parseInt(temp.slice(3, 8), 2);
2183
+ miiJson.beard.mustache.type = parseInt(temp.slice(0, 3), 2);
2184
+ temp = get(0x42);
2185
+ miiJson.beard.type = parseInt(temp.slice(5, 8), 2);
2186
+ miiJson.beard.color = parseInt(temp.slice(2, 5), 2);
2187
+ temp2 = get(0x43);
2188
+ miiJson.beard.mustache.size = parseInt(temp2.slice(6, 8) + temp.slice(0, 2), 2);
2189
+ miiJson.beard.mustache.yPosition = parseInt(temp2.slice(1, 6), 2);
2190
+ temp = get(0x44);
2191
+ miiJson.glasses.type = parseInt(temp.slice(4, 8), 2);
2192
+ miiJson.glasses.color = parseInt(temp.slice(1, 4), 2);
2193
+ temp2 = get(0x45);
2194
+ miiJson.glasses.size = parseInt(temp2.slice(5, 8) + temp[0], 2);
2195
+ miiJson.glasses.yPosition = parseInt(temp2.slice(0, 5), 2);
2196
+ temp = get(0x46);
2197
+ miiJson.mole.on = temp[7] === "0" ? false : true;
2198
+ miiJson.mole.size = parseInt(temp.slice(3, 7), 2);
2199
+ temp2 = get(0x47);
2200
+ miiJson.mole.xPosition = parseInt(temp2.slice(6, 8) + temp.slice(0, 3), 2);
2201
+ miiJson.mole.yPosition = parseInt(temp2.slice(1, 6), 2);
2202
+ miiJson.meta.type = "Default";//qk, Make this actually retrieve MiiID, SystemID, and Mii type
2203
+ miiJson.console = "3DS";
2204
+ return miiJson;
2205
+ }
2206
+ async function read3DSQR(binOrPath, returnDecryptedBin) {
2414
2207
  let qrCode;
2415
- if (/[^01]/ig.test(binOrPath)) {
2208
+ if (Buffer.isBuffer(binOrPath)) {//Buffer
2209
+ qrCode = binOrPath;
2210
+ }
2211
+ else if (/[^01]/ig.test(binOrPath)) {//File path
2416
2212
  var data = await fs.promises.readFile(binOrPath);
2417
2213
  var img = await loadImage(data);
2418
2214
  const canvas = createCanvas(img.width, img.height);
@@ -2420,12 +2216,12 @@ async function read3DSQR(binOrPath,returnDecryptedBin) {
2420
2216
  ctx.drawImage(img, 0, 0);
2421
2217
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
2422
2218
  qrCode = jsQR(imageData.data, imageData.width, imageData.height)?.binaryData;
2423
- if(!qrCode){
2219
+ if (!qrCode) {
2424
2220
  console.error("Failed to read QR Code.");
2425
2221
  return;
2426
2222
  }
2427
2223
  }
2428
- else {
2224
+ else {//String of 0s and 1s
2429
2225
  var d = binOrPath.match(/(0|1){1,8}/g);
2430
2226
  qrCode = [];
2431
2227
  d.forEach(byte => {
@@ -2433,141 +2229,30 @@ async function read3DSQR(binOrPath,returnDecryptedBin) {
2433
2229
  });
2434
2230
  }
2435
2231
  if (qrCode) {
2436
- var data = Buffer.from(decodeAesCcm(new Uint8Array(qrCode)));
2437
- if(returnDecryptedBin){
2232
+ var data;
2233
+ data = Buffer.from(decodeAesCcm(new Uint8Array(qrCode)));
2234
+ if (returnDecryptedBin) {
2438
2235
  return data;
2439
2236
  }
2440
- const miiJson = {
2441
- general:{},
2442
- perms:{},
2443
- meta:{},
2444
- face:{},
2445
- nose:{},
2446
- mouth:{},
2447
- mole:{},
2448
- hair:{},
2449
- eyebrows:{},
2450
- eyes:{},
2451
- glasses:{},
2452
- beard:{
2453
- mustache:{}
2454
- }
2455
- };
2456
- const get = address => getBinaryFromAddress(address, data);
2457
- var temp=get(0x18);
2458
- var temp2=get(0x19);
2459
- miiJson.general.birthday=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
2460
- miiJson.general.birthMonth=parseInt(temp.slice(3,7),2);
2461
- //Handle UTF-16 Names
2462
- var name = "";
2463
- for (var i = 0x1A; i < 0x2E; i += 2) {
2464
- let lo = data[i];
2465
- let hi = data[i + 1];
2466
- if (lo === 0x00 && hi === 0x00) {
2467
- break;
2468
- }
2469
- let codeUnit = (hi << 8) | lo;
2470
- name += String.fromCharCode(codeUnit);
2237
+
2238
+ var ret;
2239
+ try {
2240
+ ret = decode3DSMii(data);
2471
2241
  }
2472
- miiJson.meta.name = name.replace(/\u0000/g, "");
2473
- var cname = "";
2474
- for (var i = 0x48; i < 0x5C; i += 2) {
2475
- let lo = data[i];
2476
- let hi = data[i + 1];
2477
- if (lo === 0x00 && hi === 0x00) {
2478
- break;
2479
- }
2480
- let codeUnit = (hi << 8) | lo;
2481
- cname += String.fromCharCode(codeUnit);
2242
+ catch (e) {
2243
+ ret = decode3DSMii(qrCode);
2482
2244
  }
2483
- miiJson.meta.creatorName = cname.replace(/\u0000/g, "");
2484
- miiJson.general.height=parseInt(get(0x2E),2);
2485
- miiJson.general.weight=parseInt(get(0x2F),2);
2486
- miiJson.general.gender=+temp[7];
2487
- temp=get(0x30);
2488
- miiJson.perms.sharing=temp[7]==="1"?false:true;
2489
- miiJson.general.favoriteColor=parseInt(temp2.slice(2,6),2);
2490
- miiJson.perms.copying=get(0x01)[7]==="1"?true:false;
2491
- miiJson.hair.page=lookupTable("hairs",parseInt(get(0x32),2),true)[0];
2492
- miiJson.hair.type=lookupTable("hairs",parseInt(get(0x32),2),true)[1];
2493
- miiJson.face.type=lookupTable("faces",parseInt(temp.slice(3,7),2),false);
2494
- miiJson.face.color=parseInt(temp.slice(0,3),2);
2495
- temp=get(0x31);
2496
- miiJson.face.feature=parseInt(temp.slice(4,8),2);
2497
- miiJson.face.makeup=parseInt(temp.slice(0,4),2);
2498
- temp=get(0x34);
2499
- miiJson.eyes.page=lookupTable("eyes",parseInt(temp.slice(2,8),2),true)[0];
2500
- miiJson.eyes.type=lookupTable("eyes",parseInt(temp.slice(2,8),2),true)[1];
2501
- temp2=get(0x33);
2502
- miiJson.hair.color=parseInt(temp2.slice(5,8),2);
2503
- miiJson.hair.flipped=temp2[4]==="0"?false:true;
2504
- miiJson.eyes.color=parseInt(get(0x35)[7]+temp.slice(0,2),2);
2505
- temp=get(0x35);
2506
- miiJson.eyes.size=parseInt(temp.slice(3,7),2);
2507
- miiJson.eyes.squash=parseInt(temp.slice(0,3),2);
2508
- temp=get(0x36);
2509
- temp2=get(0x37);
2510
- miiJson.eyes.rotation=parseInt(temp.slice(3,8),2);
2511
- miiJson.eyes.distanceApart=parseInt(temp2[7]+temp.slice(0,3),2);
2512
- miiJson.eyes.yPosition=parseInt(temp2.slice(2,7),2);
2513
- temp=get(0x38);
2514
- miiJson.eyebrows.page=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true)[0];
2515
- miiJson.eyebrows.type=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true)[1];
2516
- miiJson.eyebrows.color=parseInt(temp.slice(0,3),2);
2517
- temp=get(0x39);
2518
- miiJson.eyebrows.size=parseInt(temp.slice(4,8),2);
2519
- miiJson.eyebrows.squash=parseInt(temp.slice(1,4),2);
2520
- temp=get(0x3A);
2521
- miiJson.eyebrows.rotation=parseInt(temp.slice(4,8),2);
2522
- temp2=get(0x3B);
2523
- miiJson.eyebrows.distanceApart=parseInt(temp2[7]+temp.slice(0,3),2);
2524
- miiJson.eyebrows.yPosition=parseInt(temp2.slice(2,7),2)-3;
2525
- temp=get(0x3C);
2526
- miiJson.nose.page=lookupTable("noses",parseInt(temp.slice(3,8),2),true)[0];
2527
- miiJson.nose.type=lookupTable("noses",parseInt(temp.slice(3,8),2),true)[1];
2528
- temp2=get(0x3D);
2529
- miiJson.nose.size=parseInt(temp2[7]+temp.slice(0,3),2);
2530
- miiJson.nose.yPosition=parseInt(temp2.slice(2,7),2);
2531
- temp=get(0x3E);
2532
- miiJson.mouth.page=lookupTable("mouths",parseInt(temp.slice(2,8),2),true)[0];
2533
- miiJson.mouth.type=lookupTable("mouths",parseInt(temp.slice(2,8),2),true)[1];
2534
- temp2=get(0x3F);
2535
- miiJson.mouth.color=parseInt(temp2[7]+temp.slice(0,2),2);
2536
- miiJson.mouth.size=parseInt(temp2.slice(3,7),2);
2537
- miiJson.mouth.squash=parseInt(temp2.slice(0,3),2);
2538
- temp=get(0x40);
2539
- miiJson.mouth.yPosition=parseInt(temp.slice(3,8),2);
2540
- miiJson.beard.mustache.type=parseInt(temp.slice(0,3),2);
2541
- temp=get(0x42);
2542
- miiJson.beard.type=parseInt(temp.slice(5,8),2);
2543
- miiJson.beard.color=parseInt(temp.slice(2,5),2);
2544
- temp2=get(0x43);
2545
- miiJson.beard.mustache.size=parseInt(temp2.slice(6,8)+temp.slice(0,2),2);
2546
- miiJson.beard.mustache.yPosition=parseInt(temp2.slice(1,6),2);
2547
- temp=get(0x44);
2548
- miiJson.glasses.type=parseInt(temp.slice(4,8),2);
2549
- miiJson.glasses.color=parseInt(temp.slice(1,4),2);
2550
- temp2=get(0x45);
2551
- miiJson.glasses.size=parseInt(temp2.slice(5,8)+temp[0],2);
2552
- miiJson.glasses.yPosition=parseInt(temp2.slice(0,5),2);
2553
- temp=get(0x46);
2554
- miiJson.mole.on=temp[7]==="0"?false:true;
2555
- miiJson.mole.size=parseInt(temp.slice(3,7),2);
2556
- temp2=get(0x47);
2557
- miiJson.mole.xPosition=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
2558
- miiJson.mole.yPosition=parseInt(temp2.slice(1,6),2);
2559
- miiJson.meta.type="Default";//qk, Make this actually retrieve MiiID, SystemID, and Mii type
2560
- miiJson.console="3DS";
2561
- return miiJson;
2562
- } else {
2245
+ return ret;
2246
+ }
2247
+ else {
2563
2248
  console.error('Failed to read Mii.');
2564
2249
  }
2565
2250
  }
2566
- async function renderMiiWithStudio(jsonIn){
2567
- if(!["3ds","wii u"].includes(jsonIn.console?.toLowerCase())){
2568
- jsonIn=convertMii(jsonIn);
2251
+ async function renderMiiWithStudio(jsonIn) {
2252
+ if (!["3ds", "wii u"].includes(jsonIn.console?.toLowerCase())) {
2253
+ jsonIn = convertMii(jsonIn);
2569
2254
  }
2570
- var studioMii=convertMiiToStudio(jsonIn);
2255
+ var studioMii = convertMiiToStudio(jsonIn);
2571
2256
  return await downloadImage('https://studio.mii.nintendo.com/miis/image.png?data=' + studioMii + "&width=270&type=face");
2572
2257
  }
2573
2258
  async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes) {
@@ -2593,8 +2278,8 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2593
2278
  // Create a dummy canvas for Three.js to use.
2594
2279
  const canvas = {
2595
2280
  width, height, style: {},
2596
- addEventListener() {},
2597
- removeEventListener() {},
2281
+ addEventListener() { },
2282
+ removeEventListener() { },
2598
2283
  // Return the context for 'webgl' (not webgl2)
2599
2284
  getContext: (type, _) => type === 'webgl' ? gl : null,
2600
2285
  };
@@ -2612,7 +2297,7 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2612
2297
  const scene = new THREE.Scene();
2613
2298
  scene.background = null; // Transparent background.
2614
2299
 
2615
- if(useBody){
2300
+ if (useBody) {
2616
2301
  // After: const scene = new THREE.Scene(); scene.background = null;
2617
2302
  const ambient = new THREE.AmbientLight(0xffffff, 0.15);
2618
2303
  scene.add(ambient);
@@ -2633,16 +2318,16 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2633
2318
 
2634
2319
  // Create Mii model and add to the scene.
2635
2320
  const studioRaw = parseHexOrB64ToUint8Array(data); // Parse studio data
2636
-
2321
+
2637
2322
  // Convert Uint8Array to Buffer for struct-fu compatibility
2638
2323
  const studioBuffer = Buffer.from(studioRaw);
2639
-
2324
+
2640
2325
  currentCharModel = createCharModel(studioBuffer, null, FFLShaderMaterial, ffl.module);
2641
2326
  initCharModelTextures(currentCharModel, renderer); // Initialize fully
2642
2327
  scene.add(currentCharModel.meshes); // Add to scene
2643
2328
 
2644
2329
  //Add body
2645
- if (useBody) {
2330
+ if (useBody) {
2646
2331
  if (typeof GLTFLoader === 'undefined' || !GLTFLoader) {
2647
2332
  const mod = await import('three/examples/jsm/loaders/GLTFLoader.js');
2648
2333
  GLTFLoader = mod.GLTFLoader;
@@ -2663,20 +2348,20 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2663
2348
 
2664
2349
  const body = gltf.scene;
2665
2350
 
2666
- body.position.y-=110;
2351
+ body.position.y -= 110;
2667
2352
 
2668
2353
  //Recolor
2669
2354
  body.userData.isMiiBody = true;
2670
2355
  body.traverse(o => {
2671
2356
  if (o.isMesh) {
2672
2357
  if (!o.geometry.attributes.normal) {
2673
- o.geometry.computeVertexNormals();
2358
+ o.geometry.computeVertexNormals();
2674
2359
  }
2675
2360
  const isShirt = (o.name === 'mesh_1_');
2676
2361
  o.material?.dispose?.();
2677
2362
  o.material = new THREE.MeshLambertMaterial({
2678
2363
  //["Red", "Orange", "Yellow", "Lime", "Green", "Blue", "Cyan", "Pink", "Purple", "Brown", "White", "Black"]
2679
- color: isShirt ? [0xFF2400,0xF08000,0xFFD700,0xAAFF00,0x008000,0x0000FF,0x00D7FF,0xFF69B4,0x7F00FF,0x6F4E37,0xFFFFFF,0x303030][shirtColor] : 0x808080,
2364
+ color: isShirt ? [0xFF2400, 0xF08000, 0xFFD700, 0xAAFF00, 0x008000, 0x0000FF, 0x00D7FF, 0xFF69B4, 0x7F00FF, 0x6F4E37, 0xFFFFFF, 0x303030][shirtColor] : 0x808080,
2680
2365
  emissive: isShirt ? 0x330000 : 0x222222,
2681
2366
  emissiveIntensity: 0.0
2682
2367
  });
@@ -2738,9 +2423,9 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2738
2423
  //}
2739
2424
  }
2740
2425
  }
2741
- async function renderMii(jsonIn, fflRes=getFFLRes()){
2742
- if(!["3ds","wii u"].includes(jsonIn.console?.toLowerCase())){
2743
- jsonIn=convertMii(jsonIn);
2426
+ async function renderMii(jsonIn, fflRes = getFFLRes()) {
2427
+ if (!["3ds", "wii u"].includes(jsonIn.console?.toLowerCase())) {
2428
+ jsonIn = convertMii(jsonIn);
2744
2429
  }
2745
2430
  const studioMii = convertMiiToStudio(jsonIn);
2746
2431
  const width = height = 600;
@@ -2751,153 +2436,153 @@ async function writeWiiBin(jsonIn, outPath) {
2751
2436
  if (jsonIn.console?.toLowerCase() !== "wii") {
2752
2437
  convertMii(jsonIn);
2753
2438
  }
2754
- var mii=jsonIn;
2755
- var miiBin="0";
2756
- miiBin+=mii.general.gender;
2757
- miiBin+=mii.general.birthMonth.toString(2).padStart(4,"0");
2758
- miiBin+=mii.general.birthday.toString(2).padStart(5,"0");
2759
- miiBin+=mii.general.favoriteColor.toString(2).padStart(4,"0");
2760
- miiBin+='0';
2761
- for(var i=0;i<10;i++){
2762
- if(i<mii.meta.name.length){
2763
- miiBin+=mii.meta.name.charCodeAt(i).toString(2).padStart(16,"0");
2439
+ var mii = jsonIn;
2440
+ var miiBin = "0";
2441
+ miiBin += mii.general.gender;
2442
+ miiBin += mii.general.birthMonth.toString(2).padStart(4, "0");
2443
+ miiBin += mii.general.birthday.toString(2).padStart(5, "0");
2444
+ miiBin += mii.general.favoriteColor.toString(2).padStart(4, "0");
2445
+ miiBin += '0';
2446
+ for (var i = 0; i < 10; i++) {
2447
+ if (i < mii.meta.name.length) {
2448
+ miiBin += mii.meta.name.charCodeAt(i).toString(2).padStart(16, "0");
2764
2449
  }
2765
- else{
2766
- miiBin+="0000000000000000";
2450
+ else {
2451
+ miiBin += "0000000000000000";
2767
2452
  }
2768
2453
  }
2769
- miiBin+=mii.general.height.toString(2).padStart(8,"0");
2770
- miiBin+=mii.general.weight.toString(2).padStart(8,"0");
2771
- let miiId="";
2772
- switch(mii.meta.type){
2454
+ miiBin += mii.general.height.toString(2).padStart(8, "0");
2455
+ miiBin += mii.general.weight.toString(2).padStart(8, "0");
2456
+ let miiId = "";
2457
+ switch (mii.meta.type) {
2773
2458
  case "Special":
2774
- miiId="01000110";
2775
- break;
2459
+ miiId = "01000110";
2460
+ break;
2776
2461
  case "Foreign":
2777
- miiId="11000110";
2778
- break;
2462
+ miiId = "11000110";
2463
+ break;
2779
2464
  default:
2780
- miiId="10001001";
2781
- break;
2782
- }
2783
- for(var i=0;i<3;i++){
2784
- miiId+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
2785
- }
2786
- miiBin+=miiId;
2787
- miiBin+="11111111".repeat(4);//System ID
2788
- miiBin+=mii.face.type.toString(2).padStart(3,"0");
2789
- miiBin+=mii.face.color.toString(2).padStart(3,"0");
2790
- miiBin+=mii.face.feature.toString(2).padStart(4,"0");
2791
- miiBin+="000";
2792
- if(mii.perms.mingle&&mii.meta.type.toLowerCase()==="special"){
2793
- mii.perms.mingle=false;
2465
+ miiId = "10001001";
2466
+ break;
2467
+ }
2468
+ for (var i = 0; i < 3; i++) {
2469
+ miiId += Math.floor(Math.random() * 255).toString(2).padStart(8, "0");
2470
+ }
2471
+ miiBin += miiId;
2472
+ miiBin += "11111111".repeat(4);//System ID
2473
+ miiBin += mii.face.type.toString(2).padStart(3, "0");
2474
+ miiBin += mii.face.color.toString(2).padStart(3, "0");
2475
+ miiBin += mii.face.feature.toString(2).padStart(4, "0");
2476
+ miiBin += "000";
2477
+ if (mii.perms.mingle && mii.meta.type.toLowerCase() === "special") {
2478
+ mii.perms.mingle = false;
2794
2479
  console.warn("A Special Mii cannot have Mingle on and still render on the Wii. Turned Mingle off in the output.");
2795
2480
  }
2796
- miiBin+=mii.perms.mingle?"0":"1";
2797
- miiBin+="0";
2798
- miiBin+=mii.perms.fromCheckMiiOut?"1":"0";
2799
- miiBin+=(+getKeyByValue(lookupTables.hairTable,`${mii.hair.page+1}${convTables.formatFrom[mii.hair.type]}`)).toString(2).padStart(7,"0");
2800
- miiBin+=mii.hair.color.toString(2).padStart(3,"0");
2801
- miiBin+=mii.hair.flipped?"1":"0";
2802
- miiBin+="00000";
2803
- miiBin+=(+getKeyByValue(lookupTables.eyebrowTable,`${mii.eyebrows.page+1}${convTables.formatFrom[mii.eyebrows.type]}`)).toString(2).padStart(5,"0");
2804
- miiBin+="0";
2805
- miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
2806
- miiBin+="000000";
2807
- miiBin+=mii.eyebrows.color.toString(2).padStart(3,"0");
2808
- miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
2809
- miiBin+=(mii.eyebrows.yPosition+3).toString(2).padStart(5,"0");
2810
- miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0");
2811
- miiBin+=(+getKeyByValue(lookupTables.eyeTable,`${mii.eyes.page+1}${convTables.formatFrom[mii.eyes.type]}`)).toString(2).padStart(6,"0");
2812
- miiBin+="00";
2813
- miiBin+=mii.eyes.rotation.toString(2).padStart(3,"0");
2814
- miiBin+=mii.eyes.yPosition.toString(2).padStart(5,"0");
2815
- miiBin+=mii.eyes.color.toString(2).padStart(3,"0");
2816
- miiBin+="0";
2817
- miiBin+=mii.eyes.size.toString(2).padStart(3,"0");
2818
- miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0");
2819
- miiBin+="00000";
2820
- miiBin+=lookupTables.wiiNoses[mii.nose.type].toString(2).padStart(4,"0");
2821
- miiBin+=mii.nose.size.toString(2).padStart(4,"0");
2822
- miiBin+=mii.nose.yPosition.toString(2).padStart(5,"0");
2823
- miiBin+="000";
2824
- miiBin+=(+getKeyByValue(lookupTables.mouthTable,`${mii.mouth.page+1}${convTables.formatFrom[mii.mouth.type]}`)).toString(2).padStart(5,"0");
2825
- miiBin+=mii.mouth.color.toString(2).padStart(2,"0");
2826
- miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
2827
- miiBin+=mii.mouth.yPosition.toString(2).padStart(5,"0");
2828
- miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
2829
- miiBin+=mii.glasses.color.toString(2).padStart(3,"0");
2830
- miiBin+="0";
2831
- miiBin+=mii.glasses.size.toString(2).padStart(3,"0");
2832
- miiBin+=mii.glasses.yPosition.toString(2).padStart(5,"0");
2833
- miiBin+=mii.beard.mustache.type.toString(2).padStart(2,"0");
2834
- miiBin+=mii.beard.type.toString(2).padStart(2,"0");
2835
- miiBin+=mii.beard.color.toString(2).padStart(3,"0");
2836
- miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0");
2837
- miiBin+=mii.beard.mustache.yPosition.toString(2).padStart(5,"0");
2838
- miiBin+=mii.mole.on?"1":"0";
2839
- miiBin+=mii.mole.size.toString(2).padStart(4,"0");
2840
- miiBin+=mii.mole.yPosition.toString(2).padStart(5,"0");
2841
- miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0");
2842
- miiBin+="0";
2843
- for(var i=0;i<10;i++){
2844
- if(i<mii.meta.creatorName.length){
2845
- miiBin+=mii.meta.creatorName.charCodeAt(i).toString(2).padStart(16,"0");
2481
+ miiBin += mii.perms.mingle ? "0" : "1";
2482
+ miiBin += "0";
2483
+ miiBin += mii.perms.fromCheckMiiOut ? "1" : "0";
2484
+ miiBin += (+getKeyByValue(lookupTables.hairTable, `${mii.hair.page + 1}${convTables.formatFrom[mii.hair.type]}`)).toString(2).padStart(7, "0");
2485
+ miiBin += mii.hair.color.toString(2).padStart(3, "0");
2486
+ miiBin += mii.hair.flipped ? "1" : "0";
2487
+ miiBin += "00000";
2488
+ miiBin += (+getKeyByValue(lookupTables.eyebrowTable, `${mii.eyebrows.page + 1}${convTables.formatFrom[mii.eyebrows.type]}`)).toString(2).padStart(5, "0");
2489
+ miiBin += "0";
2490
+ miiBin += mii.eyebrows.rotation.toString(2).padStart(4, "0");
2491
+ miiBin += "000000";
2492
+ miiBin += mii.eyebrows.color.toString(2).padStart(3, "0");
2493
+ miiBin += mii.eyebrows.size.toString(2).padStart(4, "0");
2494
+ miiBin += (mii.eyebrows.yPosition + 3).toString(2).padStart(5, "0");
2495
+ miiBin += mii.eyebrows.distanceApart.toString(2).padStart(4, "0");
2496
+ miiBin += (+getKeyByValue(lookupTables.eyeTable, `${mii.eyes.page + 1}${convTables.formatFrom[mii.eyes.type]}`)).toString(2).padStart(6, "0");
2497
+ miiBin += "00";
2498
+ miiBin += mii.eyes.rotation.toString(2).padStart(3, "0");
2499
+ miiBin += mii.eyes.yPosition.toString(2).padStart(5, "0");
2500
+ miiBin += mii.eyes.color.toString(2).padStart(3, "0");
2501
+ miiBin += "0";
2502
+ miiBin += mii.eyes.size.toString(2).padStart(3, "0");
2503
+ miiBin += mii.eyes.distanceApart.toString(2).padStart(4, "0");
2504
+ miiBin += "00000";
2505
+ miiBin += lookupTables.wiiNoses[mii.nose.type].toString(2).padStart(4, "0");
2506
+ miiBin += mii.nose.size.toString(2).padStart(4, "0");
2507
+ miiBin += mii.nose.yPosition.toString(2).padStart(5, "0");
2508
+ miiBin += "000";
2509
+ miiBin += (+getKeyByValue(lookupTables.mouthTable, `${mii.mouth.page + 1}${convTables.formatFrom[mii.mouth.type]}`)).toString(2).padStart(5, "0");
2510
+ miiBin += mii.mouth.color.toString(2).padStart(2, "0");
2511
+ miiBin += mii.mouth.size.toString(2).padStart(4, "0");
2512
+ miiBin += mii.mouth.yPosition.toString(2).padStart(5, "0");
2513
+ miiBin += mii.glasses.type.toString(2).padStart(4, "0");
2514
+ miiBin += mii.glasses.color.toString(2).padStart(3, "0");
2515
+ miiBin += "0";
2516
+ miiBin += mii.glasses.size.toString(2).padStart(3, "0");
2517
+ miiBin += mii.glasses.yPosition.toString(2).padStart(5, "0");
2518
+ miiBin += mii.beard.mustache.type.toString(2).padStart(2, "0");
2519
+ miiBin += mii.beard.type.toString(2).padStart(2, "0");
2520
+ miiBin += mii.beard.color.toString(2).padStart(3, "0");
2521
+ miiBin += mii.beard.mustache.size.toString(2).padStart(4, "0");
2522
+ miiBin += mii.beard.mustache.yPosition.toString(2).padStart(5, "0");
2523
+ miiBin += mii.mole.on ? "1" : "0";
2524
+ miiBin += mii.mole.size.toString(2).padStart(4, "0");
2525
+ miiBin += mii.mole.yPosition.toString(2).padStart(5, "0");
2526
+ miiBin += mii.mole.xPosition.toString(2).padStart(5, "0");
2527
+ miiBin += "0";
2528
+ for (var i = 0; i < 10; i++) {
2529
+ if (i < mii.meta.creatorName.length) {
2530
+ miiBin += mii.meta.creatorName.charCodeAt(i).toString(2).padStart(16, "0");
2846
2531
  }
2847
- else{
2848
- miiBin+="0000000000000000";
2532
+ else {
2533
+ miiBin += "0000000000000000";
2849
2534
  }
2850
2535
  }
2851
-
2536
+
2852
2537
  //Writing based on miiBin
2853
- var toWrite=miiBin.match(/.{1,8}/g);
2854
- var buffers=[];
2855
- for(var i=0;i<toWrite.length;i++){
2856
- buffers.push(parseInt(toWrite[i],2));
2538
+ var toWrite = miiBin.match(/.{1,8}/g);
2539
+ var buffers = [];
2540
+ for (var i = 0; i < toWrite.length; i++) {
2541
+ buffers.push(parseInt(toWrite[i], 2));
2857
2542
  }
2858
- toWrite=Buffer.from(buffers);
2859
- if(outPath){
2543
+ toWrite = Buffer.from(buffers);
2544
+ if (outPath) {
2860
2545
  await fs.promises.writeFile(outPath, toWrite);
2861
2546
  }
2862
- else{
2547
+ else {
2863
2548
  return toWrite;
2864
2549
  }
2865
2550
  }
2866
- async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2551
+ async function write3DSQR(miiJson, outPath, returnBin, fflRes = getFFLRes()) {
2867
2552
  //Convert the Mii if it isn't in 3DS format
2868
2553
  if (!["3ds", "wii u"].includes(miiJson.console?.toLowerCase())) {
2869
2554
  miiJson = convertMii(miiJson);
2870
2555
  }
2871
-
2556
+
2872
2557
  //Make the binary
2873
- var mii=miiJson;
2558
+ var mii = miiJson;
2874
2559
  var miiBin = "00000011";
2875
2560
  //If Special Miis are being used improperly, fix it and warn the user
2876
- if(mii.meta.type.toLowerCase()==="special"&&(mii.console.toLowerCase()==="wii u"||mii.console.toLowerCase()==="wiiu")){
2877
- mii.meta.type="Default";
2561
+ if (mii.meta.type.toLowerCase() === "special" && (mii.console.toLowerCase() === "wii u" || mii.console.toLowerCase() === "wiiu")) {
2562
+ mii.meta.type = "Default";
2878
2563
  console.warn("Wii Us do not work with Special Miis. Reverted to Default Mii.");
2879
2564
  }
2880
- if(mii.perms.sharing&&mii.meta.type==="Special"){
2881
- mii.perms.sharing=false;
2565
+ if (mii.perms.sharing && mii.meta.type === "Special") {
2566
+ mii.perms.sharing = false;
2882
2567
  console.warn("Cannot have Sharing enabled for Special Miis. Disabled Sharing in the output.");
2883
2568
  }
2884
- miiBin+="0000000";
2885
- miiBin+=mii.perms.copying?"1":"0";
2886
- miiBin+="00000000";
2887
- miiBin+="00110000";
2888
- miiBin+="1000101011010010000001101000011100011000110001100100011001100110010101100111111110111100000001110101110001000101011101100000001110100100010000000000000000000000".slice(0,8*8);
2889
- miiBin+=mii.meta.type==="Special"?"0":"1";
2890
- miiBin+="0000000";
2891
- for(var i=0;i<3;i++){
2892
- miiBin+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
2893
- }
2894
- miiBin+="0000000001000101011101100000001110100100010000000000000000000000";
2895
- miiBin+=mii.general.birthday.toString(2).padStart(5,"0").slice(2,5);
2896
- miiBin+=mii.general.birthMonth.toString(2).padStart(4,"0");
2897
- miiBin+=mii.general.gender;
2898
- miiBin+="00";
2899
- miiBin+=mii.general.favoriteColor.toString(2).padStart(4,"0");
2900
- miiBin+=mii.general.birthday.toString(2).padStart(5,"0").slice(0,2);
2569
+ miiBin += "0000000";
2570
+ miiBin += mii.perms.copying ? "1" : "0";
2571
+ miiBin += "00000000";
2572
+ miiBin += "00110000";
2573
+ miiBin += "1000101011010010000001101000011100011000110001100100011001100110010101100111111110111100000001110101110001000101011101100000001110100100010000000000000000000000".slice(0, 8 * 8);
2574
+ miiBin += mii.meta.type === "Special" ? "0" : "1";
2575
+ miiBin += "0000000";
2576
+ for (var i = 0; i < 3; i++) {
2577
+ miiBin += Math.floor(Math.random() * 255).toString(2).padStart(8, "0");
2578
+ }
2579
+ miiBin += "0000000001000101011101100000001110100100010000000000000000000000";
2580
+ miiBin += mii.general.birthday.toString(2).padStart(5, "0").slice(2, 5);
2581
+ miiBin += mii.general.birthMonth.toString(2).padStart(4, "0");
2582
+ miiBin += mii.general.gender;
2583
+ miiBin += "00";
2584
+ miiBin += mii.general.favoriteColor.toString(2).padStart(4, "0");
2585
+ miiBin += mii.general.birthday.toString(2).padStart(5, "0").slice(0, 2);
2901
2586
  for (var i = 0; i < 10; i++) {
2902
2587
  if (i < mii.meta.name.length) {
2903
2588
  let code = mii.meta.name.charCodeAt(i);
@@ -2908,69 +2593,69 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2908
2593
  miiBin += "0000000000000000";
2909
2594
  }
2910
2595
  }
2911
- miiBin+=mii.general.height.toString(2).padStart(8,"0");
2912
- miiBin+=mii.general.weight.toString(2).padStart(8,"0");
2913
- miiBin+=mii.face.color.toString(2).padStart(3,"0");
2914
- miiBin+=lookupTables.faces.values[mii.face.type].toString(2).padStart(4,"0");
2915
- miiBin+=mii.perms.sharing?"0":"1";
2916
- miiBin+=mii.face.makeup.toString(2).padStart(4,"0");
2917
- miiBin+=mii.face.feature.toString(2).padStart(4,"0");
2918
- miiBin+=lookupTables.hairs.values[mii.hair.page][mii.hair.type].toString(2).padStart(8,"0");
2919
- miiBin+="0000";
2920
- miiBin+=mii.hair.flipped?"1":"0";
2921
- miiBin+=mii.hair.color.toString(2).padStart(3,"0");
2922
- miiBin+=mii.eyes.color.toString(2).padStart(3,"0").slice(1,3);
2923
- miiBin+=lookupTables.eyes.values[mii.eyes.page][mii.eyes.type].toString(2).padStart(6,"0");
2924
- miiBin+=mii.eyes.squash.toString(2).padStart(3,"0");
2925
- miiBin+=mii.eyes.size.toString(2).padStart(4,"0");
2926
- miiBin+=mii.eyes.color.toString(2).padStart(3,"0")[0];
2927
- miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0").slice(1,4);
2928
- miiBin+=mii.eyes.rotation.toString(2).padStart(5,"0");
2929
- miiBin+="00";
2930
- miiBin+=mii.eyes.yPosition.toString(2).padStart(5,"0");
2931
- miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0")[0];
2932
- miiBin+=mii.eyebrows.color.toString(2).padStart(3,"0");
2933
- miiBin+=lookupTables.eyebrows.values[mii.eyebrows.page][mii.eyebrows.type].toString(2).padStart(5,"0");
2934
- miiBin+="0";
2935
- miiBin+=mii.eyebrows.squash.toString(2).padStart(3,"0");
2936
- miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
2937
- miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0").slice(1,4);
2938
- miiBin+="0";
2939
- miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
2940
- miiBin+="00";
2941
- miiBin+=(mii.eyebrows.yPosition+3).toString(2).padStart(5,"0");
2942
- miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0")[0];
2943
- miiBin+=mii.nose.size.toString(2).padStart(4,"0").slice(1,4);
2944
- miiBin+=lookupTables.noses.values[mii.nose.page][mii.nose.type].toString(2).padStart(5,"0");
2945
- miiBin+="00";
2946
- miiBin+=mii.nose.yPosition.toString(2).padStart(5,"0");
2947
- miiBin+=mii.nose.size.toString(2).padStart(4,"0")[0];
2948
- miiBin+=mii.mouth.color.toString(2).padStart(3,"0").slice(1,3);
2949
- miiBin+=lookupTables.mouths.values[mii.mouth.page][mii.mouth.type].toString(2).padStart(6,"0");
2950
- miiBin+=mii.mouth.squash.toString(2).padStart(3,"0");
2951
- miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
2952
- miiBin+=mii.mouth.color.toString(2).padStart(3,"0")[0];
2953
- miiBin+=mii.beard.mustache.type.toString(2).padStart(3,"0");
2954
- miiBin+=mii.mouth.yPosition.toString(2).padStart(5,"0");
2955
- miiBin+="00000000";
2956
- miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0").slice(2,4);
2957
- miiBin+=mii.beard.color.toString(2).padStart(3,"0");
2958
- miiBin+=mii.beard.type.toString(2).padStart(3,"0");
2959
- miiBin+="0";
2960
- miiBin+=mii.beard.mustache.yPosition.toString(2).padStart(5,"0");
2961
- miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0").slice(0,2);
2962
- miiBin+=mii.glasses.size.toString(2).padStart(4,"0")[3];
2963
- miiBin+=mii.glasses.color.toString(2).padStart(3,"0");
2964
- miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
2965
- miiBin+="0";
2966
- miiBin+=mii.glasses.yPosition.toString(2).padStart(4,"0");
2967
- miiBin+=mii.glasses.size.toString(2).padStart(4,"0").slice(0,3);
2968
- miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0").slice(2,5);
2969
- miiBin+=mii.mole.size.toString(2).padStart(4,"0");
2970
- miiBin+=mii.mole.on?"1":"0";
2971
- miiBin+="0";
2972
- miiBin+=mii.mole.yPosition.toString(2).padStart(5,"0");
2973
- miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0").slice(0,2);
2596
+ miiBin += mii.general.height.toString(2).padStart(8, "0");
2597
+ miiBin += mii.general.weight.toString(2).padStart(8, "0");
2598
+ miiBin += mii.face.color.toString(2).padStart(3, "0");
2599
+ miiBin += lookupTables.faces.values[mii.face.type].toString(2).padStart(4, "0");
2600
+ miiBin += mii.perms.sharing ? "0" : "1";
2601
+ miiBin += mii.face.makeup.toString(2).padStart(4, "0");
2602
+ miiBin += mii.face.feature.toString(2).padStart(4, "0");
2603
+ miiBin += lookupTables.hairs.values[mii.hair.page][mii.hair.type].toString(2).padStart(8, "0");
2604
+ miiBin += "0000";
2605
+ miiBin += mii.hair.flipped ? "1" : "0";
2606
+ miiBin += mii.hair.color.toString(2).padStart(3, "0");
2607
+ miiBin += mii.eyes.color.toString(2).padStart(3, "0").slice(1, 3);
2608
+ miiBin += lookupTables.eyes.values[mii.eyes.page][mii.eyes.type].toString(2).padStart(6, "0");
2609
+ miiBin += mii.eyes.squash.toString(2).padStart(3, "0");
2610
+ miiBin += mii.eyes.size.toString(2).padStart(4, "0");
2611
+ miiBin += mii.eyes.color.toString(2).padStart(3, "0")[0];
2612
+ miiBin += mii.eyes.distanceApart.toString(2).padStart(4, "0").slice(1, 4);
2613
+ miiBin += mii.eyes.rotation.toString(2).padStart(5, "0");
2614
+ miiBin += "00";
2615
+ miiBin += mii.eyes.yPosition.toString(2).padStart(5, "0");
2616
+ miiBin += mii.eyes.distanceApart.toString(2).padStart(4, "0")[0];
2617
+ miiBin += mii.eyebrows.color.toString(2).padStart(3, "0");
2618
+ miiBin += lookupTables.eyebrows.values[mii.eyebrows.page][mii.eyebrows.type].toString(2).padStart(5, "0");
2619
+ miiBin += "0";
2620
+ miiBin += mii.eyebrows.squash.toString(2).padStart(3, "0");
2621
+ miiBin += mii.eyebrows.size.toString(2).padStart(4, "0");
2622
+ miiBin += mii.eyebrows.distanceApart.toString(2).padStart(4, "0").slice(1, 4);
2623
+ miiBin += "0";
2624
+ miiBin += mii.eyebrows.rotation.toString(2).padStart(4, "0");
2625
+ miiBin += "00";
2626
+ miiBin += (mii.eyebrows.yPosition + 3).toString(2).padStart(5, "0");
2627
+ miiBin += mii.eyebrows.distanceApart.toString(2).padStart(4, "0")[0];
2628
+ miiBin += mii.nose.size.toString(2).padStart(4, "0").slice(1, 4);
2629
+ miiBin += lookupTables.noses.values[mii.nose.page][mii.nose.type].toString(2).padStart(5, "0");
2630
+ miiBin += "00";
2631
+ miiBin += mii.nose.yPosition.toString(2).padStart(5, "0");
2632
+ miiBin += mii.nose.size.toString(2).padStart(4, "0")[0];
2633
+ miiBin += mii.mouth.color.toString(2).padStart(3, "0").slice(1, 3);
2634
+ miiBin += lookupTables.mouths.values[mii.mouth.page][mii.mouth.type].toString(2).padStart(6, "0");
2635
+ miiBin += mii.mouth.squash.toString(2).padStart(3, "0");
2636
+ miiBin += mii.mouth.size.toString(2).padStart(4, "0");
2637
+ miiBin += mii.mouth.color.toString(2).padStart(3, "0")[0];
2638
+ miiBin += mii.beard.mustache.type.toString(2).padStart(3, "0");
2639
+ miiBin += mii.mouth.yPosition.toString(2).padStart(5, "0");
2640
+ miiBin += "00000000";
2641
+ miiBin += mii.beard.mustache.size.toString(2).padStart(4, "0").slice(2, 4);
2642
+ miiBin += mii.beard.color.toString(2).padStart(3, "0");
2643
+ miiBin += mii.beard.type.toString(2).padStart(3, "0");
2644
+ miiBin += "0";
2645
+ miiBin += mii.beard.mustache.yPosition.toString(2).padStart(5, "0");
2646
+ miiBin += mii.beard.mustache.size.toString(2).padStart(4, "0").slice(0, 2);
2647
+ miiBin += mii.glasses.size.toString(2).padStart(4, "0")[3];
2648
+ miiBin += mii.glasses.color.toString(2).padStart(3, "0");
2649
+ miiBin += mii.glasses.type.toString(2).padStart(4, "0");
2650
+ miiBin += "0";
2651
+ miiBin += mii.glasses.yPosition.toString(2).padStart(4, "0");
2652
+ miiBin += mii.glasses.size.toString(2).padStart(4, "0").slice(0, 3);
2653
+ miiBin += mii.mole.xPosition.toString(2).padStart(5, "0").slice(2, 5);
2654
+ miiBin += mii.mole.size.toString(2).padStart(4, "0");
2655
+ miiBin += mii.mole.on ? "1" : "0";
2656
+ miiBin += "0";
2657
+ miiBin += mii.mole.yPosition.toString(2).padStart(5, "0");
2658
+ miiBin += mii.mole.xPosition.toString(2).padStart(5, "0").slice(0, 2);
2974
2659
  for (var i = 0; i < 10; i++) {
2975
2660
  if (i < mii.meta.creatorName.length) {
2976
2661
  let code = mii.meta.creatorName.charCodeAt(i);
@@ -2982,10 +2667,10 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2982
2667
  }
2983
2668
  }
2984
2669
  //Writing based on the binary
2985
- var toWrite=miiBin.match(/.{1,8}/g);
2986
- var buffers=[];
2987
- for(var i=0;i<toWrite.length;i++){
2988
- buffers.push(parseInt(toWrite[i],2));
2670
+ var toWrite = miiBin.match(/.{1,8}/g);
2671
+ var buffers = [];
2672
+ for (var i = 0; i < toWrite.length; i++) {
2673
+ buffers.push(parseInt(toWrite[i], 2));
2989
2674
  }
2990
2675
  const buffer = Buffer.from(buffers);
2991
2676
  var encryptedData = Buffer.from(encodeAesCcm(new Uint8Array(buffer)));
@@ -3007,8 +2692,8 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
3007
2692
  crossOrigin: "anonymous",
3008
2693
  imageSize: 0.4 // Changes how large center area is
3009
2694
  },
3010
- qrOptions:{
3011
- errorCorrectionLevel:'H'
2695
+ qrOptions: {
2696
+ errorCorrectionLevel: 'H'
3012
2697
  }
3013
2698
  }
3014
2699
  const qrCodeImage = new QRCodeStyling({
@@ -3068,187 +2753,187 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
3068
2753
 
3069
2754
  // Get the buffer
3070
2755
  const imageBuffer = await main_img.getBufferAsync(Jimp.MIME_PNG);
3071
-
2756
+
3072
2757
  // Optionally write to file if outPath is provided
3073
2758
  if (outPath) {
3074
2759
  await main_img.writeAsync(outPath);
3075
2760
  }
3076
-
2761
+
3077
2762
  return imageBuffer;
3078
2763
  }
3079
- function make3DSChild(dad,mom,options={}){
3080
- if(!["3ds","wii u"].includes(dad.meta.console?.toLowerCase())){
3081
- dad=convertMii(dad,"wii");
3082
- }
3083
- if(!["3ds","wii u"].includes(mom.meta.console?.toLowerCase())){
3084
- mom=convertMii(dad,"wii");
3085
- }
3086
- var g=options.gender||Math.floor(Math.random()*2);
3087
- var child={
3088
- "general":{
3089
- "birthMonth":new Date().getMonth()+1,
3090
- "birthday":new Date().getDay(),
3091
- "height":64,
3092
- "weight":64,
3093
- "gender":g,
3094
- "favColor":options.favColor||favCols[Math.floor(Math.random()*favCols.length)]
2764
+ function make3DSChild(dad, mom, options = {}) {
2765
+ if (!["3ds", "wii u"].includes(dad.meta.console?.toLowerCase())) {
2766
+ dad = convertMii(dad, "wii");
2767
+ }
2768
+ if (!["3ds", "wii u"].includes(mom.meta.console?.toLowerCase())) {
2769
+ mom = convertMii(dad, "wii");
2770
+ }
2771
+ var g = options.gender || Math.floor(Math.random() * 2);
2772
+ var child = {
2773
+ "general": {
2774
+ "birthMonth": new Date().getMonth() + 1,
2775
+ "birthday": new Date().getDay(),
2776
+ "height": 64,
2777
+ "weight": 64,
2778
+ "gender": g,
2779
+ "favColor": options.favColor || favCols[Math.floor(Math.random() * favCols.length)]
3095
2780
  },
3096
- "meta":{
3097
- "name":options.name||kidNames[g][Math.floor(Math.random()*kidNames[g].length)],
3098
- "creatorName":"",
2781
+ "meta": {
2782
+ "name": options.name || kidNames[g][Math.floor(Math.random() * kidNames[g].length)],
2783
+ "creatorName": "",
3099
2784
  },
3100
- "perms":{
3101
- "sharing":true,
3102
- "copying":true
2785
+ "perms": {
2786
+ "sharing": true,
2787
+ "copying": true
3103
2788
  },
3104
- "hair":{
3105
- "page":8,//Hardcoded, needs to be generated
3106
- "type":3,// ^
3107
- "color":Math.floor(Math.random()*2)===1?dad.hair.color:mom.hair.color,
3108
- "flipped":Math.floor(Math.random()*2)===0?true:false
2789
+ "hair": {
2790
+ "page": 8,//Hardcoded, needs to be generated
2791
+ "type": 3,// ^
2792
+ "color": Math.floor(Math.random() * 2) === 1 ? dad.hair.color : mom.hair.color,
2793
+ "flipped": Math.floor(Math.random() * 2) === 0 ? true : false
3109
2794
  },
3110
- "face":{
3111
- "shape":Math.floor(Math.random()*2)===1?dad.face.shape:mom.face.shape,
3112
- "feature":Math.floor(Math.random()*2)===1?dad.face.feature:mom.face.feature,
3113
- "makeup":g===0?0:Math.floor(Math.random()*2)===1?dad.face.makeup:mom.face.makeup
2795
+ "face": {
2796
+ "shape": Math.floor(Math.random() * 2) === 1 ? dad.face.shape : mom.face.shape,
2797
+ "feature": Math.floor(Math.random() * 2) === 1 ? dad.face.feature : mom.face.feature,
2798
+ "makeup": g === 0 ? 0 : Math.floor(Math.random() * 2) === 1 ? dad.face.makeup : mom.face.makeup
3114
2799
  },
3115
- "eyes":Math.floor(Math.random()*2)===1?dad.eyes:mom.eyes,
3116
- "eyebrows":Math.floor(Math.random()*2)===1?dad.eyebrows:mom.eyebrows,
3117
- "nose":Math.floor(Math.random()*2)===1?dad.nose:mom.nose,
3118
- "mouth":Math.floor(Math.random()*2)===1?dad.mouth:mom.mouth,
3119
- "beard":{//Beards can never be generated for children allegedly, confirm before finishing
3120
- "mustache":{
3121
- "type":0,
2800
+ "eyes": Math.floor(Math.random() * 2) === 1 ? dad.eyes : mom.eyes,
2801
+ "eyebrows": Math.floor(Math.random() * 2) === 1 ? dad.eyebrows : mom.eyebrows,
2802
+ "nose": Math.floor(Math.random() * 2) === 1 ? dad.nose : mom.nose,
2803
+ "mouth": Math.floor(Math.random() * 2) === 1 ? dad.mouth : mom.mouth,
2804
+ "beard": {//Beards can never be generated for children allegedly, confirm before finishing
2805
+ "mustache": {
2806
+ "type": 0,
3122
2807
  "mustacheSize": 4,
3123
2808
  "mustacheYPos": 10
3124
2809
  },
3125
2810
  "type": 0,
3126
2811
  "color": 0
3127
2812
  },
3128
- "glasses":Math.floor(Math.random()*2)===1?dad.glasses:mom.glasses,
3129
- "mole":Math.floor(Math.random()*2)===1?dad.mole:mom.mole
2813
+ "glasses": Math.floor(Math.random() * 2) === 1 ? dad.glasses : mom.glasses,
2814
+ "mole": Math.floor(Math.random() * 2) === 1 ? dad.mole : mom.mole
3130
2815
  };
3131
- child.eyebrows.color=child.hair.color;
3132
- var c=[mom.face.color,dad.face.color];
3133
- if(c[0]>c[1]){
3134
- c[1]=c[0];
3135
- c[0]=dad.face.color;
3136
- }
3137
- child.face.color=c[0]+Math.round((c[1]-c[0])/2);
3138
- child.type="3DS";
2816
+ child.eyebrows.color = child.hair.color;
2817
+ var c = [mom.face.color, dad.face.color];
2818
+ if (c[0] > c[1]) {
2819
+ c[1] = c[0];
2820
+ c[0] = dad.face.color;
2821
+ }
2822
+ child.face.color = c[0] + Math.round((c[1] - c[0]) / 2);
2823
+ child.type = "3DS";
3139
2824
  return child;
3140
2825
  }
3141
- function generateInstructions(mii,full){
3142
- let type=mii.console?.toLowerCase();
3143
- if(type.toLowerCase()==="wii"){
3144
- var typeCheat=[1,2,3,1,2,3,1,2,3,1,2,3];
3145
- var instrs={
3146
- "base":`Select "${mii.general.gender}", and then "Start from Scratch".`,
3147
- "col":`On the info page (first tab), set the Favorite Color to ${lookupTables.favCols[mii.general.favoriteColor]} (${mii.general.favoriteColor<=5?mii.general.favoriteColor+1:mii.general.favoriteColor-5} from the left, ${mii.general.favoriteColor>5?"bottom":"top"} row).`,
3148
- "heightWeight":`On the build page (second tab), set the height to ${Math.round((100/128)*mii.general.height)}%, and the weight to ${Math.round((100/128)*mii.general.weight)}%.`,
3149
- "faceShape":`On the face page (third tab), set the shape to the one ${Math.floor(mii.face.type/2)+1} from the top, in the ${mii.face.type%2===0?"left":"right"} column.`,
3150
- "skinCol":`On the face page (third tab), set the color to the one ${mii.face.color+mii.face.color>2?-2:1} from the left, on the ${mii.face.color>2?`bottom`:`top`} row.`,
3151
- "makeup":`On the face page's makeup tab, set the makeup to the one ${Math.ceil((mii.face.feature+1)/3)} from the top, and ${typeCheat[mii.face.feature]} from the left.`,
3152
- "hairStyle":`On the hair page (fourth tab), set the hair style to the one ${typeCheat[mii.hair.type]} from the left, ${Math.ceil((mii.hair.type+1)/3)} from the top, on page ${mii.hair.page}.`,
3153
- "hairFlipped":`${mii.hair.flipped?`On the hair page (fourth tab), press the button to flip the hair.`:``}`,
3154
- "hairColor":`On the hair page (fourth tab), set the hair color to the one ${mii.hair.col+(mii.hair.col>3?-3:1)} from the left, on the ${mii.hair.col>3?`bottom`:`top`} row.`,
3155
- "eyebrowStyle":`On the eyebrow page (fifth tab), set the eyebrow style to the one ${typeCheat[mii.eyebrows.type]} from the left, ${Math.ceil((mii.eyebrows.type+1)/3)} from the top, on page ${mii.eyebrows.page}.`,
3156
- "eyebrowColor":`On the eyebrow page (fifth tab), set the eyebrow color to the one ${mii.eyebrows.color+(mii.eyebrows.color>3?-3:1)} from the left, on the ${mii.eyebrows.color>3?`bottom`:`top`} row.`,
3157
- "eyebrowY":`${mii.eyebrows.yPos!==7?`On the eyebrow page (fifth tab), `:``}${mii.eyebrows.yPosition<7?`press the up button ${7-mii.eyebrows.yPosition} times.`:mii.eyebrows.yPosition>7?`press the down button ${mii.eyebrows.yPosition-7} times.`:``}`,
3158
- "eyebrowSize":`${mii.eyebrows.size!==4?`On the eyebrow page (fifth tab), `:``}${mii.eyebrows.size<4?`press the shrink button ${4-mii.eyebrows.size} times.`:mii.eyebrows.size>4?`press the enlarge button ${mii.eyebrows.size-4} times.`:``}`,
3159
- "eyebrowRot":`${mii.eyebrows.rotation!==6?`On the eyebrow page (fifth tab), `:``}${mii.eyebrows.rotation<6?`press the rotate clockwise button ${6-mii.eyebrows.rotation} times.`:mii.eyebrows.rotation>6?`press the rotate counter-clockwise button ${mii.eyebrows.rotation-6} times.`:``}`,
3160
- "eyebrowDist":`${mii.eyebrows.distApart!==2?`On the eyebrow page (fifth tab), `:``}${mii.eyebrows.distanceApart<2?`press the closer-together button ${2-mii.eyebrows.distanceApart} times.`:mii.eyebrows.distanceApart>2?`press the further-apart button ${mii.eyebrows.distanceApart-2} times.`:``}`,
3161
- "eyeType":`On the eye page (sixth tab), set the eye type to the one ${typeCheat[mii.eyes.type]} from the left, ${Math.ceil((mii.eyes.type+1)/3)} from the top, on page ${mii.eyes.page}.`,
3162
- "eyeColor":`On the eye page (sixth tab), set the color to the one ${mii.eyes.color+(mii.eyes.color>2?-2:1)} from the left, on the ${mii.eyes.color>2?`bottom`:`top`} row.`,
3163
- "eyeY":`${mii.eyes.yPos!==12?`On the eye page (sixth tab), `:``}${mii.eyes.yPosition<12?`press the up button ${12-mii.eyes.yPosition} times.`:mii.eyes.yPosition>12?`press the down button ${mii.eyes.yPosition-12} times.`:``}`,
3164
- "eyeSize":`${mii.eyes.size!==4?`On the eye page (sixth tab), `:``}${mii.eyes.size<4?`press the shrink button ${4-mii.eyes.size} times.`:mii.eyes.size>4?`press the enlarge button ${mii.eyes.size-4} times.`:``}`,
3165
- "eyeRot":`${mii.eyes.rotation!==(mii.general.gender==="Female"?3:4)?`On the eye page (sixth tab), `:``}${mii.eyes.rotation<(mii.general.gender==="Female"?3:4)?`press the rotate clockwise button ${(mii.general.gender==="Female"?3:4)-mii.eyes.rotation} times.`:mii.eyes.rotation>(mii.general.gender==="Female"?3:4)?`press the rotate counter-clockwise button ${mii.eyes.rotation-(mii.general.gender==="Female"?3:4)} times.`:``}`,
3166
- "eyeDist":`${mii.eyes.distanceApart!==2?`On the eye page (sixth tab), `:``}${mii.eyes.distanceApart<2?`press the closer-together button ${2-mii.eyes.distanceApart} times.`:mii.eyes.distanceApart>2?`press the further-apart button ${mii.eyes.distanceApart-2} times.`:``}`,
3167
- "noseType":`On the nose page (seventh tab), set the nose to the one ${Math.ceil((mii.nose.type+1)/3)} from the top, and ${typeCheat[mii.nose.type]} from the left.`,
3168
- "noseY":`${mii.nose.yPosition!==9?`On the nose page (seventh tab), `:``}${mii.nose.yPosition<9?`press the up button ${9-mii.nose.yPosition} times.`:mii.nose.yPosition>9?`press the down button ${mii.nose.yPosition-9} times.`:``}`,
3169
- "noseSize":`${mii.nose.size!==4?`On the nose page (seventh tab), `:``}${mii.nose.size<4?`press the shrink button ${4-mii.nose.size} times.`:mii.nose.size>4?`press the enlarge button ${mii.nose.size-4} times.`:``}`,
3170
- "mouthType":`On the mouth page (eighth tab), set the mouth type to the one ${typeCheat[mii.mouth.type]} from the left, ${Math.ceil((mii.mouth.type+1)/3)} from the top, on page ${mii.mouth.page}.`,
3171
- "mouthCol":`On the mouth page (eighth tab), set the color to the one ${mii.mouth.col+1} from the left.`,
3172
- "mouthY":`${mii.mouth.yPosition!==13?`On the mouth page (eighth tab), `:``}${mii.mouth.yPosition<13?`press the up button ${13-mii.mouth.yPosition} times.`:mii.mouth.yPosition>13?`press the down button ${mii.mouth.yPosition-13} times.`:``}`,
3173
- "mouthSize":`${mii.mouth.size!==4?`On the mouth page (eighth tab), `:``}${mii.mouth.size<4?`press the shrink button ${4-mii.mouth.size} times.`:mii.mouth.size>4?`press the enlarge button ${mii.mouth.size-4} times.`:``}`,
3174
- "glasses":`On the glasses page (within the ninth tab), set the glasses to the one ${Math.ceil((mii.glasses.type+1)/3)} from the top, and ${typeCheat[mii.glasses.type]} from the left.`,
3175
- "glassesCol":`On the glasses page (within the ninth tab), set the color to the one ${mii.glasses.color+(mii.glasses.color>2?-2:1)} from the left, on the ${mii.glasses.color>2?`bottom`:`top`} row.`,
3176
- "glassesY":`${mii.glasses.yPosition!==10?`On the glasses page (within the ninth tab), `:``}${mii.glasses.yPosition<10?`press the up button ${10-mii.glasses.yPosition} times.`:mii.glasses.yPosition>10?`press the down button ${mii.glasses.yPosition-10} times.`:``}`,
3177
- "glassesSize":`${mii.glasses.size!==4?`On the glasses page (within the ninth tab), `:``}${mii.glasses.size<4?`press the shrink button ${4-mii.glasses.size} times.`:mii.glasses.size>4?`press the enlarge button ${mii.glasses.size-4} times.`:``}`,
3178
- "stache":`On the mustache page (within the ninth tab), set the mustache to the one on the ${[0,1].includes(mii.beard.mustache.type)?`top`:`bottom`}-${[0,2].includes(mii.beard.mustache.type)?`left`:`right`}.`,
3179
- "stacheY":`${mii.beard.mustache.yPosition!==10?`On the mustache page (within the ninth tab), press the `:``}${mii.beard.mustache.yPos>10?`down button ${mii.beard.mustache.yPos-10} times.`:mii.beard.mustache.yPos<10?`up button ${10-mii.beard.mustache.yPos} times.`:``}`,
3180
- "stacheSize":`${mii.beard.mustache.size!==4?`On the mustache page (within the ninth tab), `:``}${mii.beard.mustache.size<4?`press the shrink button ${4-mii.beard.mustache.size} times.`:mii.beard.mustache.size>4?`press the enlarge button ${mii.beard.mustache.size-4} times.`:``}`,
3181
- "mole":`${mii.mole.on?`On the mole page (within the ninth tab), turn the mole on.`:``}`,
3182
- "moleX":`${mii.mole.xPosition!==2?`On the mole page (within the ninth tab), press the `:``}${mii.mole.xPosition>2?`right button ${mii.mole.xPosition-2} times.`:mii.mole.xPosition<2?`left button ${2-mii.mole.xPosition} times.`:``}`,
3183
- "moleY":`${mii.mole.yPosition!==20?`On the mole page (within the ninth tab), press the `:``}${mii.mole.yPosition>20?`down button ${mii.mole.yPosition-20} times.`:mii.mole.yPosition<20?`up button ${20-mii.mole.yPosition} times.`:``}`,
3184
- "moleSize":`${mii.mole.size!==4?`On the mole page (within the ninth tab), `:``}${mii.mole.size<4?`press the shrink button ${4-mii.mole.size} times.`:mii.mole.size>4?`press the enlarge button ${mii.mole.size-4} times.`:``}`,
3185
- "beard":`On the beard page (within the ninth tab), set the beard to the one on the ${[0,1].includes(mii.beard.type)?`top`:`bottom`}-${[0,2].includes(mii.beard.type)?`left`:`right`}.`,
3186
- "beardCol":`On the mustache OR beard pages (within the ninth tab), set the color to the one ${mii.beard.col+(mii.beard.col>3?-3:1)} from the left, on the ${mii.facialHair.col>3?`bottom`:`top`} row.`,
3187
- "other":`The Nickname of this Mii is ${mii.info.name}.${mii.info.creatorName?` The creator was ${mii.info.creatorName}.`:``} Mingle was turned ${mii.info.mingle?`on`:`off`}.${mii.info.birthday!==0?` Its birthday is ${["","January","February","March","April","May","June","July","August","September","October","November","December"][mii.info.birthMonth]} ${mii.info.birthday}.`:``}`
2826
+ function generateInstructions(mii, full) {
2827
+ let type = mii.console?.toLowerCase();
2828
+ if (type.toLowerCase() === "wii") {
2829
+ var typeCheat = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3];
2830
+ var instrs = {
2831
+ "base": `Select "${mii.general.gender}", and then "Start from Scratch".`,
2832
+ "col": `On the info page (first tab), set the Favorite Color to ${lookupTables.favCols[mii.general.favoriteColor]} (${mii.general.favoriteColor <= 5 ? mii.general.favoriteColor + 1 : mii.general.favoriteColor - 5} from the left, ${mii.general.favoriteColor > 5 ? "bottom" : "top"} row).`,
2833
+ "heightWeight": `On the build page (second tab), set the height to ${Math.round((100 / 128) * mii.general.height)}%, and the weight to ${Math.round((100 / 128) * mii.general.weight)}%.`,
2834
+ "faceShape": `On the face page (third tab), set the shape to the one ${Math.floor(mii.face.type / 2) + 1} from the top, in the ${mii.face.type % 2 === 0 ? "left" : "right"} column.`,
2835
+ "skinCol": `On the face page (third tab), set the color to the one ${mii.face.color + mii.face.color > 2 ? -2 : 1} from the left, on the ${mii.face.color > 2 ? `bottom` : `top`} row.`,
2836
+ "makeup": `On the face page's makeup tab, set the makeup to the one ${Math.ceil((mii.face.feature + 1) / 3)} from the top, and ${typeCheat[mii.face.feature]} from the left.`,
2837
+ "hairStyle": `On the hair page (fourth tab), set the hair style to the one ${typeCheat[mii.hair.type]} from the left, ${Math.ceil((mii.hair.type + 1) / 3)} from the top, on page ${mii.hair.page}.`,
2838
+ "hairFlipped": `${mii.hair.flipped ? `On the hair page (fourth tab), press the button to flip the hair.` : ``}`,
2839
+ "hairColor": `On the hair page (fourth tab), set the hair color to the one ${mii.hair.col + (mii.hair.col > 3 ? -3 : 1)} from the left, on the ${mii.hair.col > 3 ? `bottom` : `top`} row.`,
2840
+ "eyebrowStyle": `On the eyebrow page (fifth tab), set the eyebrow style to the one ${typeCheat[mii.eyebrows.type]} from the left, ${Math.ceil((mii.eyebrows.type + 1) / 3)} from the top, on page ${mii.eyebrows.page}.`,
2841
+ "eyebrowColor": `On the eyebrow page (fifth tab), set the eyebrow color to the one ${mii.eyebrows.color + (mii.eyebrows.color > 3 ? -3 : 1)} from the left, on the ${mii.eyebrows.color > 3 ? `bottom` : `top`} row.`,
2842
+ "eyebrowY": `${mii.eyebrows.yPos !== 7 ? `On the eyebrow page (fifth tab), ` : ``}${mii.eyebrows.yPosition < 7 ? `press the up button ${7 - mii.eyebrows.yPosition} times.` : mii.eyebrows.yPosition > 7 ? `press the down button ${mii.eyebrows.yPosition - 7} times.` : ``}`,
2843
+ "eyebrowSize": `${mii.eyebrows.size !== 4 ? `On the eyebrow page (fifth tab), ` : ``}${mii.eyebrows.size < 4 ? `press the shrink button ${4 - mii.eyebrows.size} times.` : mii.eyebrows.size > 4 ? `press the enlarge button ${mii.eyebrows.size - 4} times.` : ``}`,
2844
+ "eyebrowRot": `${mii.eyebrows.rotation !== 6 ? `On the eyebrow page (fifth tab), ` : ``}${mii.eyebrows.rotation < 6 ? `press the rotate clockwise button ${6 - mii.eyebrows.rotation} times.` : mii.eyebrows.rotation > 6 ? `press the rotate counter-clockwise button ${mii.eyebrows.rotation - 6} times.` : ``}`,
2845
+ "eyebrowDist": `${mii.eyebrows.distApart !== 2 ? `On the eyebrow page (fifth tab), ` : ``}${mii.eyebrows.distanceApart < 2 ? `press the closer-together button ${2 - mii.eyebrows.distanceApart} times.` : mii.eyebrows.distanceApart > 2 ? `press the further-apart button ${mii.eyebrows.distanceApart - 2} times.` : ``}`,
2846
+ "eyeType": `On the eye page (sixth tab), set the eye type to the one ${typeCheat[mii.eyes.type]} from the left, ${Math.ceil((mii.eyes.type + 1) / 3)} from the top, on page ${mii.eyes.page}.`,
2847
+ "eyeColor": `On the eye page (sixth tab), set the color to the one ${mii.eyes.color + (mii.eyes.color > 2 ? -2 : 1)} from the left, on the ${mii.eyes.color > 2 ? `bottom` : `top`} row.`,
2848
+ "eyeY": `${mii.eyes.yPos !== 12 ? `On the eye page (sixth tab), ` : ``}${mii.eyes.yPosition < 12 ? `press the up button ${12 - mii.eyes.yPosition} times.` : mii.eyes.yPosition > 12 ? `press the down button ${mii.eyes.yPosition - 12} times.` : ``}`,
2849
+ "eyeSize": `${mii.eyes.size !== 4 ? `On the eye page (sixth tab), ` : ``}${mii.eyes.size < 4 ? `press the shrink button ${4 - mii.eyes.size} times.` : mii.eyes.size > 4 ? `press the enlarge button ${mii.eyes.size - 4} times.` : ``}`,
2850
+ "eyeRot": `${mii.eyes.rotation !== (mii.general.gender === "Female" ? 3 : 4) ? `On the eye page (sixth tab), ` : ``}${mii.eyes.rotation < (mii.general.gender === "Female" ? 3 : 4) ? `press the rotate clockwise button ${(mii.general.gender === "Female" ? 3 : 4) - mii.eyes.rotation} times.` : mii.eyes.rotation > (mii.general.gender === "Female" ? 3 : 4) ? `press the rotate counter-clockwise button ${mii.eyes.rotation - (mii.general.gender === "Female" ? 3 : 4)} times.` : ``}`,
2851
+ "eyeDist": `${mii.eyes.distanceApart !== 2 ? `On the eye page (sixth tab), ` : ``}${mii.eyes.distanceApart < 2 ? `press the closer-together button ${2 - mii.eyes.distanceApart} times.` : mii.eyes.distanceApart > 2 ? `press the further-apart button ${mii.eyes.distanceApart - 2} times.` : ``}`,
2852
+ "noseType": `On the nose page (seventh tab), set the nose to the one ${Math.ceil((mii.nose.type + 1) / 3)} from the top, and ${typeCheat[mii.nose.type]} from the left.`,
2853
+ "noseY": `${mii.nose.yPosition !== 9 ? `On the nose page (seventh tab), ` : ``}${mii.nose.yPosition < 9 ? `press the up button ${9 - mii.nose.yPosition} times.` : mii.nose.yPosition > 9 ? `press the down button ${mii.nose.yPosition - 9} times.` : ``}`,
2854
+ "noseSize": `${mii.nose.size !== 4 ? `On the nose page (seventh tab), ` : ``}${mii.nose.size < 4 ? `press the shrink button ${4 - mii.nose.size} times.` : mii.nose.size > 4 ? `press the enlarge button ${mii.nose.size - 4} times.` : ``}`,
2855
+ "mouthType": `On the mouth page (eighth tab), set the mouth type to the one ${typeCheat[mii.mouth.type]} from the left, ${Math.ceil((mii.mouth.type + 1) / 3)} from the top, on page ${mii.mouth.page}.`,
2856
+ "mouthCol": `On the mouth page (eighth tab), set the color to the one ${mii.mouth.col + 1} from the left.`,
2857
+ "mouthY": `${mii.mouth.yPosition !== 13 ? `On the mouth page (eighth tab), ` : ``}${mii.mouth.yPosition < 13 ? `press the up button ${13 - mii.mouth.yPosition} times.` : mii.mouth.yPosition > 13 ? `press the down button ${mii.mouth.yPosition - 13} times.` : ``}`,
2858
+ "mouthSize": `${mii.mouth.size !== 4 ? `On the mouth page (eighth tab), ` : ``}${mii.mouth.size < 4 ? `press the shrink button ${4 - mii.mouth.size} times.` : mii.mouth.size > 4 ? `press the enlarge button ${mii.mouth.size - 4} times.` : ``}`,
2859
+ "glasses": `On the glasses page (within the ninth tab), set the glasses to the one ${Math.ceil((mii.glasses.type + 1) / 3)} from the top, and ${typeCheat[mii.glasses.type]} from the left.`,
2860
+ "glassesCol": `On the glasses page (within the ninth tab), set the color to the one ${mii.glasses.color + (mii.glasses.color > 2 ? -2 : 1)} from the left, on the ${mii.glasses.color > 2 ? `bottom` : `top`} row.`,
2861
+ "glassesY": `${mii.glasses.yPosition !== 10 ? `On the glasses page (within the ninth tab), ` : ``}${mii.glasses.yPosition < 10 ? `press the up button ${10 - mii.glasses.yPosition} times.` : mii.glasses.yPosition > 10 ? `press the down button ${mii.glasses.yPosition - 10} times.` : ``}`,
2862
+ "glassesSize": `${mii.glasses.size !== 4 ? `On the glasses page (within the ninth tab), ` : ``}${mii.glasses.size < 4 ? `press the shrink button ${4 - mii.glasses.size} times.` : mii.glasses.size > 4 ? `press the enlarge button ${mii.glasses.size - 4} times.` : ``}`,
2863
+ "stache": `On the mustache page (within the ninth tab), set the mustache to the one on the ${[0, 1].includes(mii.beard.mustache.type) ? `top` : `bottom`}-${[0, 2].includes(mii.beard.mustache.type) ? `left` : `right`}.`,
2864
+ "stacheY": `${mii.beard.mustache.yPosition !== 10 ? `On the mustache page (within the ninth tab), press the ` : ``}${mii.beard.mustache.yPos > 10 ? `down button ${mii.beard.mustache.yPos - 10} times.` : mii.beard.mustache.yPos < 10 ? `up button ${10 - mii.beard.mustache.yPos} times.` : ``}`,
2865
+ "stacheSize": `${mii.beard.mustache.size !== 4 ? `On the mustache page (within the ninth tab), ` : ``}${mii.beard.mustache.size < 4 ? `press the shrink button ${4 - mii.beard.mustache.size} times.` : mii.beard.mustache.size > 4 ? `press the enlarge button ${mii.beard.mustache.size - 4} times.` : ``}`,
2866
+ "mole": `${mii.mole.on ? `On the mole page (within the ninth tab), turn the mole on.` : ``}`,
2867
+ "moleX": `${mii.mole.xPosition !== 2 ? `On the mole page (within the ninth tab), press the ` : ``}${mii.mole.xPosition > 2 ? `right button ${mii.mole.xPosition - 2} times.` : mii.mole.xPosition < 2 ? `left button ${2 - mii.mole.xPosition} times.` : ``}`,
2868
+ "moleY": `${mii.mole.yPosition !== 20 ? `On the mole page (within the ninth tab), press the ` : ``}${mii.mole.yPosition > 20 ? `down button ${mii.mole.yPosition - 20} times.` : mii.mole.yPosition < 20 ? `up button ${20 - mii.mole.yPosition} times.` : ``}`,
2869
+ "moleSize": `${mii.mole.size !== 4 ? `On the mole page (within the ninth tab), ` : ``}${mii.mole.size < 4 ? `press the shrink button ${4 - mii.mole.size} times.` : mii.mole.size > 4 ? `press the enlarge button ${mii.mole.size - 4} times.` : ``}`,
2870
+ "beard": `On the beard page (within the ninth tab), set the beard to the one on the ${[0, 1].includes(mii.beard.type) ? `top` : `bottom`}-${[0, 2].includes(mii.beard.type) ? `left` : `right`}.`,
2871
+ "beardCol": `On the mustache OR beard pages (within the ninth tab), set the color to the one ${mii.beard.col + (mii.beard.col > 3 ? -3 : 1)} from the left, on the ${mii.facialHair.col > 3 ? `bottom` : `top`} row.`,
2872
+ "other": `The Nickname of this Mii is ${mii.info.name}.${mii.info.creatorName ? ` The creator was ${mii.info.creatorName}.` : ``} Mingle was turned ${mii.info.mingle ? `on` : `off`}.${mii.info.birthday !== 0 ? ` Its birthday is ${["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][mii.info.birthMonth]} ${mii.info.birthday}.` : ``}`
3188
2873
  };
3189
- if(!full){
3190
- var defaultMiiInstrs=structuredClone(mii.general.gender==="Male"?defaultInstrs.wii.male:defaultInstrs.wii.female);
3191
- Object.keys(instrs).forEach(instr=>{
3192
- if(instrs[instr]===defaultMiiInstrs[instr]){
2874
+ if (!full) {
2875
+ var defaultMiiInstrs = structuredClone(mii.general.gender === "Male" ? defaultInstrs.wii.male : defaultInstrs.wii.female);
2876
+ Object.keys(instrs).forEach(instr => {
2877
+ if (instrs[instr] === defaultMiiInstrs[instr]) {
3193
2878
  delete instrs[instr];
3194
2879
  }
3195
2880
  });
3196
2881
  }
3197
2882
  return instrs;
3198
2883
  }
3199
- else{
3200
- var instrs={
3201
- "base":`Select "Start from Scratch", and then "${mii.general.gender}".`,
3202
- "faceShape":`On the face page (first tab), set the face shape to the one ${Math.ceil((mii.face.type+1)/3)} from the top, and ${typeCheat[mii.face.type]} from the left.`,
3203
- "skinCol":`On the face page (first tab), set the color to the one ${mii.face.color+1} from the top.`,
3204
- "makeup":`On the face page's makeup tab, set the makeup to the one ${Math.ceil((mii.face.makeup+1)/3)} from the top, and ${typeCheat[mii.face.makeup]} from the left.`,
3205
- "feature":`On the face page's wrinkles tab, set the facial feature to the one ${Math.ceil((mii.face.feature+1)/3)+1} from the top, and ${typeCheat[mii.face.makeup]} from the left.`,
3206
- "hairStyle":`On the hair page (second tab), set the hair style to the one ${Math.ceil((mii.hair.type+1)/3)} from the top, and ${typeCheat[mii.hair.type]} from the left, on page ${mii.hair.page+1}.`,
3207
- "hairFlipped":`${mii.hair.flipped?`On the hair page (second tab), press the button to flip the hair.`:``}`,
3208
- "hairColor":`On the hair page (second tab), set the hair color to the one ${mii.hair.color+1} from the top.`,
3209
- "eyebrowStyle":`On the eyebrow page (third tab), set the eyebrow style to the one ${typeCheat[mii.eyebrows.type]} from the left, ${Math.ceil((mii.eyebrows.type+1)/3)} from the top, on page ${mii.eyebrows.page+1}.`,
3210
- "eyebrowColor":`On the eyebrow page (third tab), set the eyebrow color to the one ${mii.eyebrows.color+1} from the top.`,
3211
- "eyebrowY":`${mii.eyebrows.yPosition!==7?`On the eyebrow page (third tab), `:``}${mii.eyebrows.yPosition<7?`press the up button ${7-mii.eyebrows.yPosition} times.`:mii.eyebrows.yPosition>7?`press the down button ${mii.eyebrows.yPosition-7} times.`:``}`,
3212
- "eyebrowSize":`${mii.eyebrows.size!==4?`On the eyebrow page (third tab), `:``}${mii.eyebrows.size<4?`press the shrink button ${4-mii.eyebrows.size} times.`:mii.eyebrows.size>4?`press the enlarge button ${mii.eyebrows.size-4} times.`:``}`,
3213
- "eyebrowRot":`${mii.eyebrows.rotation!==6?`On the eyebrow page (third tab), `:``}${mii.eyebrows.rotation<6?`press the rotate clockwise button ${6-mii.eyebrows.rotation} times.`:mii.eyebrows.rotation>6?`press the rotate counter-clockwise button ${mii.eyebrows.rotation-6} times.`:``}`,
3214
- "eyebrowDist":`${mii.eyebrows.distanceApart!==2?`On the eyebrow page (third tab), `:``}${mii.eyebrows.distanceApart<2?`press the closer-together button ${2-mii.eyebrows.distanceApart} times.`:mii.eyebrows.distanceApart>2?`press the further-apart button ${mii.eyebrows.distanceApart-2} times.`:``}`,
3215
- "eyebrowSquash":`${mii.eyebrows.squash!==3?`On the eyebrow page (third tab), `:``}${mii.eyebrows.squash<3?`press the squish button ${3-mii.eyebrows.squash} times.`:mii.eyebrows.squash>3?`press the un-squish button ${mii.eyebrows.squash-3} times.`:``}`,
3216
- "eyeType":`On the eye page (fourth tab), set the eye type to the one ${typeCheat[mii.eyes.type]} from the left, ${Math.ceil((mii.eyes.type+1)/3)} from the top, on page ${mii.eyes.page+1}.`,
3217
- "eyeColor":`On the eye page (fourth tab), set the color to the one ${mii.eyes.col+1} from the top.`,
3218
- "eyeY":`${mii.eyes.yPosition!==12?`On the eye page (fourth tab), `:``}${mii.eyes.yPosition<12?`press the up button ${12-mii.eyes.yPosition} times.`:mii.eyes.yPosition>12?`press the down button ${mii.eyes.yPosition-12} times.`:``}`,
3219
- "eyeSize":`${mii.eyes.size!==4?`On the eye page (fourth tab), `:``}${mii.eyes.size<4?`press the shrink button ${4-mii.eyes.size} times.`:mii.eyes.size>4?`press the enlarge button ${mii.eyes.size-4} times.`:``}`,
3220
- "eyeRot":`${mii.eyes.rotation!==(mii.general.gender==="Female"?3:4)?`On the eye page (fourth tab), `:``}${mii.eyes.rotation<(mii.general.gender==="Female"?3:4)?`press the rotate clockwise button ${(mii.general.gender==="Female"?3:4)-mii.eyes.rotation} times.`:mii.eyes.rotation>(mii.general.gender==="Female"?3:4)?`press the rotate counter-clockwise button ${mii.eyes.rotation-(mii.general.gender==="Female"?3:4)} times.`:``}`,
3221
- "eyeDist":`${mii.eyes.distanceApart!==2?`On the eye page (fourth tab), `:``}${mii.eyes.distanceApart<2?`press the closer-together button ${2-mii.eyes.distanceApart} times.`:mii.eyes.distanceApart>2?`press the further-apart button ${mii.eyes.distanceApart-2} times.`:``}`,
3222
- "eyeSquash":`${mii.eyes.squash!==3?`On the eye page (fourth tab), `:``}${mii.eyes.squash<3?`press the squish button ${3-mii.eyes.squash} times.`:mii.eyes.squash>3?`press the un-squish button ${mii.eyes.squash-3} times.`:``}`,
3223
- "noseType":`On the nose page (fifth tab), set the nose to the one ${Math.ceil((mii.nose.type+1)/3)} from the top, and ${typeCheat[mii.nose.type]} from the left, on page ${mii.nose.page}.`,
3224
- "noseY":`${mii.nose.yPosition!==9?`On the nose page (fifth tab), `:``}${mii.nose.yPosition<9?`press the up button ${9-mii.nose.yPosition} times.`:mii.nose.yPosition>9?`press the down button ${mii.nose.yPosition-9} times.`:``}`,
3225
- "noseSize":`${mii.nose.size!==4?`On the nose page (fifth tab), `:``}${mii.nose.size<4?`press the shrink button ${4-mii.nose.size} times.`:mii.nose.size>4?`press the enlarge button ${mii.nose.size-4} times.`:``}`,
3226
- "mouthType":`On the mouth page (sixth tab), set the mouth type to the one ${typeCheat[mii.mouth.type]} from the left, ${Math.ceil((mii.mouth.type+1)/3)} from the top, on page ${mii.mouth.page+1}.`,
3227
- "mouthCol":`On the mouth page (sixth tab), set the color to the one ${mii.mouth.color+1} from the top.`,
3228
- "mouthY":`${mii.mouth.yPosition!==13?`On the mouth page (sixth tab), `:``}${mii.mouth.yPosition<13?`press the up button ${13-mii.mouth.yPosition} times.`:mii.mouth.yPosition>13?`press the down button ${mii.mouth.yPosition-13} times.`:``}`,
3229
- "mouthSize":`${mii.mouth.size!==4?`On the mouth page (sixth tab), `:``}${mii.mouth.size<4?`press the shrink button ${4-mii.mouth.size} times.`:mii.mouth.size>4?`press the enlarge button ${mii.mouth.size-4} times.`:``}`,
3230
- "mouthSquash":`${mii.mouth.squash!==3?`On the mouth page (sixth tab), `:``}${mii.mouth.squash<3?`press the squish button ${3-mii.mouth.squash} times.`:mii.mouth.squash>3?`press the un-squish button ${mii.mouth.squash-3} times.`:``}`,
3231
- "glasses":`On the glasses page (within the seventh tab), set the glasses to the one ${Math.ceil((mii.glasses.type+1)/3)} from the top, and ${typeCheat[mii.glasses.type]} from the left.`,
3232
- "glassesCol":`On the glasses page (within the seventh tab), set the color to the one ${mii.glasses.col+1} from the top.`,
3233
- "glassesY":`${mii.glasses.yPosition!==10?`On the glasses page (within the seventh tab), `:``}${mii.glasses.yPosition<10?`press the up button ${10-mii.glasses.yPosition} times.`:mii.glasses.yPosition>10?`press the down button ${mii.glasses.yPosition-10} times.`:``}`,
3234
- "glassesSize":`${mii.glasses.size!==4?`On the glasses page (within the seventh tab), `:``}${mii.glasses.size<4?`press the shrink button ${4-mii.glasses.size} times.`:mii.glasses.size>4?`press the enlarge button ${mii.glasses.size-4} times.`:``}`,
3235
- "stache":`On the mustache page (within the seventh tab), set the mustache to the one on the ${[0,1].includes(mii.beard.mustache.type)?`top`:[2,3].includes(mii.beard.mustache.type)?`middle`:`bottom`}-${[0,2,4].includes(mii.beard.mustache.type)?`left`:`right`}.`,
3236
- "stacheY":`${mii.beard.mustache.yPosition!==10?`On the mustache page (within the seventh tab), press the `:``}${mii.beard.mustache.yPosition>10?`down button ${mii.beard.mustache.yPosition-10} times.`:mii.beard.mustache.yPosition<10?`up button ${10-mii.beard.mustache.yPosition} times.`:``}`,
3237
- "stacheSize":`${mii.beard.mustache.size!==4?`On the mustache page (within the seventh tab), `:``}${mii.beard.mustache.size<4?`press the shrink button ${4-mii.beard.mustache.size} times.`:mii.beard.mustache.size>4?`press the enlarge button ${mii.beard.mustache.size-4} times.`:``}`,
3238
- "mole":`${mii.mole.on?`On the mole page (within the seventh tab), turn the mole on.`:``}`,
3239
- "moleX":`${mii.mole.xPosition!==2?`On the mole page (within the seventh tab), press the `:``}${mii.mole.xPosition>2?`right button ${mii.mole.xPosition-2} times.`:mii.mole.xPosition<2?`left button ${2-mii.mole.xPosition} times.`:``}`,
3240
- "moleY":`${mii.mole.yPosition!==20?`On the mole page (within the seventh tab), press the `:``}${mii.mole.yPosition>20?`down button ${mii.mole.yPosition-20} times.`:mii.mole.yPosition<20?`up button ${20-mii.mole.yPosition} times.`:``}`,
3241
- "moleSize":`${mii.mole.size!==4?`On the mole page (within the seventh tab), `:``}${mii.mole.size<4?`press the shrink button ${4-mii.mole.size} times.`:mii.mole.size>4?`press the enlarge button ${mii.mole.size-4} times.`:``}`,
3242
- "beard":`On the beard page (within the seventh tab), set the beard to the one on the ${[0,1].includes(mii.beard.type)?`top`:[2,3].includes(mii.beard.type)?`middle`:`bottom`}-${[0,2].includes(mii.beard.type)?`left`:`right`}.`,
3243
- "beardCol":`On the mustache OR beard pages (within the seventh tab), set the color to the one ${mii.beard.color+1} from the top.`,
3244
- "heightWeight":`On the build page (eighth tab), set the height to ${Math.round((100/128)*mii.general.height)}%, and the weight to ${Math.round((100/128)*mii.general.weight)}%.`,
3245
- "col":`On the info page (after pressing "Next"), set the Favorite Color to ${mii.general.favoriteColor} (${mii.general.favoriteColor<=5?mii.general.favoriteColor+1:mii.general.favoriteColor-5} from the left, ${mii.general.favoriteColor>5?"bottom":"top"} row).`,
3246
- "other":`The Nickname of this Mii is ${mii.general.name}.${mii.general.creatorName?` The creator was ${mii.general.creatorName}.`:``} ${mii.general.birthday!==0?` Its birthday is ${["","January","February","March","April","May","June","July","August","September","October","November","December"][mii.general.birthMonth]} ${mii.general.birthday}.`:``}`
2884
+ else {
2885
+ var instrs = {
2886
+ "base": `Select "Start from Scratch", and then "${mii.general.gender}".`,
2887
+ "faceShape": `On the face page (first tab), set the face shape to the one ${Math.ceil((mii.face.type + 1) / 3)} from the top, and ${typeCheat[mii.face.type]} from the left.`,
2888
+ "skinCol": `On the face page (first tab), set the color to the one ${mii.face.color + 1} from the top.`,
2889
+ "makeup": `On the face page's makeup tab, set the makeup to the one ${Math.ceil((mii.face.makeup + 1) / 3)} from the top, and ${typeCheat[mii.face.makeup]} from the left.`,
2890
+ "feature": `On the face page's wrinkles tab, set the facial feature to the one ${Math.ceil((mii.face.feature + 1) / 3) + 1} from the top, and ${typeCheat[mii.face.makeup]} from the left.`,
2891
+ "hairStyle": `On the hair page (second tab), set the hair style to the one ${Math.ceil((mii.hair.type + 1) / 3)} from the top, and ${typeCheat[mii.hair.type]} from the left, on page ${mii.hair.page + 1}.`,
2892
+ "hairFlipped": `${mii.hair.flipped ? `On the hair page (second tab), press the button to flip the hair.` : ``}`,
2893
+ "hairColor": `On the hair page (second tab), set the hair color to the one ${mii.hair.color + 1} from the top.`,
2894
+ "eyebrowStyle": `On the eyebrow page (third tab), set the eyebrow style to the one ${typeCheat[mii.eyebrows.type]} from the left, ${Math.ceil((mii.eyebrows.type + 1) / 3)} from the top, on page ${mii.eyebrows.page + 1}.`,
2895
+ "eyebrowColor": `On the eyebrow page (third tab), set the eyebrow color to the one ${mii.eyebrows.color + 1} from the top.`,
2896
+ "eyebrowY": `${mii.eyebrows.yPosition !== 7 ? `On the eyebrow page (third tab), ` : ``}${mii.eyebrows.yPosition < 7 ? `press the up button ${7 - mii.eyebrows.yPosition} times.` : mii.eyebrows.yPosition > 7 ? `press the down button ${mii.eyebrows.yPosition - 7} times.` : ``}`,
2897
+ "eyebrowSize": `${mii.eyebrows.size !== 4 ? `On the eyebrow page (third tab), ` : ``}${mii.eyebrows.size < 4 ? `press the shrink button ${4 - mii.eyebrows.size} times.` : mii.eyebrows.size > 4 ? `press the enlarge button ${mii.eyebrows.size - 4} times.` : ``}`,
2898
+ "eyebrowRot": `${mii.eyebrows.rotation !== 6 ? `On the eyebrow page (third tab), ` : ``}${mii.eyebrows.rotation < 6 ? `press the rotate clockwise button ${6 - mii.eyebrows.rotation} times.` : mii.eyebrows.rotation > 6 ? `press the rotate counter-clockwise button ${mii.eyebrows.rotation - 6} times.` : ``}`,
2899
+ "eyebrowDist": `${mii.eyebrows.distanceApart !== 2 ? `On the eyebrow page (third tab), ` : ``}${mii.eyebrows.distanceApart < 2 ? `press the closer-together button ${2 - mii.eyebrows.distanceApart} times.` : mii.eyebrows.distanceApart > 2 ? `press the further-apart button ${mii.eyebrows.distanceApart - 2} times.` : ``}`,
2900
+ "eyebrowSquash": `${mii.eyebrows.squash !== 3 ? `On the eyebrow page (third tab), ` : ``}${mii.eyebrows.squash < 3 ? `press the squish button ${3 - mii.eyebrows.squash} times.` : mii.eyebrows.squash > 3 ? `press the un-squish button ${mii.eyebrows.squash - 3} times.` : ``}`,
2901
+ "eyeType": `On the eye page (fourth tab), set the eye type to the one ${typeCheat[mii.eyes.type]} from the left, ${Math.ceil((mii.eyes.type + 1) / 3)} from the top, on page ${mii.eyes.page + 1}.`,
2902
+ "eyeColor": `On the eye page (fourth tab), set the color to the one ${mii.eyes.col + 1} from the top.`,
2903
+ "eyeY": `${mii.eyes.yPosition !== 12 ? `On the eye page (fourth tab), ` : ``}${mii.eyes.yPosition < 12 ? `press the up button ${12 - mii.eyes.yPosition} times.` : mii.eyes.yPosition > 12 ? `press the down button ${mii.eyes.yPosition - 12} times.` : ``}`,
2904
+ "eyeSize": `${mii.eyes.size !== 4 ? `On the eye page (fourth tab), ` : ``}${mii.eyes.size < 4 ? `press the shrink button ${4 - mii.eyes.size} times.` : mii.eyes.size > 4 ? `press the enlarge button ${mii.eyes.size - 4} times.` : ``}`,
2905
+ "eyeRot": `${mii.eyes.rotation !== (mii.general.gender === "Female" ? 3 : 4) ? `On the eye page (fourth tab), ` : ``}${mii.eyes.rotation < (mii.general.gender === "Female" ? 3 : 4) ? `press the rotate clockwise button ${(mii.general.gender === "Female" ? 3 : 4) - mii.eyes.rotation} times.` : mii.eyes.rotation > (mii.general.gender === "Female" ? 3 : 4) ? `press the rotate counter-clockwise button ${mii.eyes.rotation - (mii.general.gender === "Female" ? 3 : 4)} times.` : ``}`,
2906
+ "eyeDist": `${mii.eyes.distanceApart !== 2 ? `On the eye page (fourth tab), ` : ``}${mii.eyes.distanceApart < 2 ? `press the closer-together button ${2 - mii.eyes.distanceApart} times.` : mii.eyes.distanceApart > 2 ? `press the further-apart button ${mii.eyes.distanceApart - 2} times.` : ``}`,
2907
+ "eyeSquash": `${mii.eyes.squash !== 3 ? `On the eye page (fourth tab), ` : ``}${mii.eyes.squash < 3 ? `press the squish button ${3 - mii.eyes.squash} times.` : mii.eyes.squash > 3 ? `press the un-squish button ${mii.eyes.squash - 3} times.` : ``}`,
2908
+ "noseType": `On the nose page (fifth tab), set the nose to the one ${Math.ceil((mii.nose.type + 1) / 3)} from the top, and ${typeCheat[mii.nose.type]} from the left, on page ${mii.nose.page}.`,
2909
+ "noseY": `${mii.nose.yPosition !== 9 ? `On the nose page (fifth tab), ` : ``}${mii.nose.yPosition < 9 ? `press the up button ${9 - mii.nose.yPosition} times.` : mii.nose.yPosition > 9 ? `press the down button ${mii.nose.yPosition - 9} times.` : ``}`,
2910
+ "noseSize": `${mii.nose.size !== 4 ? `On the nose page (fifth tab), ` : ``}${mii.nose.size < 4 ? `press the shrink button ${4 - mii.nose.size} times.` : mii.nose.size > 4 ? `press the enlarge button ${mii.nose.size - 4} times.` : ``}`,
2911
+ "mouthType": `On the mouth page (sixth tab), set the mouth type to the one ${typeCheat[mii.mouth.type]} from the left, ${Math.ceil((mii.mouth.type + 1) / 3)} from the top, on page ${mii.mouth.page + 1}.`,
2912
+ "mouthCol": `On the mouth page (sixth tab), set the color to the one ${mii.mouth.color + 1} from the top.`,
2913
+ "mouthY": `${mii.mouth.yPosition !== 13 ? `On the mouth page (sixth tab), ` : ``}${mii.mouth.yPosition < 13 ? `press the up button ${13 - mii.mouth.yPosition} times.` : mii.mouth.yPosition > 13 ? `press the down button ${mii.mouth.yPosition - 13} times.` : ``}`,
2914
+ "mouthSize": `${mii.mouth.size !== 4 ? `On the mouth page (sixth tab), ` : ``}${mii.mouth.size < 4 ? `press the shrink button ${4 - mii.mouth.size} times.` : mii.mouth.size > 4 ? `press the enlarge button ${mii.mouth.size - 4} times.` : ``}`,
2915
+ "mouthSquash": `${mii.mouth.squash !== 3 ? `On the mouth page (sixth tab), ` : ``}${mii.mouth.squash < 3 ? `press the squish button ${3 - mii.mouth.squash} times.` : mii.mouth.squash > 3 ? `press the un-squish button ${mii.mouth.squash - 3} times.` : ``}`,
2916
+ "glasses": `On the glasses page (within the seventh tab), set the glasses to the one ${Math.ceil((mii.glasses.type + 1) / 3)} from the top, and ${typeCheat[mii.glasses.type]} from the left.`,
2917
+ "glassesCol": `On the glasses page (within the seventh tab), set the color to the one ${mii.glasses.col + 1} from the top.`,
2918
+ "glassesY": `${mii.glasses.yPosition !== 10 ? `On the glasses page (within the seventh tab), ` : ``}${mii.glasses.yPosition < 10 ? `press the up button ${10 - mii.glasses.yPosition} times.` : mii.glasses.yPosition > 10 ? `press the down button ${mii.glasses.yPosition - 10} times.` : ``}`,
2919
+ "glassesSize": `${mii.glasses.size !== 4 ? `On the glasses page (within the seventh tab), ` : ``}${mii.glasses.size < 4 ? `press the shrink button ${4 - mii.glasses.size} times.` : mii.glasses.size > 4 ? `press the enlarge button ${mii.glasses.size - 4} times.` : ``}`,
2920
+ "stache": `On the mustache page (within the seventh tab), set the mustache to the one on the ${[0, 1].includes(mii.beard.mustache.type) ? `top` : [2, 3].includes(mii.beard.mustache.type) ? `middle` : `bottom`}-${[0, 2, 4].includes(mii.beard.mustache.type) ? `left` : `right`}.`,
2921
+ "stacheY": `${mii.beard.mustache.yPosition !== 10 ? `On the mustache page (within the seventh tab), press the ` : ``}${mii.beard.mustache.yPosition > 10 ? `down button ${mii.beard.mustache.yPosition - 10} times.` : mii.beard.mustache.yPosition < 10 ? `up button ${10 - mii.beard.mustache.yPosition} times.` : ``}`,
2922
+ "stacheSize": `${mii.beard.mustache.size !== 4 ? `On the mustache page (within the seventh tab), ` : ``}${mii.beard.mustache.size < 4 ? `press the shrink button ${4 - mii.beard.mustache.size} times.` : mii.beard.mustache.size > 4 ? `press the enlarge button ${mii.beard.mustache.size - 4} times.` : ``}`,
2923
+ "mole": `${mii.mole.on ? `On the mole page (within the seventh tab), turn the mole on.` : ``}`,
2924
+ "moleX": `${mii.mole.xPosition !== 2 ? `On the mole page (within the seventh tab), press the ` : ``}${mii.mole.xPosition > 2 ? `right button ${mii.mole.xPosition - 2} times.` : mii.mole.xPosition < 2 ? `left button ${2 - mii.mole.xPosition} times.` : ``}`,
2925
+ "moleY": `${mii.mole.yPosition !== 20 ? `On the mole page (within the seventh tab), press the ` : ``}${mii.mole.yPosition > 20 ? `down button ${mii.mole.yPosition - 20} times.` : mii.mole.yPosition < 20 ? `up button ${20 - mii.mole.yPosition} times.` : ``}`,
2926
+ "moleSize": `${mii.mole.size !== 4 ? `On the mole page (within the seventh tab), ` : ``}${mii.mole.size < 4 ? `press the shrink button ${4 - mii.mole.size} times.` : mii.mole.size > 4 ? `press the enlarge button ${mii.mole.size - 4} times.` : ``}`,
2927
+ "beard": `On the beard page (within the seventh tab), set the beard to the one on the ${[0, 1].includes(mii.beard.type) ? `top` : [2, 3].includes(mii.beard.type) ? `middle` : `bottom`}-${[0, 2].includes(mii.beard.type) ? `left` : `right`}.`,
2928
+ "beardCol": `On the mustache OR beard pages (within the seventh tab), set the color to the one ${mii.beard.color + 1} from the top.`,
2929
+ "heightWeight": `On the build page (eighth tab), set the height to ${Math.round((100 / 128) * mii.general.height)}%, and the weight to ${Math.round((100 / 128) * mii.general.weight)}%.`,
2930
+ "col": `On the info page (after pressing "Next"), set the Favorite Color to ${mii.general.favoriteColor} (${mii.general.favoriteColor <= 5 ? mii.general.favoriteColor + 1 : mii.general.favoriteColor - 5} from the left, ${mii.general.favoriteColor > 5 ? "bottom" : "top"} row).`,
2931
+ "other": `The Nickname of this Mii is ${mii.general.name}.${mii.general.creatorName ? ` The creator was ${mii.general.creatorName}.` : ``} ${mii.general.birthday !== 0 ? ` Its birthday is ${["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][mii.general.birthMonth]} ${mii.general.birthday}.` : ``}`
3247
2932
  };
3248
- if(!full){
3249
- var defaultMiiInstrs=structuredClone(mii.general.gender==="Male"?defaultInstrs["3ds"].male:defaultInstrs["3ds"].female);
3250
- Object.keys(instrs).forEach(instr=>{
3251
- if(instrs[instr]===defaultMiiInstrs[instr]){
2933
+ if (!full) {
2934
+ var defaultMiiInstrs = structuredClone(mii.general.gender === "Male" ? defaultInstrs["3ds"].male : defaultInstrs["3ds"].female);
2935
+ Object.keys(instrs).forEach(instr => {
2936
+ if (instrs[instr] === defaultMiiInstrs[instr]) {
3252
2937
  delete instrs[instr];
3253
2938
  }
3254
2939
  });
@@ -3257,26 +2942,118 @@ function generateInstructions(mii,full){
3257
2942
  }
3258
2943
  }
3259
2944
 
2945
+ function miiHeightToFeetInches(value) {
2946
+ const minInches = 36; // 3'0"
2947
+ const midInches = 69; // 5'9"
2948
+ const maxInches = 84; // 7'0"
2949
+ const midPoint = 64;
2950
+
2951
+ let totalInches;
2952
+ if (value <= midPoint) {
2953
+ // Lower half: 0–64 maps to 36–69
2954
+ totalInches = minInches + (value / midPoint) * (midInches - minInches);
2955
+ }
2956
+ else {
2957
+ // Upper half: 64–127 maps to 69–84
2958
+ totalInches = midInches + ((value - midPoint) / (127 - midPoint)) * (maxInches - midInches);
2959
+ }
2960
+
2961
+ const feet = Math.floor(totalInches / 12);
2962
+ const inches = Math.round(totalInches % 12);
2963
+ return { feet, inches, totalInches };
2964
+ }
2965
+ function inchesToMiiHeight(totalInches) {
2966
+ const minInches = 36;
2967
+ const midInches = 69;
2968
+ const maxInches = 84;
2969
+ const midPoint = 64;
2970
+
2971
+ let value;
2972
+ if (totalInches <= midInches) {
2973
+ // Below or equal to midpoint
2974
+ value = ((totalInches - minInches) / (midInches - minInches)) * midPoint;
2975
+ } else {
2976
+ // Above midpoint
2977
+ value = midPoint + ((totalInches - midInches) / (maxInches - midInches)) * (127 - midPoint);
2978
+ }
2979
+
2980
+ return Math.round(Math.max(0, Math.min(value, 127)));
2981
+ }
2982
+
2983
+ // Getting and setting Mii weights is HIGHLY EXPERIMENTAL and I am very unconfident in its output
2984
+ // ---- Tunable anchors (BMI breakpoints) ----
2985
+ const BMI_MIN = 16; // maps to Mii weight 0
2986
+ const BMI_MID = 23; // maps to Mii weight 64 (average look)
2987
+ const BMI_MAX = 40; // maps to Mii weight 127
2988
+ function heightWeightToMiiWeight(heightInches, weightLbs) {
2989
+ if (!heightInches || heightInches < 0) throw new Error("heightInches must be >= 0");
2990
+ const bmi = (703 * weightLbs) / (heightInches * heightInches);
2991
+
2992
+ let v;
2993
+ if (bmi <= BMI_MID) {
2994
+ const t = (clamp(bmi, BMI_MIN, BMI_MID) - BMI_MIN) / (BMI_MID - BMI_MIN);
2995
+ v = 0 + t * 64;
2996
+ } else {
2997
+ const t = (clamp(bmi, BMI_MID, BMI_MAX) - BMI_MID) / (BMI_MAX - BMI_MID);
2998
+ v = 64 + t * (127 - 64);
2999
+ }
3000
+ return Math.round(clamp(v, 0, 127));
3001
+ }
3002
+ function miiWeightToRealWeight(heightInches, miiWeight) {
3003
+ if (!heightInches || heightInches <= 0) heightInches = 0;
3004
+ const v = clamp(miiWeight, 0, 127);
3005
+
3006
+ let bmi;
3007
+ if (v <= 64) {
3008
+ const t = v / 64;
3009
+ bmi = BMI_MIN + t * (BMI_MID - BMI_MIN);
3010
+ } else {
3011
+ const t = (v - 64) / (127 - 64);
3012
+ bmi = BMI_MID + t * (BMI_MAX - BMI_MID);
3013
+ }
3014
+
3015
+ const pounds = (bmi * heightInches * heightInches) / 703;
3016
+ return { pounds, bmi };
3017
+ }
3018
+
3260
3019
 
3261
3020
 
3262
3021
  module.exports = {
3263
3022
  // Data
3264
3023
  Enums: require("./Enums"),
3265
3024
 
3266
- //Functions
3025
+ //Convert
3267
3026
  convertMii,
3268
3027
  convertMiiToStudio,
3269
-
3028
+ convertStudioToMii,
3029
+
3030
+ //Read
3270
3031
  readWiiBin,
3271
3032
  read3DSQR,
3272
-
3033
+
3034
+ //Render
3273
3035
  renderMiiWithStudio,
3274
3036
  renderMii,
3275
3037
 
3038
+ //Write
3276
3039
  writeWiiBin,
3277
3040
  write3DSQR,
3278
3041
 
3279
3042
  //make3DSChild, //WIP
3280
-
3281
- generateInstructions
3043
+
3044
+ //Instructions
3045
+ generateInstructions,
3046
+
3047
+ //Normalize Height and Weight 0-127 to human measurements
3048
+ miiHeightToFeetInches,
3049
+ inchesToMiiHeight,
3050
+ heightWeightToMiiWeight,//EXPERIMENTAL
3051
+ miiWeightToRealWeight,//EXPERIMENTAL
3052
+
3053
+ /*
3054
+ Handle Amiibo Functions
3055
+ insertMiiIntoAmiibo(amiiboDump, decrypted3DSMiiBuffer),
3056
+ extractMiiFromAmiibo(amiiboDump)
3057
+ */
3058
+ ...require("./amiiboHandler.js")
3282
3059
  }