Simplify QFT config

This commit is contained in:
sup39 2022-04-22 19:23:15 +09:00
parent 912a8b067c
commit df449cc97c
9 changed files with 99 additions and 95 deletions

View file

@ -51,8 +51,8 @@ Some codes store some states in the games memory starting from address 0x817F000
| ![](./docs/buffer.svg) | `0x10D` | `0x10F` | Buffer (Ingame Timer) | | ![](./docs/buffer.svg) | `0x10D` | `0x10F` | Buffer (Ingame Timer) |
| ![](./docs/reserved.svg) | `0x110` | `0x237` | QF Timer: Timer Textbox | | ![](./docs/reserved.svg) | `0x110` | `0x237` | QF Timer: Timer Textbox |
| ![](./docs/reserved.svg) | `0x238` | `0x347` | General Function (`drawText`) | | ![](./docs/reserved.svg) | `0x238` | `0x347` | General Function (`drawText`) |
| ![](./docs/buffer.svg) | `0x348` | `0x3BF` | Buffer (QF Timer) | | ![](./docs/buffer.svg) | `0x348` | `0x39B` | Buffer (QF Timer) |
| ![](./docs/unallocated.svg) | `0x3C0` | `0xFFF` | Not Allocated | | ![](./docs/unallocated.svg) | `0x39C` | `0xFFF` | Not Allocated |
### Adding translations ### Adding translations

View file

@ -1,9 +1,11 @@
# Changelog # Changelog
## Apr 22, 2022 ## Apr 22, 2022
### Shortened QFT ### Shortened QFT and simplified config
- Shortened QFT's freeze code by replacing C2 with 04(bl@event) and 07. - Shorten QFT's freeze code by replacing C2 with 04(bl@event) and 07.
- Reserve `817F0348`~`817F03BF` (120 bytes) - Reserve `817F0348`~`817F039B` (84 bytes)
- Change freezing config from [duration for each event] to [one duration + toggle for each event]
## Apr 21, 2022
### Reduced code size of Pattern Selector ### Reduced code size of Pattern Selector
Reduce 240 bytes of PS by replacing switch case with lookup table. Reduce 240 bytes of PS by replacing switch case with lookup table.

View file

@ -1,11 +1,11 @@
export const r13off = -0x6048; export const r13off = -0x6048;
/** /**
* @type {{[key: string]: [addr: number, orig: number]}} * @type {{[key: string]: {addr: number, orig: number}}}
*/ */
export const freezeCodeInfo = { export const freezeCodeInfo = {
redCoin: [0x801be474, 0x38a00000], redCoin: { addr: 0x801be474, orig: 0x38a00000 },
blueCoin: [0x801be288, 0x7c030378], // TODO QF+3 &0xfffffffc blueCoin: { addr: 0x801be288, orig: 0x7c030378 },
}; };
export const baseCode = ` export const baseCode = `

View file

@ -1,17 +1,17 @@
export const r13off = -0x6818; export const r13off = -0x6818;
/** /**
* @type {{[key: string]: [addr: number, orig: number]}} * @type {{[key: string]: {addr: number, orig: number}}}
*/ */
export const freezeCodeInfo = { export const freezeCodeInfo = {
yellowCoin: [0x80196b54, 0x8805000e], yellowCoin: { addr: 0x80196b54, orig: 0x8805000e },
redCoin: [0x80196314, 0x38a00000], redCoin: { addr: 0x80196314, orig: 0x38a00000 },
blueCoin: [0x80196128, 0x7c030378], // TODO QF+3 &0xfffffffc blueCoin: { addr: 0x80196128, orig: 0x7c030378 },
item: [0x801971f8, 0x8001001c], item: { addr: 0x801971f8, orig: 0x8001001c },
talk: [0x800eb6e4, 0x807f00b0], talk: { addr: 0x800eb6e4, orig: 0x807f00b0 },
demo: [0x800eb74c, 0x806da8b0], demo: { addr: 0x800eb74c, orig: 0x806da8b0 },
cleaned: [0x8017a3c0, 0x80010044], cleaned: { addr: 0x8017a3c0, orig: 0x80010044 },
bowser: [0x801d3c78, 0x2c1d0003], bowser: { addr: 0x801d3c78, orig: 0x2c1d0003 },
}; };
export const baseCode = ` export const baseCode = `

