Add CustomizedDisplay and preview for NTSC-J
This commit is contained in:
parent
b80d408287
commit
d31cf7d266
23 changed files with 1312 additions and 86 deletions
238
Codes.xml
238
Codes.xml
|
@ -2861,6 +2861,7 @@
|
|||
</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>
|
||||
|
@ -3089,6 +3090,7 @@
|
|||
</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>
|
||||
|
@ -3277,6 +3279,163 @@
|
|||
60000000 00000000
|
||||
</source>
|
||||
</code>
|
||||
<code>
|
||||
<category>metadata</category>
|
||||
<id>CustomizedDisplay</id>
|
||||
<title lang="en-US">Customized display</title>
|
||||
<title lang="ja-JP">カスタマイズ表示</title>
|
||||
<author>sup39(サポミク)</author>
|
||||
<version>0.1</version>
|
||||
<date>Oct 15, 2022</date>
|
||||
<dependencies>drawText</dependencies>
|
||||
<description lang="en-US">
|
||||
Shows metadata at any given time.
|
||||
|
||||
::: warning
|
||||
This code is not compatible with other Display codes.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
The preview is based on NTSC-J's font data, and may be inaccurate for NTSC-U and PAL.
|
||||
In addition, some characters may not shown properly.
|
||||
:::
|
||||
|
||||
#### Format
|
||||
Use `< ID-of-the-data | format | value-shown-in-preview >` to display a metadata.
|
||||
|
||||
Supported data:
|
||||
| ID |data|type|
|
||||
|----|----|----|
|
||||
|`x`|X coordinate of Mario|float|
|
||||
|`y`|Y coordinate of Mario|float|
|
||||
|`z`|Z coordinate of Mario|float|
|
||||
|`angle`|Angle of Mario|uint16|
|
||||
|`HSpd`|Horizontal speed of Mario|float|
|
||||
|`VSpd`|Vertical speed of Mario|float|
|
||||
|`QF`|QF offset|\{0,1,2,3}|
|
||||
|
||||
For float data, you can set the *format* to `.{digit}` to specify how many digits to show.
|
||||
|
||||
%details[
|
||||
%summary[All printable characters (for NTSC-J)]
|
||||
![Printable charaters](/img/preview/font-jp.png){style="background:black"}
|
||||
]
|
||||
|
||||
#### Preview
|
||||
</description>
|
||||
<description lang="ja-JP">
|
||||
指定した情報を表示します。
|
||||
|
||||
#### フォーマット
|
||||
ゲーム内の情報を表示するために、`< 情報のID | 表示のフォーマット | プレビューで表示する値 >`を使います。
|
||||
|
||||
表示可能の情報一覧:
|
||||
|情報のID|情報|型|
|
||||
|----|----|----|
|
||||
|`x`|マリオのX座標|float|
|
||||
|`y`|マリオのY座標|float|
|
||||
|`z`|マリオのZ座標|float|
|
||||
|`angle`|マリオの角度|uint16|
|
||||
|`HSpd`|マリオの水平速度|float|
|
||||
|`VSpd`|マリオのY速度|float|
|
||||
|`QF`|ずれたQFの数|\{0,1,2,3}|
|
||||
|
||||
float(小数)型に対して、「表示のフォーマット」を`.{桁数}`に設定して何桁まで表示するか指定できます。
|
||||
|
||||
また、全てのひらがなとカタカナ及び一部の漢字の表示も可能です。
|
||||
|
||||
%details[
|
||||
%summary[表示可能な文字一覧:]
|
||||
![表示可能な文字一覧](/img/preview/font-jp.png){style="background:black"}
|
||||
]
|
||||
|
||||
#### プレビュー
|
||||
</description>
|
||||
<source version="GMSJ01">
|
||||
C6206734 817FA000
|
||||
077FA000 00000084
|
||||
4A83B229 806D98B8
|
||||
C0230010 C0430014
|
||||
C0630018 A1230096
|
||||
C08300B0 C0A300A8
|
||||
806D97E8 81430058
|
||||
554A07BE 48000035
|
||||
5820252E 30660A59
|
||||
20252E30 660A5A20
|
||||
252E3066 0A412025
|
||||
68750A48 20252E32
|
||||
660A5620 252E3266
|
||||
0A514620 25750000
|
||||
7D0802A6 38600010
|
||||
388000C8 38A00012
|
||||
38C0FFFF 7CC73378
|
||||
4CC63242 4BFF61BD
|
||||
4AA0C6B8 00000000
|
||||
</source>
|
||||
<source version="GMSJ0A">
|
||||
C61252A0 817FA000
|
||||
077FA000 00000084
|
||||
4AAD0ECD 806D9DE8
|
||||
C0230010 C0430014
|
||||
C0630018 A1230096
|
||||
C08300B0 C0A300A8
|
||||
806D9E78 81430058
|
||||
554A07BE 48000035
|
||||
5820252E 30660A59
|
||||
20252E30 660A5A20
|
||||
252E3066 0A412025
|
||||
68750A48 20252E32
|
||||
660A5620 252E3266
|
||||
0A514620 25750000
|
||||
7D0802A6 38600010
|
||||
388000C8 38A00012
|
||||
38C0FFFF 7CC73378
|
||||
4CC63242 4BFF61BD
|
||||
4A92B224 00000000
|
||||
</source>
|
||||
<source version="GMSE01">
|
||||
C6143F14 817FA000
|
||||
077FA000 00000084
|
||||
4AAF16BD 806D9F28
|
||||
C0230010 C0430014
|
||||
C0630018 A1230096
|
||||
C08300B0 C0A300A8
|
||||
806D9FB8 81430058
|
||||
554A07BE 48000035
|
||||
5820252E 30660A59
|
||||
20252E30 660A5A20
|
||||
252E3066 0A412025
|
||||
68750A48 20252E32
|
||||
660A5620 252E3266
|
||||
0A514620 25750000
|
||||
7D0802A6 38600010
|
||||
388000C8 38A00012
|
||||
38C0FFFF 7CC73378
|
||||
4CC63242 4BFF61BD
|
||||
4A949E98 00000000
|
||||
</source>
|
||||
<source version="GMSP01">
|
||||
C6138B50 817FA000
|
||||
077FA000 00000084
|
||||
4AAE9865 806D9E50
|
||||
C0230010 C0430014
|
||||
C0630018 A1230096
|
||||
C08300B0 C0A300A8
|
||||
806D9EE0 81430058
|
||||
554A07BE 48000035
|
||||
5820252E 30660A59
|
||||
20252E30 660A5A20
|
||||
252E3066 0A412025
|
||||
68750A48 20252E32
|
||||
660A5620 252E3266
|
||||
0A514620 25750000
|
||||
7D0802A6 38600010
|
||||
388000C8 38A00012
|
||||
38C0FFFF 7CC73378
|
||||
4CC63242 4BFF61BD
|
||||
4A93EAD4 00000000
|
||||
</source>
|
||||
</code>
|
||||
<code>
|
||||
<category>qol</category>
|
||||
<presets>standard,recommended,il</presets>
|
||||
|
@ -3671,6 +3830,7 @@
|
|||
</code>
|
||||
<code>
|
||||
<category>qol</category>
|
||||
<id>PatternSelector</id>
|
||||
<title lang="en-US">Pattern Selector</title>
|
||||
<title lang="ja-JP">パターン選択</title>
|
||||
<author>sup39(サポミク)</author>
|
||||
|
@ -3678,8 +3838,6 @@
|
|||
<date>Apr 25, 2022</date>
|
||||
<dependencies>drawText</dependencies>
|
||||
<description lang="en-US">
|
||||
![Preview](/img/PatternSelector/preview.jpg)
|
||||
|
||||
Ⓑ is the cursor to select the pattern,
|
||||
and the three digits are the numbers representing the pattern.
|
||||
See below for more information.
|
||||
|
@ -3718,8 +3876,6 @@
|
|||
%object[]{data="/img/PatternSelector/PV1-3.svg"}
|
||||
</description>
|
||||
<description lang="ja-JP">
|
||||
![プレビュー](/img/PatternSelector/preview.jpg)
|
||||
|
||||
Ⓑはパターンを選択するためのカーソルであり、三つの数字はパターンの番号を表します。
|
||||
各パターンの番号は下記を参照してください。
|
||||
|
||||
|
@ -4054,6 +4210,80 @@
|
|||
7C0803A6 4E800020
|
||||
00000000 59800004
|
||||
</source>
|
||||
<source version="GMSE01">
|
||||
077F0238 00000110
|
||||
9421FED8 7C0802A6
|
||||
BF810118 7C7F1B78
|
||||
9001012C 7C9E2378
|
||||
90C100B8 7CBD2B78
|
||||
90E100BC 7D1C4378
|
||||
912100C0 914100C4
|
||||
40860024 D82100C8
|
||||
D84100D0 D86100D8
|
||||
D88100E0 D8A100E8
|
||||
D8C100F0 D8E100F8
|
||||
D9010100 39200600
|
||||
390100BC B12100A0
|
||||
39210130 912100A4
|
||||
392100A8 912100A8
|
||||
808D9FC8 38E100B8
|
||||
7FA6EB78 38A00000
|
||||
38610008 6FDE8000
|
||||
4AADDA39 3D204330
|
||||
91210108 3D40817F
|
||||
93C1010C 6FFF8000
|
||||
C00A0344 38610070
|
||||
C9810108 91210110
|
||||
3D20817F 93E10114
|
||||
FC4C0028 C0690340
|
||||
C9810110 93A10064
|
||||
FC2C0028 93A10060
|
||||
4AB59AC9 38800000
|
||||
38610070 4AB72AF9
|
||||
38C100A0 7F85E378
|
||||
388000FF 38610008
|
||||
4AADD895 BB810118
|
||||
38210128 80010004
|
||||
7C0803A6 4E800020
|
||||
00000000 59800004
|
||||
</source>
|
||||
<source version="GMSP01">
|
||||
077F0238 00000110
|
||||
9421FED8 7C0802A6
|
||||
BF810118 7C7F1B78
|
||||
9001012C 7C9E2378
|
||||
90C100B8 7CBD2B78
|
||||
90E100BC 7D1C4378
|
||||
912100C0 914100C4
|
||||
40860024 D82100C8
|
||||
D84100D0 D86100D8
|
||||
D88100E0 D8A100E8
|
||||
D8C100F0 D8E100F8
|
||||
D9010100 39200600
|
||||
390100BC B12100A0
|
||||
39210130 912100A4
|
||||
392100A8 912100A8
|
||||
808D9EF0 38E100B8
|
||||
7FA6EB78 38A00000
|
||||
38610008 6FDE8000
|
||||
4AAD5ACD 3D204330
|
||||
91210108 3D40817F
|
||||
93C1010C 6FFF8000
|
||||
C00A0344 38610070
|
||||
C9810108 91210110
|
||||
3D20817F 93E10114
|
||||
FC4C0028 C0690340
|
||||
C9810110 93A10064
|
||||
FC2C0028 93A10060
|
||||
4AB51D25 38800000
|
||||
38610070 4AB6AD19
|
||||
38C100A0 7F85E378
|
||||
388000FF 38610008
|
||||
4AAD5929 BB810118
|
||||
38210128 80010004
|
||||
7C0803A6 4E800020
|
||||
00000000 59800004
|
||||
</source>
|
||||
</code>
|
||||
<code>
|
||||
<id>InstantRestart</id>
|
||||
|
|
13
Readme.md
13
Readme.md
|
@ -23,6 +23,19 @@ The codes are stored in the `Codes.xml` file. If you want to add or change codes
|
|||
|
||||
When adding new codes keep in mind that the English title/description are mandatory.
|
||||
|
||||
#### Codes with configuration
|
||||
Codes with configuration are usually defined as vue components,
|
||||
which is defined in [site/.vuepress/components/codes/](site/.vuepress/components/codes/).
|
||||
When creating/updating those codes,
|
||||
in addition to editing the `Codes.xml` file,
|
||||
you may also need to check the following files:
|
||||
- [site/.vuepress/components/codes/codegen.js](site/.vuepress/components/codes/codegen.js):
|
||||
Specify the Gecko code generator function of the code.
|
||||
The version string will be passed as the first argument.
|
||||
- [site/.vuepress/components/codes/ui.js](site/.vuepress/components/codes/ui.js):
|
||||
Specify the vue component for the configuration of the code.
|
||||
The version string will be passed as a property.
|
||||
|
||||
#### Reserved Memory
|
||||
|
||||
Some codes store some states in the games memory starting from address 0x817F0000. To avoid collisions use a memory range in the unallocated ranges:
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
## Oct 16, 2022
|
||||
\[QFT v1.2] Fix QFT background when 112\*fontSize is not an integer
|
||||
|
||||
## Oct 15, 2022
|
||||
- Add preview for selected codes (for NTSC-J)
|
||||
- Add CustomizedDisplay
|
||||
- Add instructions to create/update codes with configuration in Readme.md
|
||||
|
||||
## Jun 8, 2022
|
||||
### Fixed 'Shine Get Timer' stopping on any cutscene started after touching a Shine
|
||||
Shoutouts to plankton for touching the Pinna 1 Shine before its spawn cutscene started, in which case the timer would stop around 8 seconds early.
|
||||
|
|
106
package-lock.json
generated
106
package-lock.json
generated
|
@ -9,6 +9,8 @@
|
|||
"version": "3.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/encoding-japanese": "^2.0.1",
|
||||
"encoding-japanese": "^2.0.0",
|
||||
"vuedraggable": "2.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1869,6 +1871,11 @@
|
|||
"@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=="
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||
|
@ -3748,6 +3755,18 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -6572,6 +6591,14 @@
|
|||
"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==",
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
|
@ -8899,12 +8926,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
@ -12582,6 +12609,18 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
|
@ -17198,18 +17237,6 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-encoding/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||
|
@ -18850,6 +18877,11 @@
|
|||
"@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=="
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||
|
@ -20451,6 +20483,15 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -22759,6 +22800,11 @@
|
|||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"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=="
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
|
@ -24528,12 +24574,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
},
|
||||
"icss-replace-symbols": {
|
||||
|
@ -27491,6 +27537,15 @@
|
|||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"dev": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -31223,17 +31278,6 @@
|
|||
"dev": true,
|
||||
"requires": {
|
||||
"iconv-lite": "0.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"whatwg-mimetype": {
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
"devDependencies": {
|
||||
"@sup39/markdown-it-attr": "1.2.2",
|
||||
"@sup39/markdown-it-inline-tag": "1.0.1",
|
||||
"@types/encoding-japanese": "^2.0.1",
|
||||
"@vuepress/plugin-back-to-top": "1.9.7",
|
||||
"@vuepress/plugin-medium-zoom": "1.9.7",
|
||||
"encoding-japanese": "^2.0.0",
|
||||
"jsdom": "20.0.0",
|
||||
"pre-commit": "1.2.2",
|
||||
"prettier": "2.7.1",
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
>
|
||||
<span v-else>{{ getLabel('codeinfo.author') }} {{ translatedCode.author }}</span>
|
||||
</div>
|
||||
<Preview v-if="showPreview" :config="previewConfig" />
|
||||
<p class="description" v-html="translatedCode.description"></p>
|
||||
<component v-if="configUI" :is="configUI" :version="version"
|
||||
:codeConfigs="codeConfigs" @config="$emit('config', {[code.id]: $event})" />
|
||||
:previewConfig="previewConfig" @config="$emit('config', {[code.id]: $event})" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -25,7 +26,7 @@ export default {
|
|||
anchor: { type: Boolean },
|
||||
code: { type: Object },
|
||||
version: { type: String },
|
||||
codeConfigs: { type: Object },
|
||||
previewConfig: { type: Object },
|
||||
},
|
||||
computed: {
|
||||
translatedCode: function () {
|
||||
|
@ -34,6 +35,15 @@ export default {
|
|||
configUI: function () {
|
||||
return configUIs[this.code.id];
|
||||
},
|
||||
showPreview() {
|
||||
return [
|
||||
'PatternSelector',
|
||||
'PASDisplay',
|
||||
'SpeedDisplay',
|
||||
'CustomizedDisplay',
|
||||
'qft',
|
||||
].includes(this.code.id); // TODO
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
|
|
|
@ -72,7 +72,7 @@ export default {
|
|||
|
||||
// generate file
|
||||
const codeSize = c.reduce((a, e) => a+e.source.length, 0)/2 + 16; // 8(00D0)+8(F000)
|
||||
console.log(codeSize, c);
|
||||
// console.log(codeSize, c);
|
||||
switch (this.format) {
|
||||
case 'gct':
|
||||
this.alertGCTCodeSize(codeSize);
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<div v-if="codes && codes.length > 0" class="help">
|
||||
<h3>{{ getLabel('headers.help') }}</h3>
|
||||
<CodeInfo v-if="!!inspectingCode" :code="inspectingCode" :version="selectedVersion"
|
||||
:codeConfigs="codeConfigs" @config="onCodeConfigChanged" />
|
||||
:previewConfig="previewConfig" @config="onCodeConfigChanged" />
|
||||
<div v-else-if="showStageLoaderHelp">
|
||||
<h3>{{ getLabel('headers.stageloader') }}</h3>
|
||||
<div>
|
||||
|
@ -106,12 +106,14 @@
|
|||
<script>
|
||||
// Data
|
||||
import gameVersions from '../data/gameVersions.json';
|
||||
import codeCategories from '../data/codeCategories.json';
|
||||
|
||||
// Util
|
||||
import { translate } from '../i18n/localeHelper';
|
||||
|
||||
// Code Configs
|
||||
import {getConfig as qftGetConfig} from './codes/qft/codegen';
|
||||
import {getConfig as getConfigQFT} from './codes/qft/codegen';
|
||||
import {getConfig as getConfigCD} from './codes/CustomizedDisplay/codegen';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -131,7 +133,11 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.codeConfigs = {
|
||||
qft: qftGetConfig(),
|
||||
qft: getConfigQFT(),
|
||||
PatternSelector: {},
|
||||
SpeedDisplay: {},
|
||||
PASDisplay: {},
|
||||
CustomizedDisplay: getConfigCD(this.version),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -207,6 +213,17 @@ export default {
|
|||
this.codeConfigs = {...this.codeConfigs, ...e};
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
previewConfig() {
|
||||
const {id, category} = this.inspectingCode ?? {};
|
||||
const {exclusive} = codeCategories.find((c) => c.identifier === category) ?? {};
|
||||
const ids = new Set(this.selectedCheats
|
||||
.filter(code => !(code.category === category && exclusive))
|
||||
.map(code => code.id));
|
||||
ids.add(id);
|
||||
return Object.fromEntries(Object.entries(this.codeConfigs).filter(([id]) => ids.has(id)));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
|
|
|
@ -1,39 +1,57 @@
|
|||
<template>
|
||||
<div class="preview-root">
|
||||
<div class="preview-ctn">
|
||||
<div :style="qft.bgStyle" />
|
||||
<PreviewString v-if="qft" :x="qft.x" :y="qft.y" :size="qft.fontSize" :color="qft.color" text="0:00:00" />
|
||||
<PreviewString v-if="mdp" :x="mdp.x" :y="mdp.y" :size="mdp.fontSize" :color="mdp.color" :text="mdp.text" />
|
||||
<PreviewString :x="16" :y="320" :size="20" :color="'#fff'" text="Pattern #0 0 0" />
|
||||
<div v-if="qft">
|
||||
<div :style="qft.bgStyle" />
|
||||
<PreviewString :x="qft.x" :y="qft.y" :size="qft.fontSize" :color="qft.color" text="0:00:00" />
|
||||
</div>
|
||||
<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="config.PatternSelector" :x="16" :y="320" :size="20" :color="'#fff'" text="Pattern #0 0 0" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { rgbaI2S, fg2Style } from './codes/utils.js';
|
||||
export default {
|
||||
props: {
|
||||
config: {type: Object},
|
||||
},
|
||||
computed: {
|
||||
mdp() {
|
||||
return {
|
||||
mdps() {
|
||||
const {config} = this;
|
||||
if (config.PASDisplay) return [{
|
||||
x: 16,
|
||||
y: 200,
|
||||
fontSize: 20,
|
||||
color: '#fff',
|
||||
text: 'X Pos -4\nY Pos 27\nZ Pos -1515\nAngle 3117\nH Spd 4.26\nV Spd 4.28',
|
||||
};
|
||||
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 i2s = (rgb, a) => '#'+rgb.toString(16).padStart(6, '0')+a.toString(16).padStart(2, '0');
|
||||
const bg = i2s(bgRGB, bgA);
|
||||
const fg1 = i2s(fgRGB, fgA);
|
||||
const bg = rgbaI2S(bgRGB, bgA);
|
||||
return {
|
||||
x, y, fontSize,
|
||||
color: fgRGB2==null || fgA2==null ? fg1 : `linear-gradient(180deg, ${fg1}, ${i2s(fgRGB2, fgA2)})`,
|
||||
color: fg2Style(fgRGB, fgA, fgRGB2, fgA2),
|
||||
bgStyle: {
|
||||
left: x+'px',
|
||||
top: (y-fontSize)+'px',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="preview-str" :style="styles.root" >
|
||||
<div v-if="text" class="preview-str" :style="styles.root" >
|
||||
<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-mask" :style="style.mask" />
|
||||
|
|
51
site/.vuepress/components/codes/CustomizedDisplay/Cell.vue
Normal file
51
site/.vuepress/components/codes/CustomizedDisplay/Cell.vue
Normal file
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div>
|
||||
<TextConfig v-model="textConfig" />
|
||||
<div>
|
||||
<div>{{l.format}}</div>
|
||||
<textarea v-model="fmt" :rows="rows" :cols="cols" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import labels from './labels.json';
|
||||
import TextConfig from '../TextConfig.vue';
|
||||
import {format2previewText} from './codegen.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TextConfig,
|
||||
},
|
||||
props: {
|
||||
value: {type: Object},
|
||||
version: {type: String},
|
||||
rows: {type: Number, default: 7},
|
||||
cols: {type: Number, default: 40},
|
||||
},
|
||||
data() {
|
||||
const {fmt, text, ...textConfig} = this.value;
|
||||
return {
|
||||
textConfig,
|
||||
fmt,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
l() {
|
||||
return labels[this.$lang] ?? labels['en-US'];
|
||||
},
|
||||
preview() {
|
||||
return format2previewText(this.fmt, this.version);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.$emit('input', {fmt: this.fmt, text: this.preview, ...this.textConfig});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
textConfig() {this.update()},
|
||||
fmt() {this.update()},
|
||||
},
|
||||
}
|
||||
</script>
|
219
site/.vuepress/components/codes/CustomizedDisplay/asm.js
Normal file
219
site/.vuepress/components/codes/CustomizedDisplay/asm.js
Normal file
|
@ -0,0 +1,219 @@
|
|||
import * as Encoding from 'encoding-japanese';
|
||||
|
||||
/**
|
||||
* @typedef {number[]} Inst
|
||||
*
|
||||
* @typedef {(
|
||||
* rT: number,
|
||||
* rA: number,
|
||||
* D: number,
|
||||
* ) => Inst} InstD
|
||||
* @typedef {(
|
||||
* rS: number,
|
||||
* rA: number,
|
||||
* D: number,
|
||||
* ) => Inst} InstDS
|
||||
*
|
||||
* @typedef {(
|
||||
* rA: number,
|
||||
* rS: number,
|
||||
* SH: number,
|
||||
* MB: number,
|
||||
* ME: number,
|
||||
* Rc: number|boolean,
|
||||
* ) => Inst} InstM
|
||||
*
|
||||
* @typedef {(
|
||||
* rT: number,
|
||||
* rA: number,
|
||||
* rB: number,
|
||||
* Rc: number|boolean,
|
||||
* ) => Inst} InstX
|
||||
* @typedef {(
|
||||
* rS: number,
|
||||
* rA: number,
|
||||
* rB: number,
|
||||
* Rc: number|boolean,
|
||||
* ) => Inst} InstXS
|
||||
*
|
||||
* @typedef {(
|
||||
* LL: number,
|
||||
* LK: number|boolean,
|
||||
* AA?: number|boolean,
|
||||
* ) => 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} rT
|
||||
* @param {number} rA
|
||||
* @param {number} D
|
||||
*/
|
||||
const InstD = (op, rT, rA, D) =>
|
||||
makeInst(((op & 0x3f) << 26) | ((rT & 0x1f) << 21) | ((rA & 0x1f) << 16) | (D & 0xffff));
|
||||
/**
|
||||
* @param {number} op
|
||||
* @param {number} rT
|
||||
* @param {number} rA
|
||||
* @param {number} rB
|
||||
* @param {number} op2
|
||||
* @param {number} Rc
|
||||
*/
|
||||
const InstX = (op, rT, rA, rB, op2, Rc) =>
|
||||
makeInst(
|
||||
((op & 0x3f) << 26) |
|
||||
((rT & 0x1f) << 21) |
|
||||
((rA & 0x1f) << 16) |
|
||||
((rB & 0x1f) << 11) |
|
||||
((op2 & 0x3ff) << 1) |
|
||||
Rc,
|
||||
);
|
||||
/**
|
||||
* @param {number} op
|
||||
* @param {number} RS
|
||||
* @param {number} RA
|
||||
* @param {number} SH
|
||||
* @param {number} MB
|
||||
* @param {number} ME
|
||||
* @param {number} Rc
|
||||
*/
|
||||
const InstM = (op, RA, RS, SH, MB, ME, Rc) =>
|
||||
makeInst(
|
||||
((op & 0x3f) << 26) |
|
||||
((RS & 0x1f) << 21) |
|
||||
((RA & 0x1f) << 16) |
|
||||
((SH & 0x1f) << 11) |
|
||||
((MB & 0x1f) << 6) |
|
||||
((ME & 0x1f) << 1) |
|
||||
Rc,
|
||||
);
|
||||
/**
|
||||
* @param {number} op
|
||||
* @param {number} LL
|
||||
* @param {number} AA
|
||||
* @param {number} LK
|
||||
*/
|
||||
const InstI = (op, LL, AA, LK) =>
|
||||
makeInst(((op & 0x3f) << 26) | ((LL & 0xffffff) << 2) | ((AA & 1) << 1) | (LK & 1));
|
||||
|
||||
/** @type {(op: number) => InstD} */
|
||||
const makeInstD = (op) => (rT, rA, D) => InstD(op, rT, rA, D);
|
||||
/** @type {(op: number) => InstDS} */
|
||||
const makeInstDS = (op) => (rA, rS, D) => InstD(op, rA, rS, D);
|
||||
/** @type {(op: number, op2: number) => InstX} */
|
||||
const makeInstX = (op, op2) => (rT, rA, rB, Rc) => InstX(op, rT, rA, rB, op2, +Rc);
|
||||
/** @type {(op: number, op2: number) => InstXS} */
|
||||
const makeInstXS = (op, op2) => (rA, rS, rB, Rc) => InstX(op, rA, rS, rB, op2, +Rc);
|
||||
/** @type {(op: number) => InstM} */
|
||||
const makeInstM = (op) => (rA, rS, SH, MB, ME, Rc) => InstM(op, rA, rS, SH, MB, ME, +Rc);
|
||||
/** @type {(op: number) => InstI} */
|
||||
const makeInstI =
|
||||
(op) =>
|
||||
(LL, LK, AA = 0) =>
|
||||
InstI(op, LL >> 2, +AA, +LK);
|
||||
|
||||
export const ASM = {
|
||||
// store rT, rA, D
|
||||
stb: makeInstD(38),
|
||||
sth: makeInstD(44),
|
||||
stw: makeInstD(36),
|
||||
stfs: makeInstD(52),
|
||||
stfd: makeInstD(54),
|
||||
stwu: makeInstD(37),
|
||||
// load rS, rA, D
|
||||
lbz: makeInstD(34),
|
||||
lhz: makeInstD(40),
|
||||
lwz: makeInstD(32),
|
||||
lfs: makeInstD(48),
|
||||
// li rT, D
|
||||
addi: makeInstD(14),
|
||||
li: (/**@type{number}*/ rT, /**@type{number}*/ D) => InstD(14, rT, 0, D),
|
||||
addis: makeInstD(15),
|
||||
lis: (/**@type{number}*/ rT, /**@type{number}*/ D) => InstD(15, rT, 0, D),
|
||||
// ori rA, rS, D
|
||||
ori: makeInstDS(24),
|
||||
// or rA, rS, rB, flag
|
||||
or: makeInstXS(31, 444),
|
||||
mr: (/**@type{number}*/ rT, /**@type{number}*/ rA, flag = 0) => InstX(31, rA, rT, rA, 444, flag),
|
||||
// mask
|
||||
rlwinm: makeInstM(21),
|
||||
// b
|
||||
b: makeInstI(18),
|
||||
// mflr
|
||||
mflr: (/**@type{number}*/ rT) => InstX(31, rT, 8, 0, 339, 0),
|
||||
mfctr: (/**@type{number}*/ rT) => InstX(31, rT, 9, 0, 339, 0),
|
||||
// mtlr
|
||||
mtlr: (/**@type{number}*/ rS) => InstX(31, rS, 8, 0, 467, 0),
|
||||
mtctr: (/**@type{number}*/ rS) => InstX(31, rS, 9, 0, 467, 0),
|
||||
// cr
|
||||
crset: (/**@type{number}*/ B) => InstX(19, B, B, B, 289, 0),
|
||||
crclr: (/**@type{number}*/ B) => InstX(19, B, B, B, 193, 0),
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} rT
|
||||
* @param {number} D
|
||||
*/
|
||||
export function liDX(rT, D) {
|
||||
if (-0x8000 <= D && D < 0x8000) {
|
||||
return ASM.li(rT, D);
|
||||
} else if ((D & 0xffff) === 0) {
|
||||
return ASM.lis(rT, D >>> 16);
|
||||
} else {
|
||||
const h = D >>> 16;
|
||||
const l = D & 0xffff;
|
||||
return [...ASM.lis(rT, h), ...ASM.ori(rT, rT, l)];
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
export function str2inst(s) {
|
||||
const fmtbuf = Encoding.convert(Encoding.stringToCode(s), 'SJIS');
|
||||
fmtbuf.push(0); // NUL terminated
|
||||
const fmtlen = fmtbuf.length;
|
||||
const fmtlen3 = fmtlen & 3;
|
||||
const pad = fmtlen3 ? 4 - fmtlen3 : 0;
|
||||
fmtbuf.push(...Array(pad).fill(0));
|
||||
const dv = new DataView(Uint8Array.from(fmtbuf).buffer);
|
||||
const insts = Array((fmtlen + pad) >> 2)
|
||||
.fill(0)
|
||||
.map((_, i) => dv.getUint32(i << 2, false));
|
||||
return insts;
|
||||
}
|
||||
|
||||
/** @param {number} pc */
|
||||
export function makeProgram(pc) {
|
||||
/** @type {Inst[]} */
|
||||
const bufs = [];
|
||||
return {
|
||||
pc,
|
||||
/**
|
||||
* @param {number} dst
|
||||
* @param {boolean} LL
|
||||
*/
|
||||
b(dst, LL = false) {
|
||||
// TODO check overflow
|
||||
this.push(ASM.b(dst - this.pc, LL));
|
||||
},
|
||||
/** @param {number} dst */
|
||||
bl(dst) {
|
||||
this.b(dst, true);
|
||||
},
|
||||
/** @param {Inst[]} codes */
|
||||
push(...codes) {
|
||||
bufs.push(...codes);
|
||||
this.pc += codes.reduce((a, e) => a + e.length, 0) << 2;
|
||||
},
|
||||
dump: () => bufs.flatMap((e) => e),
|
||||
};
|
||||
}
|
||||
|
||||
/** @param {number} x */
|
||||
export const inst2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
|
427
site/.vuepress/components/codes/CustomizedDisplay/codegen.js
Normal file
427
site/.vuepress/components/codes/CustomizedDisplay/codegen.js
Normal file
|
@ -0,0 +1,427 @@
|
|||
import { parseJSON } from '../codegen.js';
|
||||
import { ASM, makeInst, liDX, str2inst, makeProgram, inst2gecko } from './asm.js';
|
||||
export const lskey = 'config/CustomizedDisplay';
|
||||
|
||||
export const defaultConfig = [
|
||||
{
|
||||
x: 16,
|
||||
y: 200,
|
||||
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>`,
|
||||
},
|
||||
];
|
||||
|
||||
/** @param {GameVersion} version */
|
||||
export function getConfig(version) {
|
||||
/** @type {typeof defaultConfig} */
|
||||
const config = typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey));
|
||||
return (config instanceof Array ? config : defaultConfig).map(({ fmt, ...o }) => ({
|
||||
...o,
|
||||
fmt,
|
||||
text: format2previewText(fmt, version),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {number[]} Inst
|
||||
* @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),
|
||||
}),
|
||||
};
|
||||
/** @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),
|
||||
},
|
||||
];
|
||||
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: 0x80206734,
|
||||
GMSJ0A: 0x801252a0,
|
||||
GMSE01: 0x80143f14,
|
||||
GMSP01: 0x80138b50,
|
||||
};
|
||||
const addrsSetup2D = {
|
||||
GMSJ01: 0x80035228,
|
||||
GMSJ0A: 0x802caecc,
|
||||
GMSE01: 0x802eb6bc,
|
||||
GMSP01: 0x802e3864,
|
||||
};
|
||||
const addrDrawText = 0x817f0238;
|
||||
const addrDst = 0x817fa000;
|
||||
|
||||
/**
|
||||
* @param {GameVersion} version
|
||||
*/
|
||||
export default function codegen(version) {
|
||||
const config = getConfig(version);
|
||||
|
||||
let spOff = 0;
|
||||
const fcodes = /** @type {Inst[]} */ ([]);
|
||||
|
||||
for (const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, fmt } of config) {
|
||||
// color
|
||||
const colorTop = (fgRGB << 8) | fgA;
|
||||
const colorBot = fgRGB2 == null || fgA2 == null ? colorTop : (fgRGB2 << 8) | fgA;
|
||||
// prepare drawText
|
||||
const f = prepareDrawText(x, y, fontSize, colorTop, colorBot);
|
||||
format2previewText(fmt, version, f);
|
||||
// update code and sp
|
||||
const { code, spNeed } = f.makeCode();
|
||||
spOff = Math.max(spOff, spNeed);
|
||||
fcodes.push(code);
|
||||
}
|
||||
|
||||
const addrOrig = addrsOrig[version];
|
||||
const addrSetup2D = addrsSetup2D[version];
|
||||
|
||||
// program
|
||||
const program = makeProgram(addrDst);
|
||||
// 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
|
||||
const pcode = program.dump();
|
||||
const psize = pcode.length;
|
||||
return [
|
||||
makeInst((0xc6 << 24) | (addrOrig & 0x1ffffff)),
|
||||
makeInst(addrDst),
|
||||
makeInst((0x06 << 24) | (addrDst & 0x1fffffff)),
|
||||
makeInst(psize << 2),
|
||||
pcode,
|
||||
psize & 1 ? [0] : [],
|
||||
]
|
||||
.flatMap((e) => e)
|
||||
.map(inst2gecko)
|
||||
.join('');
|
||||
}
|
60
site/.vuepress/components/codes/CustomizedDisplay/config.vue
Normal file
60
site/.vuepress/components/codes/CustomizedDisplay/config.vue
Normal file
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div>
|
||||
<Preview :config="previewConfig" />
|
||||
<div v-for="c,i in config" :key="i" class="textcell">
|
||||
<button @click="config.splice(i, 1)" class="textcell-remove">×</button>
|
||||
<Cell :value="c" @input="$event => config.splice(i, 1, $event)" :version="version" />
|
||||
</div>
|
||||
<div>
|
||||
<button @click="config.push(defaultConfigCell)" class="textcell-add">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defaultConfig, getConfig, lskey, format2previewText } from './codegen.js';
|
||||
import Cell from './Cell.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Cell,
|
||||
},
|
||||
props: {
|
||||
version: {type: String},
|
||||
previewConfig: {type: Object},
|
||||
},
|
||||
data() {
|
||||
const config = getConfig();
|
||||
const defaultConfigCell = {
|
||||
text: format2previewText(defaultConfig[0].fmt, this.version),
|
||||
...defaultConfig[0],
|
||||
};
|
||||
return {config, defaultConfigCell};
|
||||
},
|
||||
watch: {
|
||||
config(config) {
|
||||
// save config
|
||||
const sconf = config.map(({text, ...o}) => ({...o}));
|
||||
localStorage.setItem(lskey, JSON.stringify(sconf));
|
||||
// emit
|
||||
this.$emit('config', config);
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.textcell {
|
||||
border: 1px solid black;
|
||||
padding: 4px 8px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
.textcell-remove {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 1.2rem;
|
||||
color: red;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"ja-JP": {
|
||||
"format": "フォーマット:"
|
||||
},
|
||||
"en-US": {
|
||||
"format": "Format:"
|
||||
}
|
||||
}
|
90
site/.vuepress/components/codes/TextConfig.vue
Normal file
90
site/.vuepress/components/codes/TextConfig.vue
Normal file
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<span>{{l.location}}(</span><input type="number" min="0" max="600" v-model.number="x"><span>, </span><input type="number" min="16" max="464" v-model.number="y"><span>)</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{l.fontSize}}</span><input type="number" min="0" v-model.number="fontSize">
|
||||
</div>
|
||||
<div>
|
||||
<span>{{fgRGB2==null ? l.fgColor : l.fgColor1}}</span><input type="color" :value="rgbI2S(fgRGB)" @change="fgRGB = rgbS2I($event.target.value)">
|
||||
<span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="fgA"><span>/255={{(fgA/2.55).toFixed(1)}}%</span>
|
||||
<input type="checkbox" :checked="fgRGB2!=null" @change="toggleGradient"><span>{{l.fgColorGrad}}</span>
|
||||
</div>
|
||||
<div v-if="fgRGB2 != null">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import labels from './labels.json';
|
||||
import {rgbI2S, rgbS2I, rgbaI2S} from './utils';
|
||||
|
||||
const makeField = key => ({
|
||||
get() {
|
||||
return this.$props.value[key];
|
||||
},
|
||||
set(value) {
|
||||
this.update({[key]: value});
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {type: Object},
|
||||
},
|
||||
computed: {
|
||||
l() {
|
||||
return labels[this.$lang] ?? labels['en-US'];
|
||||
},
|
||||
...Object.fromEntries([
|
||||
'x', 'y', 'fontSize', 'fgRGB', 'fgA', 'fgRGB2', 'fgA2',
|
||||
].map(k => [k, makeField(k)])),
|
||||
},
|
||||
methods: {
|
||||
update(o) {
|
||||
this.$emit('input', {...this.value, ...o});
|
||||
},
|
||||
toggleGradient($event) {
|
||||
if ($event.target.checked) {
|
||||
this.update({
|
||||
fgRGB2: this.fgRGB,
|
||||
fgA2: this.fgA,
|
||||
});
|
||||
} else {
|
||||
this.update({
|
||||
fgRGB2: null,
|
||||
fgA2: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
rgbI2S,
|
||||
rgbS2I,
|
||||
rgbaI2S,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
input[type=number], td.right {
|
||||
text-align: right;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 3em;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.appearance > div {
|
||||
padding: 0 0 4px;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
|
@ -1,9 +1,11 @@
|
|||
import InstantRestart from './InstantRestart/codegen.js';
|
||||
import qft from './qft/codegen.js';
|
||||
import CustomizedDisplay from './CustomizedDisplay/codegen.js';
|
||||
|
||||
export default {
|
||||
InstantRestart,
|
||||
qft,
|
||||
CustomizedDisplay,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
20
site/.vuepress/components/codes/labels.json
Normal file
20
site/.vuepress/components/codes/labels.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"ja-JP": {
|
||||
"location": "位置:",
|
||||
"fontSize": "文字サイズ:",
|
||||
"fgColor": "文字色:",
|
||||
"fgColorGrad": "グラデーション",
|
||||
"fgColor1": "文字色(上):",
|
||||
"fgColor2": "文字色(下):",
|
||||
"alpha": "不透明度="
|
||||
},
|
||||
"en-US": {
|
||||
"location": "Location: ",
|
||||
"fontSize": "Font size: ",
|
||||
"fgColor": "Font color: ",
|
||||
"fgColorGrad": "Gradient",
|
||||
"fgColor1": "Font color(Top): ",
|
||||
"fgColor2": "Font color(Bottom): ",
|
||||
"alpha": "Alpha="
|
||||
}
|
||||
}
|
|
@ -3,27 +3,12 @@
|
|||
<section v-if="['GMSJ01', 'GMSJ0A'].includes(version)" class="appearance">
|
||||
<h3>{{l.h3}}</h3>
|
||||
<div>
|
||||
<span>{{l.location}}(</span><input type="number" min="0" max="600" v-model.number="x"><span>, </span><input type="number" min="16" max="464" v-model.number="y"><span>)</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{l.fontSize}}</span><input type="number" min="0" v-model.number="fontSize">
|
||||
</div>
|
||||
<div>
|
||||
<span>{{fgRGB2==null ? l.fgColor : l.fgColor1}}</span><input type="color" :value="rgbI2S(fgRGB)" @change="fgRGB = rgbS2I($event.target.value)">
|
||||
<span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="fgA"><span>/255={{(fgA/2.55).toFixed(1)}}%</span>
|
||||
<input type="checkbox" :checked="fgRGB2!=null" @change="toggleGradient"><span>{{l.fgColorGrad}}</span>
|
||||
</div>
|
||||
<div v-if="fgRGB2 != null">
|
||||
<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>
|
||||
<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>
|
||||
<h4>{{l.preview}}</h4>
|
||||
<Preview :config="codeConfigs" />
|
||||
<div style="white-space: pre">{{l.previewNote}}</div>
|
||||
</section>
|
||||
<section class="freeze">
|
||||
<h3>{{l.freeze.h3}}</h3>
|
||||
|
@ -44,9 +29,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
// import Preview from '../../PreviewString.vue';
|
||||
import {getConfig, lskey, codes} from './codegen.js';
|
||||
import {rgbI2S, rgbS2I, rgbaI2S} from '../utils';
|
||||
import labels from './labels.json';
|
||||
import TextConfig from '../TextConfig.vue';
|
||||
|
||||
function updateConfig() {
|
||||
const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = this;
|
||||
|
@ -58,9 +44,11 @@ function updateConfig() {
|
|||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TextConfig,
|
||||
},
|
||||
props: {
|
||||
version: {type: String},
|
||||
codeConfigs: {type: Object},
|
||||
},
|
||||
methods: {
|
||||
onChangeFreeze($event, key) {
|
||||
|
@ -76,15 +64,17 @@ export default {
|
|||
this.fgA2 = null;
|
||||
}
|
||||
},
|
||||
rgbI2S: (x) => '#'+x.toString(16).padStart(6, '0'),
|
||||
rgbS2I: (s) => parseInt(s.slice(1), 16),
|
||||
rgbaI2S: (rgb, a) => '#'+rgb.toString(16).padStart(6, '0')+a.toString(16).padStart(2, '0'),
|
||||
updateConfig,
|
||||
rgbI2S,
|
||||
rgbS2I,
|
||||
rgbaI2S,
|
||||
},
|
||||
data() {
|
||||
const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = getConfig();
|
||||
return {
|
||||
x, y, fontSize, width,
|
||||
fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA,
|
||||
x, y, fontSize,
|
||||
fgRGB, fgA, fgRGB2, fgA2,
|
||||
width, bgRGB, bgA,
|
||||
freeze, freezeDuration,
|
||||
// const
|
||||
freezeKeys: Object.keys(codes[this.version]?.freezeCodeInfo ?? {}),
|
||||
|
@ -94,16 +84,19 @@ export default {
|
|||
l() {
|
||||
return labels[this.$lang] ?? labels['en-US'];
|
||||
},
|
||||
textConfig: {
|
||||
get() {
|
||||
const {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2} = this;
|
||||
return {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2};
|
||||
},
|
||||
set(value) {
|
||||
Object.assign(this, value);
|
||||
this.updateConfig();
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
x: updateConfig,
|
||||
y: updateConfig,
|
||||
fontSize: updateConfig,
|
||||
width: updateConfig,
|
||||
fgRGB: updateConfig,
|
||||
fgA: updateConfig,
|
||||
fgRGB2: updateConfig,
|
||||
fgA2: updateConfig,
|
||||
bgRGB: updateConfig,
|
||||
bgA: updateConfig,
|
||||
freezeDuration: updateConfig,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"bgColor": "背景色:",
|
||||
"alpha": "不透明度=",
|
||||
"preview": "プレビュー",
|
||||
"previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です\n※ ゲーム内のフォントはプレビューのより幅が広いです",
|
||||
"previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です",
|
||||
"freeze": {
|
||||
"h3": "一時停止",
|
||||
"duration": "長さ:",
|
||||
|
@ -39,7 +39,7 @@
|
|||
"bgColor": "Background color: ",
|
||||
"alpha": "Alpha=",
|
||||
"preview": "Preview",
|
||||
"previewNote": "※ x ranges from 0 to 600, and y ranges from 16 to 464.\n※ The font is wider in the game compared to the preview.",
|
||||
"previewNote": "※ x ranges from 0 to 600, and y ranges from 16 to 464.",
|
||||
"freeze": {
|
||||
"h3": "Freezing the timer",
|
||||
"duration": "Duration: ",
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import InstantRestart from './InstantRestart/config.vue';
|
||||
import CustomizedDisplay from './CustomizedDisplay/config.vue';
|
||||
import qft from './qft/config.vue';
|
||||
|
||||
export default {
|
||||
InstantRestart,
|
||||
CustomizedDisplay,
|
||||
qft,
|
||||
};
|
||||
|
|
15
site/.vuepress/components/codes/utils.js
Normal file
15
site/.vuepress/components/codes/utils.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/** @param {number} rgb */
|
||||
export const rgbI2S = (rgb) => '#' + rgb.toString(16).padStart(6, '0');
|
||||
/** @param {string} s */
|
||||
export const rgbS2I = (s) => parseInt(s.slice(1), 16);
|
||||
/**
|
||||
* @param {number} rgb
|
||||
* @param {number} a
|
||||
*/
|
||||
export const rgbaI2S = (rgb, a) =>
|
||||
'#' + rgb.toString(16).padStart(6, '0') + a.toString(16).padStart(2, '0');
|
||||
|
||||
export const fg2Style = (rgb, a, rgb2, a2) => {
|
||||
const c = rgbaI2S(rgb, a);
|
||||
return rgb2 == null || a2 == null ? c : `linear-gradient(180deg, ${c}, ${rgbaI2S(rgb2, a2)})`;
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 110 KiB |
Loading…
Reference in a new issue