From 493b408890f682ca4bd3ad028dd39dbeb887bbdf Mon Sep 17 00:00:00 2001 From: Luke Street Date: Thu, 5 Mar 2026 10:10:37 -0700 Subject: [PATCH] Rework to use content collections --- package.json | 1 + pnpm-lock.yaml | 39 ++++++ src/components/ProjectCard.astro | 170 ++++++++++++++++-------- src/content/config.ts | 27 ++++ src/content/projects/aurora.md | 12 ++ src/content/projects/decomp-dev.md | 23 ++++ src/content/projects/decomp-toolkit.md | 15 +++ src/content/projects/ghidra-panel.md | 22 ++++ src/content/projects/metaforce.md | 15 +++ src/content/projects/nod.md | 15 +++ src/content/projects/objdiff.md | 23 ++++ src/content/projects/powerpc-rs.md | 15 +++ src/content/projects/wibo.md | 15 +++ src/data/projects.ts | 176 ------------------------- src/layouts/Layout.astro | 19 ++- src/pages/404.astro | 7 +- src/pages/index.astro | 27 ++-- src/styles/global.css | 4 +- 18 files changed, 374 insertions(+), 251 deletions(-) create mode 100644 src/content/config.ts create mode 100644 src/content/projects/aurora.md create mode 100644 src/content/projects/decomp-dev.md create mode 100644 src/content/projects/decomp-toolkit.md create mode 100644 src/content/projects/ghidra-panel.md create mode 100644 src/content/projects/metaforce.md create mode 100644 src/content/projects/nod.md create mode 100644 src/content/projects/objdiff.md create mode 100644 src/content/projects/powerpc-rs.md create mode 100644 src/content/projects/wibo.md delete mode 100644 src/data/projects.ts diff --git a/package.json b/package.json index 4e45069..f406dbf 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "astro": "^5.7.0", + "marked": "^17.0.4", "sharp": "^0.34.5" }, "pnpm": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de6d189..5182892 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: astro: specifier: ^5.7.0 version: 5.18.0(rollup@4.59.0)(typescript@5.9.3) + marked: + specifier: ^17.0.4 + version: 17.0.4 sharp: specifier: ^0.34.5 version: 0.34.5 @@ -400,89 +403,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -556,66 +575,79 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -1057,6 +1089,11 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@17.0.4: + resolution: {integrity: sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==} + engines: {node: '>= 20'} + hasBin: true + mdast-util-definitions@6.0.0: resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} @@ -2614,6 +2651,8 @@ snapshots: markdown-table@3.0.4: {} + marked@17.0.4: {} + mdast-util-definitions@6.0.0: dependencies: '@types/mdast': 4.0.4 diff --git a/src/components/ProjectCard.astro b/src/components/ProjectCard.astro index 917c9ff..55547b2 100644 --- a/src/components/ProjectCard.astro +++ b/src/components/ProjectCard.astro @@ -1,54 +1,66 @@ --- -import { Image } from 'astro:assets'; -import type { Project } from '../data/projects'; +import { Image } from "astro:assets"; +import type { CollectionEntry } from "astro:content"; +import { marked } from "marked"; interface Props { - project: Project; + project: CollectionEntry<"projects">; } const { project } = Astro.props; +const { Content } = await project.render(); + +// Render meta as markdown inline +const metaHtml = project.data.meta + ? marked.parseInline(project.data.meta) + : null; ---
+

{project.data.name}

+
- {project.langs.map((lang) => ( - - - {lang.name} - - ))} + { + project.data.langs.map((lang) => ( + + + {lang.name} + + )) + } Star + aria-label={`Star ${project.data.repo} on GitHub`}>Star
-

{project.name}

-

{project.desc}

+

{project.data.desc}

