Skip to content

feat: add experimental TMNT Mac and iPad source host - #2125

Draft
HaomingJu wants to merge 9 commits into
ly05010419:mainfrom
HaomingJu:tmnt
Draft

HaomingJu wants to merge 9 commits into
ly05010419:mainfrom
HaomingJu:tmnt

Conversation

@HaomingJu

@HaomingJu HaomingJu commented Nov 12, 2026 •

Copy link
Copy Markdown

Summary

Adds games/tmnt: an experimental Apple silicon Mac host and iPadOS target for Teenage Mutant Ninja Turtles: Shredder's Revenge, base 1.0.0 (application ID 0100FE701475A000).

The port follows the repository's ahead-of-time ARM64 rewrite, native host/HLE and NVN-on-Metal approach. Runtime, graphics, rewriter, shader tools and build scripts live in the game's own directory. No game content, keys, saves, generated executable images or translated shaders are included; private apps are built from the player's own verified inputs.

Mac run of review revision 195363d

On Apple silicon running macOS 26.6.2, I rebuilt the host and shader service, packaged the shared pinned SPIRV-Cross CLI (f11ba9f0), and launched the app with an empty profile and no external system font pack. The opening animation rendered the Teenage Mutant Ninja Turtles logo and Splinter in the sewer. I closed the window after about a minute. The frame counter reported 60 FPS near the end. This run covered the opening sequence only.

The original cartridge/container volume was offline, so this run reused the verified private module rewrites and the original-hash RomFS from a prior local package. Fresh NSP/XCI extraction, level gameplay, audio quality and iPad device behavior were not retested. All 45 TMNT source tests and 17 shared dependency, packaging and no-Homebrew tests passed.

What works / validation scope

  • Mac, earlier private build: the contributor reported normal operation at approximately 60 FPS, working audio and disappearance of the initial pink frame. This was a user observation, not a systematic benchmark or all-chapter test.
  • iPad, earlier private build: an arm64 UIKit/Metal app was built, signed, installed and launched on one iPad. Startup logs showed opening-video decoding, audio buffers, shader translation and an approximately 60 FPS frame loop; the contributor confirmed that the picture displayed and the app ran normally.
  • These general-playability observations describe earlier private builds. The Chinese-language checks below cover the later language fix on top of 9be6a9a. iPad gameplay input, saves, lifecycle recovery and sustained stability have not been comprehensively verified.

Building

macOS

On Apple silicon with Xcode command-line tools and the game's pinned Python requirements installed:

python3 games/tmnt/build.py '<base game.nsp or .xci>' --keys '<prod.keys>' \
  --work '<fresh work directory>' --out '<fresh output>/TMNT.app'

The new entry extracts and verifies the supported 1.0.0 base game. The shared tools/deps pins supply the shader compiler, Opus, libvpx and the default .NET SDK. The TMNT movie path uses embedded stb_vorbis. The inherited fallback's external Vorbis decoder is optional and disabled. See the current build instructions for optional shared fonts and the extracted-input entry.

iPadOS

The iPad instructions cover the separate UIKit/Metal target, private module/NRO embedding, in-process shader and codec libraries, XcodeGen project generation, private-resource packaging and signing.

This is a developer build workflow, not a one-command end-user installer. It requires prepared private rewrites/exports, generated headers and assembly, a local tmnt.xcconfig, and signing/provisioning for the device. Private resources are packaged before the signed build; generated Gen files and game assets must remain outside Git.

Main implementation changes

  • Host and loading: game-local services, image mapping/relocations, guest-call bridges, NRO loading, private filesystem/save paths, audio and opening-movie integration.
  • Graphics: TMNT-specific command-buffer splitting and ordering, viewport/origin and texture-copy handling in the NVN-on-Metal layer, plus startup presentation and resource-lifetime policies. The startup approach does not add a fixed sleep to hide the problem.
  • Shaders: bundled translator sources and a game-local build path. macOS uses a shader service; iPad links the NativeAOT translator and SPIRV-Cross into the app.
  • iPad execution and packaging: rewritten module and NRO code in signed executable sections, a UIKit/Metal frontend, a smaller guest stack/heap layout and measured alias region, and private resource packaging before code signing.

Simplified and Traditional Chinese

The verified base image includes both Chinese text tables and glyphs, but title.dat omits them from its seven-language catalogue. Reporting zh-Hans to the game alone therefore still loads only the listed languages. Private packaging now generates an integrity-checked catalogue overlay with the game's cn and zh-hant identifiers, preserves the default and existing languages, and leaves the verified image unchanged. Both Mac launchers load it; iPad resource packaging verifies and copies it before signing. Older private bundles need repackaging.

