Rewrote drawText and related codes
- Reduced parameters to struct pointer + format string + varargs - Rewrote QFT, Pattern Selector, Customized Display with the new drawText function - 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
This commit is contained in:
parent
9b767a4cbb
commit
898ea733ac
32 changed files with 3347 additions and 1649 deletions
|
@ -47,7 +47,9 @@ Some codes store some states in the games memory starting from address 0x817F000
|
|||
| ![](./docs/reserved.svg) | `0x14` | `0x15` | DPad Functions: Stored Angle (Mario) |
|
||||
| ![](./docs/reserved.svg) | `0x16` | `0x1B` | DPad Functions: Stored Position (Camera) |
|
||||
| ![](./docs/reserved.svg) | `0x20` | `0x23` | Coin Count Savestate: Coin Count |
|
||||
| ![](./docs/unallocated.svg) | `0x24` | `0x93` | Not Allocated |
|
||||
| ![](./docs/reserved.svg) | `0x24` | `0x26` | Pattern Selector: Selected Pattern Numbers |
|
||||
| ![](./docs/reserved.svg) | `0x27` | `0x27` | Pattern Selector: Cursor Position |
|
||||
| ![](./docs/unallocated.svg) | `0x28` | `0x93` | Not Allocated |
|
||||
| ![](./docs/reserved.svg) | `0x94` | `0xA3` | QF Timer: Coordinates of the Text box (LTRB) |
|
||||
| ![](./docs/reserved.svg) | `0xA4` | `0xB0` | QF Timer: Timer Format String |
|
||||
| ![](./docs/reserved.svg) | `0xB0` | `0xB1` | QF Timer: (Unused) |
|
||||
|
|
|
@ -533,4 +533,424 @@
|
|||
60000000 00000000
|
||||
</source>
|
||||
</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>
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
# Changelog
|
||||
## 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
|
||||
### Updated 'Quarterframe Timer'
|
||||
Added the following options to freeze QFT:
|
||||
|
|
|
@ -136,8 +136,6 @@ export default {
|
|||
this.codeConfigs = {
|
||||
qft: getConfigQFT(),
|
||||
PatternSelector: getConfigPS(),
|
||||
SpeedDisplay: {},
|
||||
PASDisplay: {},
|
||||
CustomizedDisplay: getConfigCD(this.version),
|
||||
};
|
||||
},
|
||||
|
@ -222,7 +220,10 @@ export default {
|
|||
.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)));
|
||||
return {
|
||||
...Object.fromEntries(Object.entries(this.codeConfigs).filter(([id]) => ids.has(id))),
|
||||
_version: this.selectedVersion,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,82 +1,30 @@
|
|||
<template>
|
||||
<div class="preview-root">
|
||||
<div class="preview-ctn">
|
||||
<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="ps" :x="ps.x" :y="ps.y" :size="ps.fontSize" :color="ps.color" :text="ps.text" />
|
||||
<PreviewString :config="config.qft" :version="_version" />
|
||||
<PreviewString v-for="mdp,i in (config.CustomizedDisplay || [])"
|
||||
:key="'mdp'+i" :config="mdp" :version="_version" />
|
||||
<PreviewString :config="config.PatternSelector" :version="_version" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { rgbaI2S, fg2Style } from './codes/utils.js';
|
||||
export default {
|
||||
props: {
|
||||
config: {type: Object},
|
||||
},
|
||||
computed: {
|
||||
mdps() {
|
||||
const {config} = this;
|
||||
if (config.PASDisplay) return [{
|
||||
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',
|
||||
};
|
||||
_version() {
|
||||
const {_version} = this.config
|
||||
return _version;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div.preview-root {
|
||||
.preview-root {
|
||||
position: relative;
|
||||
width: 600px;
|
||||
height: 448px;
|
||||
|
@ -84,11 +32,8 @@ div.preview-root {
|
|||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
div.preview-ctn {
|
||||
.preview-ctn {
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
}
|
||||
div.preview-ctn * {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,47 +1,34 @@
|
|||
<template>
|
||||
<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" />
|
||||
<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 class="char-bg" :style="style.bg" />
|
||||
<div class="char-mask" :style="style.mask" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import charInfo from '../data/font-jp.json';
|
||||
import {rgbaI2S} from './codes/utils.js';
|
||||
import {measureText} from './codes/text.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
x: {type: Number},
|
||||
y: {type: Number},
|
||||
size: {type: Number},
|
||||
color: {type: String},
|
||||
text: {type: String},
|
||||
config: {type: Object},
|
||||
version: {type: String},
|
||||
},
|
||||
computed: {
|
||||
previewCssClass() {
|
||||
// TODO US
|
||||
return `preview-str preview-${['GMSJ01', 'GMSJ0A'].includes(this.version) ? 'JP' : 'EU'}`;
|
||||
},
|
||||
styles() {
|
||||
const {x: x0, y: y0, size: fontSize, color, text} = this;
|
||||
|
||||
/** @type {{x: number, y: number, u: number, v: number}[]} */
|
||||
const chars = [];
|
||||
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;
|
||||
});
|
||||
const {config, version} = this;
|
||||
const {x: x0, y: y0, fontSize, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, bgLeft, bgRight, bgTop, bgBot, text} = config;
|
||||
const fgColor = rgbaI2S(fgRGB, fgA);
|
||||
const {width, height, chars} = measureText(text, version);
|
||||
|
||||
return {
|
||||
root: {
|
||||
|
@ -60,10 +47,18 @@ export default {
|
|||
mask: {
|
||||
'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>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.preview-str {
|
||||
position: relative;
|
||||
}
|
||||
.preview-str * {
|
||||
position: absolute;
|
||||
}
|
||||
div.char-ctn {
|
||||
.char-ctn {
|
||||
isolation: isolate;
|
||||
}
|
||||
div.char-ctn > div {
|
||||
.char-ctn > div {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
div.char-bg {
|
||||
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);
|
||||
.char-mask {
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
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>
|
||||
|
|
|
@ -1,31 +1,25 @@
|
|||
import { parseJSON } from '../codegen.js';
|
||||
import { ASM, makeInst, liDX, str2inst, makeProgram, inst2gecko } from '../asm.js';
|
||||
import {
|
||||
ASM,
|
||||
makeInst,
|
||||
liDX,
|
||||
str2inst,
|
||||
makeProgram,
|
||||
inst2gecko,
|
||||
getFillRectParams,
|
||||
} from '../asm.js';
|
||||
import { measureText } from '../text.js';
|
||||
export const lskey = 'config/CustomizedDisplay';
|
||||
|
||||
export const defaultConfig = [
|
||||
{
|
||||
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>`,
|
||||
},
|
||||
];
|
||||
import configDB from './configDB.js';
|
||||
export const defaultConfig = [configDB.PAS];
|
||||
|
||||
/** @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 }) => ({
|
||||
...defaultConfig[0],
|
||||
...o,
|
||||
fmt,
|
||||
text: format2previewText(fmt, version),
|
||||
|
@ -142,14 +136,22 @@ const load = {
|
|||
};
|
||||
|
||||
/**
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} fontSize
|
||||
* @param {number} colorTop
|
||||
* @param {number} colorBot
|
||||
* @param {string} version
|
||||
* @param {{
|
||||
* x: number
|
||||
* y: number
|
||||
* fontSize: number
|
||||
* fgRGB: number
|
||||
* fgA: number
|
||||
* fgRGB2: number | null
|
||||
* fgA2: number | null
|
||||
* }} drawTextOpt
|
||||
*/
|
||||
export function prepareDrawText(x, y, fontSize, colorTop, colorBot) {
|
||||
let gpr = 9;
|
||||
export function prepareDrawText(version, { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2 }) {
|
||||
const colorTop = (fgRGB << 8) | fgA;
|
||||
const colorBot = fgRGB2 == null || fgA2 == null ? colorTop : (fgRGB2 << 8) | fgA;
|
||||
|
||||
let gpr = 5;
|
||||
let fpr = 1;
|
||||
let sp = 8;
|
||||
let fmt = '';
|
||||
|
@ -219,8 +221,8 @@ export function prepareDrawText(x, y, fontSize, colorTop, colorBot) {
|
|||
const rBase = 3;
|
||||
insts.push(pre(rBase));
|
||||
// load all params
|
||||
const rField = 5;
|
||||
const fField = 9;
|
||||
const rField = 11; // tmp GPR
|
||||
const fField = 9; // tmp FPR
|
||||
for (const {
|
||||
info: { offset: srcoff, dtype, post },
|
||||
dst,
|
||||
|
@ -258,32 +260,21 @@ export function prepareDrawText(x, y, fontSize, colorTop, colorBot) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// r8 = fmt
|
||||
const fmtbuf = str2inst(fmt);
|
||||
// r3 = opt // sizeof(opt) = 0x10
|
||||
// r4 = fmt
|
||||
const fmtbuf = str2inst(fmt, version);
|
||||
insts.push(
|
||||
// bl 4+len4(fmt)
|
||||
ASM.b(4 + (fmtbuf.length << 2), true),
|
||||
// bl 4+sizeof(opt)+len4(fmt)
|
||||
ASM.b(0x14 + (fmtbuf.length << 2), true),
|
||||
// opt
|
||||
[((x & 0xffff) << 16) | (y & 0xffff), fontSize, colorTop, colorBot],
|
||||
// .string fmt
|
||||
fmtbuf,
|
||||
// mflr r8
|
||||
ASM.mflr(8),
|
||||
// mflr r3
|
||||
ASM.mflr(3),
|
||||
// addi r4, r3, sizeof(opt)
|
||||
ASM.addi(4, 3, 0x10),
|
||||
);
|
||||
/*
|
||||
* 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 };
|
||||
},
|
||||
|
@ -369,58 +360,72 @@ export function format2previewText(input, version, f = null) {
|
|||
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;
|
||||
import addrs from '../addrs.js';
|
||||
const addrOrigOff = -0x2c; // drawWater - [-0x30, -0x18]
|
||||
const addrDst = 0x817fa000;
|
||||
|
||||
/**
|
||||
* @param {GameVersion} version
|
||||
*/
|
||||
export default function codegen(version) {
|
||||
const config = getConfig(version);
|
||||
const configs = getConfig(version);
|
||||
|
||||
let spOff = 0;
|
||||
const fcodes = /** @type {Inst[]} */ ([]);
|
||||
const bcodes = /** @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;
|
||||
for (const config of configs) {
|
||||
const { fontSize, fmt, bgA } = config;
|
||||
// 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 f = prepareDrawText(version, config);
|
||||
const text = format2previewText(fmt, version, f);
|
||||
// text code
|
||||
if (fmt.trim()) {
|
||||
// update code and sp
|
||||
const { code, spNeed } = f.makeCode();
|
||||
spOff = Math.max(spOff, spNeed);
|
||||
fcodes.push(code);
|
||||
}
|
||||
// background code
|
||||
if (bgA) {
|
||||
const { width, height } = measureText(text, version);
|
||||
const w = Math.ceil((width * fontSize) / 20);
|
||||
const h = Math.ceil((height * fontSize) / 20);
|
||||
bcodes.push(
|
||||
[
|
||||
// bl 4+sizeof(rect)+sizeof(color)
|
||||
ASM.b(0x18, true),
|
||||
// fill_rect params
|
||||
...getFillRectParams(config, measureText(text, version)),
|
||||
// mflr r3
|
||||
ASM.mflr(3),
|
||||
// addi r4, r3, sizeof(rect)
|
||||
ASM.addi(4, 3, 0x10),
|
||||
].flatMap((e) => e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const addrOrig = addrsOrig[version];
|
||||
const addrSetup2D = addrsSetup2D[version];
|
||||
const addrOrig = addrs.drawWater[version] + addrOrigOff;
|
||||
const addrFillRect = addrs.fillRect[version];
|
||||
|
||||
// program
|
||||
const program = makeProgram(addrDst);
|
||||
// addi r3, r1, 0xE90
|
||||
program.push(ASM.addi(3, 1, 0xe90));
|
||||
// la r3, ctx(r1)
|
||||
// program.push(ASM.addi(3, 1, addrs.ctxSpOff[version]));
|
||||
// addi r1, r1, -spOff
|
||||
if (spOff) program.push(ASM.addi(1, 1, -spOff));
|
||||
// bl setup
|
||||
program.bl(addrSetup2D);
|
||||
// bl J2DGrafContext::setup2D
|
||||
// program.bl(addrs.setup2D[version]);
|
||||
// (fill_rect)
|
||||
for (const code of bcodes) {
|
||||
program.push(code);
|
||||
program.bl(addrFillRect);
|
||||
}
|
||||
// (drawText)
|
||||
for (const code of fcodes) {
|
||||
program.push(code);
|
||||
program.bl(addrDrawText);
|
||||
program.bl(addrs.drawText);
|
||||
}
|
||||
// addi r1, r1, spOff
|
||||
if (spOff) program.push(ASM.addi(1, 1, spOff));
|
||||
|
|
|
@ -2,19 +2,26 @@
|
|||
<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>
|
||||
<button class="textcell-remove" @click="deletionConfirm(i)">×</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 class="btn-ctn">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import labels from './labels.json';
|
||||
import { defaultConfig, getConfig, lskey, format2previewText } from './codegen.js';
|
||||
import configDB from './configDB.js';
|
||||
import { makeUpdateConfig, makeGetLabel } from '../utils.js';
|
||||
import Cell from './Cell.vue';
|
||||
|
||||
/** @typedef {'GMSJ01'|'GMSJ0A'|'GMSE01'|'GMSP01'} GameVersion */
|
||||
export default {
|
||||
components: {
|
||||
Cell,
|
||||
|
@ -23,21 +30,35 @@ export default {
|
|||
version: {type: String},
|
||||
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() {
|
||||
const config = getConfig();
|
||||
const version = /**@type{GameVersion}*/(this.version);
|
||||
const config = getConfig(version);
|
||||
const defaultConfigCell = {
|
||||
text: format2previewText(defaultConfig[0].fmt, this.version),
|
||||
text: format2previewText(defaultConfig[0].fmt, 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);
|
||||
config: makeUpdateConfig(lskey, defaultConfig),
|
||||
},
|
||||
methods: {
|
||||
/** @param {number} i */
|
||||
deletionConfirm(i) {
|
||||
// if (window.confirm(this.l('deletionConfirm'))) {
|
||||
this.config.splice(i, 1);
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -57,4 +78,7 @@ export default {
|
|||
color: red;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-ctn button {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
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: 18,
|
||||
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>`,
|
||||
},
|
||||
rect: {
|
||||
...base,
|
||||
x: 32,
|
||||
y: 48,
|
||||
fontSize: 0,
|
||||
fmt: '',
|
||||
bgRight: 536,
|
||||
bgBot: 384,
|
||||
bgA: 0x7f,
|
||||
},
|
||||
};
|
|
@ -1,8 +1,22 @@
|
|||
{
|
||||
"ja-JP": {
|
||||
"format": "フォーマット:"
|
||||
"format": "フォーマット:",
|
||||
"deletionConfirm": "本当に削除しますか?",
|
||||
"add": {
|
||||
"PAS": "+ 位置/角度/速度表示",
|
||||
"speed": "+ 速度表示",
|
||||
"detailed": "+ 欲張りセット",
|
||||
"rect": "+ 長方形"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,15 +22,13 @@
|
|||
</template>
|
||||
|
||||
<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 {getLabels} from '../codegen.js';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
updateConfig() {
|
||||
localStorage.setItem(lskey, JSON.stringify({button: this.button}));
|
||||
},
|
||||
updateConfig: makeUpdateConfig(lskey, defaultConfig),
|
||||
toggleButton(event, value) {
|
||||
this.button = event.target.checked ?
|
||||
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 = `
|
||||
0424F32C 495A9CD5
|
||||
042069DC 495F26B8
|
||||
`;
|
||||
export const codes = [
|
||||
/* 077F9000 000001AF */ `
|
||||
3D40817F
|
||||
|
||||
export const codeBase = `
|
||||
801F0000 3D00817F
|
||||
61089147 3D40817F
|
||||
614A0024 3D80803D
|
||||
618CA9C0 7C006040
|
||||
41820048 3D80803D
|
||||
|
@ -22,38 +22,25 @@ export const codes = [
|
|||
7CE73B79 4182FFE8
|
||||
7C0838AE 280000FF
|
||||
4182FFDC 7C030378
|
||||
4E800020 38610E90
|
||||
4A83C191 3C808040
|
||||
4E800020 3C808040
|
||||
A4040D82 3C60817F
|
||||
84A30024 70000040
|
||||
85630024 70000040
|
||||
41A20050 88840019
|
||||
548007FE 7CA02850
|
||||
5480FFFE 7CA50214
|
||||
54A507BE 5480F7BF
|
||||
41A20030 28050003
|
||||
41A00008 38A00000
|
||||
548007FE 7D605850
|
||||
5480FFFE 7D6B0214
|
||||
556B07BE 5480F7BF
|
||||
41A20030 280B0003
|
||||
41A00008 39600000
|
||||
5480EFFE 5080077A
|
||||
7D8328AE 7D8C0214
|
||||
7D8358AE 7D8C0214
|
||||
280C0005 41A00008
|
||||
398CFFFB 7D8329AE
|
||||
98A30003 3821FFF0
|
||||
`,
|
||||
/* li r8 */ `
|
||||
3C002020 60002023
|
||||
54AC183E 5C00603E
|
||||
`,
|
||||
/* stb r0, fmtCS0+fmtCSD*i(r8) */ `
|
||||
89230000
|
||||
89430001 89630002
|
||||
91610008
|
||||
`,
|
||||
/* li r3~r7, drawText, b */ `
|
||||
452020FF 213200FF
|
||||
621CFF1D 32005025
|
||||
25252630 01FF4520
|
||||
20213001 FFFF621C
|
||||
1D300151 0707FF08
|
||||
3102FF36 01FF0231
|
||||
021E6E20 FF
|
||||
` /* fmt */,
|
||||
];
|
||||
398CFFFB 7D8359AE
|
||||
99630003 3C002020
|
||||
60002023 556C183E
|
||||
5C00603E 5405063E
|
||||
5407C63E 5409863E
|
||||
88C30000 89030001
|
||||
89430002 3C60817F
|
||||
60639138 38830045
|
||||
4BFF7109 4AA0D8AC
|
||||
`;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
export const addrDraw2D = 0x80125548;
|
||||
export const code04 = `
|
||||
04027900 497D1701
|
||||
04125548 496D3B4C
|
||||
`;
|
||||
|
||||
export const codes = [
|
||||
`
|
||||
3D40817F
|
||||
export const codeBase = `
|
||||
801F0000 3D00817F
|
||||
61089147 3D40817F
|
||||
614A0024 3D80803A
|
||||
618C6D70 7C006040
|
||||
41820048 3D80803A
|
||||
|
@ -23,38 +22,25 @@ export const codes = [
|
|||
7CE73B79 4182FFE8
|
||||
7C0838AE 280000FF
|
||||
4182FFDC 7C030378
|
||||
4E800020 38610E90
|
||||
4AAD1E35 3C80803F
|
||||
4E800020 3C80803F
|
||||
A404545A 3C60817F
|
||||
84A30024 70000040
|
||||
85630024 70000040
|
||||
41A20050 88840019
|
||||
548007FE 7CA02850
|
||||
5480FFFE 7CA50214
|
||||
54A507BE 5480F7BF
|
||||
41A20030 28050003
|
||||
41A00008 38A00000
|
||||
548007FE 7D605850
|
||||
5480FFFE 7D6B0214
|
||||
556B07BE 5480F7BF
|
||||
41A20030 280B0003
|
||||
41A00008 39600000
|
||||
5480EFFE 5080077A
|
||||
7D8328AE 7D8C0214
|
||||
7D8358AE 7D8C0214
|
||||
280C0005 41A00008
|
||||
398CFFFB 7D8329AE
|
||||
98A30003 3821FFF0
|
||||
`,
|
||||
`
|
||||
3C002020 60002023
|
||||
54AC183E 5C00603E
|
||||
`,
|
||||
`
|
||||
89230000
|
||||
89430001 89630002
|
||||
91610008
|
||||
`,
|
||||
`
|
||||
452020FF 213200FF
|
||||
621CFF1D 32005025
|
||||
25252630 01FF4520
|
||||
20213001 FFFF621C
|
||||
1D300151 0707FF08
|
||||
3102FF36 01FF0231
|
||||
021E6E20 FF
|
||||
`,
|
||||
];
|
||||
398CFFFB 7D8359AE
|
||||
99630003 3C002020
|
||||
60002023 556C183E
|
||||
5C00603E 5405063E
|
||||
5407C63E 5409863E
|
||||
88C30000 89030001
|
||||
89430002 3C60817F
|
||||
60639138 38830045
|
||||
4BFF7109 4A92C418
|
||||
`;
|
||||
|
|
|
@ -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,12 +1,15 @@
|
|||
import { parseJSON } from '../codegen.js';
|
||||
import { ASM, liDX, strlen, str2inst, inst2gecko } from '../asm.js';
|
||||
import { ASM, liDX, str2hex, inst2gecko, getFillRectParams } from '../asm.js';
|
||||
import { measureText } from '../text.js';
|
||||
import { int2hex } from '../utils.js';
|
||||
import addrs from '../addrs.js';
|
||||
export const lskey = 'config/PatternSelector';
|
||||
|
||||
import * as GMSJ01 from './code/GMSJ01.js';
|
||||
import * as GMSJ0A from './code/GMSJ0A.js';
|
||||
// import * as GMSE01 from './code/GMSE01.js';
|
||||
// import * as GMSP01 from './code/GMSP01.js';
|
||||
const codes = { GMSJ01, GMSJ0A };
|
||||
import * as GMSP01 from './code/GMSP01.js';
|
||||
const codes = { GMSJ01, GMSJ0A, GMSP01 };
|
||||
|
||||
export const defaultConfig = {
|
||||
x: 16,
|
||||
|
@ -16,94 +19,91 @@ export const defaultConfig = {
|
|||
fgA: 0xff,
|
||||
fgRGB2: null,
|
||||
fgA2: null,
|
||||
bgRGB: 0,
|
||||
bgA: 0,
|
||||
bgLeft: 0,
|
||||
bgRight: 0,
|
||||
bgTop: 0,
|
||||
bgBot: 0,
|
||||
label: 'Pattern ',
|
||||
};
|
||||
|
||||
/** @returns {typeof defaultConfig} */
|
||||
export function getConfig() {
|
||||
const config =
|
||||
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
||||
return { ...defaultConfig, ...config };
|
||||
const o = { ...defaultConfig, ...config };
|
||||
return { ...o, text: getPreviewText(o) };
|
||||
}
|
||||
|
||||
const addrDrawText = 0x817f0238;
|
||||
const addrCodeBase = 0x817f9000;
|
||||
const addrPV1Data1 = 0x817f9167;
|
||||
const addrFmt0 = 0x817f919d;
|
||||
/** @param {typeof defaultConfig} config */
|
||||
export const getPreviewText = ({ label }) => label + '#0 0 0';
|
||||
|
||||
const codePattern = `
|
||||
452020FF 213200FF
|
||||
621CFF1D 32005025
|
||||
25252630 01FF4520
|
||||
20213001 FFFF621C
|
||||
1D300151 0707FF08
|
||||
3102FF36 01FF0231
|
||||
021E6E20 FF
|
||||
`;
|
||||
|
||||
/** @param {keyof typeof codes} version */
|
||||
export default function codegen(version) {
|
||||
const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, label } = getConfig();
|
||||
const config = getConfig();
|
||||
const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, bgA, label } = config;
|
||||
const colorTop = (fgRGB << 8) | fgA;
|
||||
const colorBot = fgRGB2 == null || fgA2 == null ? colorTop : (fgRGB2 << 8) | fgA;
|
||||
const text = label + '>%X>%X>%X';
|
||||
const fmtCS0 = strlen(label);
|
||||
const fmtCSD = 3;
|
||||
const text = label + '%c%X%c%X%c%X';
|
||||
|
||||
const { code04, addrDraw2D, codes: cs0 } = codes[version];
|
||||
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),
|
||||
const { code04, codeBase } = codes[version];
|
||||
const code07 = [
|
||||
codeBase,
|
||||
// drawTextOpt
|
||||
int2hex(x, 2),
|
||||
int2hex(y, 2),
|
||||
int2hex(fontSize, 4),
|
||||
int2hex(colorTop, 4),
|
||||
int2hex(colorBot, 4),
|
||||
// pattern.s
|
||||
codePattern,
|
||||
// fmt
|
||||
str2hex(text, version),
|
||||
]
|
||||
.flatMap((e) => e)
|
||||
.map(inst2gecko)
|
||||
.map((s) => s.replace(/\s+/g, ''))
|
||||
.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';
|
||||
const head07 = [
|
||||
'077F9000',
|
||||
// byte count = hex length >> 1
|
||||
int2hex(code07.length >> 1, 4),
|
||||
].join('');
|
||||
|
||||
return (code04 + head07 + code07).replace(/\s+/g, '');
|
||||
// align code 07 (1 line = 16 hex digits)
|
||||
const tail07 = ''.padEnd(code07.length % 16 ? 16 - (code07.length % 16) : 0, '0');
|
||||
|
||||
// background
|
||||
const addrFillRect = addrs.fillRect[version];
|
||||
const codeBg = bgA
|
||||
? [
|
||||
0xc2000000 + ((addrs.drawWater[version] - 0x28) & 0x01ffffff),
|
||||
0x00000007,
|
||||
0x48000019, // bl trick
|
||||
// rect, color
|
||||
...getFillRectParams(config, measureText(label + '#0 0 0', version)),
|
||||
0x7c6802a6, // mtlr r3
|
||||
0x38830010, // addi r4, r3, 0x10
|
||||
0x3d800000 | (addrFillRect >>> 16), // lis r12, fill_rect@h
|
||||
0x618c0000 | (addrFillRect & 0xffff), // ori r12, r12, fill_rect@l
|
||||
0x7d8803a6, // mtlr r12
|
||||
0x4e800021, // blrl
|
||||
0x60000000, // nop
|
||||
0x00000000, // End of C2
|
||||
]
|
||||
.map(inst2gecko)
|
||||
.join('')
|
||||
: '';
|
||||
|
||||
return (code04 + head07 + code07 + tail07 + codeBg).replace(/\s+/g, '');
|
||||
}
|
||||
|
|
|
@ -11,19 +11,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {getConfig, lskey} from './codegen.js';
|
||||
import {getConfig, defaultConfig, lskey, getPreviewText} from './codegen.js';
|
||||
import labels from './labels.json';
|
||||
import TextConfig from '../TextConfig.vue';
|
||||
import { makeUpdateConfig } from '../utils.js';
|
||||
|
||||
function updateConfig() {
|
||||
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);
|
||||
}
|
||||
|
||||
const updateConfig = makeUpdateConfig(lskey, defaultConfig, getPreviewText);
|
||||
export default {
|
||||
components: {
|
||||
TextConfig,
|
||||
|
|
|
@ -15,6 +15,23 @@
|
|||
<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>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
|
@ -41,6 +58,7 @@ export default {
|
|||
},
|
||||
...Object.fromEntries([
|
||||
'x', 'y', 'fontSize', 'fgRGB', 'fgA', 'fgRGB2', 'fgA2',
|
||||
'bgRGB', 'bgA', 'bgLeft', 'bgRight', 'bgTop', 'bgBot',
|
||||
].map(k => [k, makeField(k)])),
|
||||
},
|
||||
methods: {
|
||||
|
@ -72,7 +90,7 @@ input[type=number], td.right {
|
|||
text-align: right;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 3em;
|
||||
width: 2em;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.appearance > div {
|
||||
|
|
28
site/.vuepress/components/codes/addrs.js
Normal file
28
site/.vuepress/components/codes/addrs.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
export default {
|
||||
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,
|
||||
},
|
||||
// r1 offset of J2DGrafContext in TGCConsole2::perform()
|
||||
ctxSpOff: {
|
||||
GMSJ01: 0xe90,
|
||||
GMSJ0A: 0xbec,
|
||||
GMSE01: 0xbd0,
|
||||
GMSP01: 0xbe4,
|
||||
},
|
||||
};
|
|
@ -173,16 +173,39 @@ export function liDX(rT, D) {
|
|||
}
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
export function strlen(s) {
|
||||
const fmtbuf = Encoding.convert(Encoding.stringToCode(s), 'SJIS');
|
||||
return fmtbuf.length; // not NUL terminated
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {string} version
|
||||
*/
|
||||
export function str2bytes(s, version) {
|
||||
const enc = version.startsWith('GMSJ') ? 'SJIS' : '';
|
||||
const fmtbuf = version.startsWith('GMSJ')
|
||||
? Encoding.convert(Encoding.stringToCode(s), 'SJIS') // Shift-JIS
|
||||
: Array.from(s, (c) => {
|
||||
// latin1
|
||||
const x = c.charCodeAt(0);
|
||||
// replace the char with space if it is multi-byte
|
||||
return x >= 0x100 ? 0x20 : x;
|
||||
});
|
||||
fmtbuf.push(0); // NUL terminated
|
||||
return fmtbuf;
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
export function str2inst(s) {
|
||||
const fmtbuf = Encoding.convert(Encoding.stringToCode(s), 'SJIS');
|
||||
fmtbuf.push(0); // NUL terminated
|
||||
/**
|
||||
* @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 fmtlen3 = fmtlen & 3;
|
||||
const pad = fmtlen3 ? 4 - fmtlen3 : 0;
|
||||
|
@ -223,3 +246,30 @@ export function makeProgram(pc) {
|
|||
|
||||
/** @param {number} x */
|
||||
export const inst2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
|
||||
|
||||
/**
|
||||
* @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,
|
||||
];
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
"fgColorGrad": "グラデーション",
|
||||
"fgColor1": "文字色(上):",
|
||||
"fgColor2": "文字色(下):",
|
||||
"alpha": "不透明度="
|
||||
"alpha": "不透明度=",
|
||||
"bgColor": "背景色:",
|
||||
"bgOffset": "背景位置:",
|
||||
"left": "左",
|
||||
"right": "右",
|
||||
"top": "上",
|
||||
"bottom": "下"
|
||||
},
|
||||
"en-US": {
|
||||
"location": "Location: ",
|
||||
|
@ -15,6 +21,22 @@
|
|||
"fgColorGrad": "Gradient",
|
||||
"fgColor1": "Font color(Top): ",
|
||||
"fgColor2": "Font color(Bottom): ",
|
||||
"alpha": "Alpha="
|
||||
"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 = "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { parseJSON } from '../codegen.js';
|
||||
import { getFillRectParams } from '../asm';
|
||||
import { int2hex } from '../utils';
|
||||
export const lskey = 'config/qft';
|
||||
|
||||
export const getPreviewText = () => '0:00.000';
|
||||
|
||||
export const defaultConfig = {
|
||||
x: 16,
|
||||
y: 456,
|
||||
width: 112,
|
||||
fontSize: 20,
|
||||
fgRGB: 0xffffff,
|
||||
fgA: 0xff,
|
||||
|
@ -12,6 +15,10 @@ export const defaultConfig = {
|
|||
fgA2: null,
|
||||
bgRGB: 0x000000,
|
||||
bgA: 0x80,
|
||||
bgLeft: 0,
|
||||
bgRight: 2,
|
||||
bgTop: 2,
|
||||
bgBot: 0,
|
||||
freezeDuration: 30,
|
||||
freeze: {
|
||||
yellowCoin: false,
|
||||
|
@ -35,6 +42,7 @@ export const defaultConfig = {
|
|||
},
|
||||
};
|
||||
|
||||
/** @returns {typeof defaultConfig} */
|
||||
export function getConfig() {
|
||||
const config =
|
||||
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
||||
|
@ -45,6 +53,7 @@ export function getConfig() {
|
|||
...defaultConfig.freeze,
|
||||
...config.freeze,
|
||||
},
|
||||
text: getPreviewText(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -57,8 +66,10 @@ import * as GMSP01 from './code/GMSP01.js';
|
|||
import * as GMSJ0A from './code/GMSJ0A.js';
|
||||
export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
|
||||
|
||||
import statusDB from './code/status.js';
|
||||
export const statusKeys = Object.keys(statusDB);
|
||||
import { measureText } from '../text.js';
|
||||
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
|
||||
|
@ -100,7 +111,7 @@ export default function codegen(version, baseCode) {
|
|||
const hook = freezeCodeHooks[key];
|
||||
if (hook) {
|
||||
if (key === 'blueCoin') {
|
||||
const addr = hook;
|
||||
const addr = /**@type{number}*/ (hook);
|
||||
// special: needs to adjust QF -> use separate C2 instead
|
||||
code += [
|
||||
0xc2000000 + (addr & 0x1ffffff),
|
||||
|
@ -234,25 +245,24 @@ export default function codegen(version, baseCode) {
|
|||
|
||||
// ui
|
||||
/* 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;
|
||||
code += '077F0094 0000001D';
|
||||
code += [
|
||||
x, // x1
|
||||
y - fontSize - 2, // y1
|
||||
x + width * scale, // x2
|
||||
y, // y2
|
||||
]
|
||||
.map(int2gecko)
|
||||
.join('');
|
||||
code += rect.map(int2gecko).join('');
|
||||
code += '25753a253032752e2530337500000000'; // fmt
|
||||
/* fontSize, fgColor, bgColor */
|
||||
code += '077F0110 00000010';
|
||||
const bgColor = (config.bgRGB & 0xffffff) * 256 + (config.bgA & 0xff);
|
||||
/**
|
||||
* 817F0110 drawTextOpt: {x, y, fontSize, colorTop, colorBot}
|
||||
* 817F0120 bgColor
|
||||
*/
|
||||
code += '077F0110 00000014';
|
||||
const fgColor = (config.fgRGB & 0xffffff) * 256 + (config.fgA & 0xff);
|
||||
const fgColor2 =
|
||||
((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 += '00000000';
|
||||
|
||||
return code.replace(/\s/g, '');
|
||||
}
|
||||
|
|
|
@ -2,17 +2,7 @@
|
|||
<div id="config">
|
||||
<section class="appearance">
|
||||
<h3>{{ l.h3 }}</h3>
|
||||
<div>
|
||||
<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>
|
||||
<TextConfig v-model="textConfig" />
|
||||
</section>
|
||||
<section class="freeze">
|
||||
<h3>{{ l.freeze.h3 }}</h3>
|
||||
|
@ -35,31 +25,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { getConfig, lskey, codes, statusKeys } from './codegen.js';
|
||||
import { rgbI2S, rgbS2I, rgbaI2S } from '../utils';
|
||||
import { defaultConfig, lskey, getConfig, getPreviewText, codes, statusKeys } from './codegen.js';
|
||||
import { makeUpdateConfig, 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;
|
||||
const config = {
|
||||
x,
|
||||
y,
|
||||
fontSize,
|
||||
width,
|
||||
fgRGB,
|
||||
fgA,
|
||||
fgRGB2,
|
||||
fgA2,
|
||||
bgRGB,
|
||||
bgA,
|
||||
freeze,
|
||||
freezeDuration,
|
||||
};
|
||||
localStorage.setItem(lskey, JSON.stringify(config));
|
||||
this.$emit('config', config);
|
||||
}
|
||||
|
||||
const updateConfig = makeUpdateConfig(lskey, defaultConfig, getPreviewText);
|
||||
export default {
|
||||
components: {
|
||||
TextConfig,
|
||||
|
@ -87,21 +58,9 @@ export default {
|
|||
rgbaI2S,
|
||||
},
|
||||
data() {
|
||||
const { x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration } =
|
||||
getConfig();
|
||||
const config = getConfig();
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
fontSize,
|
||||
fgRGB,
|
||||
fgA,
|
||||
fgRGB2,
|
||||
fgA2,
|
||||
width,
|
||||
bgRGB,
|
||||
bgA,
|
||||
freeze,
|
||||
freezeDuration,
|
||||
...config,
|
||||
// const
|
||||
freezeKeys: [
|
||||
...Object.keys(codes[this.version]?.freezeCodeHooks ?? {}),
|
||||
|
@ -115,8 +74,7 @@ export default {
|
|||
},
|
||||
textConfig: {
|
||||
get() {
|
||||
const { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2 } = this;
|
||||
return { x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2 };
|
||||
return this;
|
||||
},
|
||||
set(value) {
|
||||
Object.assign(this, value);
|
||||
|
@ -126,8 +84,6 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
width: updateConfig,
|
||||
bgRGB: updateConfig,
|
||||
bgA: updateConfig,
|
||||
freezeDuration: updateConfig,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
{
|
||||
"ja-JP": {
|
||||
"h3": "見た目",
|
||||
"location": "位置:",
|
||||
"fontSize": "文字サイズ:",
|
||||
"fgColor": "文字色:",
|
||||
"fgColorGrad": "グラデーション",
|
||||
"fgColor1": "文字色(上):",
|
||||
"fgColor2": "文字色(下):",
|
||||
"bgColor": "背景色:",
|
||||
"alpha": "不透明度=",
|
||||
"preview": "プレビュー",
|
||||
"previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です",
|
||||
"freeze": {
|
||||
|
@ -40,14 +32,6 @@
|
|||
},
|
||||
"en-US": {
|
||||
"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",
|
||||
"previewNote": "※ x ranges from 0 to 600, and y ranges from 16 to 464.",
|
||||
"freeze": {
|
||||
|
@ -79,14 +63,6 @@
|
|||
},
|
||||
"fr-FR": {
|
||||
"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",
|
||||
"previewNote": "※ x doit être entre 0 et 600, et y entre 16 et 464.",
|
||||
"freeze": {
|
||||
|
|
57
site/.vuepress/components/codes/text.js
Normal file
57
site/.vuepress/components/codes/text.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
/** @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
|
||||
multibyte: true,
|
||||
}
|
||||
: {
|
||||
// EU (TODO US)
|
||||
charInfo: /**@type{Record<string, CharInfo>}*/ (charInfoEU),
|
||||
rowSize: 16, // how many char in a row of the img
|
||||
multibyte: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {string} version
|
||||
*/
|
||||
export function measureText(text, version) {
|
||||
const { charInfo, rowSize, multibyte } = 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] ?? (multibyte && c.charCodeAt(0) >= 0x80 ? charInfo['?'] : 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 */
|
||||
export const rgbI2S = (rgb) => '#' + rgb.toString(16).padStart(6, '0');
|
||||
/** @param {string} s */
|
||||
|
@ -9,7 +44,18 @@ export const rgbS2I = (s) => parseInt(s.slice(1), 16);
|
|||
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)})`;
|
||||
};
|
||||
/** @type {(labels: Record<string, any>, locale: string, fallbackLocale?: string) => (key: string) => string} */
|
||||
export const makeGetLabel =
|
||||
(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;
|
||||
};
|
||||
|
|
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