From d50a806a85028319fa743fe61683b99e353cf69b Mon Sep 17 00:00:00 2001 From: QbeRoot Date: Sat, 7 Jan 2023 03:13:37 +0100 Subject: [PATCH] qft freezes rework + yoshi mount freeze --- Codes.xml | 4 +- changelog.md | 3 + .../components/codes/qft/code/GMSE01.js | 21 ++--- .../components/codes/qft/code/GMSJ01.js | 21 ++--- .../components/codes/qft/code/GMSJ0A.js | 21 ++--- .../components/codes/qft/code/GMSP01.js | 21 ++--- .../.vuepress/components/codes/qft/codegen.js | 85 +++++++------------ .../.vuepress/components/codes/qft/config.vue | 2 +- .../components/codes/qft/labels.json | 36 +++++++- 9 files changed, 116 insertions(+), 98 deletions(-) diff --git a/Codes.xml b/Codes.xml index 3ba2ce8..0a323a9 100644 --- a/Codes.xml +++ b/Codes.xml @@ -1234,10 +1234,10 @@ qft timer - Quarterframe Timer (Experimental) + Quarterframe Timer QFタイマー Noki Doki, sup39(サポミク) - 1.3 + 1.4 Dec 16, 2022 drawText drawText diff --git a/changelog.md b/changelog.md index 9f90d61..7314389 100644 --- a/changelog.md +++ b/changelog.md @@ -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' diff --git a/site/.vuepress/components/codes/qft/code/GMSE01.js b/site/.vuepress/components/codes/qft/code/GMSE01.js index 0f2fc53..a0f48a8 100644 --- a/site/.vuepress/components/codes/qft/code/GMSE01.js +++ b/site/.vuepress/components/codes/qft/code/GMSE01.js @@ -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, }; diff --git a/site/.vuepress/components/codes/qft/code/GMSJ01.js b/site/.vuepress/components/codes/qft/code/GMSJ01.js index 861c986..975a752 100644 --- a/site/.vuepress/components/codes/qft/code/GMSJ01.js +++ b/site/.vuepress/components/codes/qft/code/GMSJ01.js @@ -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, }; diff --git a/site/.vuepress/components/codes/qft/code/GMSJ0A.js b/site/.vuepress/components/codes/qft/code/GMSJ0A.js index acb5ead..ba77c58 100644 --- a/site/.vuepress/components/codes/qft/code/GMSJ0A.js +++ b/site/.vuepress/components/codes/qft/code/GMSJ0A.js @@ -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, }; diff --git a/site/.vuepress/components/codes/qft/code/GMSP01.js b/site/.vuepress/components/codes/qft/code/GMSP01.js index f92f273..8fdfefa 100644 --- a/site/.vuepress/components/codes/qft/code/GMSP01.js +++ b/site/.vuepress/components/codes/qft/code/GMSP01.js @@ -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, }; diff --git a/site/.vuepress/components/codes/qft/codegen.js b/site/.vuepress/components/codes/qft/codegen.js index a726af6..025ed11 100644 --- a/site/.vuepress/components/codes/qft/codegen.js +++ b/site/.vuepress/components/codes/qft/codegen.js @@ -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, ''); } diff --git a/site/.vuepress/components/codes/qft/config.vue b/site/.vuepress/components/codes/qft/config.vue index d825c40..2edf1e5 100644 --- a/site/.vuepress/components/codes/qft/config.vue +++ b/site/.vuepress/components/codes/qft/config.vue @@ -104,7 +104,7 @@ export default { freeze, freezeDuration, // const - freezeKeys: Object.keys(codes[this.version]?.freezeCodeInfo ?? {}), + freezeKeys: Object.keys(codes[this.version]?.freezeCodeHooks ?? {}), }; }, computed: { diff --git a/site/.vuepress/components/codes/qft/labels.json b/site/.vuepress/components/codes/qft/labels.json index 891528e..ef57431 100644 --- a/site/.vuepress/components/codes/qft/labels.json +++ b/site/.vuepress/components/codes/qft/labels.json @@ -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é" } } }