Automatic language selection also preserves Chinese script and region preferences: Hant and Taiwan/Hong Kong/Macao select Traditional Chinese, while an explicit Hans script selects Simplified Chinese. The game's language option and its saved preference remain in use. The new Mac package was launched with zh-TW without forcing TOTK_LANG, and Traditional Chinese appeared in its menu. The iPad changes have not been rebuilt or retested on device in this update.

Tests and remaining limits

  • Source tests and native probes cover preparation/metadata, relocation/loading primitives, graphics policies and packaging. Synthetic iPad tests cover NRO embedding. These do not replace playing on each platform.
  • Chinese-language fix: 44 Python source tests pass; a macOS host build and no-guest link check pass; 10 cases exercise the actual language-selection function, including Hant, Hans, TW, HK and MO. Simplified and Traditional Chinese render in local Mac startup/menu checks using copied profiles. This does not establish full gameplay or a new iPad device validation.
  • Additional language-option/restart persistence checks were interrupted by the unchanged render stream's encode stage did not drain in 2 s abort. The Chinese loading and rendered startup/menu results above are verified; in-game switching persistence is not yet revalidated.
  • Only the exact base 1.0.0 inputs listed in the hash manifest are supported. Updates, DLC and other revisions are not supported by the current extraction flow.
  • Full chapter coverage, save/restart, extended play and device coverage remain unverified. Approximately 60 FPS during startup is not evidence of complete playability.
  • Some text in the main game README predates the iPad bring-up and still says “Mac-only / no iOS”; the iPad target and its narrower validation scope are described above and in ios/README.md.
  • This description does not claim that all CI or review checks have passed.

Third-party code and licences

Source origins and adjacent licences are recorded in games/tmnt/third_party/COPIES.md and THIRD_PARTY_NOTICES.md.

  • Game-local d2r/d2r_gfx and shader-translation sources retain their MIT notices; inherited deko3d texture-layout code retains its zlib notice.
  • The Ryujinx shader and audio subsets retain MIT notices; SPIRV-Cross retains its Apache-2.0-or-MIT licence and bundled header notices.
  • stb_vorbis and bcdec retain their MIT/public-domain notices.
  • Codec dependencies are built/linked separately; no private game files or generated app binaries are contributed.
image

Keep the existing Mac host and add an iPadOS UIKit/Metal target built from verified private game inputs. Embed rewritten code and the registered NRO into signed sections, link the shader translator in-process, package private resources before signing, and reserve a measured iPad guest address layout. No game data or generated binaries are committed.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-13T12:27:57.715184Z
We couldn’t complete this review. Comment @codex review to try again.
195363d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 293317bb60

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread games/tmnt/runtime/audio_renderer.c Resolved
Comment thread games/tmnt/runtime/audio_renderer.c Resolved
Comment thread games/tmnt/scripts/package_private.py Resolved
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Failed to sample tokens
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ly05010419

Copy link
Copy Markdown
Owner