- +
+ +
- {project.meta && ( -

- )} + {metaHtml &&

} - {project.images && ( -

- {project.images.map((img) => ( - - {img.alt} - - ))} -
- )} + { + project.data.images && ( +
+ {project.data.images.map((img) => ( + + {img.alt} + + ))} +
+ ) + }
diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..0591d99 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,27 @@ +import { defineCollection, z } from 'astro:content'; + +const projects = defineCollection({ + type: 'content', + schema: ({ image }) => z.object({ + order: z.number(), + name: z.string(), + url: z.string().url(), + repo: z.string(), + desc: z.string(), + langs: z.array(z.object({ + name: z.string(), + color: z.string(), + })), + meta: z.string().optional(), + images: z.array(z.object({ + src: image(), + alt: z.string(), + href: z.string(), + rel: z.string().optional(), + })).optional(), + }), +}); + +export const collections = { + projects, +}; diff --git a/src/content/projects/aurora.md b/src/content/projects/aurora.md new file mode 100644 index 0000000..fb55386 --- /dev/null +++ b/src/content/projects/aurora.md @@ -0,0 +1,12 @@ +--- +order: 8 +name: 'aurora' +url: 'https://github.com/encounter/aurora' +repo: 'encounter/aurora' +desc: 'Source-level GameCube & Wii compatibility layer for decompilation projects' +langs: + - name: 'C++' + color: '#f34b7d' +--- + +Aurora reimplements the GX API, translating the calls to native graphics backends like Vulkan, Metal, D3D12, and WebGPU. diff --git a/src/content/projects/decomp-dev.md b/src/content/projects/decomp-dev.md new file mode 100644 index 0000000..a81ddcc --- /dev/null +++ b/src/content/projects/decomp-dev.md @@ -0,0 +1,23 @@ +--- +order: 2 +name: 'decomp.dev' +url: 'https://decomp.dev' +repo: 'encounter/decomp.dev' +desc: 'Progress hub for decompilation projects' +langs: + - name: 'Rust' + color: '#dea584' +images: + - src: '../../assets/decomp.dev-home.png' + alt: 'decomp.dev' + href: 'https://decomp.dev' + rel: 'noopener' + - src: '../../assets/decomp.dev-prime.png' + alt: 'Metroid Prime on decomp.dev' + href: 'https://decomp.dev/PrimeDecomp/prime' + rel: 'noopener' +--- + +`decomp.dev` tracks progress for more than 80 decompilation projects. With data driven by `objdiff`'s progress reports, it provides granular information down to individual translation units and functions, plus a tree view for exploring project structure. Projects can categorize units, track multiple game versions, and navigate full commit-by-commit history. + +All of this information is also exposed through its [API](https://decomp.dev/api), along with a badge system for embedding live progress in project `README`s. diff --git a/src/content/projects/decomp-toolkit.md b/src/content/projects/decomp-toolkit.md new file mode 100644 index 0000000..1574d04 --- /dev/null +++ b/src/content/projects/decomp-toolkit.md @@ -0,0 +1,15 @@ +--- +order: 4 +name: 'decomp-toolkit' +url: 'https://github.com/encounter/decomp-toolkit' +repo: 'encounter/decomp-toolkit' +desc: 'PowerPC static analyzer, binary delinker, and GameCube/Wii swiss army knife' +langs: + - name: 'Rust' + color: '#dea584' +meta: 'Used by more than 50 decompilation projects, including several now-complete ones like [The Legend of Zelda: Twilight Princess](https://github.com/zeldaret/tp) and [Mario Party 4](https://github.com/mariopartyrd/marioparty4).' +--- + +`decomp-toolkit` takes a compiled binary and produces fully relinkable objects. Its analyzer handles function and object boundary detection, signature analysis, and rebuilding relocations, all with minimal configuration. [dtk-template](https://github.com/encounter/dtk-template) is a project template and build system built on top of decomp-toolkit. + +Its approach to matching decompilation has since been applied to other platforms, including [ds-decomp](https://github.com/AetiasHax/ds-decomp) for Nintendo DS and [jeff](https://github.com/rjkiv/jeff) for Xbox 360. diff --git a/src/content/projects/ghidra-panel.md b/src/content/projects/ghidra-panel.md new file mode 100644 index 0000000..ebec0fa --- /dev/null +++ b/src/content/projects/ghidra-panel.md @@ -0,0 +1,22 @@ +--- +order: 7 +name: 'ghidra-panel' +url: 'https://github.com/encounter/ghidra-panel' +repo: 'encounter/ghidra-panel' +desc: 'Self-service portal for managing access to shared Ghidra repositories' +langs: + - name: 'Go' + color: '#00add8' + - name: 'Java' + color: '#b07219' +meta: 'Powers the collaboration infrastructure on [decomp.dev](https://ghidra.decomp.dev).' +images: + - src: '../../assets/ghidra-panel-home.png' + alt: 'ghidra-panel home' + href: 'https://media.githubusercontent.com/media/encounter/ghidra-panel/main/.github/img/home.png' + - src: '../../assets/ghidra-panel-repo.png' + alt: 'ghidra-panel repository view' + href: 'https://media.githubusercontent.com/media/encounter/ghidra-panel/main/.github/img/repo.png' +--- + +`ghidra-panel` integrates with Ghidra Server through an in-process gRPC plugin, allowing repository administrators to manage users and permissions. Users authenticate with Discord and can request repository access, sending a notification with a one-click approval link for admins. diff --git a/src/content/projects/metaforce.md b/src/content/projects/metaforce.md new file mode 100644 index 0000000..b301ea9 --- /dev/null +++ b/src/content/projects/metaforce.md @@ -0,0 +1,15 @@ +--- +order: 9 +name: 'metaforce' +url: 'https://github.com/AxioDL/metaforce' +repo: 'AxioDL/metaforce' +desc: 'Native reimplementation of the Metroid Prime engine' +langs: + - name: 'C++' + color: '#f34b7d' +meta: 'Currently on hiatus as the [matching decompilation of Metroid Prime](https://github.com/PrimeDecomp/prime) progresses.' +--- + +Metaforce (formerly URDE) started as a passion project reimplementing parts of the Metroid Prime engine, and eventually transformed into a nearly-complete non-matching decompilation. + +[Project website](https://axiodl.com) diff --git a/src/content/projects/nod.md b/src/content/projects/nod.md new file mode 100644 index 0000000..354c2e8 --- /dev/null +++ b/src/content/projects/nod.md @@ -0,0 +1,15 @@ +--- +order: 5 +name: 'nod' +url: 'https://github.com/encounter/nod' +repo: 'encounter/nod' +desc: 'GameCube and Wii disc image library and CLI tool' +langs: + - name: 'Rust' + color: '#dea584' +meta: 'Used by [TinyWiiBackupManager](https://github.com/mq1/TinyWiiBackupManager).' +--- + +`nod` supports reading and converting all GameCube and Wii disc image formats, with a simple and performant API. Open any disc image and get a `Read + Seek + BufRead` handle. Converting to ISO is just reading from that handle and writing to a file, regardless of the source format. Open a partition and get the same interface, transparently handling Wii encryption and hashing. + +Reading and writing are multithreaded, and `nod` produces smaller disc images faster than both Dolphin and NKit v2. C bindings are available for FFI. diff --git a/src/content/projects/objdiff.md b/src/content/projects/objdiff.md new file mode 100644 index 0000000..03b034f --- /dev/null +++ b/src/content/projects/objdiff.md @@ -0,0 +1,23 @@ +--- +order: 1 +name: 'objdiff' +url: 'https://github.com/encounter/objdiff' +repo: 'encounter/objdiff' +desc: 'Diffing tool for decompilation projects' +langs: + - name: 'Rust' + color: '#dea584' + - name: 'TypeScript' + color: '#3178c6' +images: + - src: '../../assets/screen-symbols.png' + alt: 'Symbol Screenshot' + href: 'https://raw.githubusercontent.com/encounter/objdiff/refs/heads/main/assets/screen-symbols.png' + - src: '../../assets/screen-diff.png' + alt: 'Diff Screenshot' + href: 'https://raw.githubusercontent.com/encounter/objdiff/refs/heads/main/assets/screen-diff.png' +--- + +`objdiff` compares object files across functions and data. It supports ARM, ARM64, MIPS, PowerPC, SuperH, and x86(-64). It provides a GUI, TUI, and JSON output for integration with other tools and agentic workflows. + +The core diffing engine compiles to WASM and runs as a [web frontend](https://github.com/encounter/objdiff-web) in [decomp.me](https://decomp.me) and as a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=decomp-dev.objdiff). Its progress reporting system powers [decomp.dev](https://decomp.dev). diff --git a/src/content/projects/powerpc-rs.md b/src/content/projects/powerpc-rs.md new file mode 100644 index 0000000..455c1b8 --- /dev/null +++ b/src/content/projects/powerpc-rs.md @@ -0,0 +1,15 @@ +--- +order: 6 +name: 'powerpc-rs' +url: 'https://github.com/encounter/powerpc-rs' +repo: 'encounter/powerpc-rs' +desc: 'PowerPC disassembler and assembler' +langs: + - name: 'Rust' + color: '#dea584' +meta: 'Used as the PowerPC backend for objdiff and decomp-toolkit.' +--- + +`powerpc-rs` is driven by a declarative instruction set definition that is compiled into Rust at build time, similar to LLVM's TableGen. It supports the full PowerPC instruction set along with Gekko/Broadway paired singles (GameCube/Wii) and Xenon VMX128 (Xbox 360) extensions. + +The disassembler has been fuzzed over all 4.29 billion possible 32-bit instruction values and runs at ~275M instructions per second. diff --git a/src/content/projects/wibo.md b/src/content/projects/wibo.md new file mode 100644 index 0000000..94e5542 --- /dev/null +++ b/src/content/projects/wibo.md @@ -0,0 +1,15 @@ +--- +order: 3 +name: 'wibo' +url: 'https://github.com/decompals/wibo' +repo: 'decompals/wibo' +desc: 'Lightweight Win32 binary loader for Linux and macOS' +langs: + - name: 'C++' + color: '#f34b7d' +meta: 'Used by [decomp.me](https://decomp.me) to run containerized Windows compilers.' +--- + +`wibo` runs 32-bit Windows command-line tools with minimal overhead. It implements a substantial portion of the Win32 API: file I/O, threading, heap management, DLL loading, TLS, and async I/O with platform-specific backends (`io_uring`, `epoll`, `kqueue`). + +On 64-bit hosts, it constrains the guest address space to the lower 2GB and bridges calling conventions with generated trampolines. Runs on macOS under Rosetta 2. diff --git a/src/data/projects.ts b/src/data/projects.ts deleted file mode 100644 index 494ebe7..0000000 --- a/src/data/projects.ts +++ /dev/null @@ -1,176 +0,0 @@ -import type { ImageMetadata } from 'astro'; -import decompDevHome from '../assets/decomp.dev-home.png'; -import decompDevPrime from '../assets/decomp.dev-prime.png'; -import screenSymbols from '../assets/screen-symbols.png'; -import screenDiff from '../assets/screen-diff.png'; -import ghidraPanelHome from '../assets/ghidra-panel-home.png'; -import ghidraPanelRepo from '../assets/ghidra-panel-repo.png'; - -export interface ProjectImage { - src: ImageMetadata; - alt: string; - href: string; - rel?: string; -} - -export interface Project { - name: string; - url: string; - repo: string; - desc: string; - langs: { name: string; color: string }[]; - body: string; - meta?: string; - images?: ProjectImage[]; -} - -const LANG = { - Rust: { name: 'Rust', color: '#dea584' }, - TypeScript: { name: 'TypeScript', color: '#3178c6' }, - Cpp: { name: 'C++', color: '#f34b7d' }, - Go: { name: 'Go', color: '#00add8' }, - Java: { name: 'Java', color: '#b07219' }, -} as const; - -export const projects: Project[] = [ - { - name: 'objdiff', - url: 'https://github.com/encounter/objdiff', - repo: 'encounter/objdiff', - desc: 'Diffing tool for decompilation projects', - langs: [LANG.Rust, LANG.TypeScript], - body: ` -

