qft freezes rework + yoshi mount freeze

This commit is contained in:
QbeRoot 2023-01-07 03:13:37 +01:00
parent 9dd72475f1
commit d50a806a85
9 changed files with 116 additions and 98 deletions

View file

@ -1234,10 +1234,10 @@
<code>
<id>qft</id>
<category>timer</category>
<title lang="en-US">Quarterframe Timer (Experimental)</title>
<title lang="en-US">Quarterframe Timer</title>
<title lang="ja-JP">QFタイマー</title>
<author>Noki Doki, sup39(サポミク)</author>
<version>1.3</version>
<version>1.4</version>
<date>Dec 16, 2022</date>
<dependencies version="GMSJ01">drawText</dependencies>
<dependencies version="GMSE01">drawText</dependencies>

View file

@ -1,4 +1,7 @@
# Changelog
## Jan 7, 2023
### Updated 'Quarterframe Timer'
Reworked the existing freezes and added the option to freeze when mounting Yoshi
## Dec 16, 2022
### Updated 'Quarterframe Timer'

View file

@ -1,15 +1,16 @@
export const r13off = -0x6048;
/**
* @type {{[key: string]: {addr: number, orig: number}}}
* @type {{[key: string]: number}}
*/
export const freezeCodeInfo = {
yellowCoin: { addr: 0x801becb4, orig: 0x8805000e },
redCoin: { addr: 0x801be474, orig: 0x38a00000 },
blueCoin: { addr: 0x801be288, orig: 0x7c030378 },
item: { addr: 0x801bf3b4, orig: 0x8001001c },
talk: { addr: 0x80298128, orig: 0x807f00b0 },
demo: { addr: 0x802981a4, orig: 0x88e7013c },
cleaned: { addr: 0x80215c58, orig: 0x80010044 },
bowser: { addr: 0x801fc0a4, orig: 0x2c1d0003 },
export const freezeCodeHooks = {
yellowCoin: 0x801bee10,
redCoin: 0x801be524,
blueCoin: 0x801be32c,
item: 0x801bf3c4,
talk: 0x80153a34,
demo: 0x80032bd8,
cleaned: 0x80215c6c,
bowser: 0x801fb7ac,
yoshi: 0x802704d4,
};

View file

@ -1,15 +1,16 @@
export const r13off = -0x6818;
/**
* @type {{[key: string]: {addr: number, orig: number}}}
* @type {{[key: string]: number}}
*/
export const freezeCodeInfo = {
yellowCoin: { addr: 0x80196b54, orig: 0x8805000e },
redCoin: { addr: 0x80196314, orig: 0x38a00000 },
blueCoin: { addr: 0x80196128, orig: 0x7c030378 },
item: { addr: 0x801971f8, orig: 0x8001001c },
talk: { addr: 0x800eb6e4, orig: 0x807f00b0 },
demo: { addr: 0x800eb760, orig: 0x88e7013c },
cleaned: { addr: 0x8017a3c0, orig: 0x80010044 },
bowser: { addr: 0x801d3c78, orig: 0x2c1d0003 },
export const freezeCodeHooks = {
yellowCoin: 0x80196cb0,
redCoin: 0x801963c4,
blueCoin: 0x801961cc,
item: 0x80197208,
talk: 0x80214f00,
demo: 0x80362434,
cleaned: 0x8017a3d4,
bowser: 0x801d3380,
yoshi: 0x8014f830,
};

View file

@ -1,15 +1,16 @@
export const r13off = -0x6188;
/**
* @type {{[key: string]: {addr: number, orig: number}}}
* @type {{[key: string]: number}}
*/
export const freezeCodeInfo = {
yellowCoin: { addr: 0x8019ea3c, orig: 0x8805000e },
redCoin: { addr: 0x8019e1fc, orig: 0x38a00000 },
blueCoin: { addr: 0x8019e010, orig: 0x7c030378 },
item: { addr: 0x8019f13c, orig: 0x8001001c },
talk: { addr: 0x80277dd0, orig: 0x807f00b0 },
demo: { addr: 0x80277e4c, orig: 0x88e7013c },
cleaned: { addr: 0x801f5af8, orig: 0x80010044 },
bowser: { addr: 0x801dbe48, orig: 0x2c1d0003 },
export const freezeCodeHooks = {
yellowCoin: 0x8019eb98,
redCoin: 0x8019e2ac,
blueCoin: 0x8019e0b4,
item: 0x8019f14c,
talk: 0x80134e58,
demo: 0x80364ae0,
cleaned: 0x801f5b0c,
bowser: 0x801db550,
yoshi: 0x80250224,
};

View file

@ -1,15 +1,16 @@
export const r13off = -0x6120;
/**
* @type {{[key: string]: {addr: number, orig: number}}}
* @type {{[key: string]: number}}}
*/
export const freezeCodeInfo = {
yellowCoin: { addr: 0x801b6b6c, orig: 0x8805000e },
redCoin: { addr: 0x801b632c, orig: 0x38a00000 },
blueCoin: { addr: 0x801b6140, orig: 0x7c030378 },
item: { addr: 0x801b726c, orig: 0x8001001c },
talk: { addr: 0x8028ffc0, orig: 0x807f00b0 },
demo: { addr: 0x8029003c, orig: 0x88e7013c },
cleaned: { addr: 0x8020db3c, orig: 0x80010044 },
bowser: { addr: 0x801f3f88, orig: 0x2c1d0003 },
export const freezeCodeHooks = {
yellowCoin: 0x801b6cc8,
redCoin: 0x801b63dc,
blueCoin: 0x801b61e4,
item: 0x801b727c,
talk: 0x801489b4,
demo: 0x80032c90,
cleaned: 0x8020db50,
bowser: 0x801f3690,
yoshi: 0x80268260,
};

View file

@ -39,7 +39,7 @@ export function getConfig() {
}
/** @param {number} x */
const inst2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
const int2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
import * as GMSJ01 from './code/GMSJ01.js';
import * as GMSE01 from './code/GMSE01.js';
@ -49,22 +49,17 @@ export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
/****
## save freeze frame, load and save QF
## this function destroys r11(freeze frame), r12
## this function destroys r11 and r12
077F0348:
lwz r11, gpMarDirector-_SDA_BASE_(r13)
lis r12, 0x817F
stw r11, 0xBC(r12)
lwz r11, -0x6818(r13)
lwz r11, 0x5C(r11)
stw r11, 0xB8(r12)
li r11, freezeDuration
stw r11, 0xBC(r12)
blr
## for each code
ORIG
li r11, xxxx
b 817f0348
04xxxxxx:
bl 817fxxxx
## for each hook (over a blr): b 817f0348
****/
const freezeCodeAddr = 0x817f0348;
/**
@ -75,24 +70,22 @@ export default function codegen(version, baseCode) {
if (!baseCode) return '';
const config = getConfig();
const { freezeCodeInfo, r13off } = codes[version] ?? {};
const { freezeCodeHooks, r13off } = codes[version] ?? {};
let code = baseCode;
const { freezeDuration: frame } = config;
// freezing code
const freezeEnableds = [];
const enabledFreezes = [];
if (frame > 0) {
for (const [key, enabled] of Object.entries(config.freeze)) {
const info = freezeCodeInfo[key];
if (enabled && info) {
const { addr, orig } = info;
const addr = freezeCodeHooks[key];
if (enabled && addr) {
if (key === 'blueCoin') {
// special: needs to adjust QF -> use separate C2 instead
code += [
0xc2000000 + (addr & 0x1ffffff),
0x00000005,
orig,
0x00000004,
0x80a3005c,
0x38a50003,
0x54a0003a,
@ -100,39 +93,45 @@ export default function codegen(version, baseCode) {
0x900500b8,
0x38000000 | (frame & 0xffff),
0x900500bc,
0x60000000,
0x00000000,
]
.map(inst2gecko)
.map(int2gecko)
.join('');
} else {
// handle regular freezing code later
freezeEnableds.push(info);
enabledFreezes.push(addr);
}
}
}
}
// handle regular freezing code
if (freezeEnableds.length <= 1) {
if (enabledFreezes.length <= 1) {
// use C2 directly
code += freezeEnableds
.flatMap(({ addr, orig }) => [
code += enabledFreezes
.flatMap((addr) => [
0xc2000000 + (addr & 0x1ffffff),
0x00000004,
orig,
0x816d0000 | (r13off & 0xffff), // lwz r11, r13off(r13)
0x3d80817f, // lis r12, 0x817F
0x816b005c, // lwz r11, 0x5C(r11)
0x916c00b8, // stw r11, 0xB8(r12)
0x39600000 | (frame & 0xffff), // li r11, frame
0x916c00bc, // stw r11, 0xBC(r12)
0x60000000, // nop
0x00000000,
])
.map(inst2gecko)
.map(int2gecko)
.join('');
} else {
const code04 = [];
const code07 = [
// could be shorter to turn this into a Gecko loop if enough freezes are enabled
const hooks = enabledFreezes.flatMap((addr) => [
0xc6000000 | (addr & 0x1ffffff),
freezeCodeAddr,
]);
const freezer = [
0x06000000 | (freezeCodeAddr & 0x1ffffff),
0x0000001c,
0x816d0000 | (r13off & 0xffff), // lwz r11, r13off(r13)
0x3d80817f, // lis r12, 0x817F
0x816b005c, // lwz r11, 0x5C(r11)
@ -140,31 +139,11 @@ export default function codegen(version, baseCode) {
0x39600000 | (frame & 0xffff), // li r11, frame
0x916c00bc, // stw r11, 0xBC(r12)
0x4e800020, // blr
0x00000000,
];
let dst = freezeCodeAddr + code07.length * 4;
// put code together
for (const { addr, orig } of freezeEnableds) {
code07.push(
orig, // [dst] original instruction
0x4c000000 + (freezeCodeAddr - dst - 4), // b freezeCode
);
code04.push(
0x04000000 | (addr & 0x1ffffff), // 04 addr
0x48000001 | (dst - addr), // bl dst
);
dst += 8;
}
// make 07 code
code07.unshift(
0x06000000 | (freezeCodeAddr & 0x1ffffff), // 07 freezeCodeAddr
code07.length * 4,
);
if (code07.length & 1) {
// odd => pad with 0
code07.push(0);
}
// apply code
code += [...code04, ...code07].map(inst2gecko).join('');
code += [...hooks, ...freezer].map(int2gecko).join('');
}
// ui
@ -178,7 +157,7 @@ export default function codegen(version, baseCode) {
x + width * scale, // x2
y, // y2
]
.map(inst2gecko)
.map(int2gecko)
.join('');
code += '25753a253032752e2530337500000000'; // fmt
/* fontSize, fgColor, bgColor */
@ -187,7 +166,7 @@ export default function codegen(version, baseCode) {
const fgColor = (config.fgRGB & 0xffffff) * 256 + (config.fgA & 0xff);
const fgColor2 =
((config.fgRGB2 ?? config.fgRGB) & 0xffffff) * 256 + ((config.fgA2 ?? config.fgA) & 0xff);
code += [fontSize, fgColor, fgColor2, bgColor].map(inst2gecko).join('');
code += [fontSize, fgColor, fgColor2, bgColor].map(int2gecko).join('');
return code.replace(/\s/g, '');
}

View file

@ -104,7 +104,7 @@ export default {
freeze,
freezeDuration,
// const
freezeKeys: Object.keys(codes[this.version]?.freezeCodeInfo ?? {}),
freezeKeys: Object.keys(codes[this.version]?.freezeCodeHooks ?? {}),
};
},
computed: {

View file

@ -24,7 +24,8 @@
"talk": "会話開始時",
"demo": "カットシーン開始時",
"cleaned": "NPCを洗った時",
"bowser": "クッパ戦の足場を破壊した時"
"bowser": "クッパ戦の足場を破壊した時",
"yoshi": "ヨッシーに乗った時"
}
}
},
@ -53,7 +54,38 @@
"talk": "When dialogue starts",
"demo": "When a cutscene starts",
"cleaned": "When an NPC is cleaned",
"bowser": "When a platform is destroyed in the Bowser fight"
"bowser": "When a platform is destroyed in the Bowser fight",
"yoshi": "When Yoshi is mounted"
}
}
},
"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": {
"h3": "Temps intermédiaires",
"duration": "Durée d'affichage : ",
"frame": "(frames)",
"sec": "(sec)",
"rows": {
"yellowCoin": "Quand une pièce jaune est ramassée",
"redCoin": "Quand une pièce rouge est ramassée",
"blueCoin": "Quand une pièce bleue est ramassée",
"item": "Quand un power-up (buse, etc.) est ramassé",
"talk": "Quand un dialogue commence",
"demo": "Quand une cutscene commence",
"cleaned": "Quand un PNJ est nettoyé",
"bowser": "Quand une plate-forme est détruite dans le combat de Bowser",
"yoshi": "Quand Yoshi est monté"
}
}
}