Thank you for bringing TMNT up as its own target. Before we can merge, please change:

  1. SPIRV-Cross: games/tmnt/third_party/spirv-cross bundles ~78k lines of source at aa217aeb. The repository builds spirv-cross from one pinned source through tools/deps (it reaches GitHub's main with our next push) (tools/deps/pins.py, currently 1.4.363.0 / f11ba9f0); please use that instead of a copy.
  2. No Homebrew: the Makefile finds opus, libvpx, libvorbis and libogg through pkg-config (lines 16–18), which in practice means Homebrew. Players should not need Homebrew; take the codecs from tools/deps, and tests/test_no_homebrew.py will check it. The runtime already carries stb_vorbis — why are libvorbis and libogg needed as well?
  3. Build entry: there is no games/tmnt/build.py, so tests/test_packaging_standard.py does not see this game, and the build needs a hand-extracted exefs/romfs. Please give it a build.py like the other games (build from the player's game file and keys) and register it in the packaging standard test, with its gaps listed.
  4. Provenance: please add games/tmnt/third_party/tmnt-import.json (origin and commit of every copied file) so tests/test_provenance.py checks it, as the other games do.
  5. README: the "What's new" line says 2026-11-09 and sits out of date order.

Also, the results in the description come from earlier private builds; please run this revision once on the Mac and say what you saw.

@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

https://github.com/ly05010419/MacNativeGames/blob/30e8b4179c4570543e2d1f68ca5b79bba8688bfc/games/tmnt/third_party/d2r_gfx/gfx/nvn/nvn_mtl.m#L12713-L12714
P2 Badge Flush deferred clears before copying with DrawTexture

In the shared Mac/iPad renderer, when a full-target ClearColor is followed by DrawTexture without an intervening ordinary draw, the clear remains pending in R.clear_c. This branch calls copy_t2t_scaled(), which only calls rp_end(); with no open render encoder, that leaves the clear pending. Consequently, mtl_sub_commit() calls rp_flush() and encodes the clear after the texture copy, erasing the fullscreen image. Flush pending clears before this copy, as the existing copy_t2t() path does.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@HaomingJu
HaomingJu marked this pull request as draft November 13, 2026 12:31

Copy link
Copy Markdown
Owner

Reviewed 195363d2d607000e989c271013ca1fe7c7698f03. The previous requested updates are now present: shared SPIRV-Cross and macOS codec dependencies, the container build entry, packaging-standard registration, the provenance inventory, and the README date correction. The audio-table fixes, external game-slot directory and iOS publisher executable bit are also present.

Please address the following before merging:

  1. Keep saves independent of the runtime binary. package_private.py:44 puts the runtime SHA-256 prefix in TOTK_PROFILE_DIR. Generating launchers with two different runtime hashes selects two different persistent profiles; fs_host.c reads saves from those profiles. Rebuilding the host therefore makes existing progress appear missing, although the old files remain. Use a stable game-specific profile, preserve/migrate existing saves without overwriting them, and version only rebuildable caches when needed. Add a regression check that changing the runtime preserves access to the same save.

  2. Flush pending clears before DrawTexture. The OP_DRAW_TEXTURE branch still calls copy_t2t_scaled() directly. That helper only calls rp_end(); when no render encoder is open, a pending full-target clear remains in R.clear_c. Submission later calls rp_flush(), placing the clear after the texture copy. The ordinary copy_t2t() path already flushes first. Fix this ordering and add a pixel-result regression for ClearColor → DrawTexture → submit, including the no-intervening-draw case. This is the earlier review finding, still present in the current head; our latest check was source analysis, not a GPU pixel reproduction.

  3. Implement the packaging requirements, rather than only registering exemptions. The standard test currently lists nine gaps: separately signed shader data; parallel pipeline-archive creation during packaging; warm-up completed before the title; build checks executed outside the new App; digest reuse; retaining the installed App directory; runtime-only updates; warm-up lists beside the shader map in build/; and checking that the archive was built from those exact lists. build_private_bundle.py still runs its link check inside the assembled App and requires fresh work/output directories. Complete these paths with behavior tests, then remove the corresponding exemptions. Current main includes the SS integration from feat(ss): The Legend of Zelda: Skyward Sword HD 1.0.1 as its own Mac target #2093 as a reference for shared packaging helpers.

    Also connect TMNT to MNG Launcher/tools/mng.py, installed-app detection and update_runtime.py --plan, using the common recorded/default work-folder rule. Adding a standalone build.py alone does not make Launcher recognize or update this title. Coordinate the TMNT-specific website inbox/uploader/collector route; do not submit its records under another game's ID.

  4. Finish the iPad dependency/tool integration. ios/tools/build_deps.py still invokes ambient cmake/ninja, and the documented workflow invokes an ambient xcodegen. Provide these through the repository's pinned dependency/tool helpers. Validate the documented entry from an environment without Homebrew tools on PATH; macOS dependency changes alone do not establish this for iPad.

  5. Update the remaining documentation and validation. The root THIRD_PARTY_NOTICES.md still names the removed TMNT SPIRV-Cross directory and old aa217aeb revision. Correct the paths/version and retain the required notices in generated packages. Investigate the documented encode stage did not drain in 2 s abort and rerun the interrupted language-switch/save/restart checks on the revised build. Report what was actually tested, including any remaining iPad limitations.

Local review checks passed: 45 TMNT source/native CPU-probe tests, 15 selected shared checks scoped to the exported review tree, and Mac/iPhone SDK syntax checks of nvn_mtl.m. These are not a full repository regression, complete app build, gameplay test or iPad device validation. Passing the packaging test with exemptions does not mean the missing requirements are implemented.

Please also update against current main: tests/test_packaging_standard.py now conflicts with the SS registration. Preserve both games' registrations when resolving it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants