miijs 2.2.2 → 2.3.1

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
1195
  }
1196
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];
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
+ }
1203
1205
  }
1204
- }
1205
1206
  }
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
- }
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
+ }
1212
1213
  }
1213
- }
1214
- return undefined;
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;
1215
1222
  }
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;
1230
+ }
1231
+ return decodeStudio(any);
1232
+ }
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;
1237
+ }
1238
+ throw new Error("Unsupported input type");
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
1665
  }
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
1666
  }
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
- }
1976
- }
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,137 +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(Buffer.isBuffer(binOrPath)){
2292
- data=binOrPath;
1956
+ if (Buffer.isBuffer(binOrPath)) {
1957
+ data = binOrPath;
2293
1958
  }
2294
1959
  else if (/[^01]/ig.test(binOrPath)) {
2295
1960
  data = await fs.promises.readFile(binOrPath);
2296
1961
  }
2297
- else{
1962
+ else {
2298
1963
  data = Buffer.from(binOrPath);
2299
1964
  }
2300
- var thisMii={
2301
- general:{},
2302
- perms:{},
2303
- meta:{},
2304
- face:{},
2305
- nose:{},
2306
- mouth:{},
2307
- mole:{},
2308
- hair:{},
2309
- eyebrows:{},
2310
- eyes:{},
2311
- glasses:{},
2312
- beard:{
2313
- 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: {}
2314
1979
  }
2315
1980
  };
2316
1981
 
2317
1982
  const get = address => getBinaryFromAddress(address, data);
2318
1983
 
2319
- var name="";
2320
- for(var i=0;i<10;i++){
2321
- 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) + "";
2322
1987
  }
2323
- thisMii.meta.name=name.replaceAll("\x00","");
2324
- var cname="";
2325
- for(var i=0;i<10;i++){
2326
- 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) + "";
2327
1992
  }
2328
- thisMii.meta.creatorName=cname.replaceAll("\x00","");
2329
- thisMii.general.gender=+get(0x00)[1];//0 for Male, 1 for Female
2330
- 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);
2331
- 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)) {
2332
1997
  case "010":
2333
- thisMii.meta.type="Special";
2334
- break;
1998
+ thisMii.meta.type = "Special";
1999
+ break;
2335
2000
  case "110":
2336
- thisMii.meta.type="Foreign";
2337
- break;
2001
+ thisMii.meta.type = "Foreign";
2002
+ break;
2338
2003
  default:
2339
- thisMii.meta.type="Default";
2340
- break;
2341
- }
2342
- 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);
2343
- var temp=get(0x20);
2344
- thisMii.face.type=parseInt(temp.slice(0,3),2);//0-7
2345
- thisMii.face.color=parseInt(temp.slice(3,6),2);//0-5
2346
- temp=get(0x21);
2347
- thisMii.face.feature=parseInt(get(0x20).slice(6,8)+temp.slice(0,2),2);//0-11
2348
- thisMii.perms.mingle=temp[5]==="0";//0 for Mingle, 1 for Don't Mingle
2349
- temp=get(0x2C);
2350
- thisMii.nose.type=+getKeyByValue(lookupTables.wiiNoses,parseInt(temp.slice(0,4),2));
2351
- thisMii.nose.size=parseInt(temp.slice(4,8),2);
2352
- thisMii.nose.yPosition=parseInt(get(0x2D).slice(0,5),2);//From top to bottom, 0-18, default 9
2353
- temp=get(0x2E);
2354
- thisMii.mouth.page=+lookupTables.mouthTable[""+parseInt(temp.slice(0,5),2)][0]-1;
2355
- 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
2356
- thisMii.mouth.color=parseInt(temp.slice(5,7),2);//0-2, refer to mouthColors array
2357
- temp2=get(0x2F);
2358
- thisMii.mouth.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-8, default 4
2359
- thisMii.mouth.yPosition=parseInt(temp2.slice(3,8),2);//0-18, default 9, from top to bottom
2360
- temp=get(0x00);
2361
- var temp2=get(0x01);
2362
- thisMii.general.birthMonth=parseInt(temp.slice(2,6),2);
2363
- thisMii.general.birthday=parseInt(temp.slice(6,8)+temp2.slice(0,3),2);
2364
- thisMii.general.favoriteColor=parseInt(temp2.slice(3,7),2);//0-11, refer to cols array
2365
- thisMii.general.height=parseInt(get(0x16),2);//0-127
2366
- thisMii.general.weight=parseInt(get(0x17),2);//0-127
2367
- thisMii.perms.fromCheckMiiOut=get(0x21)[7]==="0"?false:true;
2368
- temp=get(0x34);
2369
- temp2=get(0x35);
2370
- thisMii.mole.on=temp[0]==="0"?false:true;//0 for Off, 1 for On
2371
- thisMii.mole.size=parseInt(temp.slice(1,5),2);//0-8, default 4
2372
- thisMii.mole.xPosition=parseInt(temp2.slice(2,7),2);//0-16, Default 2
2373
- thisMii.mole.yPosition=parseInt(temp.slice(5,8)+temp2.slice(0,2),2);//Top to bottom
2374
- temp=get(0x22);
2375
- temp2=get(0x23);
2376
- thisMii.hair.page=+lookupTables.hairTable[""+parseInt(temp.slice(0,7),2)][0]-1;
2377
- 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
2378
- thisMii.hair.color=parseInt(temp[7]+temp2.slice(0,2),2);//0-7, refer to hairCols array
2379
- thisMii.hair.flipped=temp2[2]==="0"?false:true;
2380
- temp=get(0x24);
2381
- temp2=get(0x25);
2382
- thisMii.eyebrows.page=+lookupTables.eyebrowTable[""+parseInt(temp.slice(0,5),2)][0]-1;
2383
- 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
2384
- thisMii.eyebrows.rotation=parseInt(temp.slice(6,8)+temp2.slice(0,2),2);//0-11, default varies based on eyebrow type
2385
- temp=get(0x26);
2386
- temp2=get(0x27);
2387
- thisMii.eyebrows.color=parseInt(temp.slice(0,3),2);
2388
- thisMii.eyebrows.size=parseInt(temp.slice(3,7),2);//0-8, default 4
2389
- thisMii.eyebrows.yPosition=(parseInt(temp[7]+temp2.slice(0,4),2))-3;//0-15, default 10
2390
- thisMii.eyebrows.distanceApart=parseInt(temp2.slice(4,8),2);//0-12, default 2
2391
- thisMii.eyes.page=+lookupTables.eyeTable[parseInt(get(0x28).slice(0,6),2)][0]-1;//0-47, needs lookup table
2392
- 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
2393
- temp=get(0x29);
2394
- thisMii.eyes.rotation=parseInt(temp.slice(0,3),2);//0-7, default varies based on eye type
2395
- thisMii.eyes.yPosition=parseInt(temp.slice(3,8),2);//0-18, default 12, top to bottom
2396
- temp=get(0x2A);
2397
- thisMii.eyes.color=parseInt(temp.slice(0,3),2);//0-5
2398
- thisMii.eyes.size=parseInt(temp.slice(4,7),2);//0-7, default 4
2399
- temp2=get(0x2B);
2400
- thisMii.eyes.distanceApart=parseInt(temp[7]+temp2.slice(0,3),2);//0-12, default 2
2401
- temp=get(0x30);
2402
- thisMii.glasses.type=parseInt(temp.slice(0,4),2);//0-8
2403
- thisMii.glasses.color=parseInt(temp.slice(4,7),2);//0-5
2404
- temp=get(0x31);
2405
- thisMii.glasses.size=parseInt(temp.slice(0,3),2);//0-7, default 4
2406
- thisMii.glasses.yPosition=parseInt(temp.slice(3,8),2);//0-20, default 10
2407
- temp=get(0x32);
2408
- temp2=get(0x33);
2409
- thisMii.beard.mustache.type=parseInt(temp.slice(0,2),2);//0-3
2410
- thisMii.beard.type=parseInt(temp.slice(2,4),2);//0-3
2411
- thisMii.beard.color=parseInt(temp.slice(4,7),2);//0-7
2412
- thisMii.beard.mustache.size=parseInt(temp[7]+temp2.slice(0,3),2);//0-30, default 20
2413
- thisMii.beard.mustache.yPosition=parseInt(temp2.slice(3,8),2);//0-16, default 2
2414
- 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";
2415
2080
  return thisMii;
