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