View file

@ -1,11 +1,11 @@
export const r13off = -0x6188; export const r13off = -0x6188;
/** /**
* @type {{[key: string]: [addr: number, orig: number]}} * @type {{[key: string]: {addr: number, orig: number}}}
*/ */
export const freezeCodeInfo = { export const freezeCodeInfo = {
redCoin: [0x8019e1fc, 0x38a00000], redCoin: { addr: 0x8019e1fc, orig: 0x38a00000 },
blueCoin: [0x8019e010, 0x7c030378], // TODO QF+3 &0xfffffffc blueCoin: { addr: 0x8019e010, orig: 0x7c030378 },
}; };
export const baseCode = ` export const baseCode = `

View file

@ -1,11 +1,11 @@
export const r13off = -0x6120; export const r13off = -0x6120;
/** /**
* @type {{[key: string]: [addr: number, orig: number]}} * @type {{[key: string]: {addr: number, orig: number}}}
*/ */
export const freezeCodeInfo = { export const freezeCodeInfo = {
redCoin: [0x801b632c, 0x38a00000], redCoin: { addr: 0x801b632c, orig: 0x38a00000 },
blueCoin: [0x801b6140, 0x7c030378], // TODO QF+3 &0xfffffffc blueCoin: { addr: 0x801b6140, orig: 0x7c030378 },
}; };
export const baseCode = ` export const baseCode = `

View file

