Merge pull request #147 from BitPatty/drawText
Rewrote drawText and related codes
This commit is contained in:
commit
cd7639b665
38 changed files with 4144 additions and 2173 deletions
|
@ -533,4 +533,424 @@
|
||||||
60000000 00000000
|
60000000 00000000
|
||||||
</source>
|
</source>
|
||||||
</code>
|
</code>
|
||||||
|
<code>
|
||||||
|
<category>metadata</category>
|
||||||
|
<id>PASDisplay</id>
|
||||||
|
<title lang="en-US">Position/angle/speed display</title>
|
||||||
|
<title lang="de-CH">Position/Winkel/Geschw. Display</title>
|
||||||
|
<title lang="fr-FR">Affichage de position/angle/vitesse</title>
|
||||||
|
<title lang="ja-JP">位置/角度/速度表示</title>
|
||||||
|
<author>Noki Doki, sup39(サポミク)</author>
|
||||||
|
<version>1.4</version>
|
||||||
|
<date>Mar 24, 2022</date>
|
||||||
|
<dependencies version="GMSJ01">drawText</dependencies>
|
||||||
|
<dependencies version="GMSJ0A">drawText</dependencies>
|
||||||
|
<description lang="en-US">
|
||||||
|
Shows Mario's position, angle and speed at any given time.
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
This code is not compatible with the Speed Display code.
|
||||||
|
:::
|
||||||
|
</description>
|
||||||
|
<description lang="de-CH">
|
||||||
|
Zeigt Mario's Position, Winkel und Geschwindigkeit jederzeit auf dem Bildschirm an.
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
Dieser Code ist nicht kompatibel mit dem Geschwindigkeits-Display Code
|
||||||
|
:::
|
||||||
|
</description>
|
||||||
|
<description lang="fr-FR">Affiche la position, l'angle et la vitesse de Mario à tout moment.</description>
|
||||||
|
<description lang="ja-JP">常に「マリオの位置」「マリオの角度」「マリオの速度」を表示します。</description>
|
||||||
|
<source version="GMSE01">
|
||||||
|
062A6160 00000010
|
||||||
|
49553F19 60000000
|
||||||
|
60000000 60000000
|
||||||
|
042998B8 49560749
|
||||||
|
04143F14 496B6209
|
||||||
|
077FA000 000001E8
|
||||||
|
9421FFE0 7C0802A6
|
||||||
|
90010024 93E1001C
|
||||||
|
4AABD6E5 38E00200
|
||||||
|
38C00320 38A0FFD8
|
||||||
|
3880000A 38610008
|
||||||
|
4AAD01B9 814D9FC8
|
||||||
|
3FE08180 3BFFA1EC
|
||||||
|
39200000 39000002
|
||||||
|
3CE08180 38E7A164
|
||||||
|
80CA0048 38A10008
|
||||||
|
38800000 7FE3FB78
|
||||||
|
4AAD67F1 39200001
|
||||||
|
913F01B0 80010024
|
||||||
|
7C0803A6 83E1001C
|
||||||
|
38210020 4E800020
|
||||||
|
9421FFE8 7C0802A6
|
||||||
|
9001001C 93A1000C
|
||||||
|
93C10010 93E10014
|
||||||
|
7C7F1B78 81230000
|
||||||
|
83A90064 3D208180
|
||||||
|
8129A39C 2F890000
|
||||||
|
409E002C 7FE3FB78
|
||||||
|
7FA903A6 4E800421
|
||||||
|
8001001C 7C0803A6
|
||||||
|
83A1000C 83C10010
|
||||||
|
83E10014 38210018
|
||||||
|
4E800020 3D208040
|
||||||
|
6129E0E8 83C90000
|
||||||
|
3C608180 3863A1EC
|
||||||
|
4AAD6B79 C0BE00A8
|
||||||
|
C09E00B0 A0DE0096
|
||||||
|
C07E0018 C05E0014
|
||||||
|
C03E0010 3CA08180
|
||||||
|
38A5A164 38800080
|
||||||
|
4CC63242 4AB3F691
|
||||||
|
4BFFFF94 9421FFF0
|
||||||
|
7C0802A6 90010014
|
||||||
|
93E1000C 7C7F1B78
|
||||||
|
4AAF158D 38E00081
|
||||||
|
7FE6FB78 38A00000
|
||||||
|
38800000 3C608180
|
||||||
|
3863A1EC 4AAD5C5D
|
||||||
|
80010014 7C0803A6
|
||||||
|
83E1000C 38210010
|
||||||
|
4E800020 5820506F
|
||||||
|
7320252E 30660A59
|
||||||
|
20506F73 20252E30
|
||||||
|
660A5A20 506F7320
|
||||||
|
252E3066 0A416E67
|
||||||
|
6C652025 68750A48
|
||||||
|
20537064 20252E32
|
||||||
|
660A5620 53706420
|
||||||
|
252E3266 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202000 00000000
|
||||||
|
C22A66F4 00000003
|
||||||
|
38600000 3D808180
|
||||||
|
906CA39C 807F0040
|
||||||
|
60000000 00000000
|
||||||
|
</source>
|
||||||
|
<source version="GMSJ01">
|
||||||
|
04206734 495F38CD
|
||||||
|
077FA000 000000B5
|
||||||
|
9421FFF0 7C0802A6
|
||||||
|
90010014 93E1000C
|
||||||
|
7C7F1B78 4A83B215
|
||||||
|
3D208041 8149A378
|
||||||
|
3D008180 38600010
|
||||||
|
3908A074 C0AA00A8
|
||||||
|
38E0FFFF C08A00B0
|
||||||
|
38C0FFFF A12A0096
|
||||||
|
38A00014 C06A0018
|
||||||
|
388000C8 C04A0014
|
||||||
|
C02A0010 4CC63242
|
||||||
|
4BFF61E1 80010014
|
||||||
|
7FE3FB78 83E1000C
|
||||||
|
7C0803A6 38210010
|
||||||
|
4A83B1B8 5820506F
|
||||||
|
7320252E 30660A59
|
||||||
|
20506F73 20252E30
|
||||||
|
660A5A20 506F7320
|
||||||
|
252E3066 0A416E67
|
||||||
|
6C652025 68750A48
|
||||||
|
20537064 20252E32
|
||||||
|
660A5620 53706420
|
||||||
|
252E3266 00000000
|
||||||
|
</source>
|
||||||
|
<source version="GMSJ0A">
|
||||||
|
041252A0 496D4D61
|
||||||
|
077FA000 000000B5
|
||||||
|
9421FFF0 7C0802A6
|
||||||
|
90010014 93E1000C
|
||||||
|
7C7F1B78 4AAD0EB9
|
||||||
|
3D208040 8149EF88
|
||||||
|
3D008180 38600010
|
||||||
|
3908A074 C0AA00A8
|
||||||
|
38E0FFFF C08A00B0
|
||||||
|
38C0FFFF A12A0096
|
||||||
|
38A00014 C06A0018
|
||||||
|
388000C8 C04A0014
|
||||||
|
C02A0010 4CC63242
|
||||||
|
4BFF61E1 80010014
|
||||||
|
7FE3FB78 83E1000C
|
||||||
|
7C0803A6 38210010
|
||||||
|
4AAD0E5C 5820506F
|
||||||
|
7320252E 30660A59
|
||||||
|
20506F73 20252E30
|
||||||
|
660A5A20 506F7320
|
||||||
|
252E3066 0A416E67
|
||||||
|
6C652025 68750A48
|
||||||
|
20537064 20252E32
|
||||||
|
660A5620 53706420
|
||||||
|
252E326600000000
|
||||||
|
</source>
|
||||||
|
<source version="GMSP01">
|
||||||
|
0629E070 00000010
|
||||||
|
4955C009 60000000
|
||||||
|
60000000 60000000
|
||||||
|
04291750 495688B1
|
||||||
|
04138B50 496C15CD
|
||||||
|
077FA000 000001E8
|
||||||
|
9421FFE0 7C0802A6
|
||||||
|
90010024 93E1001C
|
||||||
|
4AAB56B5 38E00200
|
||||||
|
38C00320 38A0FFD8
|
||||||
|
3880000A 38610008
|
||||||
|
4AAC824D 814D9EF0
|
||||||
|
3FE08180 3BFFA1EC
|
||||||
|
39200000 39000002
|
||||||
|
3CE08180 38E7A164
|
||||||
|
80CA0048 38A10008
|
||||||
|
38800000 7FE3FB78
|
||||||
|
4AACE999 39200001
|
||||||
|
913F01B0 80010024
|
||||||
|
7C0803A6 83E1001C
|
||||||
|
38210020 4E800020
|
||||||
|
9421FFE8 7C0802A6
|
||||||
|
9001001C 93A1000C
|
||||||
|
93C10010 93E10014
|
||||||
|
7C7F1B78 81230000
|
||||||
|
83A90064 3D208180
|
||||||
|
8129A39C 2F890000
|
||||||
|
409E002C 7FE3FB78
|
||||||
|
7FA903A6 4E800421
|
||||||
|
8001001C 7C0803A6
|
||||||
|
83A1000C 83C10010
|
||||||
|
83E10014 38210018
|
||||||
|
4E800020 3D208040
|
||||||
|
612957B0 83C90000
|
||||||
|
3C608180 3863A1EC
|
||||||
|
4AACED21 C0BE00A8
|
||||||
|
C09E00B0 A0DE0096
|
||||||
|
C07E0018 C05E0014
|
||||||
|
C03E0010 3CA08180
|
||||||
|
38A5A164 38800080
|
||||||
|
4CC63242 4AB37811
|
||||||
|
4BFFFF94 9421FFF0
|
||||||
|
7C0802A6 90010014
|
||||||
|
93E1000C 7C7F1B78
|
||||||
|
4AAE9735 38E00081
|
||||||
|
7FE6FB78 38A00000
|
||||||
|
38800000 3C608180
|
||||||
|
3863A1EC 4AACDE05
|
||||||
|
80010014 7C0803A6
|
||||||
|
83E1000C 38210010
|
||||||
|
4E800020 5820506F
|
||||||
|
7320252E 30660A59
|
||||||
|
20506F73 20252E30
|
||||||
|
660A5A20 506F7320
|
||||||
|
252E3066 0A416E67
|
||||||
|
6C652025 68750A48
|
||||||
|
20537064 20252E32
|
||||||
|
660A5620 53706420
|
||||||
|
252E3266 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202000 00000000
|
||||||
|
C229E64C 00000003
|
||||||
|
38600000 3D808180
|
||||||
|
906CA39C 807F0040
|
||||||
|
60000000 00000000
|
||||||
|
</source>
|
||||||
|
</code>
|
||||||
|
<code>
|
||||||
|
<category>metadata</category>
|
||||||
|
<id>SpeedDisplay</id>
|
||||||
|
<title lang="en-US">Speed display</title>
|
||||||
|
<title lang="de-CH">Geschwindigkeits-Display</title>
|
||||||
|
<title lang="fr-FR">Affichage de vitesse</title>
|
||||||
|
<title lang="ja-JP">速度表示</title>
|
||||||
|
<author>Noki Doki, sup39(サポミク)</author>
|
||||||
|
<version>1.5</version>
|
||||||
|
<date>Mar 24, 2022</date>
|
||||||
|
<dependencies version="GMSJ01">drawText</dependencies>
|
||||||
|
<dependencies version="GMSJ0A">drawText</dependencies>
|
||||||
|
<description lang="en-US">
|
||||||
|
Shows Mario's speed at any given time.
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
This code is not compatible with the Position/Angle/Speed Display code.
|
||||||
|
:::
|
||||||
|
</description>
|
||||||
|
<description lang="de-CH">
|
||||||
|
Zeigt Mario's Geschwindigkeit jederzeit auf dem Bildschirm an.
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
Dieser Code ist nicht kompatibel mit dem Position/Winkel/Gewschw. Display Code
|
||||||
|
:::
|
||||||
|
</description>
|
||||||
|
<description lang="fr-FR">Affiche la vitesse de Mario à tout moment.</description>
|
||||||
|
<description lang="ja-JP">常に「マリオの速度」を表示します。</description>
|
||||||
|
<source version="GMSE01">
|
||||||
|
062A6160 00000010
|
||||||
|
49553F19 60000000
|
||||||
|
60000000 60000000
|
||||||
|
042998B8 49560749
|
||||||
|
04143F14 496B61F9
|
||||||
|
077FA000 00000188
|
||||||
|
9421FFE0 7C0802A6
|
||||||
|
90010024 93E1001C
|
||||||
|
4AABD6E5 38E00200
|
||||||
|
38C00320 38A0FFD8
|
||||||
|
3880000A 38610008
|
||||||
|
4AAD01B9 814D9FC8
|
||||||
|
3FE08180 3BFFA214
|
||||||
|
39200000 39000002
|
||||||
|
3CE08180 38E7A154
|
||||||
|
80CA0048 38A10008
|
||||||
|
38800000 7FE3FB78
|
||||||
|
4AAD67F1 39200001
|
||||||
|
913F01B0 80010024
|
||||||
|
7C0803A6 83E1001C
|
||||||
|
38210020 4E800020
|
||||||
|
9421FFE8 7C0802A6
|
||||||
|
9001001C 93C10010
|
||||||
|
93E10014 7C7F1B78
|
||||||
|
81230000 83C90064
|
||||||
|
3D208180 8129A3C4
|
||||||
|
2C090000 40820028
|
||||||
|
7FE3FB78 7FC903A6
|
||||||
|
4E800421 8001001C
|
||||||
|
7C0803A6 83C10010
|
||||||
|
83E10014 38210018
|
||||||
|
4E800020 93A1000C
|
||||||
|
3D208040 6129E0E8
|
||||||
|
83A90000 3C608180
|
||||||
|
3863A214 4AAD6B7D
|
||||||
|
C05D00A8 C03D00B0
|
||||||
|
3CA08180 38A5A154
|
||||||
|
38800030 4CC63242
|
||||||
|
4AB3F6A5 83A1000C
|
||||||
|
4BFFFFA0 9421FFF0
|
||||||
|
7C0802A6 90010014
|
||||||
|
93E1000C 7C7F1B78
|
||||||
|
4AAF159D 38E00081
|
||||||
|
7FE6FB78 38A00000
|
||||||
|
38800000 3C608180
|
||||||
|
3863A214 4AAD5C6D
|
||||||
|
80010014 7C0803A6
|
||||||
|
83E1000C 38210010
|
||||||
|
4E800020 48205370
|
||||||
|
6420252E 32660A56
|
||||||
|
20537064 20252E32
|
||||||
|
66202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202000 00000000
|
||||||
|
C22A66F4 00000003
|
||||||
|
38600000 3D808180
|
||||||
|
906CA3C4 807F0040
|
||||||
|
60000000 00000000
|
||||||
|
</source>
|
||||||
|
<source version="GMSJ01">
|
||||||
|
04206734 495F38CD
|
||||||
|
077FA000 0000007A
|
||||||
|
9421FFF0 7C0802A6
|
||||||
|
90010014 93E1000C
|
||||||
|
7C7F1B78 4A83B215
|
||||||
|
3D208041 8129A378
|
||||||
|
3D008180 38600010
|
||||||
|
3908A064 C04900A8
|
||||||
|
38E0FFFF C02900B0
|
||||||
|
38C0FFFF 38A00014
|
||||||
|
388000F0 4CC63242
|
||||||
|
4BFF61F1 80010014
|
||||||
|
7FE3FB78 83E1000C
|
||||||
|
7C0803A6 38210010
|
||||||
|
4A83B1C8 48205370
|
||||||
|
6420252E 32660A56
|
||||||
|
20537064 20252E32
|
||||||
|
66000000 00000000
|
||||||
|
</source>
|
||||||
|
<source version="GMSJ0A">
|
||||||
|
041252A0 496D4D61
|
||||||
|
077FA000 0000007A
|
||||||
|
9421FFF0 7C0802A6
|
||||||
|
90010014 93E1000C
|
||||||
|
7C7F1B78 4AAD0EB9
|
||||||
|
3D208040 8129EF88
|
||||||
|
3D008180 38600010
|
||||||
|
3908A064 C04900A8
|
||||||
|
38E0FFFF C02900B0
|
||||||
|
38C0FFFF 38A00014
|
||||||
|
388000F0 4CC63242
|
||||||
|
4BFF61F1 80010014
|
||||||
|
7FE3FB78 83E1000C
|
||||||
|
7C0803A6 38210010
|
||||||
|
4AAD0E6C 48205370
|
||||||
|
6420252E 32660A56
|
||||||
|
20537064 20252E32
|
||||||
|
66000000 00000000
|
||||||
|
</source>
|
||||||
|
<source version="GMSP01">
|
||||||
|
0629E070 00000010
|
||||||
|
4955C009 60000000
|
||||||
|
60000000 60000000
|
||||||
|
04291750 495688B1
|
||||||
|
04138B50 496C15BD
|
||||||
|
077FA000 00000188
|
||||||
|
9421FFE0 7C0802A6
|
||||||
|
90010024 93E1001C
|
||||||
|
4AAB56B5 38E00200
|
||||||
|
38C00320 38A0FFD8
|
||||||
|
3880000A 38610008
|
||||||
|
4AAC824D 814D9EF0
|
||||||
|
3FE08180 3BFFA214
|
||||||
|
39200000 39000002
|
||||||
|
3CE08180 38E7A154
|
||||||
|
80CA0048 38A10008
|
||||||
|
38800000 7FE3FB78
|
||||||
|
4AACE999 39200001
|
||||||
|
913F01B0 80010024
|
||||||
|
7C0803A6 83E1001C
|
||||||
|
38210020 4E800020
|
||||||
|
9421FFE8 7C0802A6
|
||||||
|
9001001C 93C10010
|
||||||
|
93E10014 7C7F1B78
|
||||||
|
81230000 83C90064
|
||||||
|
3D208180 8129A3C4
|
||||||
|
2C090000 40820028
|
||||||
|
7FE3FB78 7FC903A6
|
||||||
|
4E800421 8001001C
|
||||||
|
7C0803A6 83C10010
|
||||||
|
83E10014 38210018
|
||||||
|
4E800020 93A1000C
|
||||||
|
3D208040 612957B0
|
||||||
|
83A90000 3C608180
|
||||||
|
3863A214 4AACED25
|
||||||
|
C05D00A8 C03D00B0
|
||||||
|
3CA08180 38A5A154
|
||||||
|
38800030 4CC63242
|
||||||
|
4AB37825 83A1000C
|
||||||
|
4BFFFFA0 9421FFF0
|
||||||
|
7C0802A6 90010014
|
||||||
|
93E1000C 7C7F1B78
|
||||||
|
4AAE9745 38E00081
|
||||||
|
7FE6FB78 38A00000
|
||||||
|
38800000 3C608180
|
||||||
|
3863A214 4AACDE15
|
||||||
|
80010014 7C0803A6
|
||||||
|
83E1000C 38210010
|
||||||
|
4E800020 48205370
|
||||||
|
6420252E 32660A56
|
||||||
|
20537064 20252E32
|
||||||
|
66202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202020 20202020
|
||||||
|
20202000 00000000
|
||||||
|
C229E64C 00000003
|
||||||
|
38600000 3D808180
|
||||||
|
906CA3C4 807F0040
|
||||||
|
60000000 00000000
|
||||||
|
</source>
|
||||||
|
</code>
|
||||||
</codes>
|
</codes>
|
||||||
|
|
21
changelog.md
21
changelog.md
|
@ -29,6 +29,27 @@ Disable Z menu for Instant Level Select to prevent broken texture
|
||||||
### Updated 'Instant Level Select'
|
### Updated 'Instant Level Select'
|
||||||
Overwrite button input value to prevent Level Select being activated when AreaLock is enabled
|
Overwrite button input value to prevent Level Select being activated when AreaLock is enabled
|
||||||
|
|
||||||
|
## Jan 31, 2023
|
||||||
|
### Updated 'Pattern Selector'
|
||||||
|
- Rewrote with C2 + separated config 06
|
||||||
|
- Remove label option
|
||||||
|
### Updated 'Customized Display'
|
||||||
|
- Rewrote with C2
|
||||||
|
- Implemented a more complex assembler/compiler to support function call
|
||||||
|
- Add options
|
||||||
|
- Invincibility Timer
|
||||||
|
- Pollution Degree
|
||||||
|
- Spin Jump Condition Check
|
||||||
|
|
||||||
|
## Jan 28, 2023
|
||||||
|
### Rewrote 'drawText'
|
||||||
|
- Reduced parameters to struct pointer + format string + varargs
|
||||||
|
- Rewrote QFT, Pattern Selector, Customized Display with the new drawText function
|
||||||
|
### Improved Preview
|
||||||
|
- Added PAL font (TODO: NTSC-U)
|
||||||
|
- Merged P/A/S Display and Speed Display to Customized Display
|
||||||
|
- Provided background options to Pattern Selector and Customized Display
|
||||||
|
|
||||||
## Jan 10, 2023
|
## Jan 10, 2023
|
||||||
### Updated 'Quarterframe Timer'
|
### Updated 'Quarterframe Timer'
|
||||||
Added the following options to freeze QFT:
|
Added the following options to freeze QFT:
|
||||||
|
|
29
package-lock.json
generated
29
package-lock.json
generated
|
@ -14,10 +14,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sup39/markdown-it-attr": "1.2.2",
|
"@sup39/markdown-it-attr": "1.2.2",
|
||||||
"@sup39/markdown-it-inline-tag": "1.0.1",
|
"@sup39/markdown-it-inline-tag": "1.0.1",
|
||||||
"@types/encoding-japanese": "^2.0.1",
|
|
||||||
"@vuepress/plugin-back-to-top": "1.9.8",
|
"@vuepress/plugin-back-to-top": "1.9.8",
|
||||||
"@vuepress/plugin-medium-zoom": "1.9.8",
|
"@vuepress/plugin-medium-zoom": "1.9.8",
|
||||||
"encoding-japanese": "^2.0.0",
|
|
||||||
"jsdom": "21.1.0",
|
"jsdom": "21.1.0",
|
||||||
"pre-commit": "1.2.2",
|
"pre-commit": "1.2.2",
|
||||||
"prettier": "2.8.3",
|
"prettier": "2.8.3",
|
||||||
|
@ -1896,12 +1894,6 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/encoding-japanese": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/encoding-japanese/-/encoding-japanese-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-JaCXs2HLniKY8xXeWlg8MAtd4iKhNh8LwutW3yDMWY4usEdTZ2va1x9kd8V3179OAIUTgGQVA63XJrHettpVFQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/express": {
|
"node_modules/@types/express": {
|
||||||
"version": "4.17.13",
|
"version": "4.17.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||||
|
@ -6604,15 +6596,6 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/encoding-japanese": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
@ -19065,12 +19048,6 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/encoding-japanese": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/encoding-japanese/-/encoding-japanese-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-JaCXs2HLniKY8xXeWlg8MAtd4iKhNh8LwutW3yDMWY4usEdTZ2va1x9kd8V3179OAIUTgGQVA63XJrHettpVFQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/express": {
|
"@types/express": {
|
||||||
"version": "4.17.13",
|
"version": "4.17.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||||
|
@ -22969,12 +22946,6 @@
|
||||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"encoding-japanese": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"end-of-stream": {
|
"end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
|
|
@ -22,10 +22,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sup39/markdown-it-attr": "1.2.2",
|
"@sup39/markdown-it-attr": "1.2.2",
|
||||||
"@sup39/markdown-it-inline-tag": "1.0.1",
|
"@sup39/markdown-it-inline-tag": "1.0.1",
|
||||||
"@types/encoding-japanese": "^2.0.1",
|
|
||||||
"@vuepress/plugin-back-to-top": "1.9.8",
|
"@vuepress/plugin-back-to-top": "1.9.8",
|
||||||
"@vuepress/plugin-medium-zoom": "1.9.8",
|
"@vuepress/plugin-medium-zoom": "1.9.8",
|
||||||
"encoding-japanese": "^2.0.0",
|
|
||||||
"jsdom": "21.1.0",
|
"jsdom": "21.1.0",
|
||||||
"pre-commit": "1.2.2",
|
"pre-commit": "1.2.2",
|
||||||
"prettier": "2.8.3",
|
"prettier": "2.8.3",
|
||||||
|
|
|
@ -132,15 +132,6 @@ export default {
|
||||||
codeConfigs: {},
|
codeConfigs: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.codeConfigs = {
|
|
||||||
qft: getConfigQFT(),
|
|
||||||
PatternSelector: getConfigPS(),
|
|
||||||
SpeedDisplay: {},
|
|
||||||
PASDisplay: {},
|
|
||||||
CustomizedDisplay: getConfigCD(this.version),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
getLabel(key) {
|
getLabel(key) {
|
||||||
return translate(key, this.$lang);
|
return translate(key, this.$lang);
|
||||||
|
@ -168,6 +159,13 @@ export default {
|
||||||
JSON.stringify({ version: e }),
|
JSON.stringify({ version: e }),
|
||||||
]);
|
]);
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
// update config for preview
|
||||||
|
this.codeConfigs = {
|
||||||
|
qft: getConfigQFT(),
|
||||||
|
PatternSelector: getConfigPS(),
|
||||||
|
CustomizedDisplay: getConfigCD(e),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
onFormatChanged(e) {
|
onFormatChanged(e) {
|
||||||
this.selectedFormat = e;
|
this.selectedFormat = e;
|
||||||
|
@ -222,7 +220,10 @@ export default {
|
||||||
.filter(code => !(code.category === category && exclusive))
|
.filter(code => !(code.category === category && exclusive))
|
||||||
.map(code => code.id));
|
.map(code => code.id));
|
||||||
ids.add(id);
|
ids.add(id);
|
||||||
return Object.fromEntries(Object.entries(this.codeConfigs).filter(([id]) => ids.has(id)));
|
return {
|
||||||
|
...Object.fromEntries(Object.entries(this.codeConfigs).filter(([id]) => ids.has(id))),
|
||||||
|
_version: this.selectedVersion,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,82 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="preview-root">
|
<div class="preview-root">
|
||||||
<div class="preview-ctn">
|
<div class="preview-ctn">
|
||||||
<div v-if="qft">
|
<PreviewString :config="config.qft" :version="_version" />
|
||||||
<div :style="qft.bgStyle" />
|
<PreviewString v-for="mdp,i in (config.CustomizedDisplay || [])"
|
||||||
<PreviewString :x="qft.x" :y="qft.y" :size="qft.fontSize" :color="qft.color" text="0:00:00" />
|
:key="'mdp'+i" :config="mdp" :version="_version" />
|
||||||
</div>
|
<PreviewString :config="config.PatternSelector" :version="_version" />
|
||||||
<div v-if="mdps">
|
|
||||||
<PreviewString v-for="mdp,i in mdps" :key="i"
|
|
||||||
:x="mdp.x" :y="mdp.y" :size="mdp.fontSize" :color="mdp.color" :text="mdp.text" />
|
|
||||||
</div>
|
|
||||||
<PreviewString v-if="ps" :x="ps.x" :y="ps.y" :size="ps.fontSize" :color="ps.color" :text="ps.text" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { rgbaI2S, fg2Style } from './codes/utils.js';
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
config: {type: Object},
|
config: {type: Object},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
mdps() {
|
_version() {
|
||||||
const {config} = this;
|
const {_version} = this.config
|
||||||
if (config.PASDisplay) return [{
|
return _version;
|
||||||
x: 16,
|
|
||||||
y: 200,
|
|
||||||
fontSize: 20,
|
|
||||||
color: '#fff',
|
|
||||||
text: 'X Pos -39\nY Pos 1207\nZ Pos -4193\nAngle 65535\nH Spd 15.15\nV Spd -31.17',
|
|
||||||
}];
|
|
||||||
if (config.SpeedDisplay) return [{
|
|
||||||
x: 16,
|
|
||||||
y: 240,
|
|
||||||
fontSize: 20,
|
|
||||||
color: '#fff',
|
|
||||||
text: 'H Spd 15.15\nV Spd -31.17',
|
|
||||||
}];
|
|
||||||
if (config.CustomizedDisplay) {
|
|
||||||
return config.CustomizedDisplay.map(({fgRGB, fgA, fgRGB2, fgA2, ...o}) => ({
|
|
||||||
...o,
|
|
||||||
color: fg2Style(fgRGB, fgA, fgRGB2, fgA2),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
qft() {
|
|
||||||
const {config: {qft}} = this;
|
|
||||||
if (qft == null) return;
|
|
||||||
const {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, width} = qft;
|
|
||||||
const bg = rgbaI2S(bgRGB, bgA);
|
|
||||||
return {
|
|
||||||
x, y, fontSize,
|
|
||||||
color: fg2Style(fgRGB, fgA, fgRGB2, fgA2),
|
|
||||||
bgStyle: {
|
|
||||||
left: x+'px',
|
|
||||||
top: (y-fontSize)+'px',
|
|
||||||
width: (width*fontSize/20)+'px',
|
|
||||||
height: fontSize+'px',
|
|
||||||
background: bg,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
ps() {
|
|
||||||
const {config: {PatternSelector: ps}} = this;
|
|
||||||
if (ps == null) return;
|
|
||||||
const {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, label} = ps;
|
|
||||||
return {
|
|
||||||
x, y, fontSize,
|
|
||||||
color: fg2Style(fgRGB, fgA, fgRGB2, fgA2),
|
|
||||||
text: label+'#0 0 0',
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div.preview-root {
|
.preview-root {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 600px;
|
width: 600px;
|
||||||
height: 448px;
|
height: 448px;
|
||||||
|
@ -84,11 +32,8 @@ div.preview-root {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
div.preview-ctn {
|
.preview-ctn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -16px;
|
top: -16px;
|
||||||
}
|
}
|
||||||
div.preview-ctn * {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,47 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="text" class="preview-str" :style="styles.root" >
|
<div v-if="config">
|
||||||
|
<div :style="styles.bg" />
|
||||||
|
<div :class="previewCssClass" :style="styles.root" >
|
||||||
<div v-for="style, i in styles.chars" :key="i" class="char-ctn" :style="style.ctn">
|
<div v-for="style, i in styles.chars" :key="i" class="char-ctn" :style="style.ctn">
|
||||||
<div class="char-bg" :style="style.bg" />
|
<div class="char-bg" :style="style.bg" />
|
||||||
<div class="char-mask" :style="style.mask" />
|
<div class="char-mask" :style="style.mask" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import charInfo from '../data/font-jp.json';
|
import {rgbaI2S} from './codes/utils.js';
|
||||||
|
import {measureText} from './codes/text.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
x: {type: Number},
|
config: {type: Object},
|
||||||
y: {type: Number},
|
version: {type: String},
|
||||||
size: {type: Number},
|
|
||||||
color: {type: String},
|
|
||||||
text: {type: String},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
previewCssClass() {
|
||||||
|
// TODO US
|
||||||
|
return `preview-str preview-${['GMSJ01', 'GMSJ0A'].includes(this.version) ? 'JP' : 'EU'}`;
|
||||||
|
},
|
||||||
styles() {
|
styles() {
|
||||||
const {x: x0, y: y0, size: fontSize, color, text} = this;
|
const {config, version} = this;
|
||||||
|
const {x: x0, y: y0, fontSize, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, bgLeft, bgRight, bgTop, bgBot, text} = config;
|
||||||
/** @type {{x: number, y: number, u: number, v: number}[]} */
|
const fgColor = rgbaI2S(fgRGB, fgA);
|
||||||
const chars = [];
|
const {width, height, chars} = measureText(text, version);
|
||||||
let x = 0;
|
|
||||||
let y = 0;
|
|
||||||
let useKerning = false;
|
|
||||||
text.split('').forEach(c => {
|
|
||||||
const {index, kerning, width} = charInfo[c] ?? charInfo[' '];
|
|
||||||
if (c === '\n') {
|
|
||||||
useKerning = false;
|
|
||||||
x = 0;
|
|
||||||
y += 20;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (useKerning) x -= kerning;
|
|
||||||
useKerning = true;
|
|
||||||
// uv
|
|
||||||
const [u, v] = [index%25*20, (index/25|0)*20];
|
|
||||||
chars.push({x, y, u, v});
|
|
||||||
// next
|
|
||||||
x += width + kerning;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
root: {
|
root: {
|
||||||
|
@ -60,10 +47,18 @@ export default {
|
||||||
mask: {
|
mask: {
|
||||||
'mask-position': offset,
|
'mask-position': offset,
|
||||||
'-webkit-mask-position': offset,
|
'-webkit-mask-position': offset,
|
||||||
background: color,
|
background: fgRGB2 == null || fgA2 == null ? fgColor :
|
||||||
|
`linear-gradient(180deg, ${fgColor}, ${rgbaI2S(fgRGB2, fgA2)})`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
bg: {
|
||||||
|
left: x0 - bgLeft + 'px',
|
||||||
|
top: y0 - fontSize - bgTop + 'px',
|
||||||
|
width: (width * fontSize) / 20 + bgLeft + bgRight + 'px',
|
||||||
|
height: (height * fontSize) / 20 + bgTop + bgBot + 'px',
|
||||||
|
background: rgbaI2S(bgRGB, bgA),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -71,27 +66,42 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
* {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
.preview-str {
|
.preview-str {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.preview-str * {
|
.preview-str * {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
div.char-ctn {
|
.char-ctn {
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
div.char-ctn > div {
|
.char-ctn > div {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
div.char-bg {
|
.char-mask {
|
||||||
background: url(/img/preview/font-jp.png);
|
|
||||||
}
|
|
||||||
div.char-mask {
|
|
||||||
mask-image: url(/img/preview/font-jp.png);
|
|
||||||
-webkit-mask-image: url(/img/preview/font-jp.png);
|
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
-webkit-mask-repeat: no-repeat;
|
-webkit-mask-repeat: no-repeat;
|
||||||
mix-blend-mode: multiply;
|
mix-blend-mode: multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-JP .char-bg {
|
||||||
|
background: url(/img/preview/font-JP.png);
|
||||||
|
}
|
||||||
|
.preview-JP .char-mask {
|
||||||
|
mask-image: url(/img/preview/font-JP.png);
|
||||||
|
-webkit-mask-image: url(/img/preview/font-JP.png);
|
||||||
|
}
|
||||||
|
.preview-EU .char-bg {
|
||||||
|
background: url(/img/preview/font-EU.png);
|
||||||
|
}
|
||||||
|
.preview-EU .char-mask {
|
||||||
|
mask-image: url(/img/preview/font-EU.png);
|
||||||
|
-webkit-mask-image: url(/img/preview/font-EU.png);
|
||||||
|
}
|
||||||
|
/* TODO US */
|
||||||
</style>
|
</style>
|
||||||
|
|
214
site/.vuepress/components/codes/CustomizedDisplay/assembler.js
Normal file
214
site/.vuepress/components/codes/CustomizedDisplay/assembler.js
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
import { ASM, liDX, makeProgram, insts2hex } from '../asm.js';
|
||||||
|
import { splitArray } from '../utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {number} Inst
|
||||||
|
* @typedef {{type: 'call', addr: number, prep?: Inst[]}} CallInst -- function call instruction
|
||||||
|
* @typedef {{type: 'struct', reg: number, hex: string}} LoadStructInst -- struct pointer load instruction
|
||||||
|
* @typedef {Inst|CallInst|LoadStructInst} ASMInst
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ASMInst[]} insts
|
||||||
|
* @param {number} stackFrameSize
|
||||||
|
*/
|
||||||
|
export function assemble(insts, stackFrameSize) {
|
||||||
|
const rAddr = 12;
|
||||||
|
const rData = 31;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [0]: data
|
||||||
|
* [.]: body
|
||||||
|
*/
|
||||||
|
const p = makeProgram(0);
|
||||||
|
|
||||||
|
/** @type {Map<number, Map<string, number>>} */
|
||||||
|
const callCounts = new Map(); // [addr][prep] = count
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
const structDB = new Set();
|
||||||
|
|
||||||
|
// summarize data
|
||||||
|
let hasRepeatCall = false;
|
||||||
|
let loadStructCount = 0;
|
||||||
|
let sizeWithoutBLTrick = 0; // # of instruction
|
||||||
|
for (const inst of insts) {
|
||||||
|
if (typeof inst === 'number') continue;
|
||||||
|
const { type } = inst;
|
||||||
|
if (type === 'call') {
|
||||||
|
const { addr, prep = [] } = inst;
|
||||||
|
const prepKey = insts2hex(prep);
|
||||||
|
const prepCounts = callCounts.get(addr);
|
||||||
|
if (prepCounts == null) {
|
||||||
|
callCounts.set(addr, new Map([[prepKey, 1]]));
|
||||||
|
} else {
|
||||||
|
prepCounts.set(prepKey, (prepCounts.get(prepKey) ?? 0) + 1);
|
||||||
|
hasRepeatCall = true;
|
||||||
|
}
|
||||||
|
} else if (type === 'struct') {
|
||||||
|
const { hex } = inst;
|
||||||
|
structDB.add(hex);
|
||||||
|
loadStructCount++;
|
||||||
|
sizeWithoutBLTrick += ((hex.length + 7) >>> 3) + 2; // bl L; mflr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useSharedCall = hasRepeatCall || callCounts.size > 2;
|
||||||
|
const sizeWithBLTrick =
|
||||||
|
((Array.from(structDB.entries()).reduce((a, [hex]) => a + hex.length, 0) + 7) >>> 3) +
|
||||||
|
loadStructCount + // addi rReg, rData, off
|
||||||
|
(stackFrameSize > 0 ? 0 : 2) +
|
||||||
|
(useSharedCall ? 0 : 1) +
|
||||||
|
3; // stw, mflr, lwz
|
||||||
|
const useBLTrick = sizeWithBLTrick < sizeWithoutBLTrick;
|
||||||
|
|
||||||
|
/** @type {Map<number, {fallback: number, preps: Map<string, number>}>} */
|
||||||
|
const offFuncs = new Map();
|
||||||
|
/** @type {number|null} */
|
||||||
|
let offCall = null;
|
||||||
|
/** @type {Map<string, number>} */
|
||||||
|
const offStructs = new Map();
|
||||||
|
|
||||||
|
if (useSharedCall) {
|
||||||
|
let off = 0;
|
||||||
|
/** @type {string[]} */
|
||||||
|
const funcHexs = [];
|
||||||
|
for (const [addr, prepCounts] of callCounts.entries()) {
|
||||||
|
// put repeated call only
|
||||||
|
if (Array.from(prepCounts).reduce((a, [k, c]) => a + c, 0) <= 1) continue;
|
||||||
|
/** @type {Map<string, number>} */
|
||||||
|
const preps = new Map();
|
||||||
|
const prepKeys = Array.from(prepCounts.keys()).filter((k) => k !== '');
|
||||||
|
let hex = '';
|
||||||
|
// TODO optimize only when one prep (excluding '') is used
|
||||||
|
if (prepKeys.length === 1) {
|
||||||
|
const [prep] = prepKeys;
|
||||||
|
preps.set(prep, off);
|
||||||
|
hex += prep;
|
||||||
|
}
|
||||||
|
offFuncs.set(addr, { fallback: off + hex.length / 2, preps });
|
||||||
|
// liDX addr
|
||||||
|
hex += insts2hex(liDX(rAddr, addr));
|
||||||
|
// b call
|
||||||
|
off += 4 + hex.length / 2;
|
||||||
|
// push
|
||||||
|
funcHexs.push(hex);
|
||||||
|
}
|
||||||
|
const offDst = Math.max(0, off - 4);
|
||||||
|
offCall = offDst;
|
||||||
|
|
||||||
|
// make data
|
||||||
|
/** callX: */
|
||||||
|
funcHexs.forEach((hex, i, arr) => {
|
||||||
|
p.pushHex(hex);
|
||||||
|
// b call # except the last 1
|
||||||
|
if (i < arr.length - 1) {
|
||||||
|
p.b(offDst);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/** call: */
|
||||||
|
p.push(ASM.mtctr(rAddr), ASM.bctr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// add struct data
|
||||||
|
if (useBLTrick) {
|
||||||
|
// [4-byte aligned data, chars data]
|
||||||
|
const [aligned, chars] = splitArray(structDB, (hex) => hex.length % 8 === 0);
|
||||||
|
for (const data of [aligned, chars]) {
|
||||||
|
for (const hex of data) {
|
||||||
|
offStructs.set(hex, p.pc);
|
||||||
|
p.pushHex(hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make 4-byte alignment
|
||||||
|
p.align();
|
||||||
|
}
|
||||||
|
|
||||||
|
// make body
|
||||||
|
const dataSize = p.pc;
|
||||||
|
/** mflr rData */
|
||||||
|
if (useBLTrick) p.push(ASM.mflr(rData));
|
||||||
|
/** for each ASM instructions */
|
||||||
|
for (const inst of insts) {
|
||||||
|
if (typeof inst === 'number') {
|
||||||
|
p.push(inst);
|
||||||
|
} else if (inst.type === 'call') {
|
||||||
|
const { prep = [], addr } = inst;
|
||||||
|
const prepKey = insts2hex(prep);
|
||||||
|
const off = offFuncs.get(addr);
|
||||||
|
if (off == null) {
|
||||||
|
// fallback to prepare and load addr manually
|
||||||
|
p.push(...prep, ...liDX(rAddr, addr));
|
||||||
|
// call
|
||||||
|
if (offCall == null) {
|
||||||
|
p.push(
|
||||||
|
// mtctr rAddr
|
||||||
|
ASM.mtctr(rAddr),
|
||||||
|
// bctrl
|
||||||
|
ASM.bctr(true),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
p.bl(offCall);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// bl to callX directly
|
||||||
|
const { fallback, preps } = off;
|
||||||
|
const dst = preps.get(prepKey);
|
||||||
|
if (dst == null) {
|
||||||
|
// fallback to prepare manually
|
||||||
|
p.pushHex(prepKey);
|
||||||
|
p.bl(fallback);
|
||||||
|
} else {
|
||||||
|
// bl to dst directly
|
||||||
|
p.bl(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (inst.type === 'struct') {
|
||||||
|
const { reg, hex } = inst;
|
||||||
|
const off = offStructs.get(hex);
|
||||||
|
if (off == null) {
|
||||||
|
// fallback to use BL trick here
|
||||||
|
/** bl L */
|
||||||
|
const d = ((hex.length + 7) >> 3) << 2;
|
||||||
|
p.push(ASM.b(4 + d, true));
|
||||||
|
/** (data) */
|
||||||
|
p.pushHex(hex);
|
||||||
|
p.align();
|
||||||
|
/** L: mflr rReg */
|
||||||
|
p.push(ASM.mflr(reg));
|
||||||
|
} else {
|
||||||
|
// use addi directly
|
||||||
|
p.push(ASM.addi(reg, rData, off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Inst[]} */
|
||||||
|
const prologue = [];
|
||||||
|
if (useBLTrick) stackFrameSize += 4; // for r31
|
||||||
|
if (stackFrameSize > 0) {
|
||||||
|
// stack frame size: 16-byte align
|
||||||
|
stackFrameSize = ((stackFrameSize + 0xf) >> 4) << 4;
|
||||||
|
// stwu r1
|
||||||
|
prologue.push(ASM.stwu(1, 1, -stackFrameSize));
|
||||||
|
if (useBLTrick) {
|
||||||
|
// stw r31
|
||||||
|
prologue.push(ASM.stw(31, 1, stackFrameSize - 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useSharedCall || useBLTrick) {
|
||||||
|
prologue.push(ASM.b(4 + dataSize, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Inst[]} */
|
||||||
|
const epilogue = [];
|
||||||
|
if (stackFrameSize > 0) {
|
||||||
|
if (useBLTrick) {
|
||||||
|
// lwz r31
|
||||||
|
epilogue.push(ASM.lwz(31, 1, stackFrameSize - 4));
|
||||||
|
}
|
||||||
|
// addi r1
|
||||||
|
epilogue.push(ASM.addi(1, 1, stackFrameSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
return insts2hex(prologue) + p.hex + insts2hex(epilogue);
|
||||||
|
}
|
|
@ -1,31 +1,25 @@
|
||||||
import { parseJSON } from '../codegen.js';
|
import { parseJSON } from '../codegen.js';
|
||||||
import { ASM, makeInst, liDX, str2inst, makeProgram, inst2gecko } from '../asm.js';
|
import { ASM, liDX, str2inst, makeProgram, inst2gecko, getFillRectParams } from '../asm.js';
|
||||||
|
import { measureText } from '../text.js';
|
||||||
|
import { addrs } from '../addrs.js';
|
||||||
|
import { parseFormat } from './format.js';
|
||||||
|
import { assemble } from './assembler.js';
|
||||||
|
import { drawText, fillRect } from './functions.js';
|
||||||
export const lskey = 'config/CustomizedDisplay';
|
export const lskey = 'config/CustomizedDisplay';
|
||||||
|
|
||||||
export const defaultConfig = [
|
import configDB from './configDB.js';
|
||||||
{
|
export const defaultConfig = [configDB.PAS];
|
||||||
x: 16,
|
|
||||||
y: 192,
|
|
||||||
fontSize: 18,
|
|
||||||
fgRGB: 0xffffff,
|
|
||||||
fgA: 0xff,
|
|
||||||
fgRGB2: null,
|
|
||||||
fgA2: null,
|
|
||||||
fmt: `X <x|.0|39.39>
|
|
||||||
Y <y|.0|1207.39>
|
|
||||||
Z <z|.0|-4193.6>
|
|
||||||
A <angle||65535>
|
|
||||||
H <HSpd|.2|15.15>
|
|
||||||
V <VSpd|.2|-31.17>
|
|
||||||
QF <QF||0>`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
/** @type {(...args: Parameters<typeof parseFormat>) => string} */
|
||||||
|
export const format2previewText = (input, version) => parseFormat(input, version).preview;
|
||||||
|
|
||||||
|
/** @typedef {'GMSJ01'|'GMSJ0A'|'GMSE01'|'GMSP01'} GameVersion */
|
||||||
/** @param {GameVersion} version */
|
/** @param {GameVersion} version */
|
||||||
export function getConfig(version) {
|
export function getConfig(version) {
|
||||||
/** @type {typeof defaultConfig} */
|
/** @type {typeof defaultConfig} */
|
||||||
const config = typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey));
|
const config = typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey));
|
||||||
return (config instanceof Array ? config : defaultConfig).map(({ fmt, ...o }) => ({
|
return (config instanceof Array ? config : defaultConfig).map(({ fmt, ...o }) => ({
|
||||||
|
...defaultConfig[0],
|
||||||
...o,
|
...o,
|
||||||
fmt,
|
fmt,
|
||||||
text: format2previewText(fmt, version),
|
text: format2previewText(fmt, version),
|
||||||
|
@ -33,412 +27,47 @@ export function getConfig(version) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {number[]} Inst
|
* @typedef {Parameters<assemble>[0][number]} ASMInst
|
||||||
* @typedef {8|16|32|'float'} DataType
|
|
||||||
* @typedef {(gpr: number)=>Inst} GPRHandler -- (src=gpr, dst=gpr)
|
|
||||||
* @typedef {{type: 'gpr'|'fpr'|'sp', index: number}} Dst
|
|
||||||
* @typedef {'GMSJ01'|'GMSJ0A'|'GMSE01'|'GMSP01'} GameVersion
|
|
||||||
*
|
|
||||||
* @typedef {{
|
|
||||||
* offset: number
|
|
||||||
* dtype: DataType
|
|
||||||
* post?: GPRHandler
|
|
||||||
* }} FieldInfo
|
|
||||||
*
|
|
||||||
* @typedef {{
|
|
||||||
* id: string
|
|
||||||
* pre: GPRHandler
|
|
||||||
* }} Base
|
|
||||||
*
|
|
||||||
* @typedef {{
|
|
||||||
* pre: GPRHandler
|
|
||||||
* fields: {info: FieldInfo, dst: Dst}[]
|
|
||||||
* }} Entry
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @typedef {{[ver in GameVersion]: GPRHandler}} VBase */
|
|
||||||
const bases = {
|
|
||||||
gpMarioOriginal: /**@type{VBase}*/ ({
|
|
||||||
GMSJ01: (rT) => ASM.lwz(rT, 13, -0x6748),
|
|
||||||
GMSE01: (rT) => ASM.lwz(rT, 13, -0x60d8),
|
|
||||||
GMSP01: (rT) => ASM.lwz(rT, 13, -0x61b0),
|
|
||||||
GMSJ0A: (rT) => ASM.lwz(rT, 13, -0x6218),
|
|
||||||
}),
|
|
||||||
gpMarDirector: /**@type{VBase}*/ ({
|
|
||||||
GMSJ01: (rT) => ASM.lwz(rT, 13, -0x6818),
|
|
||||||
GMSE01: (rT) => ASM.lwz(rT, 13, -0x6048),
|
|
||||||
GMSP01: (rT) => ASM.lwz(rT, 13, -0x6120),
|
|
||||||
GMSJ0A: (rT) => ASM.lwz(rT, 13, -0x6188),
|
|
||||||
}),
|
|
||||||
gpCamera: /**@type{VBase}*/ ({
|
|
||||||
GMSJ01: (rT) => ASM.lwz(rT, 13, -0x5750),
|
|
||||||
GMSE01: (rT) => ASM.lwz(rT, 13, -0x7118),
|
|
||||||
GMSP01: (rT) => ASM.lwz(rT, 13, -0x7158),
|
|
||||||
GMSJ0A: (rT) => ASM.lwz(rT, 13, -0x5768),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
/** @typedef {keyof typeof bases} BaseId */
|
|
||||||
|
|
||||||
/** @type {({id: string, base: BaseId, fmt: string, preview: number} & FieldInfo)[]} */
|
|
||||||
const fields = [
|
|
||||||
{ id: 'x', base: 'gpMarioOriginal', dtype: 'float', offset: 0x10, fmt: '%.0f', preview: 426.39 },
|
|
||||||
{ id: 'y', base: 'gpMarioOriginal', dtype: 'float', offset: 0x14, fmt: '%.0f', preview: -427.39 },
|
|
||||||
{ id: 'z', base: 'gpMarioOriginal', dtype: 'float', offset: 0x18, fmt: '%.0f', preview: 428.39 },
|
|
||||||
{ id: 'angle', base: 'gpMarioOriginal', dtype: 16, offset: 0x96, fmt: '%hu', preview: 1207 },
|
|
||||||
{
|
|
||||||
id: 'HSpd',
|
|
||||||
base: 'gpMarioOriginal',
|
|
||||||
dtype: 'float',
|
|
||||||
offset: 0xb0,
|
|
||||||
fmt: '%.2f',
|
|
||||||
preview: 15.15,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'VSpd',
|
|
||||||
base: 'gpMarioOriginal',
|
|
||||||
dtype: 'float',
|
|
||||||
offset: 0xa8,
|
|
||||||
fmt: '%.2f',
|
|
||||||
preview: -31.17,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'QF',
|
|
||||||
base: 'gpMarDirector',
|
|
||||||
dtype: 32,
|
|
||||||
offset: 0x58,
|
|
||||||
fmt: '%u',
|
|
||||||
preview: 0,
|
|
||||||
post: (rT) => ASM.rlwinm(rT, rT, 0, 30, 31, false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'CAngle',
|
|
||||||
base: 'gpCamera',
|
|
||||||
dtype: 16,
|
|
||||||
offset: 0xa6,
|
|
||||||
fmt: '%hu',
|
|
||||||
preview: 9,
|
|
||||||
post: (rT) => ASM.addi(rT, rT, -0x8000), // offset by 0x8000
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const fieldDB = Object.fromEntries(
|
|
||||||
fields.map(({ id, base, fmt, preview, ...info }) => [
|
|
||||||
id.toLowerCase(),
|
|
||||||
{ base, info, fmt, preview },
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const store = {
|
|
||||||
8: ASM.stb,
|
|
||||||
16: ASM.sth,
|
|
||||||
32: ASM.stw,
|
|
||||||
float: ASM.stfs,
|
|
||||||
double: ASM.stfd,
|
|
||||||
};
|
|
||||||
const load = {
|
|
||||||
8: ASM.lbz,
|
|
||||||
16: ASM.lhz,
|
|
||||||
32: ASM.lwz,
|
|
||||||
float: ASM.lfs,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} x
|
|
||||||
* @param {number} y
|
|
||||||
* @param {number} fontSize
|
|
||||||
* @param {number} colorTop
|
|
||||||
* @param {number} colorBot
|
|
||||||
*/
|
|
||||||
export function prepareDrawText(x, y, fontSize, colorTop, colorBot) {
|
|
||||||
let gpr = 9;
|
|
||||||
let fpr = 1;
|
|
||||||
let sp = 8;
|
|
||||||
let fmt = '';
|
|
||||||
let hasFloat = false;
|
|
||||||
/** @type {{[id: string]: Entry}} */
|
|
||||||
const entries = {};
|
|
||||||
|
|
||||||
/** @returns {Dst} */
|
|
||||||
function allocInt() {
|
|
||||||
if (gpr <= 10) {
|
|
||||||
return { type: 'gpr', index: gpr++ };
|
|
||||||
} else {
|
|
||||||
/** @type {Dst} */
|
|
||||||
const dst = { type: 'sp', index: sp };
|
|
||||||
sp += 4;
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** @returns {Dst} */
|
|
||||||
function allocFloat() {
|
|
||||||
hasFloat = true;
|
|
||||||
if (fpr <= 8) {
|
|
||||||
return { type: 'fpr', index: fpr++ };
|
|
||||||
} else {
|
|
||||||
sp += sp & 4; // align 8
|
|
||||||
/** @type {Dst} */
|
|
||||||
const dst = { type: 'sp', index: sp };
|
|
||||||
sp += 8;
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** @param {Base} base */
|
|
||||||
const getEntry = (base) =>
|
|
||||||
entries[base.id] ??
|
|
||||||
(entries[base.id] = {
|
|
||||||
pre: base.pre,
|
|
||||||
fields: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* @param {string} format
|
|
||||||
* @param {Base} base
|
|
||||||
* @param {FieldInfo} field
|
|
||||||
*/
|
|
||||||
pushValue(format, base, field) {
|
|
||||||
fmt += format;
|
|
||||||
getEntry(base).fields.push({
|
|
||||||
info: field,
|
|
||||||
dst: (field.dtype === 'float' ? allocFloat : allocInt)(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {string} text
|
|
||||||
*/
|
|
||||||
pushText(text) {
|
|
||||||
fmt += text.replace(/%/g, '%%');
|
|
||||||
},
|
|
||||||
makeCode() {
|
|
||||||
/** @type {Inst[]} */
|
|
||||||
const insts = [];
|
|
||||||
// sp
|
|
||||||
const spAdd = sp === 8 ? 0 : ((sp >> 4) + (sp & 0xf ? 1 : 0)) << 4;
|
|
||||||
// params
|
|
||||||
for (const { pre, fields: params } of Object.values(entries)) {
|
|
||||||
// load base to gpr
|
|
||||||
const rBase = 3;
|
|
||||||
insts.push(pre(rBase));
|
|
||||||
// load all params
|
|
||||||
const rField = 5;
|
|
||||||
const fField = 9;
|
|
||||||
for (const {
|
|
||||||
info: { offset: srcoff, dtype, post },
|
|
||||||
dst,
|
|
||||||
} of params) {
|
|
||||||
if (dst.type === 'sp') {
|
|
||||||
const dstoff = dst.index;
|
|
||||||
if (dtype === 'float') {
|
|
||||||
insts.push(
|
|
||||||
// lfs fField, offset(rBase)
|
|
||||||
load.float(fField, rBase, srcoff),
|
|
||||||
// post
|
|
||||||
post?.(fField) ?? [],
|
|
||||||
// stfd fField, dst.index(r1)
|
|
||||||
store.double(fField, 1, dstoff),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
insts.push(
|
|
||||||
// load rField, offset(rBase)
|
|
||||||
load[dtype](rField, rBase, srcoff),
|
|
||||||
// post
|
|
||||||
post?.(rField) ?? [],
|
|
||||||
// stw rField, dst.index(r1)
|
|
||||||
store[32](rField, 1, dstoff),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// load to register
|
|
||||||
const { index: rDst } = dst;
|
|
||||||
insts.push(
|
|
||||||
// load rDst
|
|
||||||
load[dtype](rDst, rBase, srcoff),
|
|
||||||
// post
|
|
||||||
post?.(rDst) ?? [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// r8 = fmt
|
|
||||||
const fmtbuf = str2inst(fmt);
|
|
||||||
insts.push(
|
|
||||||
// bl 4+len4(fmt)
|
|
||||||
ASM.b(4 + (fmtbuf.length << 2), true),
|
|
||||||
// .string fmt
|
|
||||||
fmtbuf,
|
|
||||||
// mflr r8
|
|
||||||
ASM.mflr(8),
|
|
||||||
);
|
|
||||||
/*
|
|
||||||
* r3 = x
|
|
||||||
* r4 = y
|
|
||||||
* r5 = fontSize
|
|
||||||
* r6 = colorTop
|
|
||||||
* r7 = colorBot
|
|
||||||
*/
|
|
||||||
insts.push(
|
|
||||||
liDX(3, x),
|
|
||||||
liDX(4, y),
|
|
||||||
liDX(5, fontSize),
|
|
||||||
liDX(6, colorTop),
|
|
||||||
colorTop === colorBot ? ASM.mr(7, 6) : liDX(7, colorBot),
|
|
||||||
);
|
|
||||||
// cr{set|clr} 6
|
|
||||||
insts.push((hasFloat ? ASM.crset : ASM.crclr)(6));
|
|
||||||
// DONE
|
|
||||||
return { code: insts.flatMap((e) => e), spNeed: spAdd };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const dtype2fmtinfo = {
|
|
||||||
8: { prefix: 'hh', mask: 0xff },
|
|
||||||
16: { prefix: 'h', mask: 0xffff },
|
|
||||||
32: { prefix: '', mask: 0xffffffff },
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} input
|
|
||||||
* @param {GameVersion} version
|
|
||||||
* @param {ReturnType<typeof prepareDrawText>|null} f
|
|
||||||
*/
|
|
||||||
export function format2previewText(input, version, f = null) {
|
|
||||||
const regex = /<(.*?)>/g;
|
|
||||||
let preview = '';
|
|
||||||
/** @type {RegExpExecArray|null} */
|
|
||||||
let m = null;
|
|
||||||
let i0 = 0;
|
|
||||||
while ((m = regex.exec(input))) {
|
|
||||||
const { index: i } = m;
|
|
||||||
// text
|
|
||||||
const text = input.slice(i0, i);
|
|
||||||
f?.pushText(text);
|
|
||||||
preview += text;
|
|
||||||
// arg
|
|
||||||
const [fieldId, fmt0, pvw0] = m[1].split('|');
|
|
||||||
const field = fieldDB[fieldId.toLowerCase()];
|
|
||||||
if (field) {
|
|
||||||
const { base: baseId, info, fmt: fmt1, preview: pvw1 } = field;
|
|
||||||
const { dtype } = info;
|
|
||||||
const fmt2 = fmt0 || fmt1;
|
|
||||||
let ipvw = +pvw0;
|
|
||||||
if (!pvw0 || !isFinite(ipvw)) ipvw = pvw1;
|
|
||||||
let fmt;
|
|
||||||
let pvw;
|
|
||||||
let padfmt = '';
|
|
||||||
if (dtype === 'float') {
|
|
||||||
const m = fmt2.trim().match(/^(?:%?(\d*)\.)?(\d+)([eEf]?)$/);
|
|
||||||
padfmt = m?.[1] || '';
|
|
||||||
const digit = +(m?.[2] || 0);
|
|
||||||
const suffix = m?.[3] || 'f';
|
|
||||||
fmt = `%${padfmt}.${digit}${suffix}`;
|
|
||||||
pvw = ipvw[suffix === 'f' ? 'toFixed' : 'toExponential'](digit);
|
|
||||||
if (suffix === 'E') pvw = pvw.toUpperCase();
|
|
||||||
} else {
|
|
||||||
const { prefix, mask } = dtype2fmtinfo[dtype];
|
|
||||||
ipvw &= mask;
|
|
||||||
const m = fmt2.trim().match(/^%?(\d*)h{,2}([dioxXu])$/);
|
|
||||||
padfmt = m?.[1] || '';
|
|
||||||
const t = m?.[2] || 'u';
|
|
||||||
fmt = `%${padfmt}${prefix}${t}`;
|
|
||||||
if ('di'.includes(t)) {
|
|
||||||
if (ipvw > mask >>> 1) ipvw -= mask;
|
|
||||||
pvw = ipvw.toString(10);
|
|
||||||
} else if (t === 'o') {
|
|
||||||
pvw = (ipvw >>> 0).toString(8);
|
|
||||||
} else if ('xX'.includes(t)) {
|
|
||||||
pvw = (ipvw >>> 0).toString(16);
|
|
||||||
} else {
|
|
||||||
pvw = (ipvw >>> 0).toString(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pvw = pvw.padStart(+padfmt, padfmt[0] === '0' ? '0' : ' ');
|
|
||||||
f?.pushValue(fmt, { id: baseId, pre: bases[baseId][version] }, info);
|
|
||||||
preview += pvw;
|
|
||||||
} else {
|
|
||||||
// fail to parse
|
|
||||||
f?.pushText(m[0]);
|
|
||||||
preview += m[0];
|
|
||||||
}
|
|
||||||
// next
|
|
||||||
i0 = i + m[0].length;
|
|
||||||
}
|
|
||||||
const text = input.slice(i0);
|
|
||||||
f?.pushText(text);
|
|
||||||
preview += text;
|
|
||||||
// DONE
|
|
||||||
return preview;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addrsOrig = {
|
|
||||||
GMSJ01: 0x80206a00 - 0x2c,
|
|
||||||
GMSJ0A: 0x8012556c - 0x2c,
|
|
||||||
GMSE01: 0x801441e0 - 0x2c,
|
|
||||||
GMSP01: 0x80138e1c - 0x2c,
|
|
||||||
};
|
|
||||||
const addrsSetup2D = {
|
|
||||||
GMSJ01: 0x80035228,
|
|
||||||
GMSJ0A: 0x802caecc,
|
|
||||||
GMSE01: 0x802eb6bc,
|
|
||||||
GMSP01: 0x802e3864,
|
|
||||||
};
|
|
||||||
const addrDrawText = 0x817f0238;
|
|
||||||
const addrDst = 0x817fa000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {GameVersion} version
|
* @param {GameVersion} version
|
||||||
*/
|
*/
|
||||||
export default function codegen(version) {
|
export default function codegen(version) {
|
||||||
const config = getConfig(version);
|
const configs = getConfig(version);
|
||||||
|
|
||||||
let spOff = 0;
|
let stackFrameSize = 0;
|
||||||
const fcodes = /** @type {Inst[]} */ ([]);
|
|
||||||
|
|
||||||
for (const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, fmt } of config) {
|
/** @type {ASMInst[]} */
|
||||||
// color
|
const asm = [];
|
||||||
const colorTop = (fgRGB << 8) | fgA;
|
|
||||||
const colorBot = fgRGB2 == null || fgA2 == null ? colorTop : (fgRGB2 << 8) | fgA;
|
for (const config of configs) {
|
||||||
// prepare drawText
|
const { fmt, bgA } = config;
|
||||||
const f = prepareDrawText(x, y, fontSize, colorTop, colorBot);
|
const { preview, format, fields } = parseFormat(fmt, version);
|
||||||
format2previewText(fmt, version, f);
|
|
||||||
// update code and sp
|
// fill_rect
|
||||||
const { code, spNeed } = f.makeCode();
|
if (bgA) {
|
||||||
spOff = Math.max(spOff, spNeed);
|
asm.push(...fillRect(version, config, measureText(preview, version)));
|
||||||
fcodes.push(code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const addrOrig = addrsOrig[version];
|
// drawText
|
||||||
const addrSetup2D = addrsSetup2D[version];
|
if (fmt.trim()) {
|
||||||
|
const { insts, sp } = drawText(version, config, format, fields);
|
||||||
// program
|
stackFrameSize = Math.max(stackFrameSize, sp);
|
||||||
const program = makeProgram(addrDst);
|
asm.push(...insts);
|
||||||
// addi r3, r1, 0xE90
|
}
|
||||||
program.push(ASM.addi(3, 1, 0xe90));
|
|
||||||
// addi r1, r1, -spOff
|
|
||||||
if (spOff) program.push(ASM.addi(1, 1, -spOff));
|
|
||||||
// bl setup
|
|
||||||
program.bl(addrSetup2D);
|
|
||||||
// (drawText)
|
|
||||||
for (const code of fcodes) {
|
|
||||||
program.push(code);
|
|
||||||
program.bl(addrDrawText);
|
|
||||||
}
|
}
|
||||||
// addi r1, r1, spOff
|
|
||||||
if (spOff) program.push(ASM.addi(1, 1, spOff));
|
|
||||||
// b orig+4
|
|
||||||
program.b(addrOrig + 4);
|
|
||||||
|
|
||||||
// dump code
|
let body = assemble(asm, stackFrameSize);
|
||||||
const pcode = program.dump();
|
// align code
|
||||||
const psize = pcode.length;
|
if (body.length % 16 === 0) body += '60000000';
|
||||||
return [
|
body += '00000000';
|
||||||
makeInst((0xc6 << 24) | (addrOrig & 0x1ffffff)),
|
|
||||||
makeInst(addrDst),
|
const addrDst = addrs.drawWater[version] - 0x2c; // [-0x30, -0x18]
|
||||||
makeInst((0x06 << 24) | (addrDst & 0x1fffffff)),
|
return (
|
||||||
makeInst(psize << 2),
|
[
|
||||||
pcode,
|
0xc2000000 | (addrDst & 0x1fffffff),
|
||||||
psize & 1 ? [0] : [],
|
body.length >>> 4, // 16 hex-digits per line
|
||||||
]
|
]
|
||||||
.flatMap((e) => e)
|
.flatMap((e) => e)
|
||||||
.map(inst2gecko)
|
.map(inst2gecko)
|
||||||
.join('');
|
.join('') + body
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,26 @@
|
||||||
<div>
|
<div>
|
||||||
<Preview :config="previewConfig" />
|
<Preview :config="previewConfig" />
|
||||||
<div v-for="c,i in config" :key="i" class="textcell">
|
<div v-for="c,i in config" :key="i" class="textcell">
|
||||||
<button @click="config.splice(i, 1)" class="textcell-remove">×</button>
|
<button class="textcell-remove" @click="deletionConfirm(i)">×</button>
|
||||||
<Cell :value="c" @input="$event => config.splice(i, 1, $event)" :version="version" />
|
<Cell :value="c" @input="$event => config.splice(i, 1, $event)" :version="version" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="btn-ctn">
|
||||||
<button @click="config.push(defaultConfigCell)" class="textcell-add">+</button>
|
<button @click="config.push(db.PAS)">{{l('add.PAS')}}</button>
|
||||||
|
<button @click="config.push(db.speed)">{{l('add.speed')}}</button>
|
||||||
|
<button @click="config.push(db.detailed)">{{l('add.detailed')}}</button>
|
||||||
|
<button @click="config.push(db.rect)">{{l('add.rect')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import labels from './labels.json';
|
||||||
import { defaultConfig, getConfig, lskey, format2previewText } from './codegen.js';
|
import { defaultConfig, getConfig, lskey, format2previewText } from './codegen.js';
|
||||||
|
import configDB from './configDB.js';
|
||||||
|
import { makeUpdateConfig, makeGetLabel } from '../utils.js';
|
||||||
import Cell from './Cell.vue';
|
import Cell from './Cell.vue';
|
||||||
|
|
||||||
|
/** @typedef {'GMSJ01'|'GMSJ0A'|'GMSE01'|'GMSP01'} GameVersion */
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Cell,
|
Cell,
|
||||||
|
@ -23,21 +30,35 @@ export default {
|
||||||
version: {type: String},
|
version: {type: String},
|
||||||
previewConfig: {type: Object},
|
previewConfig: {type: Object},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
l() {
|
||||||
|
return makeGetLabel(labels, this.$lang);
|
||||||
|
},
|
||||||
|
db() {
|
||||||
|
const version = /**@type{GameVersion}*/(this.version);
|
||||||
|
return Object.fromEntries(Object.entries(configDB).map(([k, v]) => [
|
||||||
|
k,
|
||||||
|
{...v, text: format2previewText(v.fmt, version)},
|
||||||
|
]));
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
const config = getConfig();
|
const version = /**@type{GameVersion}*/(this.version);
|
||||||
|
const config = getConfig(version);
|
||||||
const defaultConfigCell = {
|
const defaultConfigCell = {
|
||||||
text: format2previewText(defaultConfig[0].fmt, this.version),
|
text: format2previewText(defaultConfig[0].fmt, version),
|
||||||
...defaultConfig[0],
|
...defaultConfig[0],
|
||||||
};
|
};
|
||||||
return {config, defaultConfigCell};
|
return {config, defaultConfigCell};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
config(config) {
|
config: makeUpdateConfig(lskey, defaultConfig),
|
||||||
// save config
|
},
|
||||||
const sconf = config.map(({text, ...o}) => ({...o}));
|
methods: {
|
||||||
localStorage.setItem(lskey, JSON.stringify(sconf));
|
/** @param {number} i */
|
||||||
// emit
|
deletionConfirm(i) {
|
||||||
this.$emit('config', config);
|
// if (window.confirm(this.l('deletionConfirm'))) {
|
||||||
|
this.config.splice(i, 1);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -57,4 +78,7 @@ export default {
|
||||||
color: red;
|
color: red;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.btn-ctn button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
const base = {
|
||||||
|
fontSize: 20,
|
||||||
|
fgRGB: 0xffffff,
|
||||||
|
fgA: 0xff,
|
||||||
|
fgRGB2: null,
|
||||||
|
fgA2: null,
|
||||||
|
bgRGB: 0,
|
||||||
|
bgA: 0,
|
||||||
|
bgLeft: 0,
|
||||||
|
bgRight: 0,
|
||||||
|
bgTop: 0,
|
||||||
|
bgBot: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
PAS: {
|
||||||
|
...base,
|
||||||
|
x: 16,
|
||||||
|
y: 200,
|
||||||
|
fmt: `X Pos <x|.0|39.39>
|
||||||
|
Y Pos <y|.0|1207.39>
|
||||||
|
Z Pos <z|.0|-4193.6>
|
||||||
|
Angle <angle||65535>
|
||||||
|
H Spd <HSpd|.2|15.15>
|
||||||
|
V Spd <VSpd|.2|-31.17>`,
|
||||||
|
},
|
||||||
|
speed: {
|
||||||
|
...base,
|
||||||
|
x: 16,
|
||||||
|
y: 240,
|
||||||
|
fmt: `H Spd <HSpd|.2|15.15>
|
||||||
|
V Spd <VSpd|.2|-31.17>`,
|
||||||
|
},
|
||||||
|
detailed: {
|
||||||
|
...base,
|
||||||
|
x: 16,
|
||||||
|
y: 192,
|
||||||
|
fontSize: 16,
|
||||||
|
fmt: `X <x|.0|39.39>
|
||||||
|
Y <y|.0|1207.39>
|
||||||
|
Z <z|.0|-4193.6>
|
||||||
|
A <angle||65535>
|
||||||
|
C <CAngle||9>
|
||||||
|
H <HSpd|.2|15.15>
|
||||||
|
V <VSpd|.2|-31.17>
|
||||||
|
QF <QF||0>
|
||||||
|
I <invinc||30>
|
||||||
|
G <goop||36368>
|
||||||
|
Spin <spin||>`,
|
||||||
|
},
|
||||||
|
rect: {
|
||||||
|
...base,
|
||||||
|
x: 32,
|
||||||
|
y: 48,
|
||||||
|
fontSize: 0,
|
||||||
|
fmt: '',
|
||||||
|
bgRight: 536,
|
||||||
|
bgBot: 384,
|
||||||
|
bgA: 0x7f,
|
||||||
|
},
|
||||||
|
};
|
125
site/.vuepress/components/codes/CustomizedDisplay/fields.js
Normal file
125
site/.vuepress/components/codes/CustomizedDisplay/fields.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import { ASM } from '../asm.js';
|
||||||
|
import { makeDirectLoaderASM, makeFunctionLoader, rTmp, fTmp } from './loader.js';
|
||||||
|
import { addrs, r13offs } from '../addrs.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {ReturnType<import('./loader.js').makeFunctionLoader>} Loader
|
||||||
|
* @typedef {ReturnType<Loader['asm']>[number]} ASMInst
|
||||||
|
* @typedef {Parameters<Loader['asm']>[0]} GameVersion
|
||||||
|
*
|
||||||
|
* @typedef {Parameters<makeDirectLoaderASM>[1]} DirectLoadFunc
|
||||||
|
* @typedef {{
|
||||||
|
* dtype: Loader['dtype'],
|
||||||
|
* base: keyof typeof bases,
|
||||||
|
* offset: number,
|
||||||
|
* postprocess?: (rT: number)=>ASMInst[]
|
||||||
|
* }} DirectLoader
|
||||||
|
*/
|
||||||
|
|
||||||
|
const r13bases = /**@type{{[k in keyof typeof r13offs]: DirectLoadFunc}}*/ (
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(r13offs).map(([k, ver2off]) => [
|
||||||
|
k,
|
||||||
|
/** @type {DirectLoadFunc} */
|
||||||
|
(rT, ver) => [ASM.lwz(rT, 13, ver2off[ver])],
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
export const bases = {
|
||||||
|
...r13bases,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {Array<(Loader|DirectLoader) & {id:string} & (
|
||||||
|
* {fmt: string, preview: number} |
|
||||||
|
* {fmt: '%s', preview: (ver: GameVersion) => string}
|
||||||
|
* )>}
|
||||||
|
*/
|
||||||
|
export const fields = [
|
||||||
|
{ id: 'x', base: 'gpMarioOriginal', dtype: 'float', offset: 0x10, fmt: '%.0f', preview: 426.39 },
|
||||||
|
{ id: 'y', base: 'gpMarioOriginal', dtype: 'float', offset: 0x14, fmt: '%.0f', preview: -427.39 },
|
||||||
|
{ id: 'z', base: 'gpMarioOriginal', dtype: 'float', offset: 0x18, fmt: '%.0f', preview: 428.39 },
|
||||||
|
{ id: 'angle', base: 'gpMarioOriginal', dtype: 16, offset: 0x96, fmt: '%hu', preview: 1207 },
|
||||||
|
{
|
||||||
|
id: 'HSpd',
|
||||||
|
base: 'gpMarioOriginal',
|
||||||
|
dtype: 'float',
|
||||||
|
offset: 0xb0,
|
||||||
|
fmt: '%.2f',
|
||||||
|
preview: 15.15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'VSpd',
|
||||||
|
base: 'gpMarioOriginal',
|
||||||
|
dtype: 'float',
|
||||||
|
offset: 0xa8,
|
||||||
|
fmt: '%.2f',
|
||||||
|
preview: -31.17,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'QF',
|
||||||
|
base: 'gpMarDirector',
|
||||||
|
dtype: 32,
|
||||||
|
offset: 0x58,
|
||||||
|
fmt: '%u',
|
||||||
|
preview: 0,
|
||||||
|
postprocess: (rT) => [ASM.rlwinm(rT, rT, 0, 30, 31, false)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'CAngle',
|
||||||
|
base: 'gpCamera',
|
||||||
|
dtype: 16,
|
||||||
|
offset: 0xa6,
|
||||||
|
fmt: '%hu',
|
||||||
|
preview: 9,
|
||||||
|
postprocess: (rT) => [ASM.addi(rT, rT, -0x8000)], // offset by 0x8000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'invinc',
|
||||||
|
base: 'gpMarioOriginal',
|
||||||
|
dtype: 16,
|
||||||
|
offset: 0x14c,
|
||||||
|
fmt: '%hd',
|
||||||
|
preview: 30,
|
||||||
|
postprocess: (rT) => [ASM.rlwinm(rT, rT, 30, 2, 31, false)], // QF to frame (>>2)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'goop',
|
||||||
|
fmt: '%d',
|
||||||
|
preview: 600,
|
||||||
|
...makeFunctionLoader(32, (ver) => [
|
||||||
|
{
|
||||||
|
type: 'call',
|
||||||
|
addr: addrs.getPollutionDegree[ver],
|
||||||
|
prep: [ASM.lwz(3, 13, r13offs.gpPollution[ver])],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'spin',
|
||||||
|
fmt: '%s',
|
||||||
|
// TODO better char mapping
|
||||||
|
preview: (ver) => String.fromCharCode(ver.startsWith('GMSJ') ? 0xff20 : 0x40),
|
||||||
|
dtype: 32,
|
||||||
|
calling: true,
|
||||||
|
asm: (ver, dst) => [
|
||||||
|
{
|
||||||
|
type: 'call',
|
||||||
|
addr: addrs.checkStickRotate[ver],
|
||||||
|
prep: [
|
||||||
|
ASM.lwz(3, 13, r13offs.gpMarioOriginal[ver]),
|
||||||
|
ASM.stwu(1, 1, -0x10),
|
||||||
|
ASM.addi(4, 1, 8),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// 0 (A) 0
|
||||||
|
{ type: 'struct', reg: rTmp, hex: ver.startsWith('GMSJ') ? '00819700' : '004000' },
|
||||||
|
...(dst.type === 'stack'
|
||||||
|
? [ASM.add(3, rTmp, 3), ASM.stw(3, 1, dst.off)]
|
||||||
|
: [ASM.add(dst.num, rTmp, 3)]),
|
||||||
|
// finalize
|
||||||
|
ASM.addi(1, 1, 0x10),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const fieldDB = Object.fromEntries(fields.map((o) => [o.id.toLowerCase(), o]));
|
92
site/.vuepress/components/codes/CustomizedDisplay/format.js
Normal file
92
site/.vuepress/components/codes/CustomizedDisplay/format.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import { fieldDB } from './fields.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Parameters<import('./loader.js').Loader['asm']>[0]} GameVersion
|
||||||
|
*/
|
||||||
|
|
||||||
|
const dtype2fmtinfo = {
|
||||||
|
8: { prefix: 'hh', mask: 0xff },
|
||||||
|
16: { prefix: 'h', mask: 0xffff },
|
||||||
|
32: { prefix: '', mask: 0xffffffff },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {GameVersion} version
|
||||||
|
*/
|
||||||
|
export function parseFormat(input, version) {
|
||||||
|
const regex = /<(.*?)>/g;
|
||||||
|
let preview = '';
|
||||||
|
let format = '';
|
||||||
|
/** @type {(typeof fieldDB)[string][]} */
|
||||||
|
const fields = [];
|
||||||
|
/** @type {RegExpExecArray|null} */
|
||||||
|
let m = null;
|
||||||
|
let i0 = 0;
|
||||||
|
while ((m = regex.exec(input))) {
|
||||||
|
const { index: i } = m;
|
||||||
|
// text
|
||||||
|
const text = input.slice(i0, i);
|
||||||
|
preview += text;
|
||||||
|
format += text.replace(/%/g, '%%');
|
||||||
|
// arg
|
||||||
|
const [fieldId, fmt0, pvw0] = m[1].split('|');
|
||||||
|
const field = fieldDB[fieldId.toLowerCase()];
|
||||||
|
if (field) {
|
||||||
|
const { dtype } = field;
|
||||||
|
let fmt;
|
||||||
|
let pvw;
|
||||||
|
if (typeof field.preview === 'function') {
|
||||||
|
// TODO preview of %s field
|
||||||
|
fmt = field.fmt;
|
||||||
|
pvw = field.preview(version);
|
||||||
|
} else {
|
||||||
|
const fmt2 = fmt0 || field.fmt;
|
||||||
|
let ipvw = +pvw0;
|
||||||
|
if (!pvw0 || !isFinite(ipvw)) ipvw = field.preview;
|
||||||
|
let padfmt = '';
|
||||||
|
if (dtype === 'float') {
|
||||||
|
const m = fmt2.trim().match(/^(?:%?(\d*)\.)?(\d+)([eEf]?)$/);
|
||||||
|
padfmt = m?.[1] || '';
|
||||||
|
const digit = +(m?.[2] || 0);
|
||||||
|
const suffix = m?.[3] || 'f';
|
||||||
|
fmt = `%${padfmt}.${digit}${suffix}`;
|
||||||
|
pvw = ipvw[suffix === 'f' ? 'toFixed' : 'toExponential'](digit);
|
||||||
|
if (suffix === 'E') pvw = pvw.toUpperCase();
|
||||||
|
} else {
|
||||||
|
const { prefix, mask } = dtype2fmtinfo[dtype];
|
||||||
|
ipvw &= mask;
|
||||||
|
const m = fmt2.trim().match(/^%?(\d*)h{,2}([dioxXu])$/);
|
||||||
|
padfmt = m?.[1] || '';
|
||||||
|
const t = m?.[2] || 'u';
|
||||||
|
fmt = `%${padfmt}${prefix}${t}`;
|
||||||
|
if ('di'.includes(t)) {
|
||||||
|
if (ipvw > mask >>> 1) ipvw -= mask;
|
||||||
|
pvw = ipvw.toString(10);
|
||||||
|
} else if (t === 'o') {
|
||||||
|
pvw = (ipvw >>> 0).toString(8);
|
||||||
|
} else if ('xX'.includes(t)) {
|
||||||
|
pvw = (ipvw >>> 0).toString(16);
|
||||||
|
} else {
|
||||||
|
pvw = (ipvw >>> 0).toString(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pvw = pvw.padStart(+padfmt, padfmt[0] === '0' ? '0' : ' ');
|
||||||
|
}
|
||||||
|
preview += pvw;
|
||||||
|
format += fmt;
|
||||||
|
fields.push(field);
|
||||||
|
} else {
|
||||||
|
// fail to parse
|
||||||
|
preview += m[0];
|
||||||
|
format += m[0].replace(/%/g, '%%');
|
||||||
|
}
|
||||||
|
// next
|
||||||
|
i0 = i + m[0].length;
|
||||||
|
}
|
||||||
|
const text = input.slice(i0);
|
||||||
|
preview += text;
|
||||||
|
format += text.replace(/%/g, '%%');
|
||||||
|
// DONE
|
||||||
|
return { preview, format, fields };
|
||||||
|
}
|
140
site/.vuepress/components/codes/CustomizedDisplay/functions.js
Normal file
140
site/.vuepress/components/codes/CustomizedDisplay/functions.js
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import { ASM, $load, insts2hex, str2hex, getDrawTextOpt, getFillRectParams } from '../asm.js';
|
||||||
|
import { addrs } from '../addrs.js';
|
||||||
|
import { rTmp, fTmp } from './loader.js';
|
||||||
|
import { bases } from './fields.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('./loader.js').Loader} Loader
|
||||||
|
* @typedef {Parameters<Loader['asm']>[0]} GameVersion
|
||||||
|
* @typedef {ReturnType<Loader['asm']>[number]} ASMInst
|
||||||
|
* @typedef {(typeof import('./fields.js').fieldDB)[string]} Field
|
||||||
|
*
|
||||||
|
* @typedef {{type: 'reg', num: number}} LoadDstReg
|
||||||
|
* @typedef {{type: 'stack', off: number}} LoadDstStack
|
||||||
|
* @typedef {LoadDstReg|LoadDstStack} LoadDst
|
||||||
|
* @typedef {{
|
||||||
|
* dtype: Loader['dtype'],
|
||||||
|
* base: keyof typeof import('./fields').bases,
|
||||||
|
* offset: number,
|
||||||
|
* postprocess?: (rT: number)=>ASMInst[]
|
||||||
|
* }} DirectLoader
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GameVersion} version
|
||||||
|
* @param {Parameters<getDrawTextOpt>[0]} opt
|
||||||
|
* @param {string} fmt
|
||||||
|
* @param {Field[]} fields
|
||||||
|
*/
|
||||||
|
export function drawText(version, opt, fmt, fields) {
|
||||||
|
/** @type {ASMInst[]} */
|
||||||
|
const insts = [];
|
||||||
|
|
||||||
|
let gpr = 5;
|
||||||
|
let fpr = 1;
|
||||||
|
let sp = 8;
|
||||||
|
/** @type {Map<DirectLoader['base'], ({dst: LoadDst} & Pick<DirectLoader, 'dtype'|'offset'|'postprocess'>)[]>} */
|
||||||
|
const simples = new Map();
|
||||||
|
/** @type {{asm: Loader['asm'], dst: LoadDstStack, dtype: Loader['dtype']}[]} */
|
||||||
|
const callingStacks = [];
|
||||||
|
/** @type {{asm: Loader['asm'], dst: LoadDstReg, dtype: Loader['dtype']}[]} */
|
||||||
|
const callingRegs = [];
|
||||||
|
/** @type {{asm: Loader['asm'], dst: LoadDst, dtype: Loader['dtype']}[]} */
|
||||||
|
const directs = [];
|
||||||
|
for (const entry of fields) {
|
||||||
|
const { dtype } = entry;
|
||||||
|
const isFloat = dtype === 'float';
|
||||||
|
/** @type {LoadDst} */
|
||||||
|
let dst;
|
||||||
|
if (isFloat && fpr <= 8) {
|
||||||
|
dst = { type: 'reg', num: fpr++ };
|
||||||
|
} else if (!isFloat && gpr <= 10) {
|
||||||
|
dst = { type: 'reg', num: gpr++ };
|
||||||
|
} else {
|
||||||
|
if (isFloat) sp = ((sp + 7) >> 3) << 3;
|
||||||
|
dst = { type: 'stack', off: sp };
|
||||||
|
sp += isFloat ? 8 : 4;
|
||||||
|
}
|
||||||
|
// push
|
||||||
|
if ('asm' in entry) {
|
||||||
|
const { asm, calling } = entry;
|
||||||
|
(calling ? (dst.type === 'stack' ? callingStacks : callingRegs) : directs).push({
|
||||||
|
dtype,
|
||||||
|
asm,
|
||||||
|
dst,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const { base, offset, postprocess } = entry;
|
||||||
|
const item = { dst, dtype, offset, postprocess };
|
||||||
|
const arr = simples.get(base);
|
||||||
|
if (arr == null) {
|
||||||
|
simples.set(base, [item]);
|
||||||
|
} else {
|
||||||
|
arr.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insts.push(...callingStacks.flatMap((inst) => inst.asm(version, inst.dst)));
|
||||||
|
|
||||||
|
const callingRegLast = callingRegs.pop();
|
||||||
|
/** @type {ASMInst[]} */
|
||||||
|
const instsLoadFromStack = [];
|
||||||
|
callingRegs.forEach((inst) => {
|
||||||
|
const isFloat = inst.dtype === 'float';
|
||||||
|
if (isFloat) sp = ((sp + 7) >> 3) << 3;
|
||||||
|
insts.push(...inst.asm(version, { type: 'stack', off: sp }));
|
||||||
|
instsLoadFromStack.push((isFloat ? ASM.lfd : ASM.lwz)(inst.dst.num, 1, sp));
|
||||||
|
sp += isFloat ? 8 : 4;
|
||||||
|
});
|
||||||
|
// last
|
||||||
|
if (callingRegLast) {
|
||||||
|
insts.push(...callingRegLast.asm(version, callingRegLast.dst));
|
||||||
|
}
|
||||||
|
// load from stack
|
||||||
|
insts.push(...instsLoadFromStack);
|
||||||
|
|
||||||
|
// directs
|
||||||
|
insts.push(...directs.flatMap((inst) => inst.asm(version, inst.dst)));
|
||||||
|
|
||||||
|
// simples
|
||||||
|
const rBase = 3;
|
||||||
|
for (const [base, items] of simples.entries()) {
|
||||||
|
// load base
|
||||||
|
insts.push(...bases[base](rBase, version));
|
||||||
|
// load all var
|
||||||
|
for (const { dtype, offset, dst, postprocess } of items) {
|
||||||
|
if (dst.type === 'stack') {
|
||||||
|
insts.push(
|
||||||
|
$load[dtype](rTmp, rBase, offset),
|
||||||
|
...(postprocess?.(rTmp) ?? []),
|
||||||
|
(dtype === 'float' ? ASM.stfd : ASM.stw)(rTmp, 1, dst.off),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
insts.push($load[dtype](dst.num, rBase, offset), ...(postprocess?.(dst.num) ?? []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// r3 = drawTextOpt
|
||||||
|
insts.push({ type: 'struct', reg: 3, hex: insts2hex(getDrawTextOpt(opt)) });
|
||||||
|
// r4 = fmt
|
||||||
|
insts.push({ type: 'struct', reg: 4, hex: str2hex(fmt, version) });
|
||||||
|
|
||||||
|
// call
|
||||||
|
insts.push({ type: 'call', addr: addrs.drawText });
|
||||||
|
return { insts, sp };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GameVersion} version
|
||||||
|
* @param {Parameters<getFillRectParams>[0]} config
|
||||||
|
* @param {Parameters<getFillRectParams>[1]} size
|
||||||
|
* @returns {ASMInst[]}
|
||||||
|
*/
|
||||||
|
export const fillRect = (version, config, size) => [
|
||||||
|
// r3, r4 = opt
|
||||||
|
{ type: 'struct', reg: 3, hex: insts2hex(getFillRectParams(config, size)) },
|
||||||
|
// call
|
||||||
|
{ type: 'call', addr: addrs.fillRect[version], prep: [ASM.addi(4, 3, 0x10)] },
|
||||||
|
];
|
|
@ -1,8 +1,22 @@
|
||||||
{
|
{
|
||||||
"ja-JP": {
|
"ja-JP": {
|
||||||
"format": "フォーマット:"
|
"format": "フォーマット:",
|
||||||
|
"deletionConfirm": "本当に削除しますか?",
|
||||||
|
"add": {
|
||||||
|
"PAS": "+ 位置/角度/速度表示",
|
||||||
|
"speed": "+ 速度表示",
|
||||||
|
"detailed": "+ 欲張りセット",
|
||||||
|
"rect": "+ 長方形"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"en-US": {
|
"en-US": {
|
||||||
"format": "Format:"
|
"format": "Format:",
|
||||||
|
"deletionConfirm": "Are you sure to delete?",
|
||||||
|
"add": {
|
||||||
|
"PAS": "+ Position/Angle/Speed Display",
|
||||||
|
"speed": "+ Speed Display",
|
||||||
|
"detailed": "+ Detailed Display",
|
||||||
|
"rect": "+ Rectangle"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
68
site/.vuepress/components/codes/CustomizedDisplay/loader.js
Normal file
68
site/.vuepress/components/codes/CustomizedDisplay/loader.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { ASM, $load, $store } from '../asm.js';
|
||||||
|
import { assemble } from './assembler.js';
|
||||||
|
|
||||||
|
export const rTmp = 12;
|
||||||
|
export const fTmp = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Parameters<import('./assembler.js').assemble>[0] extends (infer U)[] ? U : never} ASMInst
|
||||||
|
* @typedef {'GMSJ01'|'GMSJ0A'|'GMSE01'|'GMSP01'} GameVersion
|
||||||
|
* @typedef {{type: 'reg', num: number}} LoadDstReg
|
||||||
|
* @typedef {{type: 'stack', off: number}} LoadDstStack
|
||||||
|
* @typedef {LoadDstReg|LoadDstStack} LoadDst
|
||||||
|
* @typedef {{
|
||||||
|
* asm(version: GameVersion, dst: LoadDst): ASMInst[]
|
||||||
|
* dtype: 8|16|32|'float'
|
||||||
|
* calling: boolean
|
||||||
|
* }} Loader
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Loader['dtype']} dtype
|
||||||
|
* @param {(reg: number, version: GameVersion)=>ASMInst[]} load
|
||||||
|
* @param {(rD: number, version: GameVersion)=>ASMInst[]} [postprocess]
|
||||||
|
* @returns {Loader['asm']}
|
||||||
|
*/
|
||||||
|
export const makeDirectLoaderASM = (dtype, load, postprocess) => (version, dst) => {
|
||||||
|
const { type } = dst;
|
||||||
|
if (type == 'reg') {
|
||||||
|
const { num } = dst;
|
||||||
|
return [...load(num, version), ...(postprocess?.(num, version) ?? [])];
|
||||||
|
} else {
|
||||||
|
const { num, st } =
|
||||||
|
dtype === 'float' ? { num: fTmp, st: ASM.stfd } : { num: rTmp, st: ASM.stw };
|
||||||
|
return [...load(num, version), ...(postprocess?.(num, version) ?? []), st(num, 1, dst.off)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Loader['dtype']} dtype
|
||||||
|
* @param {(version: GameVersion)=>ASMInst[]} load
|
||||||
|
* @returns {Loader['asm']}
|
||||||
|
*/
|
||||||
|
export const makeFunctionLoaderASM = (dtype, load) => (version, dst) => {
|
||||||
|
const { type } = dst;
|
||||||
|
const base = load(version);
|
||||||
|
if (type == 'reg') {
|
||||||
|
const { num } = dst;
|
||||||
|
if (dtype === 'float') {
|
||||||
|
return num === 1 ? base : [...base, ASM.fmr(num, 1)];
|
||||||
|
} else {
|
||||||
|
return num === 3 ? base : [...base, ASM.mr(num, 3)];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { off } = dst;
|
||||||
|
return [...base, dtype === 'float' ? ASM.stfd(1, 1, off) : ASM.stw(3, 1, off)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Loader['dtype']} dtype
|
||||||
|
* @param {(version: GameVersion)=>ASMInst[]} load
|
||||||
|
* @returns {Loader}
|
||||||
|
*/
|
||||||
|
export const makeFunctionLoader = (dtype, load) => ({
|
||||||
|
dtype,
|
||||||
|
asm: makeFunctionLoaderASM(dtype, load),
|
||||||
|
calling: true,
|
||||||
|
});
|
|
@ -22,15 +22,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getConfig, lskey, buttonValues} from './codegen.js';
|
import { makeUpdateConfig } from '../utils.js';
|
||||||
|
import {getConfig, defaultConfig, lskey, buttonValues} from './codegen.js';
|
||||||
import labels from './labels.json';
|
import labels from './labels.json';
|
||||||
import {getLabels} from '../codegen.js';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
updateConfig() {
|
updateConfig: makeUpdateConfig(lskey, defaultConfig),
|
||||||
localStorage.setItem(lskey, JSON.stringify({button: this.button}));
|
|
||||||
},
|
|
||||||
toggleButton(event, value) {
|
toggleButton(event, value) {
|
||||||
this.button = event.target.checked ?
|
this.button = event.target.checked ?
|
||||||
this.button | value : // ON
|
this.button | value : // ON
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
export const code04 = `
|
||||||
|
0403B6FC 497BD905
|
||||||
|
041441BC 496B4ED8
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const codeBase = `
|
||||||
|
801F0000 3D00817F
|
||||||
|
61089147 3D40817F
|
||||||
|
614A0024 3D80803B
|
||||||
|
618C6178 7C006040
|
||||||
|
41820048 3D80803B
|
||||||
|
618C3F88 7C006040
|
||||||
|
40820034 819F0004
|
||||||
|
898C0013 508C442E
|
||||||
|
39280031 88080007
|
||||||
|
7CEA00AE A0080005
|
||||||
|
7C006040 41820024
|
||||||
|
39080007 7C084840
|
||||||
|
4180FFE4 4A854070
|
||||||
|
2C05FFFF 4082FFF8
|
||||||
|
88EA0000 39080031
|
||||||
|
7CE73B79 4182FFE8
|
||||||
|
7C0838AE 280000FF
|
||||||
|
4182FFDC 7C030378
|
||||||
|
4E800020 3C808040
|
||||||
|
A4044486 3C60817F
|
||||||
|
85630024 70000040
|
||||||
|
41A20050 88840019
|
||||||
|
548007FE 7D605850
|
||||||
|
5480FFFE 7D6B0214
|
||||||
|
556B07BE 5480F7BF
|
||||||
|
41A20030 280B0003
|
||||||
|
41A00008 39600000
|
||||||
|
5480EFFE 5080077A
|
||||||
|
7D8358AE 7D8C0214
|
||||||
|
280C0005 41A00008
|
||||||
|
398CFFFB 7D8359AE
|
||||||
|
99630003 3C002020
|
||||||
|
60002023 556C183E
|
||||||
|
5C00603E 5405063E
|
||||||
|
5407C63E 5409863E
|
||||||
|
88C30000 89030001
|
||||||
|
89430002 3C60817F
|
||||||
|
60639138 38830045
|
||||||
|
4BFF7109 4A94B08C
|
||||||
|
`;
|
|
@ -1,11 +1,11 @@
|
||||||
export const addrDraw2D = 0x802069dc;
|
|
||||||
export const code04 = `
|
export const code04 = `
|
||||||
0424F32C 495A9CD5
|
0424F32C 495A9CD5
|
||||||
042069DC 495F26B8
|
042069DC 495F26B8
|
||||||
`;
|
`;
|
||||||
export const codes = [
|
|
||||||
/* 077F9000 000001AF */ `
|
export const codeBase = `
|
||||||
3D40817F
|
801F0000 3D00817F
|
||||||
|
61089147 3D40817F
|
||||||
614A0024 3D80803D
|
614A0024 3D80803D
|
||||||
618CA9C0 7C006040
|
618CA9C0 7C006040
|
||||||
41820048 3D80803D
|
41820048 3D80803D
|
||||||
|
@ -22,38 +22,25 @@ export const codes = [
|
||||||
7CE73B79 4182FFE8
|
7CE73B79 4182FFE8
|
||||||
7C0838AE 280000FF
|
7C0838AE 280000FF
|
||||||
4182FFDC 7C030378
|
4182FFDC 7C030378
|
||||||
4E800020 38610E90
|
4E800020 3C808040
|
||||||
4A83C191 3C808040
|
|
||||||
A4040D82 3C60817F
|
A4040D82 3C60817F
|
||||||
84A30024 70000040
|
85630024 70000040
|
||||||
41A20050 88840019
|
41A20050 88840019
|
||||||
548007FE 7CA02850
|
548007FE 7D605850
|
||||||
5480FFFE 7CA50214
|
5480FFFE 7D6B0214
|
||||||
54A507BE 5480F7BF
|
556B07BE 5480F7BF
|
||||||
41A20030 28050003
|
41A20030 280B0003
|
||||||
41A00008 38A00000
|
41A00008 39600000
|
||||||
5480EFFE 5080077A
|
5480EFFE 5080077A
|
||||||
7D8328AE 7D8C0214
|
7D8358AE 7D8C0214
|
||||||
280C0005 41A00008
|
280C0005 41A00008
|
||||||
398CFFFB 7D8329AE
|
398CFFFB 7D8359AE
|
||||||
98A30003 3821FFF0
|
99630003 3C002020
|
||||||
`,
|
60002023 556C183E
|
||||||
/* li r8 */ `
|
5C00603E 5405063E
|
||||||
3C002020 60002023
|
5407C63E 5409863E
|
||||||
54AC183E 5C00603E
|
88C30000 89030001
|
||||||
`,
|
89430002 3C60817F
|
||||||
/* stb r0, fmtCS0+fmtCSD*i(r8) */ `
|
60639138 38830045
|
||||||
89230000
|
4BFF7109 4AA0D8AC
|
||||||
89430001 89630002
|
`;
|
||||||
91610008
|
|
||||||
`,
|
|
||||||
/* li r3~r7, drawText, b */ `
|
|
||||||
452020FF 213200FF
|
|
||||||
621CFF1D 32005025
|
|
||||||
25252630 01FF4520
|
|
||||||
20213001 FFFF621C
|
|
||||||
1D300151 0707FF08
|
|
||||||
3102FF36 01FF0231
|
|
||||||
021E6E20 FF
|
|
||||||
` /* fmt */,
|
|
||||||
];
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
export const addrDraw2D = 0x80125548;
|
|
||||||
export const code04 = `
|
export const code04 = `
|
||||||
04027900 497D1701
|
04027900 497D1701
|
||||||
04125548 496D3B4C
|
04125548 496D3B4C
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const codes = [
|
export const codeBase = `
|
||||||
`
|
801F0000 3D00817F
|
||||||
3D40817F
|
61089147 3D40817F
|
||||||
614A0024 3D80803A
|
614A0024 3D80803A
|
||||||
618C6D70 7C006040
|
618C6D70 7C006040
|
||||||
41820048 3D80803A
|
41820048 3D80803A
|
||||||
|
@ -23,38 +22,25 @@ export const codes = [
|
||||||
7CE73B79 4182FFE8
|
7CE73B79 4182FFE8
|
||||||
7C0838AE 280000FF
|
7C0838AE 280000FF
|
||||||
4182FFDC 7C030378
|
4182FFDC 7C030378
|
||||||
4E800020 38610E90
|
4E800020 3C80803F
|
||||||
4AAD1E35 3C80803F
|
|
||||||
A404545A 3C60817F
|
A404545A 3C60817F
|
||||||
84A30024 70000040
|
85630024 70000040
|
||||||
41A20050 88840019
|
41A20050 88840019
|
||||||
548007FE 7CA02850
|
548007FE 7D605850
|
||||||
5480FFFE 7CA50214
|
5480FFFE 7D6B0214
|
||||||
54A507BE 5480F7BF
|
556B07BE 5480F7BF
|
||||||
41A20030 28050003
|
41A20030 280B0003
|
||||||
41A00008 38A00000
|
41A00008 39600000
|
||||||
5480EFFE 5080077A
|
5480EFFE 5080077A
|
||||||
7D8328AE 7D8C0214
|
7D8358AE 7D8C0214
|
||||||
280C0005 41A00008
|
280C0005 41A00008
|
||||||
398CFFFB 7D8329AE
|
398CFFFB 7D8359AE
|
||||||
98A30003 3821FFF0
|
99630003 3C002020
|
||||||
`,
|
60002023 556C183E
|
||||||
`
|
5C00603E 5405063E
|
||||||
3C002020 60002023
|
5407C63E 5409863E
|
||||||
54AC183E 5C00603E
|
88C30000 89030001
|
||||||
`,
|
89430002 3C60817F
|
||||||
`
|
60639138 38830045
|
||||||
89230000
|
4BFF7109 4A92C418
|
||||||
89430001 89630002
|
`;
|
||||||
91610008
|
|
||||||
`,
|
|
||||||
`
|
|
||||||
452020FF 213200FF
|
|
||||||
621CFF1D 32005025
|
|
||||||
25252630 01FF4520
|
|
||||||
20213001 FFFF621C
|
|
||||||
1D300151 0707FF08
|
|
||||||
3102FF36 01FF0231
|
|
||||||
021E6E20 FF
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
export const code04 = `
|
||||||
|
0403B54C 497BDAB5
|
||||||
|
04138DF8 496C029C
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const codeBase = `
|
||||||
|
801F0000 3D00817F
|
||||||
|
61089147 3D40817F
|
||||||
|
614A0024 3D80803A
|
||||||
|
618CDF98 7C006040
|
||||||
|
41820048 3D80803A
|
||||||
|
618CBDA8 7C006040
|
||||||
|
40820034 819F0004
|
||||||
|
898C0013 508C442E
|
||||||
|
39280031 88080007
|
||||||
|
7CEA00AE A0080005
|
||||||
|
7C006040 41820024
|
||||||
|
39080007 7C084840
|
||||||
|
4180FFE4 4A8531C4
|
||||||
|
2C05FFFF 4082FFF8
|
||||||
|
88EA0000 39080031
|
||||||
|
7CE73B79 4182FFE8
|
||||||
|
7C0838AE 280000FF
|
||||||
|
4182FFDC 7C030378
|
||||||
|
4E800020 3C808040
|
||||||
|
A404BC26 3C60817F
|
||||||
|
85630024 70000040
|
||||||
|
41A20050 88840019
|
||||||
|
548007FE 7D605850
|
||||||
|
5480FFFE 7D6B0214
|
||||||
|
556B07BE 5480F7BF
|
||||||
|
41A20030 280B0003
|
||||||
|
41A00008 39600000
|
||||||
|
5480EFFE 5080077A
|
||||||
|
7D8358AE 7D8C0214
|
||||||
|
280C0005 41A00008
|
||||||
|
398CFFFB 7D8359AE
|
||||||
|
99630003 3C002020
|
||||||
|
60002023 556C183E
|
||||||
|
5C00603E 5405063E
|
||||||
|
5407C63E 5409863E
|
||||||
|
88C30000 89030001
|
||||||
|
89430002 3C60817F
|
||||||
|
60639138 38830045
|
||||||
|
4BFF7109 4A93FCC8
|
||||||
|
`;
|
|
@ -1,109 +1,65 @@
|
||||||
import { parseJSON } from '../codegen.js';
|
import { parseJSON } from '../codegen.js';
|
||||||
import { ASM, liDX, strlen, str2inst, inst2gecko } from '../asm.js';
|
import { ASM, liDX, str2hex, insts2hex, getFillRectParams, getDrawTextOpt } from '../asm.js';
|
||||||
|
import { measureText } from '../text.js';
|
||||||
|
import { int2hex } from '../utils.js';
|
||||||
|
import { addrs } from '../addrs.js';
|
||||||
export const lskey = 'config/PatternSelector';
|
export const lskey = 'config/PatternSelector';
|
||||||
|
|
||||||
import * as GMSJ01 from './code/GMSJ01.js';
|
import * as GMSJ01 from './code/GMSJ01.js';
|
||||||
import * as GMSJ0A from './code/GMSJ0A.js';
|
import * as GMSJ0A from './code/GMSJ0A.js';
|
||||||
// import * as GMSE01 from './code/GMSE01.js';
|
// import * as GMSE01 from './code/GMSE01.js';
|
||||||
// import * as GMSP01 from './code/GMSP01.js';
|
import * as GMSP01 from './code/GMSP01.js';
|
||||||
const codes = { GMSJ01, GMSJ0A };
|
const codes = { GMSJ01, GMSJ0A, GMSP01 };
|
||||||
|
|
||||||
export const defaultConfig = {
|
export const defaultConfig = {
|
||||||
x: 16,
|
x: 498,
|
||||||
y: 320,
|
y: 462,
|
||||||
fontSize: 20,
|
fontSize: 14,
|
||||||
fgRGB: 0xffffff,
|
fgRGB: 0xffffff,
|
||||||
fgA: 0xff,
|
fgA: 0xff,
|
||||||
fgRGB2: null,
|
fgRGB2: null,
|
||||||
fgA2: null,
|
fgA2: null,
|
||||||
label: 'Pattern ',
|
bgRGB: 0,
|
||||||
|
bgA: 128,
|
||||||
|
bgLeft: 2,
|
||||||
|
bgRight: 4,
|
||||||
|
bgTop: 2,
|
||||||
|
bgBot: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @returns {typeof defaultConfig} */
|
/** @returns {typeof defaultConfig} */
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
const config =
|
const config =
|
||||||
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
||||||
return { ...defaultConfig, ...config };
|
const o = { ...defaultConfig, ...config };
|
||||||
|
return { ...o, text: getPreviewText() };
|
||||||
}
|
}
|
||||||
|
|
||||||
const addrDrawText = 0x817f0238;
|
export const getPreviewText = () => '#0 0 0';
|
||||||
const addrCodeBase = 0x817f9000;
|
|
||||||
const addrPV1Data1 = 0x817f9167;
|
|
||||||
const addrFmt0 = 0x817f919d;
|
|
||||||
|
|
||||||
/** @param {keyof typeof codes} version */
|
/**
|
||||||
export default function codegen(version) {
|
* @param {keyof typeof codes} version
|
||||||
const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, label } = getConfig();
|
* @param {string} baseCode
|
||||||
const colorTop = (fgRGB << 8) | fgA;
|
*/
|
||||||
const colorBot = fgRGB2 == null || fgA2 == null ? colorTop : (fgRGB2 << 8) | fgA;
|
export default function codegen(version, baseCode) {
|
||||||
const text = label + '>%X>%X>%X';
|
const config = getConfig();
|
||||||
const fmtCS0 = strlen(label);
|
let code = baseCode;
|
||||||
const fmtCSD = 3;
|
code += '077F0410 00000066';
|
||||||
|
// fillRectOpt
|
||||||
|
code += insts2hex(getFillRectParams(config, measureText(getPreviewText(), version)));
|
||||||
|
// drawTextOpt
|
||||||
|
code += insts2hex(getDrawTextOpt(config));
|
||||||
|
// fmt
|
||||||
|
code += '25632558256325582563255800';
|
||||||
|
// pattern data
|
||||||
|
code += `452020 FF213200
|
||||||
|
FF621CFF 1D320050
|
||||||
|
25252526 3001FF45
|
||||||
|
20202130 01FFFF62
|
||||||
|
1C1D3001 510707FF
|
||||||
|
083102FF 3601FF02
|
||||||
|
31021E6E 20FF0000
|
||||||
|
`;
|
||||||
|
|
||||||
const { code04, addrDraw2D, codes: cs0 } = codes[version];
|
return code.replace(/\s/g, '');
|
||||||
const cs = cs0.map((e) => e.replace(/\s+/g, ''));
|
|
||||||
const params = [
|
|
||||||
liDX(3, x),
|
|
||||||
liDX(4, y),
|
|
||||||
liDX(5, fontSize),
|
|
||||||
liDX(6, colorTop),
|
|
||||||
colorTop === colorBot ? ASM.mr(7, 6) : liDX(7, colorBot),
|
|
||||||
].flatMap((e) => e);
|
|
||||||
const extraOffset = (params.length - 5) << 2; // default: 5 inst
|
|
||||||
let pc;
|
|
||||||
// 07
|
|
||||||
let code07 = '801F0000';
|
|
||||||
/* li32 rEntry, .data.patterns.PV1-1 */
|
|
||||||
code07 += liDX(8, addrPV1Data1 + extraOffset)
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('');
|
|
||||||
code07 += cs[0];
|
|
||||||
/* li r8 */
|
|
||||||
code07 += liDX(8, addrFmt0 + extraOffset)
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('');
|
|
||||||
code07 += cs[1];
|
|
||||||
/* stb r0/12/12, fmtCS0+fmtCSD*i(r8) */
|
|
||||||
code07 += [
|
|
||||||
ASM.stb(0, 8, fmtCS0),
|
|
||||||
0x540cc63e,
|
|
||||||
ASM.stb(12, 8, fmtCS0 + fmtCSD),
|
|
||||||
0x540c863e,
|
|
||||||
ASM.stb(12, 8, fmtCS0 + fmtCSD * 2),
|
|
||||||
]
|
|
||||||
.flatMap((e) => e)
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('');
|
|
||||||
/* (code) */
|
|
||||||
code07 += cs[2];
|
|
||||||
/* r3~r7 */
|
|
||||||
code07 += params.map(inst2gecko).join('');
|
|
||||||
code07 += '4CC63182'; // crclr 6
|
|
||||||
/* bl drawText */
|
|
||||||
pc = addrCodeBase + (code07.length >> 1);
|
|
||||||
code07 += ASM.b(addrDrawText - pc, true)
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('');
|
|
||||||
/* addi */
|
|
||||||
code07 += '38210010';
|
|
||||||
/* b 4+$b$.draw2d */
|
|
||||||
pc = addrCodeBase + (code07.length >> 1);
|
|
||||||
code07 += ASM.b(4 + addrDraw2D - pc, false)
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('');
|
|
||||||
/* (code) */
|
|
||||||
code07 += cs[3];
|
|
||||||
/* fmt */
|
|
||||||
// prepend 1 dummy char as 'FF' in code
|
|
||||||
code07 += str2inst('.' + text)
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('')
|
|
||||||
.slice(2);
|
|
||||||
|
|
||||||
const head07 = [0x06000000 | (addrCodeBase & 0x1ffffff), code07.length >> 1]
|
|
||||||
.map(inst2gecko)
|
|
||||||
.join('');
|
|
||||||
// align code 07 (8 digit = 4 byte)
|
|
||||||
if (code07.length & 8) code07 += '00000000';
|
|
||||||
|
|
||||||
return (code04 + head07 + code07).replace(/\s+/g, '');
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,26 +4,16 @@
|
||||||
<Preview :config="previewConfig" />
|
<Preview :config="previewConfig" />
|
||||||
<div class="config-spacer" />
|
<div class="config-spacer" />
|
||||||
<TextConfig v-model="textConfig" />
|
<TextConfig v-model="textConfig" />
|
||||||
<div>
|
|
||||||
{{l.label}} <input v-model="label">
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getConfig, lskey} from './codegen.js';
|
import {getConfig, defaultConfig, lskey, getPreviewText} from './codegen.js';
|
||||||
import labels from './labels.json';
|
import labels from './labels.json';
|
||||||
import TextConfig from '../TextConfig.vue';
|
import TextConfig from '../TextConfig.vue';
|
||||||
|
import { makeUpdateConfig } from '../utils.js';
|
||||||
|
|
||||||
function updateConfig() {
|
const updateConfig = makeUpdateConfig(lskey, defaultConfig, getPreviewText);
|
||||||
const {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, label} = this;
|
|
||||||
const config = {
|
|
||||||
x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, label,
|
|
||||||
};
|
|
||||||
localStorage.setItem(lskey, JSON.stringify(config));
|
|
||||||
this.$emit('config', config);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
TextConfig,
|
TextConfig,
|
||||||
|
@ -53,9 +43,6 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
label: updateConfig,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,23 @@
|
||||||
<span>{{l.fgColor2}}</span><input type="color" :value="rgbI2S(fgRGB2)" @change="fgRGB2 = rgbS2I($event.target.value)">
|
<span>{{l.fgColor2}}</span><input type="color" :value="rgbI2S(fgRGB2)" @change="fgRGB2 = rgbS2I($event.target.value)">
|
||||||
<span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="fgA2"><span>/255={{(fgA2/2.55).toFixed(1)}}%</span>
|
<span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="fgA2"><span>/255={{(fgA2/2.55).toFixed(1)}}%</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<span>{{ l.bgColor }}</span
|
||||||
|
><input type="color" :value="rgbI2S(bgRGB)" @change="bgRGB = rgbS2I($event.target.value)" />
|
||||||
|
<span>{{ l.alpha }}</span
|
||||||
|
><input type="number" min="0" max="254" v-model.number="bgA" /><span
|
||||||
|
>/255={{ (bgA / 2.55).toFixed(1) }}%</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ l.bgOffset }}</span>
|
||||||
|
<span>{{ l.left }}</span><input type="number" v-model.number="bgLeft" />
|
||||||
|
<span>{{ l.right }}</span><input type="number" v-model.number="bgRight" />
|
||||||
|
<span>{{ l.top }}</span><input type="number" v-model.number="bgTop" />
|
||||||
|
<span>{{ l.bottom }}</span><input type="number" v-model.number="bgBot" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -41,6 +58,7 @@ export default {
|
||||||
},
|
},
|
||||||
...Object.fromEntries([
|
...Object.fromEntries([
|
||||||
'x', 'y', 'fontSize', 'fgRGB', 'fgA', 'fgRGB2', 'fgA2',
|
'x', 'y', 'fontSize', 'fgRGB', 'fgA', 'fgRGB2', 'fgA2',
|
||||||
|
'bgRGB', 'bgA', 'bgLeft', 'bgRight', 'bgTop', 'bgBot',
|
||||||
].map(k => [k, makeField(k)])),
|
].map(k => [k, makeField(k)])),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -72,7 +90,7 @@ input[type=number], td.right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
width: 3em;
|
width: 2em;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
.appearance > div {
|
.appearance > div {
|
||||||
|
|
68
site/.vuepress/components/codes/addrs.js
Normal file
68
site/.vuepress/components/codes/addrs.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
export const addrs = {
|
||||||
|
drawText: 0x817f0238,
|
||||||
|
drawWater: {
|
||||||
|
GMSJ01: 0x80206a00,
|
||||||
|
GMSJ0A: 0x8012556c,
|
||||||
|
GMSE01: 0x801441e0,
|
||||||
|
GMSP01: 0x80138e1c,
|
||||||
|
},
|
||||||
|
fillRect: {
|
||||||
|
GMSJ01: 0x80201ea8,
|
||||||
|
GMSJ0A: 0x80121660,
|
||||||
|
GMSE01: 0x80140390,
|
||||||
|
GMSP01: 0x80134f0c,
|
||||||
|
},
|
||||||
|
setup2D: {
|
||||||
|
GMSJ01: 0x80035228,
|
||||||
|
GMSJ0A: 0x802caecc,
|
||||||
|
GMSE01: 0x802eb6bc,
|
||||||
|
GMSP01: 0x802e3864,
|
||||||
|
},
|
||||||
|
getPollutionDegree: {
|
||||||
|
GMSJ01: 0x801ef6b8,
|
||||||
|
GMSE01: 0x8019db20,
|
||||||
|
GMSP01: 0x801963a8,
|
||||||
|
GMSJ0A: 0x8017e26c,
|
||||||
|
},
|
||||||
|
checkStickRotate: {
|
||||||
|
GMSJ01: 0x80130758,
|
||||||
|
GMSE01: 0x80251304,
|
||||||
|
GMSP01: 0x80249090,
|
||||||
|
GMSJ0A: 0x80231054,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const r13offs = {
|
||||||
|
gpMarioOriginal: {
|
||||||
|
GMSJ01: -0x6748,
|
||||||
|
GMSE01: -0x60d8,
|
||||||
|
GMSP01: -0x61b0,
|
||||||
|
GMSJ0A: -0x6218,
|
||||||
|
},
|
||||||
|
gpMarDirector: {
|
||||||
|
GMSJ01: -0x6818,
|
||||||
|
GMSE01: -0x6048,
|
||||||
|
GMSP01: -0x6120,
|
||||||
|
GMSJ0A: -0x6188,
|
||||||
|
},
|
||||||
|
gpCamera: {
|
||||||
|
GMSJ01: -0x5750,
|
||||||
|
GMSE01: -0x7118,
|
||||||
|
GMSP01: -0x7158,
|
||||||
|
GMSJ0A: -0x5768,
|
||||||
|
},
|
||||||
|
gpPollution: {
|
||||||
|
GMSJ01: -0x6518,
|
||||||
|
GMSE01: -0x62f0,
|
||||||
|
GMSP01: -0x63c8,
|
||||||
|
GMSJ0A: -0x6430,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// r1 offset of J2DGrafContext in TGCConsole2::perform()
|
||||||
|
export const ctxSpOff = {
|
||||||
|
GMSJ01: 0xe90,
|
||||||
|
GMSJ0A: 0xbec,
|
||||||
|
GMSE01: 0xbd0,
|
||||||
|
GMSP01: 0xbe4,
|
||||||
|
};
|
|
@ -1,7 +1,8 @@
|
||||||
import * as Encoding from 'encoding-japanese';
|
import charInfoJP from '../../data/charInfo-JP.json';
|
||||||
|
import charInfoEU from '../../data/charInfo-EU.json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {number[]} Inst
|
* @typedef {number} Inst
|
||||||
*
|
*
|
||||||
* @typedef {(
|
* @typedef {(
|
||||||
* rT: number,
|
* rT: number,
|
||||||
|
@ -27,7 +28,7 @@ import * as Encoding from 'encoding-japanese';
|
||||||
* rT: number,
|
* rT: number,
|
||||||
* rA: number,
|
* rA: number,
|
||||||
* rB: number,
|
* rB: number,
|
||||||
* Rc: number|boolean,
|
* Rc?: number|boolean,
|
||||||
* ) => Inst} InstX
|
* ) => Inst} InstX
|
||||||
* @typedef {(
|
* @typedef {(
|
||||||
* rS: number,
|
* rS: number,
|
||||||
|
@ -43,13 +44,6 @@ import * as Encoding from 'encoding-japanese';
|
||||||
* ) => Inst} InstI
|
* ) => Inst} InstI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @param {number} inst */
|
|
||||||
export const makeInst = (inst) => {
|
|
||||||
// const buf = Inst.alloc(4);
|
|
||||||
// buf.writeUint32BE(inst >>> 0);
|
|
||||||
// return buf;
|
|
||||||
return [inst];
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* @param {number} op
|
* @param {number} op
|
||||||
* @param {number} rT
|
* @param {number} rT
|
||||||
|
@ -57,7 +51,7 @@ export const makeInst = (inst) => {
|
||||||
* @param {number} D
|
* @param {number} D
|
||||||
*/
|
*/
|
||||||
const InstD = (op, rT, rA, D) =>
|
const InstD = (op, rT, rA, D) =>
|
||||||
makeInst(((op & 0x3f) << 26) | ((rT & 0x1f) << 21) | ((rA & 0x1f) << 16) | (D & 0xffff));
|
((op & 0x3f) << 26) | ((rT & 0x1f) << 21) | ((rA & 0x1f) << 16) | (D & 0xffff);
|
||||||
/**
|
/**
|
||||||
* @param {number} op
|
* @param {number} op
|
||||||
* @param {number} rT
|
* @param {number} rT
|
||||||
|
@ -67,14 +61,12 @@ const InstD = (op, rT, rA, D) =>
|
||||||
* @param {number} Rc
|
* @param {number} Rc
|
||||||
*/
|
*/
|
||||||
const InstX = (op, rT, rA, rB, op2, Rc) =>
|
const InstX = (op, rT, rA, rB, op2, Rc) =>
|
||||||
makeInst(
|
|
||||||
((op & 0x3f) << 26) |
|
((op & 0x3f) << 26) |
|
||||||
((rT & 0x1f) << 21) |
|
((rT & 0x1f) << 21) |
|
||||||
((rA & 0x1f) << 16) |
|
((rA & 0x1f) << 16) |
|
||||||
((rB & 0x1f) << 11) |
|
((rB & 0x1f) << 11) |
|
||||||
((op2 & 0x3ff) << 1) |
|
((op2 & 0x3ff) << 1) |
|
||||||
Rc,
|
Rc;
|
||||||
);
|
|
||||||
/**
|
/**
|
||||||
* @param {number} op
|
* @param {number} op
|
||||||
* @param {number} RS
|
* @param {number} RS
|
||||||
|
@ -85,15 +77,13 @@ const InstX = (op, rT, rA, rB, op2, Rc) =>
|
||||||
* @param {number} Rc
|
* @param {number} Rc
|
||||||
*/
|
*/
|
||||||
const InstM = (op, RA, RS, SH, MB, ME, Rc) =>
|
const InstM = (op, RA, RS, SH, MB, ME, Rc) =>
|
||||||
makeInst(
|
|
||||||
((op & 0x3f) << 26) |
|
((op & 0x3f) << 26) |
|
||||||
((RS & 0x1f) << 21) |
|
((RS & 0x1f) << 21) |
|
||||||
((RA & 0x1f) << 16) |
|
((RA & 0x1f) << 16) |
|
||||||
((SH & 0x1f) << 11) |
|
((SH & 0x1f) << 11) |
|
||||||
((MB & 0x1f) << 6) |
|
((MB & 0x1f) << 6) |
|
||||||
((ME & 0x1f) << 1) |
|
((ME & 0x1f) << 1) |
|
||||||
Rc,
|
Rc;
|
||||||
);
|
|
||||||
/**
|
/**
|
||||||
* @param {number} op
|
* @param {number} op
|
||||||
* @param {number} LL
|
* @param {number} LL
|
||||||
|
@ -101,14 +91,17 @@ const InstM = (op, RA, RS, SH, MB, ME, Rc) =>
|
||||||
* @param {number} LK
|
* @param {number} LK
|
||||||
*/
|
*/
|
||||||
const InstI = (op, LL, AA, LK) =>
|
const InstI = (op, LL, AA, LK) =>
|
||||||
makeInst(((op & 0x3f) << 26) | ((LL & 0xffffff) << 2) | ((AA & 1) << 1) | (LK & 1));
|
((op & 0x3f) << 26) | ((LL & 0xffffff) << 2) | ((AA & 1) << 1) | (LK & 1);
|
||||||
|
|
||||||
/** @type {(op: number) => InstD} */
|
/** @type {(op: number) => InstD} */
|
||||||
const makeInstD = (op) => (rT, rA, D) => InstD(op, rT, rA, D);
|
const makeInstD = (op) => (rT, rA, D) => InstD(op, rT, rA, D);
|
||||||
/** @type {(op: number) => InstDS} */
|
/** @type {(op: number) => InstDS} */
|
||||||
const makeInstDS = (op) => (rA, rS, D) => InstD(op, rA, rS, D);
|
const makeInstDS = (op) => (rA, rS, D) => InstD(op, rA, rS, D);
|
||||||
/** @type {(op: number, op2: number) => InstX} */
|
/** @type {(op: number, op2: number) => InstX} */
|
||||||
const makeInstX = (op, op2) => (rT, rA, rB, Rc) => InstX(op, rT, rA, rB, op2, +Rc);
|
const makeInstX =
|
||||||
|
(op, op2) =>
|
||||||
|
(rT, rA, rB, Rc = 0) =>
|
||||||
|
InstX(op, rT, rA, rB, op2, +Rc);
|
||||||
/** @type {(op: number, op2: number) => InstXS} */
|
/** @type {(op: number, op2: number) => InstXS} */
|
||||||
const makeInstXS = (op, op2) => (rA, rS, rB, Rc) => InstX(op, rA, rS, rB, op2, +Rc);
|
const makeInstXS = (op, op2) => (rA, rS, rB, Rc) => InstX(op, rA, rS, rB, op2, +Rc);
|
||||||
/** @type {(op: number) => InstM} */
|
/** @type {(op: number) => InstM} */
|
||||||
|
@ -132,6 +125,9 @@ export const ASM = {
|
||||||
lhz: makeInstD(40),
|
lhz: makeInstD(40),
|
||||||
lwz: makeInstD(32),
|
lwz: makeInstD(32),
|
||||||
lfs: makeInstD(48),
|
lfs: makeInstD(48),
|
||||||
|
lfd: makeInstD(50),
|
||||||
|
// add
|
||||||
|
add: makeInstX(31, 266),
|
||||||
// li rT, D
|
// li rT, D
|
||||||
addi: makeInstD(14),
|
addi: makeInstD(14),
|
||||||
li: (/**@type{number}*/ rT, /**@type{number}*/ D) => InstD(14, rT, 0, D),
|
li: (/**@type{number}*/ rT, /**@type{number}*/ D) => InstD(14, rT, 0, D),
|
||||||
|
@ -144,8 +140,10 @@ export const ASM = {
|
||||||
mr: (/**@type{number}*/ rT, /**@type{number}*/ rA, flag = 0) => InstX(31, rA, rT, rA, 444, flag),
|
mr: (/**@type{number}*/ rT, /**@type{number}*/ rA, flag = 0) => InstX(31, rA, rT, rA, 444, flag),
|
||||||
// mask
|
// mask
|
||||||
rlwinm: makeInstM(21),
|
rlwinm: makeInstM(21),
|
||||||
|
rlwimi: makeInstM(20),
|
||||||
// b
|
// b
|
||||||
b: makeInstI(18),
|
b: makeInstI(18),
|
||||||
|
bctr: (/**@type{number|boolean}*/ LK = 0) => InstX(19, 0b10100, 0, 0, 528, LK ? 1 : 0),
|
||||||
// mflr
|
// mflr
|
||||||
mflr: (/**@type{number}*/ rT) => InstX(31, rT, 8, 0, 339, 0),
|
mflr: (/**@type{number}*/ rT) => InstX(31, rT, 8, 0, 339, 0),
|
||||||
mfctr: (/**@type{number}*/ rT) => InstX(31, rT, 9, 0, 339, 0),
|
mfctr: (/**@type{number}*/ rT) => InstX(31, rT, 9, 0, 339, 0),
|
||||||
|
@ -155,6 +153,22 @@ export const ASM = {
|
||||||
// cr
|
// cr
|
||||||
crset: (/**@type{number}*/ B) => InstX(19, B, B, B, 289, 0),
|
crset: (/**@type{number}*/ B) => InstX(19, B, B, B, 289, 0),
|
||||||
crclr: (/**@type{number}*/ B) => InstX(19, B, B, B, 193, 0),
|
crclr: (/**@type{number}*/ B) => InstX(19, B, B, B, 193, 0),
|
||||||
|
// float
|
||||||
|
fmr: (/**@type{number}*/ fT, /**@type{number}*/ fB, Rc = 0) => InstX(63, fT, 0, fB, 72, Rc),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const $load = {
|
||||||
|
8: ASM.lbz,
|
||||||
|
16: ASM.lhz,
|
||||||
|
32: ASM.lwz,
|
||||||
|
float: ASM.lfs,
|
||||||
|
};
|
||||||
|
export const $store = {
|
||||||
|
8: ASM.stb,
|
||||||
|
16: ASM.sth,
|
||||||
|
32: ASM.stw,
|
||||||
|
float: ASM.stfs,
|
||||||
|
double: ASM.stfd,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,26 +177,46 @@ export const ASM = {
|
||||||
*/
|
*/
|
||||||
export function liDX(rT, D) {
|
export function liDX(rT, D) {
|
||||||
if (-0x8000 <= D && D < 0x8000) {
|
if (-0x8000 <= D && D < 0x8000) {
|
||||||
return ASM.li(rT, D);
|
return [ASM.li(rT, D)];
|
||||||
} else if ((D & 0xffff) === 0) {
|
} else if ((D & 0xffff) === 0) {
|
||||||
return ASM.lis(rT, D >>> 16);
|
return [ASM.lis(rT, D >>> 16)];
|
||||||
} else {
|
} else {
|
||||||
const h = D >>> 16;
|
const h = D >>> 16;
|
||||||
const l = D & 0xffff;
|
const l = D & 0xffff;
|
||||||
return [...ASM.lis(rT, h), ...ASM.ori(rT, rT, l)];
|
return [ASM.lis(rT, h), ASM.ori(rT, rT, l)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {string} s */
|
/**
|
||||||
export function strlen(s) {
|
* @param {string} s
|
||||||
const fmtbuf = Encoding.convert(Encoding.stringToCode(s), 'SJIS');
|
* @param {string} version
|
||||||
return fmtbuf.length; // not NUL terminated
|
*/
|
||||||
}
|
export function str2bytes(s, version) {
|
||||||
|
/** @type {Record<string, (typeof charInfoJP)[' ']>} */
|
||||||
/** @param {string} s */
|
const charInfo = version.startsWith('GMSJ') ? charInfoJP : charInfoEU; // TODO US
|
||||||
export function str2inst(s) {
|
const fmtbuf = Array.from(s).flatMap((c) => {
|
||||||
const fmtbuf = Encoding.convert(Encoding.stringToCode(s), 'SJIS');
|
const code = charInfo[c]?.code ?? c.charCodeAt(0);
|
||||||
|
return code >= 0x100 ? [code >> 16, code & 0xff] : [code];
|
||||||
|
});
|
||||||
fmtbuf.push(0); // NUL terminated
|
fmtbuf.push(0); // NUL terminated
|
||||||
|
return fmtbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @param {string} version
|
||||||
|
*/
|
||||||
|
export const str2hex = (s, version) =>
|
||||||
|
str2bytes(s, version)
|
||||||
|
.map((x) => x.toString(16).toUpperCase().padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @param {string} version
|
||||||
|
*/
|
||||||
|
export function str2inst(s, version) {
|
||||||
|
const fmtbuf = str2bytes(s, version);
|
||||||
const fmtlen = fmtbuf.length;
|
const fmtlen = fmtbuf.length;
|
||||||
const fmtlen3 = fmtlen & 3;
|
const fmtlen3 = fmtlen & 3;
|
||||||
const pad = fmtlen3 ? 4 - fmtlen3 : 0;
|
const pad = fmtlen3 ? 4 - fmtlen3 : 0;
|
||||||
|
@ -194,12 +228,13 @@ export function str2inst(s) {
|
||||||
return insts;
|
return insts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {number} pc */
|
/**
|
||||||
export function makeProgram(pc) {
|
* @param {number} pc
|
||||||
/** @type {Inst[]} */
|
* @param {string} [hex]
|
||||||
const bufs = [];
|
*/
|
||||||
return {
|
export const makeProgram = (pc, hex = '') => ({
|
||||||
pc,
|
pc,
|
||||||
|
hex,
|
||||||
/**
|
/**
|
||||||
* @param {number} dst
|
* @param {number} dst
|
||||||
* @param {boolean} LL
|
* @param {boolean} LL
|
||||||
|
@ -214,12 +249,69 @@ export function makeProgram(pc) {
|
||||||
},
|
},
|
||||||
/** @param {Inst[]} codes */
|
/** @param {Inst[]} codes */
|
||||||
push(...codes) {
|
push(...codes) {
|
||||||
bufs.push(...codes);
|
this.hex += codes.map(inst2gecko).join('');
|
||||||
this.pc += codes.reduce((a, e) => a + e.length, 0) << 2;
|
this.pc += codes.length << 2;
|
||||||
},
|
},
|
||||||
dump: () => bufs.flatMap((e) => e),
|
/** @param {string} data */
|
||||||
};
|
pushHex(data) {
|
||||||
|
this.hex += data;
|
||||||
|
this.pc += data.length >> 1;
|
||||||
|
},
|
||||||
|
align() {
|
||||||
|
const l4 = this.pc % 4;
|
||||||
|
if (l4) {
|
||||||
|
const diff = 4 - l4;
|
||||||
|
this.hex += ''.padEnd(diff << 1, '0');
|
||||||
|
this.pc += diff;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/** @param {number} x */
|
/** @param {number} x */
|
||||||
export const inst2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
|
export const inst2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
|
||||||
|
/** @param {Inst[]} insts */
|
||||||
|
export const insts2hex = (insts) => insts.map(inst2gecko).join('');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{
|
||||||
|
* x: number,
|
||||||
|
* y: number,
|
||||||
|
* fontSize: number,
|
||||||
|
* bgRGB: number,
|
||||||
|
* bgA: number,
|
||||||
|
* bgLeft: number,
|
||||||
|
* bgRight: number,
|
||||||
|
* bgTop: number,
|
||||||
|
* bgBot: number
|
||||||
|
* }} opt
|
||||||
|
* @param {{width: number, height: number}} size
|
||||||
|
**/
|
||||||
|
export const getFillRectParams = (
|
||||||
|
{ x, y, fontSize, bgRGB, bgA, bgLeft, bgRight, bgTop, bgBot },
|
||||||
|
{ width, height },
|
||||||
|
) => [
|
||||||
|
// rect
|
||||||
|
x - bgLeft, // x0
|
||||||
|
y - fontSize - bgTop, // y0
|
||||||
|
x + Math.ceil((width * fontSize) / 20) + bgRight, // x1
|
||||||
|
y - fontSize + Math.ceil((height * fontSize) / 20) + bgBot, // y1
|
||||||
|
// color
|
||||||
|
(bgRGB << 8) | bgA,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{
|
||||||
|
* x: number,
|
||||||
|
* y: number,
|
||||||
|
* fontSize: number,
|
||||||
|
* fgRGB: number
|
||||||
|
* fgA: number
|
||||||
|
* fgRGB2: number | null
|
||||||
|
* fgA2: number | null
|
||||||
|
* }} config
|
||||||
|
*/
|
||||||
|
export function getDrawTextOpt({ x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2 }) {
|
||||||
|
const colorTop = (fgRGB << 8) | fgA;
|
||||||
|
const colorBot = fgRGB2 == null || fgA2 == null ? colorTop : (fgRGB2 << 8) | fgA;
|
||||||
|
return [((x & 0xffff) << 16) | (y & 0xffff), fontSize, colorTop, colorBot];
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,13 @@
|
||||||
"fgColorGrad": "グラデーション",
|
"fgColorGrad": "グラデーション",
|
||||||
"fgColor1": "文字色(上):",
|
"fgColor1": "文字色(上):",
|
||||||
"fgColor2": "文字色(下):",
|
"fgColor2": "文字色(下):",
|
||||||
"alpha": "不透明度="
|
"alpha": "不透明度=",
|
||||||
|
"bgColor": "背景色:",
|
||||||
|
"bgOffset": "背景位置:",
|
||||||
|
"left": "左",
|
||||||
|
"right": "右",
|
||||||
|
"top": "上",
|
||||||
|
"bottom": "下"
|
||||||
},
|
},
|
||||||
"en-US": {
|
"en-US": {
|
||||||
"location": "Location: ",
|
"location": "Location: ",
|
||||||
|
@ -15,6 +21,22 @@
|
||||||
"fgColorGrad": "Gradient",
|
"fgColorGrad": "Gradient",
|
||||||
"fgColor1": "Font color(Top): ",
|
"fgColor1": "Font color(Top): ",
|
||||||
"fgColor2": "Font color(Bottom): ",
|
"fgColor2": "Font color(Bottom): ",
|
||||||
|
"alpha": "Alpha=",
|
||||||
|
"bgColor": "Background color: ",
|
||||||
|
"bgOffset": "Background offset: ",
|
||||||
|
"left": "Left",
|
||||||
|
"right": "Right",
|
||||||
|
"top": "Top",
|
||||||
|
"bottom": "Bottom"
|
||||||
|
},
|
||||||
|
"fr-FR": {
|
||||||
|
"location": "Position : ",
|
||||||
|
"fontSize": "Taille de police : ",
|
||||||
|
"fgColor": "Couleur du texte : ",
|
||||||
|
"fgColorGrad": "Dégradé",
|
||||||
|
"fgColor1": "Couleur du texte (haut) : ",
|
||||||
|
"fgColor2": "Couleur du texte (bas) : ",
|
||||||
|
"bgColor": "Couleur de fond : ",
|
||||||
"alpha": "Alpha = "
|
"alpha": "Alpha = "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { parseJSON } from '../codegen.js';
|
import { parseJSON } from '../codegen.js';
|
||||||
|
import { getFillRectParams } from '../asm';
|
||||||
|
import { int2hex } from '../utils';
|
||||||
export const lskey = 'config/qft';
|
export const lskey = 'config/qft';
|
||||||
|
|
||||||
|
export const getPreviewText = () => '0:00.000';
|
||||||
|
|
||||||
export const defaultConfig = {
|
export const defaultConfig = {
|
||||||
x: 16,
|
x: 16,
|
||||||
y: 456,
|
y: 456,
|
||||||
width: 112,
|
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fgRGB: 0xffffff,
|
fgRGB: 0xffffff,
|
||||||
fgA: 0xff,
|
fgA: 0xff,
|
||||||
|
@ -12,6 +15,10 @@ export const defaultConfig = {
|
||||||
fgA2: null,
|
fgA2: null,
|
||||||
bgRGB: 0x000000,
|
bgRGB: 0x000000,
|
||||||
bgA: 0x80,
|
bgA: 0x80,
|
||||||
|
bgLeft: 0,
|
||||||
|
bgRight: 2,
|
||||||
|
bgTop: 2,
|
||||||
|
bgBot: 0,
|
||||||
freezeDuration: 30,
|
freezeDuration: 30,
|
||||||
freeze: {
|
freeze: {
|
||||||
yellowCoin: false,
|
yellowCoin: false,
|
||||||
|
@ -35,6 +42,7 @@ export const defaultConfig = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @returns {typeof defaultConfig} */
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
const config =
|
const config =
|
||||||
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
||||||
|
@ -45,6 +53,7 @@ export function getConfig() {
|
||||||
...defaultConfig.freeze,
|
...defaultConfig.freeze,
|
||||||
...config.freeze,
|
...config.freeze,
|
||||||
},
|
},
|
||||||
|
text: getPreviewText(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +66,10 @@ import * as GMSP01 from './code/GMSP01.js';
|
||||||
import * as GMSJ0A from './code/GMSJ0A.js';
|
import * as GMSJ0A from './code/GMSJ0A.js';
|
||||||
export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
|
export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
|
||||||
|
|
||||||
import statusDB from './code/status.js';
|
import { measureText } from '../text.js';
|
||||||
export const statusKeys = Object.keys(statusDB);
|
import statusDB0 from './code/status.js';
|
||||||
|
const statusDB = /**@type{Record<string,number[]>}*/ (statusDB0);
|
||||||
|
export const statusKeys = Object.keys(statusDB0);
|
||||||
|
|
||||||
/****
|
/****
|
||||||
## save freeze frame, load and save QF
|
## save freeze frame, load and save QF
|
||||||
|
@ -100,7 +111,7 @@ export default function codegen(version, baseCode) {
|
||||||
const hook = freezeCodeHooks[key];
|
const hook = freezeCodeHooks[key];
|
||||||
if (hook) {
|
if (hook) {
|
||||||
if (key === 'blueCoin') {
|
if (key === 'blueCoin') {
|
||||||
const addr = hook;
|
const addr = /**@type{number}*/ (hook);
|
||||||
// special: needs to adjust QF -> use separate C2 instead
|
// special: needs to adjust QF -> use separate C2 instead
|
||||||
code += [
|
code += [
|
||||||
0xc2000000 + (addr & 0x1ffffff),
|
0xc2000000 + (addr & 0x1ffffff),
|
||||||
|
@ -234,25 +245,24 @@ export default function codegen(version, baseCode) {
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
/* bounds */
|
/* bounds */
|
||||||
const { x, y, fontSize, width } = config;
|
const { x, y, fontSize, bgLeft, bgRight, bgTop, bgBot } = config;
|
||||||
|
const rect = getFillRectParams(config, measureText(getPreviewText(), version));
|
||||||
|
const [bgColor] = rect.splice(-1);
|
||||||
const scale = fontSize / 20;
|
const scale = fontSize / 20;
|
||||||
code += '077F0094 0000001D';
|
code += '077F0094 0000001D';
|
||||||
code += [
|
code += rect.map(int2gecko).join('');
|
||||||
x, // x1
|
|
||||||
y - fontSize - 2, // y1
|
|
||||||
x + width * scale, // x2
|
|
||||||
y, // y2
|
|
||||||
]
|
|
||||||
.map(int2gecko)
|
|
||||||
.join('');
|
|
||||||
code += '25753a253032752e2530337500000000'; // fmt
|
code += '25753a253032752e2530337500000000'; // fmt
|
||||||
/* fontSize, fgColor, bgColor */
|
/**
|
||||||
code += '077F0110 00000010';
|
* 817F0110 drawTextOpt: {x, y, fontSize, colorTop, colorBot}
|
||||||
const bgColor = (config.bgRGB & 0xffffff) * 256 + (config.bgA & 0xff);
|
* 817F0120 bgColor
|
||||||
|
*/
|
||||||
|
code += '077F0110 00000014';
|
||||||
const fgColor = (config.fgRGB & 0xffffff) * 256 + (config.fgA & 0xff);
|
const fgColor = (config.fgRGB & 0xffffff) * 256 + (config.fgA & 0xff);
|
||||||
const fgColor2 =
|
const fgColor2 =
|
||||||
((config.fgRGB2 ?? config.fgRGB) & 0xffffff) * 256 + ((config.fgA2 ?? config.fgA) & 0xff);
|
((config.fgRGB2 ?? config.fgRGB) & 0xffffff) * 256 + ((config.fgA2 ?? config.fgA) & 0xff);
|
||||||
|
code += [x, y].map((x) => int2hex(x, 2)).join('');
|
||||||
code += [fontSize, fgColor, fgColor2, bgColor].map(int2gecko).join('');
|
code += [fontSize, fgColor, fgColor2, bgColor].map(int2gecko).join('');
|
||||||
|
code += '00000000';
|
||||||
|
|
||||||
return code.replace(/\s/g, '');
|
return code.replace(/\s/g, '');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,7 @@
|
||||||
<div id="config">
|
<div id="config">
|
||||||
<section class="appearance">
|
<section class="appearance">
|
||||||
<h3>{{ l.h3 }}</h3>
|
<h3>{{ l.h3 }}</h3>
|
||||||
<div>
|
|
||||||
<TextConfig v-model="textConfig" />
|
<TextConfig v-model="textConfig" />
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ l.bgColor }}</span
|
|
||||||
><input type="color" :value="rgbI2S(bgRGB)" @change="bgRGB = rgbS2I($event.target.value)" />
|
|
||||||
<span>{{ l.alpha }}</span
|
|
||||||
><input type="number" min="0" max="255" v-model.number="bgA" /><span
|
|
||||||
>/255={{ (bgA / 2.55).toFixed(1) }}%</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<section class="freeze">
|
<section class="freeze">
|
||||||
<h3>{{ l.freeze.h3 }}</h3>
|
<h3>{{ l.freeze.h3 }}</h3>
|
||||||
|
@ -35,31 +25,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getConfig, lskey, codes, statusKeys } from './codegen.js';
|
import { defaultConfig, lskey, getConfig, getPreviewText, codes, statusKeys } from './codegen.js';
|
||||||
import { rgbI2S, rgbS2I, rgbaI2S } from '../utils';
|
import { makeUpdateConfig, rgbI2S, rgbS2I, rgbaI2S } from '../utils';
|
||||||
import labels from './labels.json';
|
import labels from './labels.json';
|
||||||
import TextConfig from '../TextConfig.vue';
|
import TextConfig from '../TextConfig.vue';
|
||||||
|
|
||||||
function updateConfig() {
|
const updateConfig = makeUpdateConfig(lskey, defaultConfig, getPreviewText);
|
||||||
const { x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration } = this;
|
|
||||||
const config = {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
fontSize,
|
|
||||||
width,
|
|
||||||
fgRGB,
|
|
||||||
fgA,
|
|
||||||
fgRGB2,
|
|
||||||
fgA2,
|
|
||||||
bgRGB,
|
|
||||||
bgA,
|
|
||||||
freeze,
|
|
||||||
freezeDuration,
|
|
||||||
};
|
|
||||||
localStorage.setItem(lskey, JSON.stringify(config));
|
|
||||||
this.$emit('config', config);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
TextConfig,
|
TextConfig,
|
||||||
|
@ -87,21 +58,9 @@ export default {
|
||||||
rgbaI2S,
|
rgbaI2S,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const { x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration } =
|
const config = getConfig();
|
||||||
getConfig();
|
|
||||||
return {
|
return {
|
||||||
x,
|
...config,
|
||||||
y,
|
|
||||||
fontSize,
|
|
||||||
fgRGB,
|
|
||||||
fgA,
|
|
||||||
fgRGB2,
|
|
||||||
fgA2,
|
|
||||||
width,
|
|
||||||
bgRGB,
|
|
||||||
bgA,
|
|
||||||
freeze,
|
|
||||||
freezeDuration,
|
|
||||||
// const
|
// const
|
||||||
freezeKeys: [
|
freezeKeys: [
|
||||||
...Object.keys(codes[this.version]?.freezeCodeHooks ?? {}),
|
...Object.keys(codes[this.version]?.freezeCodeHooks ?? {}),
|
||||||
|
@ -115,8 +74,7 @@ export default {
|
||||||
},
|
},
|
||||||
textConfig: {
|
textConfig: {
|
||||||
get() {
|
get() {
|
||||||
const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2 } = this;
|
return this;
|
||||||
return { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2 };
|
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
Object.assign(this, value);
|
Object.assign(this, value);
|
||||||
|
@ -126,8 +84,6 @@ export default {
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
width: updateConfig,
|
width: updateConfig,
|
||||||
bgRGB: updateConfig,
|
|
||||||
bgA: updateConfig,
|
|
||||||
freezeDuration: updateConfig,
|
freezeDuration: updateConfig,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"ja-JP": {
|
"ja-JP": {
|
||||||
"h3": "見た目",
|
"h3": "見た目",
|
||||||
"location": "位置:",
|
|
||||||
"fontSize": "文字サイズ:",
|
|
||||||
"fgColor": "文字色:",
|
|
||||||
"fgColorGrad": "グラデーション",
|
|
||||||
"fgColor1": "文字色(上):",
|
|
||||||
"fgColor2": "文字色(下):",
|
|
||||||
"bgColor": "背景色:",
|
|
||||||
"alpha": "不透明度=",
|
|
||||||
"preview": "プレビュー",
|
"preview": "プレビュー",
|
||||||
"previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です",
|
"previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です",
|
||||||
"freeze": {
|
"freeze": {
|
||||||
|
@ -40,14 +32,6 @@
|
||||||
},
|
},
|
||||||
"en-US": {
|
"en-US": {
|
||||||
"h3": "Appearance",
|
"h3": "Appearance",
|
||||||
"location": "Location: ",
|
|
||||||
"fontSize": "Font size: ",
|
|
||||||
"fgColor": "Font color: ",
|
|
||||||
"fgColorGrad": "Gradient",
|
|
||||||
"fgColor1": "Font color (top): ",
|
|
||||||
"fgColor2": "Font color (bottom): ",
|
|
||||||
"bgColor": "Background color: ",
|
|
||||||
"alpha": "Alpha=",
|
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"previewNote": "※ x ranges from 0 to 600, and y ranges from 16 to 464.",
|
"previewNote": "※ x ranges from 0 to 600, and y ranges from 16 to 464.",
|
||||||
"freeze": {
|
"freeze": {
|
||||||
|
@ -79,14 +63,6 @@
|
||||||
},
|
},
|
||||||
"fr-FR": {
|
"fr-FR": {
|
||||||
"h3": "Apparence",
|
"h3": "Apparence",
|
||||||
"location": "Position : ",
|
|
||||||
"fontSize": "Taille de police : ",
|
|
||||||
"fgColor": "Couleur du texte : ",
|
|
||||||
"fgColorGrad": "Dégradé",
|
|
||||||
"fgColor1": "Couleur du texte (haut) : ",
|
|
||||||
"fgColor2": "Couleur du texte (bas) : ",
|
|
||||||
"bgColor": "Couleur de fond : ",
|
|
||||||
"alpha": "Alpha = ",
|
|
||||||
"preview": "Aperçu",
|
"preview": "Aperçu",
|
||||||
"previewNote": "※ x doit être entre 0 et 600, et y entre 16 et 464.",
|
"previewNote": "※ x doit être entre 0 et 600, et y entre 16 et 464.",
|
||||||
"freeze": {
|
"freeze": {
|
||||||
|
|
54
site/.vuepress/components/codes/text.js
Normal file
54
site/.vuepress/components/codes/text.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/** @typedef {{index: number, kerning: number, width: number, code: number}} CharInfo */
|
||||||
|
import charInfoJP from '../../data/charInfo-JP.json';
|
||||||
|
import charInfoEU from '../../data/charInfo-EU.json';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} version
|
||||||
|
*/
|
||||||
|
const getFontInfo = (version) =>
|
||||||
|
['GMSJ01', 'GMSJ0A'].includes(version)
|
||||||
|
? {
|
||||||
|
// JP
|
||||||
|
charInfo: /**@type{Record<string, CharInfo>}*/ (charInfoJP),
|
||||||
|
rowSize: 24, // how many char in a row of the img
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
// EU (TODO US)
|
||||||
|
charInfo: /**@type{Record<string, CharInfo>}*/ (charInfoEU),
|
||||||
|
rowSize: 16, // how many char in a row of the img
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @param {string} version
|
||||||
|
*/
|
||||||
|
export function measureText(text, version) {
|
||||||
|
const { charInfo, rowSize } = getFontInfo(version);
|
||||||
|
|
||||||
|
/** @type {{x: number, y: number, u: number, v: number}[]} */
|
||||||
|
const chars = [];
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
let w = 0;
|
||||||
|
let useKerning = false;
|
||||||
|
text.split('').forEach((c) => {
|
||||||
|
const { index, kerning, width } = charInfo[c] ?? charInfo['?'];
|
||||||
|
if (c === '\n') {
|
||||||
|
useKerning = false;
|
||||||
|
x = 0;
|
||||||
|
y += 20;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (useKerning) x -= kerning;
|
||||||
|
useKerning = true;
|
||||||
|
// uv
|
||||||
|
const [u, v] = [(index % rowSize) * 20, ((index / rowSize) | 0) * 20];
|
||||||
|
chars.push({ x, y, u, v });
|
||||||
|
// next
|
||||||
|
x += width + kerning;
|
||||||
|
// update width
|
||||||
|
if (x > w) w = x;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { chars, width: w, height: y + 20 };
|
||||||
|
}
|
|
@ -1,3 +1,38 @@
|
||||||
|
/**
|
||||||
|
* @template T extends {Record<string, any>|Record<string, any>[]}
|
||||||
|
* @param {string} lskey
|
||||||
|
* @param {T} defaultConfig
|
||||||
|
* @param {(config: T)=>string} [makeText]
|
||||||
|
*/
|
||||||
|
export function makeUpdateConfig(lskey, defaultConfig, makeText) {
|
||||||
|
const configKeys = Object.keys(defaultConfig);
|
||||||
|
/** @type {(o: any)=>T} */
|
||||||
|
const makeConfig =
|
||||||
|
defaultConfig instanceof Array
|
||||||
|
? (o) => o.config
|
||||||
|
: (o) => Object.fromEntries(configKeys.map((k) => [k, o[k]]));
|
||||||
|
|
||||||
|
/** @this {any} */
|
||||||
|
return function updateConfig() {
|
||||||
|
// save config to localStorage
|
||||||
|
const config = makeConfig(this);
|
||||||
|
localStorage.setItem(lskey, JSON.stringify(config));
|
||||||
|
// emit `config` event to parent
|
||||||
|
this.$emit('config', makeText ? { ...config, text: makeText(config) } : config);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} x -- number to convert
|
||||||
|
* @param {number} size -- byte count
|
||||||
|
*/
|
||||||
|
export const int2hex = (x, size) =>
|
||||||
|
(x >>> 0)
|
||||||
|
.toString(16)
|
||||||
|
.toUpperCase()
|
||||||
|
.padStart(size << 1, '0')
|
||||||
|
.slice(-(size << 1));
|
||||||
|
|
||||||
/** @param {number} rgb */
|
/** @param {number} rgb */
|
||||||
export const rgbI2S = (rgb) => '#' + rgb.toString(16).padStart(6, '0');
|
export const rgbI2S = (rgb) => '#' + rgb.toString(16).padStart(6, '0');
|
||||||
/** @param {string} s */
|
/** @param {string} s */
|
||||||
|
@ -9,7 +44,35 @@ export const rgbS2I = (s) => parseInt(s.slice(1), 16);
|
||||||
export const rgbaI2S = (rgb, a) =>
|
export const rgbaI2S = (rgb, a) =>
|
||||||
'#' + rgb.toString(16).padStart(6, '0') + a.toString(16).padStart(2, '0');
|
'#' + rgb.toString(16).padStart(6, '0') + a.toString(16).padStart(2, '0');
|
||||||
|
|
||||||
export const fg2Style = (rgb, a, rgb2, a2) => {
|
/** @type {(labels: Record<string, any>, locale: string, fallbackLocale?: string) => (key: string) => string} */
|
||||||
const c = rgbaI2S(rgb, a);
|
export const makeGetLabel =
|
||||||
return rgb2 == null || a2 == null ? c : `linear-gradient(180deg, ${c}, ${rgbaI2S(rgb2, a2)})`;
|
(labels, locale, fallbackLocale = 'en-US') =>
|
||||||
|
(key) => {
|
||||||
|
const segs = key.split('.');
|
||||||
|
for (const localeTry of [locale, fallbackLocale]) {
|
||||||
|
let o = labels[localeTry];
|
||||||
|
for (const seg of segs) {
|
||||||
|
if (o == null) break;
|
||||||
|
o = o[seg];
|
||||||
|
}
|
||||||
|
if (o != null) return o;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {Iterable<T>} arr
|
||||||
|
* @param {(val: T) => boolean} tester
|
||||||
|
* @returns {[positive: T[], negative: T[]]}
|
||||||
|
*/
|
||||||
|
export function splitArray(arr, tester) {
|
||||||
|
/** @type {T[]} */
|
||||||
|
const positive = [];
|
||||||
|
/** @type {T[]} */
|
||||||
|
const negative = [];
|
||||||
|
for (const val of arr) {
|
||||||
|
(tester(val) ? positive : negative).push(val);
|
||||||
|
}
|
||||||
|
return [positive, negative];
|
||||||
|
}
|
||||||
|
|
1346
site/.vuepress/data/charInfo-EU.json
Normal file
1346
site/.vuepress/data/charInfo-EU.json
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
BIN
site/.vuepress/public/img/preview/font-EU.png
Normal file
BIN
site/.vuepress/public/img/preview/font-EU.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
site/.vuepress/public/img/preview/font-JP.png
Normal file
BIN
site/.vuepress/public/img/preview/font-JP.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
Binary file not shown.
Before Width: | Height: | Size: 38 KiB |
Loading…
Reference in a new issue