objdiff compares object files across functions and data. It supports ARM, ARM64, MIPS, PowerPC, SuperH, and x86(-64). It provides a GUI, TUI, and JSON output for integration with other tools and agentic workflows.

-

The core diffing engine compiles to WASM and runs as a web frontend in decomp.me and as a VS Code extension. Its progress reporting system powers decomp.dev.

- `, - images: [ - { - src: screenSymbols, - alt: 'Symbol Screenshot', - href: 'https://raw.githubusercontent.com/encounter/objdiff/refs/heads/main/assets/screen-symbols.png', - }, - { - src: screenDiff, - alt: 'Diff Screenshot', - href: 'https://raw.githubusercontent.com/encounter/objdiff/refs/heads/main/assets/screen-diff.png', - }, - ], - }, - { - name: 'decomp.dev', - url: 'https://decomp.dev', - repo: 'encounter/decomp.dev', - desc: 'Progress hub for decompilation projects', - langs: [LANG.Rust], - body: ` -

decomp.dev tracks progress for more than 80 decompilation projects. With data driven by objdiff's progress reports, it provides granular information down to individual translation units and functions, plus a tree view for exploring project structure. Projects can categorize units, track multiple game versions, and navigate full commit-by-commit history.

-

All of this information is also exposed through its API, along with a badge system for embedding live progress in project READMEs.