@ -12,15 +12,16 @@ export const defaultConfig = {
fgA2: null, fgA2: null,
bgRGB: 0x000000, bgRGB: 0x000000,
bgA: 0x80, bgA: 0x80,
freezeDuration: 30,
freeze: { freeze: {
yellowCoin: 0, yellowCoin: false,
redCoin: 30, redCoin: true,
blueCoin: 30, blueCoin: true,
item: 30, item: true,
talk: 30, talk: true,
demo: 30, demo: true,
cleaned: 30, cleaned: true,
bowser: 30, // onBathhubGripDestroyed bowser: true, // onBathhubGripDestroyed
}, },
}; };
@ -36,8 +37,10 @@ export function getConfig() {
}; };
} }
const int16 = (x) => (x < 0 ? x + 0x10000 : x).toString(16).padStart(4, '0').slice(-4); const int16 = (x) =>
const int32 = (x) => (x < 0 ? x + 0x100000000 : x).toString(16).padStart(8, '0').slice(-8); (x < 0 ? x + 0x10000 : x).toString(16).padStart(4, '0').slice(-4).toUpperCase();
const int32 = (x) =>
(x < 0 ? x + 0x100000000 : x).toString(16).padStart(8, '0').slice(-8).toUpperCase();
import * as GMSJ01 from './code/GMSJ01.js'; import * as GMSJ01 from './code/GMSJ01.js';
import * as GMSE01 from './code/GMSE01.js'; import * as GMSE01 from './code/GMSE01.js';
@ -71,78 +74,81 @@ export default function codegen(version) {
if (baseCode == null) return ''; if (baseCode == null) return '';
let code = baseCode; let code = baseCode;
const { freezeDuration: frame } = config;
// freezing code // freezing code
const freezeConfigs = []; const freezeEnableds = [];
for (const [key, frame] of Object.entries(config.freeze)) { if (frame > 0) {
const info = freezeCodeInfo[key]; for (const [key, enabled] of Object.entries(config.freeze)) {
if (frame > 0 && info) { const info = freezeCodeInfo[key];
const [addr, orig] = info; if (enabled && info) {
if (key === 'blueCoin') { const { addr, orig } = info;
// special: needs to adjust QF -> use separate C2 instead if (key === 'blueCoin') {
code += [ // special: needs to adjust QF -> use separate C2 instead
0xc2000000 + (addr & 0x1ffffff), code += [
0x00000005, 0xc2000000 + (addr & 0x1ffffff),
orig, 0x00000005,
0x80a3005c, orig,
0x38a50003, 0x80a3005c,
0x54a0003a, 0x38a50003,
0x3ca0817f, 0x54a0003a,
0x900500b8, 0x3ca0817f,
0x38000000 | (frame & 0xffff), 0x900500b8,
0x900500bc, 0x38000000 | (frame & 0xffff),
0x60000000, 0x900500bc,
0x00000000, 0x60000000,
] 0x00000000,
.map(int32) ]
.join(''); .map(int32)
} else { .join('');
// handle regular freezing code later } else {
freezeConfigs.push({ frame, addr, orig }); // handle regular freezing code later
freezeEnableds.push(info);
}
} }
} }
} }
// handle regular freezing code // handle regular freezing code
if (freezeConfigs.length <= 1) { if (freezeEnableds.length <= 1) {
// use C2 directly // use C2 directly
code += freezeConfigs code += freezeEnableds
.flatMap(({ frame, addr, orig }) => [ .flatMap(({ addr, orig }) => [
0xc2000000 + (addr & 0x1ffffff), 0xc2000000 + (addr & 0x1ffffff),
0x00000004, 0x00000004,
0x39600000 | (frame & 0xffff), // li r11, frame orig,
0x3d80817f, // lis r12, 0x817F
0x916c00bc, // stw r11, 0xBC(r12)
0x816d0000 | (r13off & 0xffff), // lwz r11, r13off(r13) 0x816d0000 | (r13off & 0xffff), // lwz r11, r13off(r13)
0x3d80817f, // lis r12, 0x817F
0x816b005c, // lwz r11, 0x5C(r11) 0x816b005c, // lwz r11, 0x5C(r11)
0x916c00b8, // stw r11, 0xB8(r12) 0x916c00b8, // stw r11, 0xB8(r12)
orig, 0x39600000 | (frame & 0xffff), // li r11, frame
0x916c00bc, // stw r11, 0xBC(r12)
0x00000000, 0x00000000,
]) ])
.map(int32) .map(int32)
.join(''); .join('');
} else { } else {
let dst = freezeCodeAddr + 24;
const code04 = []; const code04 = [];
const code07 = [ const code07 = [
0x3d80817f, // lis r12, 0x817F
0x916c00bc, // stw r11, 0xBC(r12)
0x816d0000 | (r13off & 0xffff), // lwz r11, r13off(r13) 0x816d0000 | (r13off & 0xffff), // lwz r11, r13off(r13)
0x3d80817f, // lis r12, 0x817F
0x816b005c, // lwz r11, 0x5C(r11) 0x816b005c, // lwz r11, 0x5C(r11)
0x916c00b8, // stw r11, 0xB8(r12) 0x916c00b8, // stw r11, 0xB8(r12)
0x39600000 | (frame & 0xffff), // li r11, frame
0x916c00bc, // stw r11, 0xBC(r12)
0x4e800020, // blr 0x4e800020, // blr
]; ];
let dst = freezeCodeAddr + code07.length * 4;
// put code together // put code together
for (const { frame, addr, orig } of freezeConfigs) { for (const { addr, orig } of freezeEnableds) {
code07.push( code07.push(
orig, // [dst] original instruction orig, // [dst] original instruction
0x39600000 | (frame & 0xffff), // li r11, $frame 0x4c000000 + (freezeCodeAddr - dst - 4), // b freezeCode
0x4c000000 + (freezeCodeAddr - dst - 8), // b freezeCode
); );
code04.push( code04.push(
0x04000000 | (addr & 0x1ffffff), // 04 addr 0x04000000 | (addr & 0x1ffffff), // 04 addr
0x48000001 | (dst - addr), // bl dst 0x48000001 | (dst - addr), // bl dst
); );
dst += 12; dst += 8;
} }
// make 07 code // make 07 code
code07.unshift( code07.unshift(

View file

@ -41,15 +41,15 @@
</section> </section>
<section class="freeze"> <section class="freeze">
<h3>{{l.freeze.h3}}</h3> <h3>{{l.freeze.h3}}</h3>
<div>
{{l.freeze.duration}}<input type="number" min="0" max="32767" v-model="freezeDuration"> {{l.freeze.frame}}
= {{(freezeDuration*1001/30000).toFixed(2)}} {{l.freeze.sec}}
</div>
<table> <table>
<thead>
<th v-for="s in l.freeze.th" :key="s">{{s}}</th>
</thead>
<tbody> <tbody>
<tr v-for="key in freezeKeys" :key="key"> <tr v-for="key in freezeKeys" :key="key">
<td>{{l.freeze.rows[key]}}</td> <td>{{l.freeze.rows[key]}}</td>
<td><input type="number" :value="freeze[key]" @change="onChangeFreeze($event, key)"></td> <td><input type="checkbox" :checked="freeze[key]" @change="onChangeFreeze($event, key)"></td>
<td class="right">{{(freeze[key]*1001/30000).toFixed(2)}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -63,9 +63,9 @@ import labels from './labels.json';
import {getLabels} from '../codegen.js'; import {getLabels} from '../codegen.js';
function updateConfig() { function updateConfig() {
const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze} = this; const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = this;
localStorage.setItem(lskey, JSON.stringify({ localStorage.setItem(lskey, JSON.stringify({
x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration
})); }));
} }
@ -76,7 +76,7 @@ export default {
methods: { methods: {
updateConfig, updateConfig,
onChangeFreeze($event, key) { onChangeFreeze($event, key) {
this.freeze[key] = parseInt($event.target.value); this.freeze[key] = $event.target.checked;
this.updateConfig(); this.updateConfig();
}, },
toggleGradient($event) { toggleGradient($event) {
@ -93,11 +93,11 @@ export default {
rgbaI2S: (rgb, a) => '#'+rgb.toString(16).padStart(6, '0')+a.toString(16).padStart(2, '0'), rgbaI2S: (rgb, a) => '#'+rgb.toString(16).padStart(6, '0')+a.toString(16).padStart(2, '0'),
}, },
data() { data() {
const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze} = getConfig(); const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = getConfig();
return { return {
x, y, fontSize, width, x, y, fontSize, width,
fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA,
freeze, freeze, freezeDuration,
// const // const
freezeKeys: Object.keys(codes[this.version]?.freezeCodeInfo ?? {}), freezeKeys: Object.keys(codes[this.version]?.freezeCodeInfo ?? {}),
}; };
@ -118,6 +118,7 @@ export default {
fgA2: updateConfig, fgA2: updateConfig,
bgRGB: updateConfig, bgRGB: updateConfig,
bgA: updateConfig, bgA: updateConfig,
freezeDuration: updateConfig,
}, },
} }
</script> </script>
@ -126,7 +127,7 @@ export default {
input[type=number], td.right { input[type=number], td.right {
text-align: right; text-align: right;
} }
.appearance input[type="number"] { input[type="number"] {
width: 3em; width: 3em;
margin: 0 2px; margin: 0 2px;
} }
@ -134,15 +135,6 @@ input[type=number], td.right {
padding: 0 0 4px; padding: 0 0 4px;
} }
th {
text-align: center;
}
td > input[type=number] {
width: 8em;
max-width: 100%;
box-sizing: border-box;
}
input[type=number] { input[type=number] {
-moz-appearance: textfield; -moz-appearance: textfield;
} }

View file

@ -13,7 +13,9 @@
"previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です\n※ ゲーム内のフォントはプレビューのより幅が広いです", "previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です\n※ ゲーム内のフォントはプレビューのより幅が広いです",
"freeze": { "freeze": {
"h3": "一時停止", "h3": "一時停止",
"th": ["タイミング", "停止フレーム数", "停止秒数"], "duration": "長さ:",
"frame": "(フレーム)",
"sec": "(秒)",
"rows": { "rows": {
"yellowCoin": "黄コインを取った時", "yellowCoin": "黄コインを取った時",
"redCoin": "赤コインを取った時", "redCoin": "赤コインを取った時",
@ -40,7 +42,9 @@
"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.\n※ The font is wider in the game compared to the preview.",
"freeze": { "freeze": {
"h3": "Freezing the timer", "h3": "Freezing the timer",
"th": ["Timing", "Duration(frame)", "Duration(sec)"], "duration": "Duration: ",
"frame": "(frame)",
"sec": "(sec)",
"rows": { "rows": {
"yellowCoin": "When collected a yellow coin", "yellowCoin": "When collected a yellow coin",
"redCoin": "When collected a red coin", "redCoin": "When collected a red coin",