2416
2081
  }
2417
- 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) {
2418
2207
  let qrCode;
2419
- if (/[^01]/ig.test(binOrPath)) {
2208
+ if (Buffer.isBuffer(binOrPath)) {//Buffer
2209
+ qrCode = binOrPath;
2210
+ }
2211
+ else if (/[^01]/ig.test(binOrPath)) {//File path
2420
2212
  var data = await fs.promises.readFile(binOrPath);
2421
2213
  var img = await loadImage(data);
2422
2214
  const canvas = createCanvas(img.width, img.height);
@@ -2424,12 +2216,12 @@ async function read3DSQR(binOrPath,returnDecryptedBin) {
2424
2216
  ctx.drawImage(img, 0, 0);
2425
2217
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
2426
2218
  qrCode = jsQR(imageData.data, imageData.width, imageData.height)?.binaryData;
2427
- if(!qrCode){
2219
+ if (!qrCode) {
2428
2220
  console.error("Failed to read QR Code.");
2429
2221
  return;
2430
2222
  }
2431
2223
  }
2432
- else {
2224
+ else {//String of 0s and 1s
2433
2225
  var d = binOrPath.match(/(0|1){1,8}/g);
2434
2226
  qrCode = [];
2435
2227
  d.forEach(byte => {
@@ -2437,141 +2229,30 @@ async function read3DSQR(binOrPath,returnDecryptedBin) {
2437
2229
  });
2438
2230
  }
2439
2231
  if (qrCode) {
2440
- var data = Buffer.from(decodeAesCcm(new Uint8Array(qrCode)));
2441
- if(returnDecryptedBin){
2232
+ var data;
2233
+ data = Buffer.from(decodeAesCcm(new Uint8Array(qrCode)));
2234
+ if (returnDecryptedBin) {
2442
2235
  return data;
2443
2236
  }
2444
- const miiJson = {
2445
- general:{},
2446
- perms:{},
2447
- meta:{},
2448
- face:{},
2449
- nose:{},
2450
- mouth:{},
2451
- mole:{},
2452
- hair:{},
2453
- eyebrows:{},
2454
- eyes:{},
2455
- glasses:{},
2456
- beard:{
2457
- mustache:{}
2458
- }
2459
- };
2460
- const get = address => getBinaryFromAddress(address, data);
2461
- var temp=get(0x18);
2462
- var temp2=get(0x19);
2463
- miiJson.general.birthday=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
2464
- miiJson.general.birthMonth=parseInt(temp.slice(3,7),2);
2465
- //Handle UTF-16 Names
2466
- var name = "";
2467
- for (var i = 0x1A; i < 0x2E; i += 2) {
2468
- let lo = data[i];
2469
- let hi = data[i + 1];
2470
- if (lo === 0x00 && hi === 0x00) {
2471
- break;
2472
- }
2473
- let codeUnit = (hi << 8) | lo;
2474
- name += String.fromCharCode(codeUnit);
2237
+
2238
+ var ret;
2239
+ try {
2240
+ ret = decode3DSMii(data);
2475
2241
  }
2476
- miiJson.meta.name = name.replace(/\u0000/g, "");
2477
- var cname = "";
2478
- for (var i = 0x48; i < 0x5C; i += 2) {
2479
- let lo = data[i];
2480
- let hi = data[i + 1];
2481
- if (lo === 0x00 && hi === 0x00) {
2482
- break;
2483
- }
2484
- let codeUnit = (hi << 8) | lo;
2485
- cname += String.fromCharCode(codeUnit);
2242
+ catch (e) {
2243
+ ret = decode3DSMii(qrCode);
2486
2244
  }
2487
- miiJson.meta.creatorName = cname.replace(/\u0000/g, "");
2488
- miiJson.general.height=parseInt(get(0x2E),2);
2489
- miiJson.general.weight=parseInt(get(0x2F),2);
2490
- miiJson.general.gender=+temp[7];
2491
- temp=get(0x30);
2492
- miiJson.perms.sharing=temp[7]==="1"?false:true;
2493
- miiJson.general.favoriteColor=parseInt(temp2.slice(2,6),2);
2494
- miiJson.perms.copying=get(0x01)[7]==="1"?true:false;
2495
- miiJson.hair.page=lookupTable("hairs",parseInt(get(0x32),2),true)[0];
2496
- miiJson.hair.type=lookupTable("hairs",parseInt(get(0x32),2),true)[1];
2497
- miiJson.face.type=lookupTable("faces",parseInt(temp.slice(3,7),2),false);
2498
- miiJson.face.color=parseInt(temp.slice(0,3),2);
2499
- temp=get(0x31);
2500
- miiJson.face.feature=parseInt(temp.slice(4,8),2);
2501
- miiJson.face.makeup=parseInt(temp.slice(0,4),2);
2502
- temp=get(0x34);
2503
- miiJson.eyes.page=lookupTable("eyes",parseInt(temp.slice(2,8),2),true)[0];
2504
- miiJson.eyes.type=lookupTable("eyes",parseInt(temp.slice(2,8),2),true)[1];
2505
- temp2=get(0x33);
2506
- miiJson.hair.color=parseInt(temp2.slice(5,8),2);
2507
- miiJson.hair.flipped=temp2[4]==="0"?false:true;
2508
- miiJson.eyes.color=parseInt(get(0x35)[7]+temp.slice(0,2),2);
2509
- temp=get(0x35);
2510
- miiJson.eyes.size=parseInt(temp.slice(3,7),2);
2511
- miiJson.eyes.squash=parseInt(temp.slice(0,3),2);
2512
- temp=get(0x36);
2513
- temp2=get(0x37);
2514
- miiJson.eyes.rotation=parseInt(temp.slice(3,8),2);
2515
- miiJson.eyes.distanceApart=parseInt(temp2[7]+temp.slice(0,3),2);
2516
- miiJson.eyes.yPosition=parseInt(temp2.slice(2,7),2);
2517
- temp=get(0x38);
2518
- miiJson.eyebrows.page=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true)[0];
2519
- miiJson.eyebrows.type=lookupTable("eyebrows",parseInt(temp.slice(3,8),2),true)[1];
2520
- miiJson.eyebrows.color=parseInt(temp.slice(0,3),2);
2521
- temp=get(0x39);
2522
- miiJson.eyebrows.size=parseInt(temp.slice(4,8),2);
2523
- miiJson.eyebrows.squash=parseInt(temp.slice(1,4),2);
2524
- temp=get(0x3A);
2525
- miiJson.eyebrows.rotation=parseInt(temp.slice(4,8),2);
2526
- temp2=get(0x3B);
2527
- miiJson.eyebrows.distanceApart=parseInt(temp2[7]+temp.slice(0,3),2);
2528
- miiJson.eyebrows.yPosition=parseInt(temp2.slice(2,7),2)-3;
2529
- temp=get(0x3C);
2530
- miiJson.nose.page=lookupTable("noses",parseInt(temp.slice(3,8),2),true)[0];
2531
- miiJson.nose.type=lookupTable("noses",parseInt(temp.slice(3,8),2),true)[1];
2532
- temp2=get(0x3D);
2533
- miiJson.nose.size=parseInt(temp2[7]+temp.slice(0,3),2);
2534
- miiJson.nose.yPosition=parseInt(temp2.slice(2,7),2);
2535
- temp=get(0x3E);
2536
- miiJson.mouth.page=lookupTable("mouths",parseInt(temp.slice(2,8),2),true)[0];
2537
- miiJson.mouth.type=lookupTable("mouths",parseInt(temp.slice(2,8),2),true)[1];
2538
- temp2=get(0x3F);
2539
- miiJson.mouth.color=parseInt(temp2[7]+temp.slice(0,2),2);
2540
- miiJson.mouth.size=parseInt(temp2.slice(3,7),2);
2541
- miiJson.mouth.squash=parseInt(temp2.slice(0,3),2);
2542
- temp=get(0x40);
2543
- miiJson.mouth.yPosition=parseInt(temp.slice(3,8),2);
2544
- miiJson.beard.mustache.type=parseInt(temp.slice(0,3),2);
2545
- temp=get(0x42);
2546
- miiJson.beard.type=parseInt(temp.slice(5,8),2);
2547
- miiJson.beard.color=parseInt(temp.slice(2,5),2);
2548
- temp2=get(0x43);
2549
- miiJson.beard.mustache.size=parseInt(temp2.slice(6,8)+temp.slice(0,2),2);
2550
- miiJson.beard.mustache.yPosition=parseInt(temp2.slice(1,6),2);
2551
- temp=get(0x44);
2552
- miiJson.glasses.type=parseInt(temp.slice(4,8),2);
2553
- miiJson.glasses.color=parseInt(temp.slice(1,4),2);
2554
- temp2=get(0x45);
2555
- miiJson.glasses.size=parseInt(temp2.slice(5,8)+temp[0],2);
2556
- miiJson.glasses.yPosition=parseInt(temp2.slice(0,5),2);
2557
- temp=get(0x46);
2558
- miiJson.mole.on=temp[7]==="0"?false:true;
2559
- miiJson.mole.size=parseInt(temp.slice(3,7),2);
2560
- temp2=get(0x47);
2561
- miiJson.mole.xPosition=parseInt(temp2.slice(6,8)+temp.slice(0,3),2);
2562
- miiJson.mole.yPosition=parseInt(temp2.slice(1,6),2);
2563
- miiJson.meta.type="Default";//qk, Make this actually retrieve MiiID, SystemID, and Mii type
2564
- miiJson.console="3DS";
2565
- return miiJson;
2566
- } else {
2245
+ return ret;
2246
+ }
2247
+ else {
2567
2248
  console.error('Failed to read Mii.');
2568
2249
  }
2569
2250
  }
2570
- async function renderMiiWithStudio(jsonIn){
2571
- if(!["3ds","wii u"].includes(jsonIn.console?.toLowerCase())){
2572
- jsonIn=convertMii(jsonIn);
2251
+ async function renderMiiWithStudio(jsonIn) {
2252
+ if (!["3ds", "wii u"].includes(jsonIn.console?.toLowerCase())) {
2253
+ jsonIn = convertMii(jsonIn);
2573
2254
  }
2574
- var studioMii=convertMiiToStudio(jsonIn);
2255
+ var studioMii = convertMiiToStudio(jsonIn);
2575
2256
  return await downloadImage('https://studio.mii.nintendo.com/miis/image.png?data=' + studioMii + "&width=270&type=face");
2576
2257
  }
2577
2258
  async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes) {
@@ -2597,8 +2278,8 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2597
2278
  // Create a dummy canvas for Three.js to use.
2598
2279
  const canvas = {
2599
2280
  width, height, style: {},
2600
- addEventListener() {},
2601
- removeEventListener() {},
2281
+ addEventListener() { },
2282
+ removeEventListener() { },
2602
2283
  // Return the context for 'webgl' (not webgl2)
2603
2284
  getContext: (type, _) => type === 'webgl' ? gl : null,
2604
2285
  };
@@ -2616,7 +2297,7 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2616
2297
  const scene = new THREE.Scene();
2617
2298
  scene.background = null; // Transparent background.
2618
2299
 
2619
- if(useBody){
2300
+ if (useBody) {
2620
2301
  // After: const scene = new THREE.Scene(); scene.background = null;
2621
2302
  const ambient = new THREE.AmbientLight(0xffffff, 0.15);
2622
2303
  scene.add(ambient);
@@ -2637,16 +2318,16 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2637
2318
 
2638
2319
  // Create Mii model and add to the scene.
2639
2320
  const studioRaw = parseHexOrB64ToUint8Array(data); // Parse studio data
2640
-
2321
+
2641
2322
  // Convert Uint8Array to Buffer for struct-fu compatibility
2642
2323
  const studioBuffer = Buffer.from(studioRaw);
2643
-
2324
+
2644
2325
  currentCharModel = createCharModel(studioBuffer, null, FFLShaderMaterial, ffl.module);
2645
2326
  initCharModelTextures(currentCharModel, renderer); // Initialize fully
2646
2327
  scene.add(currentCharModel.meshes); // Add to scene
2647
2328
 
2648
2329
  //Add body
2649
- if (useBody) {
2330
+ if (useBody) {
2650
2331
  if (typeof GLTFLoader === 'undefined' || !GLTFLoader) {
2651
2332
  const mod = await import('three/examples/jsm/loaders/GLTFLoader.js');
2652
2333
  GLTFLoader = mod.GLTFLoader;
@@ -2667,20 +2348,20 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2667
2348
 
2668
2349
  const body = gltf.scene;
2669
2350
 
2670
- body.position.y-=110;
2351
+ body.position.y -= 110;
2671
2352
 
2672
2353
  //Recolor
2673
2354
  body.userData.isMiiBody = true;
2674
2355
  body.traverse(o => {
2675
2356
  if (o.isMesh) {
2676
2357
  if (!o.geometry.attributes.normal) {
2677
- o.geometry.computeVertexNormals();
2358
+ o.geometry.computeVertexNormals();
2678
2359
  }
2679
2360
  const isShirt = (o.name === 'mesh_1_');
2680
2361
  o.material?.dispose?.();
2681
2362
  o.material = new THREE.MeshLambertMaterial({
2682
2363
  //["Red", "Orange", "Yellow", "Lime", "Green", "Blue", "Cyan", "Pink", "Purple", "Brown", "White", "Black"]
2683
- 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,
2684
2365
  emissive: isShirt ? 0x330000 : 0x222222,
2685
2366
  emissiveIntensity: 0.0
2686
2367
  });
@@ -2742,9 +2423,9 @@ async function createFFLMiiIcon(data, width, height, useBody, shirtColor, fflRes
2742
2423
  //}
2743
2424
  }
2744
2425
  }
2745
- async function renderMii(jsonIn, fflRes=getFFLRes()){
2746
- if(!["3ds","wii u"].includes(jsonIn.console?.toLowerCase())){
2747
- jsonIn=convertMii(jsonIn);
2426
+ async function renderMii(jsonIn, fflRes = getFFLRes()) {
2427
+ if (!["3ds", "wii u"].includes(jsonIn.console?.toLowerCase())) {
2428
+ jsonIn = convertMii(jsonIn);
2748
2429
  }
2749
2430
  const studioMii = convertMiiToStudio(jsonIn);
2750
2431
  const width = height = 600;
@@ -2755,153 +2436,153 @@ async function writeWiiBin(jsonIn, outPath) {
2755
2436
  if (jsonIn.console?.toLowerCase() !== "wii") {
2756
2437
  convertMii(jsonIn);
2757
2438
  }
2758
- var mii=jsonIn;
2759
- var miiBin="0";
2760
- miiBin+=mii.general.gender;
2761
- miiBin+=mii.general.birthMonth.toString(2).padStart(4,"0");
2762
- miiBin+=mii.general.birthday.toString(2).padStart(5,"0");
2763
- miiBin+=mii.general.favoriteColor.toString(2).padStart(4,"0");
2764
- miiBin+='0';
2765
- for(var i=0;i<10;i++){
2766
- if(i<mii.meta.name.length){
2767
- 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");
2768
2449
  }
2769
- else{
2770
- miiBin+="0000000000000000";
2450
+ else {
2451
+ miiBin += "0000000000000000";
2771
2452
  }
2772
2453
  }
2773
- miiBin+=mii.general.height.toString(2).padStart(8,"0");
2774
- miiBin+=mii.general.weight.toString(2).padStart(8,"0");
2775
- let miiId="";
2776
- 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) {
2777
2458
  case "Special":
2778
- miiId="01000110";
2779
- break;
2459
+ miiId = "01000110";
2460
+ break;
2780
2461
  case "Foreign":
2781
- miiId="11000110";
2782
- break;
2462
+ miiId = "11000110";
2463
+ break;
2783
2464
  default:
2784
- miiId="10001001";
2785
- break;
2786
- }
2787
- for(var i=0;i<3;i++){
2788
- miiId+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
2789
- }
2790
- miiBin+=miiId;
2791
- miiBin+="11111111".repeat(4);//System ID
2792
- miiBin+=mii.face.type.toString(2).padStart(3,"0");
2793
- miiBin+=mii.face.color.toString(2).padStart(3,"0");
2794
- miiBin+=mii.face.feature.toString(2).padStart(4,"0");
2795
- miiBin+="000";
2796
- if(mii.perms.mingle&&mii.meta.type.toLowerCase()==="special"){
2797
- 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;
2798
2479
  console.warn("A Special Mii cannot have Mingle on and still render on the Wii. Turned Mingle off in the output.");
2799
2480
  }
2800
- miiBin+=mii.perms.mingle?"0":"1";
2801
- miiBin+="0";
2802
- miiBin+=mii.perms.fromCheckMiiOut?"1":"0";
2803
- miiBin+=(+getKeyByValue(lookupTables.hairTable,`${mii.hair.page+1}${convTables.formatFrom[mii.hair.type]}`)).toString(2).padStart(7,"0");
2804
- miiBin+=mii.hair.color.toString(2).padStart(3,"0");
2805
- miiBin+=mii.hair.flipped?"1":"0";
2806
- miiBin+="00000";
2807
- miiBin+=(+getKeyByValue(lookupTables.eyebrowTable,`${mii.eyebrows.page+1}${convTables.formatFrom[mii.eyebrows.type]}`)).toString(2).padStart(5,"0");
2808
- miiBin+="0";
2809
- miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
2810
- miiBin+="000000";
2811
- miiBin+=mii.eyebrows.color.toString(2).padStart(3,"0");
2812
- miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
2813
- miiBin+=(mii.eyebrows.yPosition+3).toString(2).padStart(5,"0");
2814
- miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0");
2815
- miiBin+=(+getKeyByValue(lookupTables.eyeTable,`${mii.eyes.page+1}${convTables.formatFrom[mii.eyes.type]}`)).toString(2).padStart(6,"0");
2816
- miiBin+="00";
2817
- miiBin+=mii.eyes.rotation.toString(2).padStart(3,"0");
2818
- miiBin+=mii.eyes.yPosition.toString(2).padStart(5,"0");
2819
- miiBin+=mii.eyes.color.toString(2).padStart(3,"0");
2820
- miiBin+="0";
2821
- miiBin+=mii.eyes.size.toString(2).padStart(3,"0");
2822
- miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0");
2823
- miiBin+="00000";
2824
- miiBin+=lookupTables.wiiNoses[mii.nose.type].toString(2).padStart(4,"0");
2825
- miiBin+=mii.nose.size.toString(2).padStart(4,"0");
2826
- miiBin+=mii.nose.yPosition.toString(2).padStart(5,"0");
2827
- miiBin+="000";
2828
- miiBin+=(+getKeyByValue(lookupTables.mouthTable,`${mii.mouth.page+1}${convTables.formatFrom[mii.mouth.type]}`)).toString(2).padStart(5,"0");
2829
- miiBin+=mii.mouth.color.toString(2).padStart(2,"0");
2830
- miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
2831
- miiBin+=mii.mouth.yPosition.toString(2).padStart(5,"0");
2832
- miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
2833
- miiBin+=mii.glasses.color.toString(2).padStart(3,"0");
2834
- miiBin+="0";
2835
- miiBin+=mii.glasses.size.toString(2).padStart(3,"0");
2836
- miiBin+=mii.glasses.yPosition.toString(2).padStart(5,"0");
2837
- miiBin+=mii.beard.mustache.type.toString(2).padStart(2,"0");
2838
- miiBin+=mii.beard.type.toString(2).padStart(2,"0");
2839
- miiBin+=mii.beard.color.toString(2).padStart(3,"0");
2840
- miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0");
2841
- miiBin+=mii.beard.mustache.yPosition.toString(2).padStart(5,"0");
2842
- miiBin+=mii.mole.on?"1":"0";
2843
- miiBin+=mii.mole.size.toString(2).padStart(4,"0");
2844
- miiBin+=mii.mole.yPosition.toString(2).padStart(5,"0");
2845
- miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0");
2846
- miiBin+="0";
2847
- for(var i=0;i<10;i++){
2848
- if(i<mii.meta.creatorName.length){
2849
- 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");
2850
2531
  }
2851
- else{
2852
- miiBin+="0000000000000000";
2532
+ else {
2533
+ miiBin += "0000000000000000";
2853
2534
  }
2854
2535
  }
2855
-
2536
+
2856
2537
  //Writing based on miiBin
2857
- var toWrite=miiBin.match(/.{1,8}/g);
2858
- var buffers=[];
2859
- for(var i=0;i<toWrite.length;i++){
2860
- 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));
2861
2542
  }
2862
- toWrite=Buffer.from(buffers);
2863
- if(outPath){
2543
+ toWrite = Buffer.from(buffers);
2544
+ if (outPath) {
2864
2545
  await fs.promises.writeFile(outPath, toWrite);
2865
2546
  }
2866
- else{
2547
+ else {
2867
2548
  return toWrite;
2868
2549
  }
2869
2550
  }
2870
- async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2551
+ async function write3DSQR(miiJson, outPath, returnBin, fflRes = getFFLRes()) {
2871
2552
  //Convert the Mii if it isn't in 3DS format
2872
2553
  if (!["3ds", "wii u"].includes(miiJson.console?.toLowerCase())) {
2873
2554
  miiJson = convertMii(miiJson);
2874
2555
  }
2875
-
2556
+
2876
2557
  //Make the binary
2877
- var mii=miiJson;
2558
+ var mii = miiJson;
2878
2559
  var miiBin = "00000011";
2879
2560
  //If Special Miis are being used improperly, fix it and warn the user
2880
- if(mii.meta.type.toLowerCase()==="special"&&(mii.console.toLowerCase()==="wii u"||mii.console.toLowerCase()==="wiiu")){
2881
- 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";
2882
2563
  console.warn("Wii Us do not work with Special Miis. Reverted to Default Mii.");
2883
2564
  }
2884
- if(mii.perms.sharing&&mii.meta.type==="Special"){
2885
- mii.perms.sharing=false;
2565
+ if (mii.perms.sharing && mii.meta.type === "Special") {
2566
+ mii.perms.sharing = false;
2886
2567
  console.warn("Cannot have Sharing enabled for Special Miis. Disabled Sharing in the output.");
2887
2568
  }
2888
- miiBin+="0000000";
2889
- miiBin+=mii.perms.copying?"1":"0";
2890
- miiBin+="00000000";
2891
- miiBin+="00110000";
2892
- miiBin+="1000101011010010000001101000011100011000110001100100011001100110010101100111111110111100000001110101110001000101011101100000001110100100010000000000000000000000".slice(0,8*8);
2893
- miiBin+=mii.meta.type==="Special"?"0":"1";
2894
- miiBin+="0000000";
2895
- for(var i=0;i<3;i++){
2896
- miiBin+=Math.floor(Math.random()*255).toString(2).padStart(8,"0");
2897
- }
2898
- miiBin+="0000000001000101011101100000001110100100010000000000000000000000";
2899
- miiBin+=mii.general.birthday.toString(2).padStart(5,"0").slice(2,5);
2900
- miiBin+=mii.general.birthMonth.toString(2).padStart(4,"0");
2901
- miiBin+=mii.general.gender;
2902
- miiBin+="00";
2903
- miiBin+=mii.general.favoriteColor.toString(2).padStart(4,"0");
2904
- 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);
2905
2586
  for (var i = 0; i < 10; i++) {
2906
2587
  if (i < mii.meta.name.length) {
2907
2588
  let code = mii.meta.name.charCodeAt(i);
@@ -2912,69 +2593,69 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2912
2593
  miiBin += "0000000000000000";
2913
2594
  }
2914
2595
  }
2915
- miiBin+=mii.general.height.toString(2).padStart(8,"0");
2916
- miiBin+=mii.general.weight.toString(2).padStart(8,"0");
2917
- miiBin+=mii.face.color.toString(2).padStart(3,"0");
2918
- miiBin+=lookupTables.faces.values[mii.face.type].toString(2).padStart(4,"0");
2919
- miiBin+=mii.perms.sharing?"0":"1";
2920
- miiBin+=mii.face.makeup.toString(2).padStart(4,"0");
2921
- miiBin+=mii.face.feature.toString(2).padStart(4,"0");
2922
- miiBin+=lookupTables.hairs.values[mii.hair.page][mii.hair.type].toString(2).padStart(8,"0");
2923
- miiBin+="0000";
2924
- miiBin+=mii.hair.flipped?"1":"0";
2925
- miiBin+=mii.hair.color.toString(2).padStart(3,"0");
2926
- miiBin+=mii.eyes.color.toString(2).padStart(3,"0").slice(1,3);
2927
- miiBin+=lookupTables.eyes.values[mii.eyes.page][mii.eyes.type].toString(2).padStart(6,"0");
2928
- miiBin+=mii.eyes.squash.toString(2).padStart(3,"0");
2929
- miiBin+=mii.eyes.size.toString(2).padStart(4,"0");
2930
- miiBin+=mii.eyes.color.toString(2).padStart(3,"0")[0];
2931
- miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0").slice(1,4);
2932
- miiBin+=mii.eyes.rotation.toString(2).padStart(5,"0");
2933
- miiBin+="00";
2934
- miiBin+=mii.eyes.yPosition.toString(2).padStart(5,"0");
2935
- miiBin+=mii.eyes.distanceApart.toString(2).padStart(4,"0")[0];
2936
- miiBin+=mii.eyebrows.color.toString(2).padStart(3,"0");
2937
- miiBin+=lookupTables.eyebrows.values[mii.eyebrows.page][mii.eyebrows.type].toString(2).padStart(5,"0");
2938
- miiBin+="0";
2939
- miiBin+=mii.eyebrows.squash.toString(2).padStart(3,"0");
2940
- miiBin+=mii.eyebrows.size.toString(2).padStart(4,"0");
2941
- miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0").slice(1,4);
2942
- miiBin+="0";
2943
- miiBin+=mii.eyebrows.rotation.toString(2).padStart(4,"0");
2944
- miiBin+="00";
2945
- miiBin+=(mii.eyebrows.yPosition+3).toString(2).padStart(5,"0");
2946
- miiBin+=mii.eyebrows.distanceApart.toString(2).padStart(4,"0")[0];
2947
- miiBin+=mii.nose.size.toString(2).padStart(4,"0").slice(1,4);
2948
- miiBin+=lookupTables.noses.values[mii.nose.page][mii.nose.type].toString(2).padStart(5,"0");
2949
- miiBin+="00";
2950
- miiBin+=mii.nose.yPosition.toString(2).padStart(5,"0");
2951
- miiBin+=mii.nose.size.toString(2).padStart(4,"0")[0];
2952
- miiBin+=mii.mouth.color.toString(2).padStart(3,"0").slice(1,3);
2953
- miiBin+=lookupTables.mouths.values[mii.mouth.page][mii.mouth.type].toString(2).padStart(6,"0");
2954
- miiBin+=mii.mouth.squash.toString(2).padStart(3,"0");
2955
- miiBin+=mii.mouth.size.toString(2).padStart(4,"0");
2956
- miiBin+=mii.mouth.color.toString(2).padStart(3,"0")[0];
2957
- miiBin+=mii.beard.mustache.type.toString(2).padStart(3,"0");
2958
- miiBin+=mii.mouth.yPosition.toString(2).padStart(5,"0");
2959
- miiBin+="00000000";
2960
- miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0").slice(2,4);
2961
- miiBin+=mii.beard.color.toString(2).padStart(3,"0");
2962
- miiBin+=mii.beard.type.toString(2).padStart(3,"0");
2963
- miiBin+="0";
2964
- miiBin+=mii.beard.mustache.yPosition.toString(2).padStart(5,"0");
2965
- miiBin+=mii.beard.mustache.size.toString(2).padStart(4,"0").slice(0,2);
2966
- miiBin+=mii.glasses.size.toString(2).padStart(4,"0")[3];
2967
- miiBin+=mii.glasses.color.toString(2).padStart(3,"0");
2968
- miiBin+=mii.glasses.type.toString(2).padStart(4,"0");
2969
- miiBin+="0";
2970
- miiBin+=mii.glasses.yPosition.toString(2).padStart(4,"0");
2971
- miiBin+=mii.glasses.size.toString(2).padStart(4,"0").slice(0,3);
2972
- miiBin+=mii.mole.xPosition.toString(2).padStart(5,"0").slice(2,5);
2973
- miiBin+=mii.mole.size.toString(2).padStart(4,"0");
2974
- miiBin+=mii.mole.on?"1":"0";
2975
- miiBin+="0";
2976
- miiBin+=mii.mole.yPosition.toString(2).padStart(5,"0");
2977
- 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);
2978
2659
  for (var i = 0; i < 10; i++) {
2979
2660
  if (i < mii.meta.creatorName.length) {
2980
2661
  let code = mii.meta.creatorName.charCodeAt(i);
@@ -2986,10 +2667,10 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
2986
2667
  }
2987
2668
  }
2988
2669
  //Writing based on the binary
2989
- var toWrite=miiBin.match(/.{1,8}/g);
2990
- var buffers=[];
2991
- for(var i=0;i<toWrite.length;i++){
2992
- 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));
2993
2674
  }
2994
2675
  const buffer = Buffer.from(buffers);
2995
2676
  var encryptedData = Buffer.from(encodeAesCcm(new Uint8Array(buffer)));
@@ -3011,8 +2692,8 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
3011
2692
  crossOrigin: "anonymous",
3012
2693
  imageSize: 0.4 // Changes how large center area is
3013
2694
  },
3014
- qrOptions:{
3015
- errorCorrectionLevel:'H'
2695
+ qrOptions: {
2696
+ errorCorrectionLevel: 'H'
3016
2697
  }
3017
2698
  }
3018
2699
  const qrCodeImage = new QRCodeStyling({
@@ -3072,187 +2753,187 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
3072
2753
 
3073
2754
  // Get the buffer
3074
2755
  const imageBuffer = await main_img.getBufferAsync(Jimp.MIME_PNG);
3075
-
2756
+
3076
2757
  // Optionally write to file if outPath is provided
3077
2758
  if (outPath) {
3078
2759
  await main_img.writeAsync(outPath);
3079
2760
  }
3080
-
2761
+
3081
2762
  return imageBuffer;
3082
2763
  }
3083
- function make3DSChild(dad,mom,options={}){
3084
- if(!["3ds","wii u"].includes(dad.meta.console?.toLowerCase())){
3085
- dad=convertMii(dad,"wii");
3086
- }
3087
- if(!["3ds","wii u"].includes(mom.meta.console?.toLowerCase())){
3088
- mom=convertMii(dad,"wii");
3089
- }
3090
- var g=options.gender||Math.floor(Math.random()*2);
3091
- var child={
3092
- "general":{
3093
- "birthMonth":new Date().getMonth()+1,
3094
- "birthday":new Date().getDay(),
3095
- "height":64,
3096
- "weight":64,
3097
- "gender":g,
3098
- "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)]
3099
2780
  },
3100
- "meta":{
3101
- "name":options.name||kidNames[g][Math.floor(Math.random()*kidNames[g].length)],
3102
- "creatorName":"",
2781
+ "meta": {
2782
+ "name": options.name || kidNames[g][Math.floor(Math.random() * kidNames[g].length)],
2783
+ "creatorName": "",
3103
2784
  },
3104
- "perms":{
3105
- "sharing":true,
3106
- "copying":true
2785
+ "perms": {
2786
+ "sharing": true,
2787
+ "copying": true
3107
2788
  },
3108
- "hair":{
3109
- "page":8,//Hardcoded, needs to be generated
3110
- "type":3,// ^
3111
- "color":Math.floor(Math.random()*2)===1?dad.hair.color:mom.hair.color,
3112
- "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
3113
2794
  },
3114
- "face":{
3115
- "shape":Math.floor(Math.random()*2)===1?dad.face.shape:mom.face.shape,
3116
- "feature":Math.floor(Math.random()*2)===1?dad.face.feature:mom.face.feature,
3117
- "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
3118
2799
  },
3119
- "eyes":Math.floor(Math.random()*2)===1?dad.eyes:mom.eyes,
3120
- "eyebrows":Math.floor(Math.random()*2)===1?dad.eyebrows:mom.eyebrows,
3121
- "nose":Math.floor(Math.random()*2)===1?dad.nose:mom.nose,
3122
- "mouth":Math.floor(Math.random()*2)===1?dad.mouth:mom.mouth,
3123
- "beard":{//Beards can never be generated for children allegedly, confirm before finishing
3124
- "mustache":{
3125
- "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,
3126
2807
  "mustacheSize": 4,
3127
2808
  "mustacheYPos": 10
3128
2809
  },
3129
2810
  "type": 0,
3130
2811
  "color": 0
3131
2812
  },
3132
- "glasses":Math.floor(Math.random()*2)===1?dad.glasses:mom.glasses,
3133
- "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
3134
2815
  };
3135
- child.eyebrows.color=child.hair.color;
3136
- var c=[mom.face.color,dad.face.color];
3137
- if(c[0]>c[1]){
3138
- c[1]=c[0];
3139
- c[0]=dad.face.color;
3140
- }
3141
- child.face.color=c[0]+Math.round((c[1]-c[0])/2);
3142
- 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";
3143
2824
  return child;
3144
2825
  }
3145
- function generateInstructions(mii,full){
3146
- let type=mii.console?.toLowerCase();
3147
- if(type.toLowerCase()==="wii"){
3148
- var typeCheat=[1,2,3,1,2,3,1,2,3,1,2,3];
3149
- var instrs={
3150
- "base":`Select "${mii.general.gender}", and then "Start from Scratch".`,
3151
- "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).`,
3152
- "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)}%.`,
3153
- "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.`,
3154
- "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.`,
3155
- "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.`,
3156
- "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}.`,
3157
- "hairFlipped":`${mii.hair.flipped?`On the hair page (fourth tab), press the button to flip the hair.`:``}`,
3158
- "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.`,
3159
- "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}.`,
3160
- "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.`,
3161
- "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.`:``}`,
3162
- "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.`:``}`,
3163
- "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.`:``}`,
3164
- "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.`:``}`,
3165
- "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}.`,
3166
- "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.`,
3167
- "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.`:``}`,
3168
- "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.`:``}`,
3169
- "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.`:``}`,
3170
- "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.`:``}`,
3171
- "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.`,
3172
- "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.`:``}`,
3173
- "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.`:``}`,
3174
- "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}.`,
3175
- "mouthCol":`On the mouth page (eighth tab), set the color to the one ${mii.mouth.col+1} from the left.`,
3176
- "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.`:``}`,
3177
- "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.`:``}`,
3178
- "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.`,
3179
- "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.`,
3180
- "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.`:``}`,
3181
- "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.`:``}`,
3182
- "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`}.`,
3183
- "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.`:``}`,
3184
- "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.`:``}`,
3185
- "mole":`${mii.mole.on?`On the mole page (within the ninth tab), turn the mole on.`:``}`,
3186
- "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.`:``}`,
3187
- "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.`:``}`,
3188
- "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.`:``}`,
3189
- "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`}.`,
3190
- "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.`,
3191
- "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}.` : ``}`
3192
2873
  };
3193
- if(!full){
3194
- var defaultMiiInstrs=structuredClone(mii.general.gender==="Male"?defaultInstrs.wii.male:defaultInstrs.wii.female);
3195
- Object.keys(instrs).forEach(instr=>{
3196
- 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]) {
3197
2878
  delete instrs[instr];
3198
2879
  }
3199
2880
  });
3200
2881
  }
3201
2882
  return instrs;
3202
2883
  }
3203
- else{
3204
- var instrs={
3205
- "base":`Select "Start from Scratch", and then "${mii.general.gender}".`,
3206
- "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.`,
3207
- "skinCol":`On the face page (first tab), set the color to the one ${mii.face.color+1} from the top.`,
3208
- "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.`,
3209
- "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.`,
3210
- "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}.`,
3211
- "hairFlipped":`${mii.hair.flipped?`On the hair page (second tab), press the button to flip the hair.`:``}`,
3212
- "hairColor":`On the hair page (second tab), set the hair color to the one ${mii.hair.color+1} from the top.`,
3213
- "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}.`,
3214
- "eyebrowColor":`On the eyebrow page (third tab), set the eyebrow color to the one ${mii.eyebrows.color+1} from the top.`,
3215
- "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.`:``}`,
3216
- "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.`:``}`,
3217
- "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.`:``}`,
3218
- "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.`:``}`,
3219
- "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.`:``}`,
3220
- "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}.`,
3221
- "eyeColor":`On the eye page (fourth tab), set the color to the one ${mii.eyes.col+1} from the top.`,
3222
- "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.`:``}`,
3223
- "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.`:``}`,
3224
- "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.`:``}`,
3225
- "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.`:``}`,
3226
- "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.`:``}`,
3227
- "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}.`,
3228
- "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.`:``}`,
3229
- "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.`:``}`,
3230
- "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}.`,
3231
- "mouthCol":`On the mouth page (sixth tab), set the color to the one ${mii.mouth.color+1} from the top.`,
3232
- "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.`:``}`,
3233
- "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.`:``}`,
3234
- "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.`:``}`,
3235
- "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.`,
3236
- "glassesCol":`On the glasses page (within the seventh tab), set the color to the one ${mii.glasses.col+1} from the top.`,
3237
- "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.`:``}`,
3238
- "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.`:``}`,
3239
- "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`}.`,
3240
- "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.`:``}`,
3241
- "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.`:``}`,
3242
- "mole":`${mii.mole.on?`On the mole page (within the seventh tab), turn the mole on.`:``}`,
3243
- "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.`:``}`,
3244
- "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.`:``}`,
3245
- "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.`:``}`,
3246
- "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`}.`,
3247
- "beardCol":`On the mustache OR beard pages (within the seventh tab), set the color to the one ${mii.beard.color+1} from the top.`,
3248
- "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)}%.`,
3249
- "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).`,
3250
- "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}.` : ``}`
3251
2932
  };
3252
- if(!full){
3253
- var defaultMiiInstrs=structuredClone(mii.general.gender==="Male"?defaultInstrs["3ds"].male:defaultInstrs["3ds"].female);
3254
- Object.keys(instrs).forEach(instr=>{
3255
- 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]) {
3256
2937
  delete instrs[instr];
3257
2938
  }
3258
2939
  });
@@ -3298,61 +2979,41 @@ function inchesToMiiHeight(totalInches) {
3298
2979
 
3299
2980
  return Math.round(Math.max(0, Math.min(value, 127)));
3300
2981
  }
3301
- // ---- Tunable anchors (BMI breakpoints) ----
3302
- const BMI_MIN = 16; // maps to Mii weight 0
3303
- const BMI_MID = 23; // maps to Mii weight 64 (average look)
3304
- const BMI_MAX = 40; // maps to Mii weight 127
3305
2982
 
3306
- // Convenience: clamp helper
3307
- const clamp = (x, lo, hi) => Math.max(lo, Math.min(hi, x));
3308
-
3309
- /**
3310
- * Convert real-world height & weight to Mii weight (0–127) using BMI.
3311
- * @param {number} heightInches - Total height in inches
3312
- * @param {number} weightLbs - Weight in pounds
3313
- * @returns {number} - Mii weight index (0–127)
3314
- */
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
3315
2988
  function heightWeightToMiiWeight(heightInches, weightLbs) {
3316
- if (!heightInches || heightInches <= 0) throw new Error("heightInches must be > 0");
3317
- const bmi = (703 * weightLbs) / (heightInches * heightInches);
3318
-
3319
- let v;
3320
- if (bmi <= BMI_MID) {
3321
- // Map BMI_MIN..BMI_MID -> 0..64
3322
- const t = (clamp(bmi, BMI_MIN, BMI_MID) - BMI_MIN) / (BMI_MID - BMI_MIN);
3323
- v = 0 + t * 64;
3324
- } else {
3325
- // Map BMI_MID..BMI_MAX -> 64..127
3326
- const t = (clamp(bmi, BMI_MID, BMI_MAX) - BMI_MID) / (BMI_MAX - BMI_MID);
3327
- v = 64 + t * (127 - 64);
3328
- }
3329
- return Math.round(clamp(v, 0, 127));
3330
- }
2989
+ if (!heightInches || heightInches < 0) throw new Error("heightInches must be >= 0");
2990
+ const bmi = (703 * weightLbs) / (heightInches * heightInches);
3331
2991
 
3332
- /**
3333
- * Convert Mii weight (0–127) back to a realistic real-world weight (lbs)
3334
- * for a given height, by inverting the BMI mapping above.
3335
- * @param {number} heightInches - Total height in inches
3336
- * @param {number} miiWeight - 0..127
3337
- * @returns {{ pounds:number, bmi:number }}
3338
- */
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
+ }
3339
3002
  function miiWeightToRealWeight(heightInches, miiWeight) {
3340
- if (!heightInches || heightInches <= 0) heightInches=0;
3341
- const v = clamp(miiWeight, 0, 127);
3342
-
3343
- let bmi;
3344
- if (v <= 64) {
3345
- // Invert 0..64 -> BMI_MIN..BMI_MID
3346
- const t = v / 64;
3347
- bmi = BMI_MIN + t * (BMI_MID - BMI_MIN);
3348
- } else {
3349
- // Invert 64..127 -> BMI_MID..BMI_MAX
3350
- const t = (v - 64) / (127 - 64);
3351
- bmi = BMI_MID + t * (BMI_MAX - BMI_MID);
3352
- }
3353
-
3354
- const pounds = (bmi * heightInches * heightInches) / 703;
3355
- return { pounds, bmi };
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 };
3356
3017
  }
3357
3018
 
3358
3019
 
@@ -3361,25 +3022,38 @@ module.exports = {
3361
3022
  // Data
3362
3023
  Enums: require("./Enums"),
3363
3024
 
3364
- //Functions
3025
+ //Convert
3365
3026
  convertMii,
3366
3027
  convertMiiToStudio,
3367
-
3028
+ convertStudioToMii,
3029
+
3030
+ //Read
3368
3031
  readWiiBin,
3369
3032
  read3DSQR,
3370
-
3033
+
3034
+ //Render
3371
3035
  renderMiiWithStudio,
3372
3036
  renderMii,
3373
3037
 
3038
+ //Write
3374
3039
  writeWiiBin,
3375
3040
  write3DSQR,
3376
3041
 
3377
3042
  //make3DSChild, //WIP
3378
-
3043
+
3044
+ //Instructions
3379
3045
  generateInstructions,
3380
3046
 
3047
+ //Normalize Height and Weight 0-127 to human measurements
3381
3048
  miiHeightToFeetInches,
3382
3049
  inchesToMiiHeight,
3383
- heightWeightToMiiWeight,
3384
- miiWeightToRealWeight
3050
+ heightWeightToMiiWeight,//EXPERIMENTAL
3051
+ miiWeightToRealWeight,//EXPERIMENTAL
3052
+
3053
+ /*
3054
+ Handle Amiibo Functions
3055
+ insertMiiIntoAmiibo(amiiboDump, decrypted3DSMiiBuffer),
3056
+ extractMiiFromAmiibo(amiiboDump)
3057
+ */
3058
+ ...require("./amiiboHandler.js")
3385
3059
  }