diff --git a/changelog.md b/changelog.md index 7314389..10f9223 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,10 @@ # Changelog +## Jan 10, 2023 +### Updated 'Quarterframe Timer' +Added the following options to freeze QFT: +- When Mario holds, throws, puts down an object +- When Mario triple jumps, spin jumps, ledge grabs, wall kicks, bounces, rope jumps + ## Jan 7, 2023 ### Updated 'Quarterframe Timer' Reworked the existing freezes and added the option to freeze when mounting Yoshi diff --git a/site/.vuepress/components/codes/qft/code/GMSE01.js b/site/.vuepress/components/codes/qft/code/GMSE01.js index 21042a3..d140c0f 100644 --- a/site/.vuepress/components/codes/qft/code/GMSE01.js +++ b/site/.vuepress/components/codes/qft/code/GMSE01.js @@ -1,7 +1,8 @@ export const r13off = -0x6048; +export const onChangeStatusAddr = 0x802541c8; /** - * @type {{[key: string]: number}} + * @type {{[key: string]: number|{addr: number, orig: number}}} */ export const freezeCodeHooks = { yellowCoin: 0x801bee10, @@ -13,4 +14,6 @@ export const freezeCodeHooks = { cleaned: 0x80215c6c, bowser: 0x801fb7ac, yoshi: 0x802704d4, + take: { addr: 0x8023f9a8, orig: 0x801f0384 }, + drop: { addr: 0x802437d4, orig: 0x38000000 }, }; diff --git a/site/.vuepress/components/codes/qft/code/GMSJ01.js b/site/.vuepress/components/codes/qft/code/GMSJ01.js index 2b42a7e..d06901e 100644 --- a/site/.vuepress/components/codes/qft/code/GMSJ01.js +++ b/site/.vuepress/components/codes/qft/code/GMSJ01.js @@ -1,7 +1,8 @@ export const r13off = -0x6818; +export const onChangeStatusAddr = 0x801335b8; /** - * @type {{[key: string]: number}} + * @type {{[key: string]: number|{addr: number, orig: number}}} */ export const freezeCodeHooks = { yellowCoin: 0x80196cb0, @@ -13,4 +14,6 @@ export const freezeCodeHooks = { cleaned: 0x8017a3d4, bowser: 0x801d3380, yoshi: 0x8014f830, + take: { addr: 0x8011eae4, orig: 0x801f0384 }, + drop: { addr: 0x80122964, orig: 0x38000000 }, }; diff --git a/site/.vuepress/components/codes/qft/code/GMSJ0A.js b/site/.vuepress/components/codes/qft/code/GMSJ0A.js index 52b44f4..e9f1cdb 100644 --- a/site/.vuepress/components/codes/qft/code/GMSJ0A.js +++ b/site/.vuepress/components/codes/qft/code/GMSJ0A.js @@ -1,7 +1,8 @@ export const r13off = -0x6188; +export const onChangeStatusAddr = 0x80233f18; /** - * @type {{[key: string]: number}} + * @type {{[key: string]: number|{addr: number, orig: number}}} */ export const freezeCodeHooks = { yellowCoin: 0x8019eb98, @@ -13,4 +14,6 @@ export const freezeCodeHooks = { cleaned: 0x801f5b0c, bowser: 0x801db550, yoshi: 0x80250224, + take: { addr: 0x8021f6f0, orig: 0x801f0384 }, + drop: { addr: 0x8022351c, orig: 0x38000000 }, }; diff --git a/site/.vuepress/components/codes/qft/code/GMSP01.js b/site/.vuepress/components/codes/qft/code/GMSP01.js index 3234e18..d4550cd 100644 --- a/site/.vuepress/components/codes/qft/code/GMSP01.js +++ b/site/.vuepress/components/codes/qft/code/GMSP01.js @@ -1,7 +1,8 @@ export const r13off = -0x6120; +export const onChangeStatusAddr = 0x8024bf54; /** - * @type {{[key: string]: number}}} + * @type {{[key: string]: number|{addr: number, orig: number}}} */ export const freezeCodeHooks = { yellowCoin: 0x801b6cc8, @@ -13,4 +14,6 @@ export const freezeCodeHooks = { cleaned: 0x8020db50, bowser: 0x801f3690, yoshi: 0x80268260, + take: { addr: 0x80237734, orig: 0x801f0384 }, + drop: { addr: 0x8023b560, orig: 0x38000000 }, }; diff --git a/site/.vuepress/components/codes/qft/code/status.js b/site/.vuepress/components/codes/qft/code/status.js new file mode 100644 index 0000000..99bb01d --- /dev/null +++ b/site/.vuepress/components/codes/qft/code/status.js @@ -0,0 +1,9 @@ +export default { + put: [0x80000387], + tripleJump: [0x882], + spinJump: [0x895, 0x896], + ledgeGrab: [0x3800034b], + wallKick: [0x2000886], + bounce: [0x884], + ropeJump: [0x892, 0x893], +}; diff --git a/site/.vuepress/components/codes/qft/codegen.js b/site/.vuepress/components/codes/qft/codegen.js index 08671a4..88a286f 100644 --- a/site/.vuepress/components/codes/qft/codegen.js +++ b/site/.vuepress/components/codes/qft/codegen.js @@ -23,6 +23,15 @@ export const defaultConfig = { cleaned: true, bowser: true, // onBathhubGripDestroyed yoshi: true, + take: true, + drop: true, + put: true, + tripleJump: true, + spinJump: true, + ledgeGrab: true, + wallKick: true, + ropeJump: true, + bounce: true, }, }; @@ -48,6 +57,9 @@ 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); + /**** ## save freeze frame, load and save QF ## this function destroys r11 and r12 @@ -71,18 +83,24 @@ export default function codegen(version, baseCode) { if (!baseCode) return ''; const config = getConfig(); - const { freezeCodeHooks, r13off } = codes[version] ?? {}; + const { freezeCodeHooks, r13off, onChangeStatusAddr } = codes[version] ?? {}; let code = baseCode; const { freezeDuration: frame } = config; // freezing code const enabledFreezes = []; + const statuses = []; if (frame > 0) { for (const [key, enabled] of Object.entries(config.freeze)) { - const addr = freezeCodeHooks[key]; - if (enabled && addr) { + if (!enabled) continue; + // add status + statuses.push(...(statusDB[key] ?? [])); + // add hook + const hook = freezeCodeHooks[key]; + if (hook) { if (key === 'blueCoin') { + const addr = hook; // special: needs to adjust QF -> use separate C2 instead code += [ 0xc2000000 + (addr & 0x1ffffff), @@ -100,15 +118,32 @@ export default function codegen(version, baseCode) { ] .map(int2gecko) .join(''); - } else { + } else if (typeof hook === 'number') { // handle regular freezing code later + const addr = hook; enabledFreezes.push(addr); + } else { + // {addr: number, orig: number} + // separate C2 code to handle orig + const { addr, orig } = hook; + code += [ + 0xc2000000 + (addr & 0x1ffffff), + 0x00000003, + 0x3d800000 + (freezeCodeAddr >>> 16), // lis r12, freezeCodeAddr@h + 0x618c0000 + (freezeCodeAddr & 0xffff), // ori r12, r12, freezeCodeAddr@l + 0x7d8803a6, // mtlr r12 + 0x4e800021, // blrl + orig, + 0x00000000, + ] + .map(int2gecko) + .join(''); } } } } // handle regular freezing code - if (enabledFreezes.length <= 1) { + if (enabledFreezes.length <= 1 && statuses.length === 0) { // use C2 directly code += enabledFreezes .flatMap((addr) => [ @@ -149,6 +184,54 @@ export default function codegen(version, baseCode) { code += [...hooks, ...freezer].map(int2gecko).join(''); } + // onChangeStatus hook + if (statuses.length) { + const c = [ + // check each status + ...statuses.flatMap((x, i) => { + const cr = i > 0 ? 0x800000 : 0; // i>0 ? cr1 : cr0 + const c = + x < 0x10000 + ? [ + 0x281d0000 + cr + x, // cmplwi crX, r29, $x + ] + : [ + 0x3c000000 + (x >>> 16), // lis r0, $x@h + 0x60000000 + (x & 0xffff), // ori r0, r0, $x@l + 0x7c1d0040 + cr, // cmplw crX, r29, r0 + ]; + if (i > 0) { + // cror 4*cr0+eq, 4*cr0+eq, 4*cr1+eq + c.push(0x4c423382); + } + return c; + }), + // freeze + 0x3d800000 + (freezeCodeAddr >>> 16), // lis r12, freezeCodeAddr@h + 0x618c0000 + (freezeCodeAddr & 0xffff), // ori r12, r12, freezeCodeAddr@l + 0x7d8803a6, // mtlr r12 + 0x4d820021, // beqlrl + // orig + 0x38000000, // li r0, 0 + ]; + + // pad nop + if (c.length % 2 === 0) { + c.push(0x60000000); + } + // end of C2 + c.push(0x00000000); + + // apply code + code += [ + 0xc2000000 + (onChangeStatusAddr & 0x1ffffff), + c.length >> 1, // line count + ...c, + ] + .map(int2gecko) + .join(''); + } + // ui /* bounds */ const { x, y, fontSize, width } = config; diff --git a/site/.vuepress/components/codes/qft/config.vue b/site/.vuepress/components/codes/qft/config.vue index 2edf1e5..cc0437c 100644 --- a/site/.vuepress/components/codes/qft/config.vue +++ b/site/.vuepress/components/codes/qft/config.vue @@ -35,14 +35,13 @@