- `, - images: [ - { - src: decompDevHome, - alt: 'decomp.dev', - href: 'https://decomp.dev', - rel: 'noopener', - }, - { - src: decompDevPrime, - alt: 'Metroid Prime on decomp.dev', - href: 'https://decomp.dev/PrimeDecomp/prime', - rel: 'noopener', - }, - ], - }, - { - name: 'wibo', - url: 'https://github.com/decompals/wibo', - repo: 'decompals/wibo', - desc: 'Lightweight Win32 binary loader for Linux and macOS', - langs: [LANG.Cpp], - body: ` -

wibo runs 32-bit Windows command-line tools with minimal overhead. It implements a substantial portion of the Win32 API: file I/O, threading, heap management, DLL loading, TLS, and async I/O with platform-specific backends (io_uring, epoll, kqueue). On 64-bit hosts, it constrains the guest address space to the lower 2GB and bridges calling conventions with generated trampolines. Runs on macOS under Rosetta 2.

- `, - meta: 'Used by decomp.me to run Windows compilers in containers.', - }, - { - name: 'decomp-toolkit', - url: 'https://github.com/encounter/decomp-toolkit', - repo: 'encounter/decomp-toolkit', - desc: 'PowerPC static analyzer, binary delinker, and GameCube/Wii swiss army knife', - langs: [LANG.Rust], - body: ` -

decomp-toolkit takes a compiled binary and produces fully relinkable objects. Its analyzer handles function and object boundary detection, signature analysis, and rebuilding relocations, all with minimal configuration. dtk-template is a project template and build system built on top of decomp-toolkit.

-

Its approach to matching decompilation has since been applied to other platforms, including ds-decomp for Nintendo DS and jeff for Xbox 360.

- `, - meta: 'Used by more than 50 decompilation projects, including several now-complete ones like The Legend of Zelda: Twilight Princess and Mario Party 4.', - }, - { - name: 'nod', - url: 'https://github.com/encounter/nod', - repo: 'encounter/nod', - desc: 'GameCube and Wii disc image library and CLI tool', - langs: [LANG.Rust], - body: ` -

nod supports reading and converting all GameCube and Wii disc image formats, with a simple and performant API. Open any disc image and get a Read + Seek + BufRead handle. Converting to ISO is just reading from that handle and writing to a file, regardless of the source format. Open a partition and get the same interface, transparently handling Wii encryption and hashing.

-

Reading and writing are multithreaded, and nod produces smaller disc images faster than both Dolphin and NKit v2. C bindings are available for FFI.

- `, - meta: 'Used by TinyWiiBackupManager.', - }, - { - name: 'powerpc-rs', - url: 'https://github.com/encounter/powerpc-rs', - repo: 'encounter/powerpc-rs', - desc: 'PowerPC disassembler and assembler', - langs: [LANG.Rust], - body: ` -

powerpc-rs is driven by a declarative instruction set definition that is compiled into Rust at build time, similar to LLVM's TableGen. It supports the full PowerPC instruction set along with Gekko/Broadway paired singles (GameCube/Wii) and Xenon VMX128 (Xbox 360) extensions.

-

The disassembler has been fuzzed over all 4.29 billion possible 32-bit instruction values and runs at ~275M instructions per second.

- `, - meta: 'Used as the PowerPC backend for objdiff and decomp-toolkit.', - }, - { - name: 'ghidra-panel', - url: 'https://github.com/encounter/ghidra-panel', - repo: 'encounter/ghidra-panel', - desc: 'Self-service portal for managing access to shared Ghidra repositories', - langs: [LANG.Go, LANG.Java], - body: ` -

ghidra-panel integrates with Ghidra Server through an in-process gRPC plugin, allowing repository administrators to manage users and permissions. Users authenticate with Discord and can request repository access, sending a notification with a one-click approval link for admins.

- `, - meta: 'Powers the collaboration infrastructure on decomp.dev.', - images: [ - { - src: ghidraPanelHome, - alt: 'ghidra-panel home', - href: 'https://media.githubusercontent.com/media/encounter/ghidra-panel/main/.github/img/home.png', - }, - { - src: ghidraPanelRepo, - alt: 'ghidra-panel repository view', - href: 'https://media.githubusercontent.com/media/encounter/ghidra-panel/main/.github/img/repo.png', - }, - ], - }, - { - name: 'aurora', - url: 'https://github.com/encounter/aurora', - repo: 'encounter/aurora', - desc: 'Source-level GameCube & Wii compatibility layer for decompilation projects', - langs: [LANG.Cpp], - body: ` -

Aurora reimplements the GX API, translating the calls to native graphics backends like Vulkan, Metal, D3D12, and WebGPU.

- `, - }, - { - name: 'metaforce', - url: 'https://github.com/AxioDL/metaforce', - repo: 'AxioDL/metaforce', - desc: 'Native reimplementation of the Metroid Prime engine', - langs: [LANG.Cpp], - body: ` -

Metaforce (formerly URDE) started as a passion project reimplementing parts of the Metroid Prime engine, and eventually transformed into a nearly-complete non-matching decompilation.

-

Project website

- `, - meta: 'Currently on hiatus as the matching decompilation of Metroid Prime progresses.', - }, -]; diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 1eb6f97..e09be39 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -23,7 +23,10 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site); {description && } - + @@ -36,7 +39,11 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site); rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap" /> - + @@ -104,10 +111,10 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site); .masthead-title small { display: block; - margin-top: .25rem; + margin-top: 0.25rem; font-family: var(--body-font); font-weight: 400; - font-size: .875rem; + font-size: 0.875rem; color: var(--gray-500); letter-spacing: 0.02em; } @@ -117,11 +124,11 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site); padding-bottom: var(--spacer-2); border-top: 1px solid var(--border-color); font-family: var(--heading-font); - font-size: .75rem; + font-size: 0.75rem; color: var(--gray-500); } diff --git a/src/pages/404.astro b/src/pages/404.astro index b6547c7..8b0b409 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -1,11 +1,12 @@ --- -import Layout from '../layouts/Layout.astro'; +import Layout from "../layouts/Layout.astro"; --- - +

404: Page not found

- Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. + Sorry, we've misplaced that URL or it's pointing to something that doesn't + exist. Head back home to try finding it again.

diff --git a/src/pages/index.astro b/src/pages/index.astro index 5f35650..5ddf153 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,15 +1,20 @@ --- -import Layout from '../layouts/Layout.astro'; -import ProjectCard from '../components/ProjectCard.astro'; -import { projects } from '../data/projects'; +import Layout from "../layouts/Layout.astro"; +import ProjectCard from "../components/ProjectCard.astro"; +import { getCollection } from "astro:content"; + +const projects = (await getCollection("projects")).sort( + (a, b) => a.data.order - b.data.order, +); ---

- I build tools for game decompilation and reverse engineering, with a focus on GameCube and Wii. + I build tools for game decompilation and reverse engineering, with a focus + on GameCube and Wii.