<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://talonbrave.info/feed.xml" rel="self" type="application/atom+xml" /><link href="https://talonbrave.info/" rel="alternate" type="text/html" /><updated>2026-04-29T15:49:43+00:00</updated><id>https://talonbrave.info/feed.xml</id><title type="html">TalonBrave.info</title><subtitle>Video-Game Preservation &amp; Research Blog</subtitle><entry><title type="html">Lingering in Infinite</title><link href="https://talonbrave.info/2026/04/29/bioshock-infinite.html" rel="alternate" type="text/html" title="Lingering in Infinite" /><published>2026-04-29T00:00:00+00:00</published><updated>2026-04-29T00:00:00+00:00</updated><id>https://talonbrave.info/2026/04/29/bioshock-infinite</id><content type="html" xml:base="https://talonbrave.info/2026/04/29/bioshock-infinite.html"><![CDATA[<p>I’d recently started another playthrough of BioShock Infinite on PlayStation 3 (don’t ask), when a particular model caught my eye.
“Huh, I wonder how many more are still lingering in the game,” I said to myself, in an empty room with the curtains closed.
The answer?
More than I expected.</p>

<p>Noting the lack of documentation on this and desiring to take a bit of a break from what I’ve been working on, a fixation took upon me, and I did an incredibly normal sane thing; I took a look at every single model in the game, to see which potentially carried over from an earlier point of development, when the game used a different art style altogether, and provided supporting evidence where possible.</p>

<!-- more -->

<p>For those that don’t know, Infinite had quite a lengthy development cycle. So long it apparently warranted its own <a href="https://en.wikipedia.org/wiki/Development_of_BioShock_Infinite">Wikipedia page</a>.
Given the prolonged development cycle the original BioShock also went through, and seemingly the new BioShock <a href="https://www.pcgamer.com/games/fps/its-a-bad-day-for-bioshock-fans-bioshock-4-studio-heads-have-been-removed-as-development-struggles-while-a-bioshock-remake-in-development-has-been-cancelled/">is <em>also</em> going through</a>, perhaps this should be viewed as something of a tradition at this point, as Infinite evidently wasn’t unique in this respect.</p>

<p>Anyway, what we’re primarily interested in here is that at one point, very early on, the team experimented with a particular art style known as <a href="https://en.wikipedia.org/wiki/Art_Nouveau">Art Nouveau</a>.</p>

<p class="tb-img-subtitle"><img src="/assets/2026/infinite/Paris_Metro_2_Porte_Dauphine_Libellule.JPG" alt="Paris metro station entrance." />
A metro station entrance in Paris. A good example of the Art Nouveau style.</p>

<p>It’s beautiful, I love it to death.</p>

<p>It’s worth giving Robert Schmutzler’s Art Nouveau book a read <a href="https://archive.org/details/artnouveau0000schm/">here</a> or Geoffrey Warren’s All Color Book of Art Nouveau <a href="https://archive.org/details/allcolourbookofa00warr/">here</a> if you want to see more examples of the style. There are many others if you look under the topic on Internet Archive <a href="https://archive.org/search.php?query=subject%3A%22Art+nouveau%22">here</a>.
It wouldn’t actually be all that surprising if some of these are the same books the art team on Infinite referenced themselves at some point or another.</p>

<p>Mind, I’m not an expert on the subject, beyond an admirer of it, but a rather funny titbit that gave me a little chuckle is that Nouveau translates as <em>New</em> in French.
So it’s uh, <em>Art New</em>.
But in France it was referred to as <em>Style moderne</em>, so, <em>style modern</em>.
And if I understand right, the term apparently originated in the UK where it was referred to as <a href="https://en.wikipedia.org/wiki/Modern_Style_(British_Art_Nouveau_style)"><em>Modern Style</em></a>?
Very creative…</p>

<p>Below are some screenshots showing an earlier iteration of the game which incorporated this style. Some of these are from <em>The Art of BioShock Infinite</em> book, which I do recommend giving a look!</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/jamie-mcnulty-bs3-03.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-03_200x113.jpg" /></a><a href="/assets/2026/infinite/jamie-mcnulty-bs3-04.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-04_200x113.jpg" /></a><a href="/assets/2026/infinite/jamie-mcnulty-bs3-05.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-05_200x113.jpg" /></a><a href="/assets/2026/infinite/jamie-mcnulty-bs3-06.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-06_200x113.jpg" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/jamie-mcnulty-bs3-07.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-07_200x99.jpg" /></a><a href="/assets/2026/infinite/jamie-mcnulty-bs3-08.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-08_200x99.jpg" /></a><a href="/assets/2026/infinite/art-snip-00.png"><img src="/_thumbs/assets/2026/infinite/art-snip-00_200x99.png" /></a><a href="/assets/2026/infinite/art-snip-01.png"><img src="/_thumbs/assets/2026/infinite/art-snip-01_200x99.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/art-snip-02.png"><img src="/_thumbs/assets/2026/infinite/art-snip-02_200x85.png" /></a><a href="/assets/2026/infinite/art-snip-03.png"><img src="/_thumbs/assets/2026/infinite/art-snip-03_200x85.png" /></a><a href="/assets/2026/infinite/art-snip-04.png"><img src="/_thumbs/assets/2026/infinite/art-snip-04_200x85.png" /></a><a href="/assets/2026/infinite/art-snip-05.png"><img src="/_thumbs/assets/2026/infinite/art-snip-05_200x85.png" /></a></div></div>

<p>And some artwork, for good measure.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/9.jpg"><img src="/_thumbs/assets/2026/infinite/9_200x156.jpg" /></a><a href="/assets/2026/infinite/Bioshock_Infinite-Cabaret.jpg"><img src="/_thumbs/assets/2026/infinite/Bioshock_Infinite-Cabaret_200x156.jpg" /></a><a href="/assets/2026/infinite/mothman+in+flight.jpg"><img src="/_thumbs/assets/2026/infinite/mothman+in+flight_200x156.jpg" /></a><a href="/assets/2026/infinite/tumblr_mo7d4vwgqC1suo8gho7_r1_1280.jpg"><img src="/_thumbs/assets/2026/infinite/tumblr_mo7d4vwgqC1suo8gho7_r1_1280_200x156.jpg" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/tumblr_mo7d4vwgqC1suo8gho10_r1_1280.jpg"><img src="/_thumbs/assets/2026/infinite/tumblr_mo7d4vwgqC1suo8gho10_r1_1280_178x200.jpg" /></a><a href="/assets/2026/infinite/sinc_infiniteprepro2.jpg"><img src="/_thumbs/assets/2026/infinite/sinc_infiniteprepro2_178x200.jpg" /></a><a href="/assets/2026/infinite/jamie-mcnulty-bs3-01.jpg"><img src="/_thumbs/assets/2026/infinite/jamie-mcnulty-bs3-01_178x200.jpg" /></a><a href="/assets/2026/infinite/DemoGeo_Balcony3.jpg"><img src="/_thumbs/assets/2026/infinite/DemoGeo_Balcony3_178x200.jpg" /></a></div></div>

<p>Why did they drop it?</p>

<p>At PAX 2011, it was said they felt the style was <a href="https://web.archive.org/web/20110629041259/http://www.1up.com/news/postcard-pax-iterative-evolution-bioshock#:~:text=Ultimately%2C%20they%20felt%2C%20the%20Art%20Noveau%20style%20was%20too%20claustrophobic%2C%20too%20similar%20to%20Rapture.">“… claustrophobic, too similar to Rapture”</a>. This was again mentioned during an interview with Shawn Robertson, lead artist, with Engadget in 2011; <a href="https://www.engadget.com/2011-06-10-lead-artist-shawn-robertson-talks-the-aesthetic-evolution-of-bio.html#:~:text=The%20first%20few%20maps%20we%20built%20might%20as%20well%20have%20been%20Rapture.%20We%20brought%20the%20clouds%20in%2C%20it%20was%20dark%20and%20stormy%2C%20the%20clouds%20had%20a%20greenish%20tinge%20to%20them%2C%20it%20was%20very%20claustrophobic.">“… first few maps we built might as well have been Rapture. We brought the clouds in, it was dark and stormy, the clouds had a greenish tinge to them, it was very claustrophobic.”</a></p>

<p>Additionally, according to Jamie McNulty, the lead environment artist on the game, <a href="https://www.artstation.com/artwork/bJAVv#:~:text=We%20learned%20how%20hard%20it%20was%20to%20have%20so%20many%20french%20curves%20and%20round%20things%20in%20a%20level%20and%20running%20well%20on%20an%20xbox%20and%20ps3.">“… we learned how hard it was to have so many french curves and round things in a level and running well on an xbox [360] and ps3.”</a> So besides wanting to move away from Rapture a bit more, the decision may have also been due to technical concerns.</p>

<p>But this is why we’re here today!
While the art style was ultimately dropped, it’s not entirely gone from Columbia, and elements of it do actually survive in the finished title.
Very subtly, mind.</p>

<h2 id="interior-set">Interior Set</h2>

<p>First up we’re going to look at a set of models referred to as <code class="language-plaintext highlighter-rouge">Int_Set_01</code>, which are essentially a collection of interior building blocks.
These are actually used widely throughout the game, for all sorts of things.
Even as part of a dressing table in one location!</p>

<p>Reusing models like this all over the place is a widespread practice.
Without getting too technical, it’s a lot cheaper to do this instead of building your level out with lots of dense BSP details as the engine can more easily cache the geometry on the GPU and even use instancing; fewer explicit requests to the GPU and less work on the CPU.</p>

<p>You would be very surprised how many uses you can get out of rotating and scaling things around.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/int_set_01_00.png"><img src="/_thumbs/assets/2026/infinite/int_set_01_00_200x161.png" /></a><a href="/assets/2026/infinite/int_set_01_01.png"><img src="/_thumbs/assets/2026/infinite/int_set_01_01_200x161.png" /></a><a href="/assets/2026/infinite/int_set_01_02.png"><img src="/_thumbs/assets/2026/infinite/int_set_01_02_200x161.png" /></a></div></div>

<p>To my surprise there’s no <code class="language-plaintext highlighter-rouge">Int_Set_02</code> etc., and the only other “Int_Set_” collection is called <code class="language-plaintext highlighter-rouge">NrsyInt_Set_01</code>, which is very clearly something produced <em>after</em> the change in art style (so we’ll be skipping that).</p>

<p>Below is a gallery of each model individually just to give you a better look (mind these are without the specular and normal map).</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.01.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.01_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.02.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.02_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.03.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.03_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.04.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.04_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.05.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.05_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.06.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.06_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.07.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.07_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.08.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.08_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.09.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.09_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.10.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.10_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.11.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.11_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.12.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.12_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.13.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.13_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.14.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.14_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.15.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.15_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.16.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.16_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.17.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.17_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.18.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.18_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.19.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.19_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.20.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.20_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.21.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.21_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.22.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.22_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.23.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.23_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.24.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.24_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.25.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.25_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.26.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.26_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.27.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.27_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.28.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.28_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.29.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.29_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.30.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.30_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.31.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.31_200x109.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.32.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.32_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/int_set_01/int_set_01.33.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.33_200x108.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.34.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.34_200x108.png" /></a><a href="/assets/2026/infinite/int_set_01/int_set_01.35.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/int_set_01.35_200x108.png" /></a></div></div>

<p>It’s worth mentioning that for some of these there are alternate textures.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2026/infinite/int_set_01/Int_Set_01.Textures.Int_Set_01_Window_DIFF.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/Int_Set_01.Textures.Int_Set_01_Window_DIFF_200x200.png" /></a><a href="/assets/2026/infinite/int_set_01/Int_Set_01.Textures.Int_Set_01_Window_GOLD_DIFF.png"><img src="/_thumbs/assets/2026/infinite/int_set_01/Int_Set_01.Textures.Int_Set_01_Window_GOLD_DIFF_200x200.png" /></a></div></div>

<p>The Art Nouveau aesthetic is quite subtle for these, likely why they’re still in such wide use, but you can see some of it seeping through with the curved organic elements coming through on a few of them.</p>

<p>I appreciate it if you’re not quite convinced or perhaps even confused for the moment. Perhaps, even afraid…
Do not fear!
To support my point, we can actually see some of these models in use in an old screenshot.
Per my uh, rather crude overlay here.</p>

<p><img src="/assets/2026/infinite/int_set_01/compare.gif" alt="Comparison gif" /></p>

<p>So yeah, these, or at least certainly some of them, do appear to have been carried over from the earlier work on the game.
Neat, right?
It makes you really appreciate the legacy some props in the game might have that you otherwise might not have thought about.</p>

<p>That said, this isn’t all that surprising.
Models are quite time-consuming to produce.
And again, the <em>style</em> is very subtle for these; they still fit very well with the Columbia that shipped.
So much so that I’d not even realised until working on this piece (I started off looking at something <em>completely</em> different when I’d started).</p>

<p>Something I’d noticed while working on the comparison above is that the taller columns are just the shorter columns stretched like so.</p>

<p><img src="/assets/2026/infinite/int_set_01/column_compare.gif" alt="Column comparison" /></p>

<p>This is despite there being two taller columns in the set (<a href="/assets/2026/infinite/int_set_01/int_set_01.13.png">here</a> and <a href="/assets/2026/infinite/int_set_01/int_set_01.14.png">here</a>), which may imply that those weren’t yet created at the time they made the environment featured in the screenshot.
Perhaps the level designers stretching the shorter column like this prompted the artists to create the taller columns?</p>

<p>They really got a lot of use out of this set of models in general.
I don’t want to go too crazy, as we’ve got plenty more to look at, but even in some early paintovers you can see many of them being used; just as <strong>one</strong> example I’ve highlighted below where they’ve reused the same model with different scales applied to get a lot of use out of it.</p>

<p><a href="/assets/2026/infinite/int_set_01/reuse-scale.png"><img src="/assets/2026/infinite/int_set_01/reuse-scale_thumb.png" alt="Example of just one prop." /></a></p>

<p>See if you can spot some others!
There’s <a href="https://archive.thedatadungeon.com/bioshock_infinite_2013/images/artwork/jorge_lacera/tumblr_mo7d4vwgqc1suo8gho3_r1_1280.jpg">another paintover</a> where you can see some of these being used if you look close, but I think you get the idea.
The team got good use out of these, and they survived seemingly almost from start to end!</p>

<p>As a final note of curiosity, in the <a href="https://www.youtube.com/watch?v=p5it-pAYmKY">September 2010 demo</a> there’s actually an interesting variation of the set that appears to be painted with gold trims.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/vlcsnap-2026-04-24-12h12m43s979.png"><img src="/_thumbs/assets/2026/infinite/vlcsnap-2026-04-24-12h12m43s979_200x112.png" /></a><a href="/assets/2026/infinite/vlcsnap-2026-04-24-12h13m46s087.png"><img src="/_thumbs/assets/2026/infinite/vlcsnap-2026-04-24-12h13m46s087_200x112.png" /></a><a href="/assets/2026/infinite/vlcsnap-2026-04-24-14h47m31s521.png"><img src="/_thumbs/assets/2026/infinite/vlcsnap-2026-04-24-14h47m31s521_200x112.png" /></a></div></div>

<p>This is the only bit of public media I’m aware of where these show up, and this texture variation is apparently gone from the final game.</p>

<p>There’s a lot more I could say about that 2010 demo, but I’ll fortunately refrain, at least for today.</p>

<h2 id="furniture">Furniture</h2>

<p>Let’s look at our next models here, all part of a furniture set.
I’m fairly sure a lot of people have noticed a couple of these already, but again, not really documented anywhere to my knowledge.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/furniture/nouveau_chair.png"><img src="/_thumbs/assets/2026/infinite/furniture/nouveau_chair_200x112.png" /></a><a href="/assets/2026/infinite/furniture/nouveau_couch.png"><img src="/_thumbs/assets/2026/infinite/furniture/nouveau_couch_200x112.png" /></a><a href="/assets/2026/infinite/furniture/nouveau_runner_rug.png"><img src="/_thumbs/assets/2026/infinite/furniture/nouveau_runner_rug_200x112.png" /></a><a href="/assets/2026/infinite/furniture/nouveau_runner_rug2.png"><img src="/_thumbs/assets/2026/infinite/furniture/nouveau_runner_rug2_200x112.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/furniture/nouveaurunner_rug_round.png"><img src="/_thumbs/assets/2026/infinite/furniture/nouveaurunner_rug_round_200x112.png" /></a></div></div>

<p>The names for each of these are prefixed with the name <em>Nouveau</em>, so they’re explicitly indicated to fit that style, and they certainly appear to do so.
Well, certainly the first two.
The others are just, uhm, rugs.</p>

<p>You can see some better renders of the chair and sofa produced by the artist that created them available below.
The first one, which shows the high-poly version of the model, really gives you an appreciation for the amount of detail the artists put into these.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2026/infinite/sofa_chair_high.jpg"><img src="/_thumbs/assets/2026/infinite/sofa_chair_high_200x78.jpg" /></a><a href="/assets/2026/infinite/sofa_chair_low.jpg"><img src="/_thumbs/assets/2026/infinite/sofa_chair_low_200x78.jpg" /></a></div></div>

<p>And as before, they’re used all over the damn place in the final game.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/furniture/furniture_chair.png"><img src="/_thumbs/assets/2026/infinite/furniture/furniture_chair_200x165.png" /></a><a href="/assets/2026/infinite/furniture/furniture_couch.png"><img src="/_thumbs/assets/2026/infinite/furniture/furniture_couch_200x165.png" /></a><a href="/assets/2026/infinite/furniture/furniture_couch_2.png"><img src="/_thumbs/assets/2026/infinite/furniture/furniture_couch_2_200x165.png" /></a></div></div>

<p>Clearly quite a favourite bit of furnishing in Columbia.</p>

<p>We can see what appears to be the sofa in the screenshot we did the comparison for earlier.
There is also what appears to be the same chair, possibly tipped over in the foreground but certainly in the background at least.</p>

<p><img src="/assets/2026/infinite/int_set_01/compare_3.gif" alt="Comparison gif" /></p>

<p>I struggled a little bit more than expected trying to get it to match up, so only did so for the sofa.
Which is still far from perfect, but you get the idea.</p>

<p><img src="/assets/2026/infinite/int_set_01/compare_2.gif" alt="Comparison gif" /></p>

<p>The rugs visible in the same screenshot may be the same as those within the same set, but it’s really too hard to tell for certain.
Not to mention they look slightly redder?
I think it’s quite likely to at least be the same model, but I just can’t be sure about the texture.</p>

<p>The only other texture variation I can see for the same model in the final game is called <code class="language-plaintext highlighter-rouge">Nouveau_Runner_RugFink</code> and that absolutely isn’t the same.</p>

<p>The chair and sofa, of course, also appear in the previously mentioned September 2010 demo, looking much the same as they do in the final game.</p>

<h2 id="glass-walkway">Glass Walkway</h2>

<p>Next up is probably my favourite set just because it’s so distinct and yet <em>so</em> easy to miss.
Particularly because you’ll never think to look up!</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/glasswalkway/screenshot_GlassWalkway.GlassWalkway_EndCap_20260423_160042423.png"><img src="/_thumbs/assets/2026/infinite/glasswalkway/screenshot_GlassWalkway.GlassWalkway_EndCap_20260423_160042423_200x109.png" /></a><a href="/assets/2026/infinite/glasswalkway/screenshot_GlassWalkway.GlassWalkway_Straight_20260423_160128845.png"><img src="/_thumbs/assets/2026/infinite/glasswalkway/screenshot_GlassWalkway.GlassWalkway_Straight_20260423_160128845_200x109.png" /></a><a href="/assets/2026/infinite/glasswalkway/screenshot_GlassWalkway.GlassWalkway_Straight_NoGlass_20260423_160143048.png"><img src="/_thumbs/assets/2026/infinite/glasswalkway/screenshot_GlassWalkway.GlassWalkway_Straight_NoGlass_20260423_160143048_200x109.png" /></a></div></div>

<p>It’s quite clear from the texturing work on the glass here that these are following the Art Nouveau styling.</p>

<p>It actually took me a little while to track these down in one of the levels.
At least the version of the model using the stylised glass.
I’d spotted it while playing, then totally forgot where it was and wasted a good few hours feeling like I’d hallucinated it.
Anyway, you can see an example of it used in the final game per the screenshot below.</p>

<p><a href="/assets/2026/infinite/glasswalkway/walkway_screeny.png"><img src="/assets/2026/infinite/glasswalkway/walkway_screeny_thumb.png" alt="Glass featured in the final game." /></a></p>

<p>What caught me off guard a little bit with this one is that we can see this in the paintover we were looking at earlier, as you can see below.
I don’t think a full on comparison is necessary here as it’s very obvious.</p>

<p><a href="/assets/2026/infinite/art-paintover-00.png"><img src="/assets/2026/infinite/glasswalkway/art_thumb.png" alt="Paintover." /></a></p>

<p>But that’s not all!
We can also see what appears to be the same models in <a href="https://youtu.be/8f1udEYsRa4?t=210">this very short clip</a> from the earlier iteration of the game, shown at PAX East in 2011.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/glasswalkway/vlcsnap-2026-04-20-08h35m01s440.png"><img src="/_thumbs/assets/2026/infinite/glasswalkway/vlcsnap-2026-04-20-08h35m01s440_200x112.png" /></a><a href="/assets/2026/infinite/glasswalkway/vlcsnap-2026-04-20-08h35m19s951.png"><img src="/_thumbs/assets/2026/infinite/glasswalkway/vlcsnap-2026-04-20-08h35m19s951_200x112.png" /></a><a href="/assets/2026/infinite/glasswalkway/vlcsnap-2026-04-20-08h35m33s984.png"><img src="/_thumbs/assets/2026/infinite/glasswalkway/vlcsnap-2026-04-20-08h35m33s984_200x112.png" /></a></div></div>

<p>It’s notable that there’s a special end piece visible in the paintover and other images above, allowing it to curve round.
This specific model doesn’t appear to exist in the final game, though there’s still a spot for it that remains in the texture sheet.</p>

<p><a href="/assets/2026/infinite/glasswalkway/GlassWalkway.GlassWalkway_DIFF.png"><img src="/assets/2026/infinite/glasswalkway/GlassWalkway.GlassWalkway_DIFF_thumb.png" alt="Texture sheet." /></a></p>

<h2 id="tesla-coils">Tesla Coils</h2>

<p>These took me a little while when I’d first looked at them, because they don’t really have any distinct look going for them, so didn’t really pass the initial <em>style</em> test.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/tesla/screenshot_TeslaCoils.TeslaCoil1_MESH.LOD0_20260425_123833832.png"><img src="/_thumbs/assets/2026/infinite/tesla/screenshot_TeslaCoils.TeslaCoil1_MESH.LOD0_20260425_123833832_200x109.png" /></a><a href="/assets/2026/infinite/tesla/screenshot_TeslaCoils.TeslaCoil2_MESH.LOD0_20260425_123853739.png"><img src="/_thumbs/assets/2026/infinite/tesla/screenshot_TeslaCoils.TeslaCoil2_MESH.LOD0_20260425_123853739_200x109.png" /></a><a href="/assets/2026/infinite/tesla/screenshot_TeslaCoils.TeslaCoil3_MESH.LOD0_20260425_123908502.png"><img src="/_thumbs/assets/2026/infinite/tesla/screenshot_TeslaCoils.TeslaCoil3_MESH.LOD0_20260425_123908502_200x109.png" /></a></div></div>

<p>There were tesla coils visible in the earlier screenshots of Infinite, but did they really match?
They certainly <em>looked</em> similar.
And eventually, yeah, I did conclude at least one of them is most certainly the same model.</p>

<p>Below is another poor attempt at an overlay, apologies!</p>

<p><img src="/assets/2026/infinite/tesla/compare_00.gif" alt="Comparison gif" /></p>

<p>So while these don’t fall under the Art Nouveau style, they do appear to be yet another asset certainly carried over from the earlier iteration of the game.</p>

<p>These are particularly hard to match up due to the models having a lot more going on, often scaled up and down to various proportions, and the few screenshots we’ve got are not showing them fantastically clearly.</p>

<p>Here’s how they actually look in the final game.
You’ll notice much like those earlier screenshots, the glass bulb near the bottom of the coil is supposed to be see-through.</p>

<p><a href="/assets/2026/infinite/tesla/tesla_final.png"><img src="/assets/2026/infinite/tesla/tesla_final_thumb.png" alt="Tesla coils in the final game." /></a></p>

<p>One thing perculiar in <a href="/assets/2026/infinite/jamie-mcnulty-bs3-04.jpg">this</a> screenshot is that it looks like there are possibly other variations that are not in the final game?</p>

<p>What stood out to me was this detailing, which isn’t visible with the models we’ve got.</p>

<p><a href="/assets/2026/infinite/tesla/detailing.png"><img src="/assets/2026/infinite/tesla/detailing_thumb.png" alt="Some detailing on the shaft of the tesla." /></a></p>

<p>Well, unless I’m mistaken anyway.
That’s enough about tesla coils.</p>

<h2 id="weapons">Weapons</h2>

<p>This is already documented elsewhere, but for those that don’t know, the older third-person variation of the machine gun featured in the <a href="https://me.ign.com/en/bioshock-infinite/70575/bioshock-infinites-original-elizabeth-prototype">Gibson Girl clip</a> is still amongst the models in the final game, as seen below.</p>

<p><a href="/assets/2026/infinite/other/MG_TP_RIG.png"><img src="/assets/2026/infinite/other/MG_TP_RIG_thumb.png" alt="Old machine gun model." /></a></p>

<p>This is under a <code class="language-plaintext highlighter-rouge">Weapons_Trashcan</code> set, so, I guess they had already scrapped it but for whatever reason it still ended up being included.</p>

<p>The machine gun that ended up being used in the game has the same model name but is prefixed with “new”, so it was quite explicitly created to replace this older design.</p>

<p>Another weapon that survived, as you may have noticed earlier if you have a keen eye, is the Hand Cannon!
We can see it featured in the PAX East 2011 clip from earlier (though in this instance I pulled these images from GameSpot’s feed, as it’s <em>slightly</em> clearer there).</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/other/handcannon_pax_1.png"><img src="/_thumbs/assets/2026/infinite/other/handcannon_pax_1_200x112.png" /></a><a href="/assets/2026/infinite/other/handcannon_pax_2.png"><img src="/_thumbs/assets/2026/infinite/other/handcannon_pax_2_200x112.png" /></a><a href="/assets/2026/infinite/other/handcannon_pax_3.png"><img src="/_thumbs/assets/2026/infinite/other/handcannon_pax_3_200x112.png" /></a></div></div>

<p>Though the textures seem to have been altered since this point in development.</p>

<p>It also shows up in a short, clearly early, clip, during Shawn Robertson’s GDC presentation, <a href="https://www.gdcvault.com/play/1020545/Creating-BioShock-Infinite-s">Creating Bioshock Infinite’s Elizabeth</a>, here.</p>

<p><a href="/assets/2026/infinite/other/vlcsnap-2026-04-18-22h29m32s332.png"><img src="/assets/2026/infinite/other/vlcsnap-2026-04-18-22h29m32s332.png" alt="Hand Cannon, again." /></a></p>

<p>This certainly looks like the same texture in use in the PAX East 2011 clip, so gives us a better look at it.
Generally, it looks incredibly similar to the final Hand Cannon design from what little can be made out.
Just generally more dull-looking, though this could just be down to lighting.</p>

<h2 id="awning">Awning</h2>

<p>Thought I was done?
So did I…
There’s an awning model in the game with different textures.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/awning/awning_green.png"><img src="/_thumbs/assets/2026/infinite/awning/awning_green_200x112.png" /></a><a href="/assets/2026/infinite/awning/awning_red.png"><img src="/_thumbs/assets/2026/infinite/awning/awning_red_200x112.png" /></a><a href="/assets/2026/infinite/awning/awning_red_stripes.png"><img src="/_thumbs/assets/2026/infinite/awning/awning_red_stripes_200x112.png" /></a><a href="/assets/2026/infinite/awning/awning_b.png"><img src="/_thumbs/assets/2026/infinite/awning/awning_b_200x112.png" /></a></div></div>

<p>The dark red awning seen above appears to be visible in the screenshot <a href="/assets/2026/infinite/art-snip-01.png">here</a>.
Due to the perspective in the screenshot, it was too difficult to do a proper overlay for this one, but I’m fairly certain it’s the same model.</p>

<p>So this appears to be another one that was carried over.
Which, much like the interior set we looked at earlier, isn’t too surprising as it still fits in well.</p>

<h2 id="special-mentions">Special Mentions</h2>

<p>For the sake of time, I’ll just quickly run through some things that didn’t seem worth having entire sections about.
These are mostly things that <em>look</em> like they might have come from the Art Nouveau iteration, but I can’t really prove it with any sort of solid comparison.</p>

<h3 id="planter">Planter</h3>

<p>Another fairly distinct one, but only a couple of models appear under this set, appearing to only utilise the top part of the texture sheet, implying that other models are actually missing.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/other/landscaping_render.png"><img src="/_thumbs/assets/2026/infinite/other/landscaping_render_200x107.png" /></a><a href="/assets/2026/infinite/other/landscaping.Textures.planter_COLOR.png"><img src="/_thumbs/assets/2026/infinite/other/landscaping.Textures.planter_COLOR_200x107.png" /></a><a href="/assets/2026/infinite/other/landscaping_uv.png"><img src="/_thumbs/assets/2026/infinite/other/landscaping_uv_200x107.png" /></a></div></div>

<p>Worth adding the circular model in the first image does appear in the September 2010 footage as well, very briefly.</p>

<h3 id="bar-set">Bar Set</h3>

<p>These I strongly suspect comes from the earlier era of development. Some of them appear in at least the <a href="/assets/2026/infinite/vlcsnap-2026-04-24-12h13m46s087.png">September 2010 footage</a> but unfortunately, aren’t visible in anything earlier.
Hence, why they’re here under <em>special mentions</em>.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/bar_set/bar_set_1.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_1_200x109.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_2.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_2_200x109.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_3.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_3_200x109.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_4.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_4_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/bar_set/bar_set_5.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_5_200x109.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_6.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_6_200x109.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_7.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_7_200x109.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_8.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_8_200x109.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/bar_set/bar_set_9.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_9_200x109.png" /></a></div></div>

<p>Once again, a very popular set in Columbia.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2026/infinite/bar_set/bar_set_screen_1.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_screen_1_200x139.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_screen_2.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_screen_2_200x139.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_screen_3.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_screen_3_200x139.png" /></a><a href="/assets/2026/infinite/bar_set/bar_set_screen_4.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_screen_4_200x139.png" /></a></div></div>

<p>I think the <a href="/assets/2026/infinite/bar_set/bar_set_screen_1.png">first screenshot</a> above is actually quite a good example of the difference in styling, where you can visibly see the clearly Art Nouveau inspired design sticking out a little alongside those produced after the shift.</p>

<p>Below are those assets side-by-side, so you can view the difference more clearly for yourself.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2026/infinite/bar_set/bar_set_7.png"><img src="/_thumbs/assets/2026/infinite/bar_set/bar_set_7_200x148.png" /></a><a href="/assets/2026/infinite/bar_set/shelf_example.png"><img src="/_thumbs/assets/2026/infinite/bar_set/shelf_example_200x148.png" /></a></div></div>

<h3 id="furniture-tables">Furniture Tables</h3>

<p>Yup, tables.</p>

<p>Like a lot of what we’ve looked at before, again, very popular in Columbia, apparently.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/other/furniture_tables_1.png"><img src="/_thumbs/assets/2026/infinite/other/furniture_tables_1_200x109.png" /></a><a href="/assets/2026/infinite/other/furniture_tables_2.png"><img src="/_thumbs/assets/2026/infinite/other/furniture_tables_2_200x109.png" /></a><a href="/assets/2026/infinite/other/furniture_tables_3.png"><img src="/_thumbs/assets/2026/infinite/other/furniture_tables_3_200x109.png" /></a></div></div>

<h3 id="bar-assets">Bar Assets</h3>

<p>A couple of lamps; one broken and one not.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2026/infinite/other/bar_assets_1.png"><img src="/_thumbs/assets/2026/infinite/other/bar_assets_1_200x109.png" /></a><a href="/assets/2026/infinite/other/bar_assets_2.png"><img src="/_thumbs/assets/2026/infinite/other/bar_assets_2_200x109.png" /></a></div></div>

<p>The glass pattern on these is very similar to that of the uh, central ceiling chandelier light thing, visible in <a href="/assets/2026/infinite/art-snip-04.png">this</a> shot, but not the same.</p>

<p>The <em>non-broken</em> version of the lamp can be found all over the place.</p>

<h3 id="nouveau-chimney">Nouveau Chimney</h3>

<p>If it wasn’t for the name this uses internally, it wouldn’t have been mentioned.</p>

<p>Surprisingly, there are no chimneys visible in the earliest screenshots of BioShock Infinite, or earliest clips either, unless I’m blind.
So I couldn’t verify it. Into the <em>special mentions</em> pit with ye!</p>

<p><a href="/assets/2026/infinite/other/chimney.png"><img src="/assets/2026/infinite/other/chimney_thumb.png" alt="Screenshot showing a chimney model." /></a></p>

<h3 id="nouveau-grate">Nouveau Grate</h3>

<p>Not sure at all where this is still used in the final game.</p>

<p><a href="/assets/2026/infinite/other/grate.png"><img src="/assets/2026/infinite/other/grate_thumb.png" alt="Screenshot showing a grate model." /></a></p>

<h3 id="metal-grate">Metal Grate</h3>

<p>I know the first of is used in parts of the Hall of Heroes in the final game, but I’m not so sure about the others.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2026/infinite/other/metal_grate_1.png"><img src="/_thumbs/assets/2026/infinite/other/metal_grate_1_200x148.png" /></a><a href="/assets/2026/infinite/other/metal_grate_2.png"><img src="/_thumbs/assets/2026/infinite/other/metal_grate_2_200x148.png" /></a><a href="/assets/2026/infinite/other/metal_grate_3.png"><img src="/_thumbs/assets/2026/infinite/other/metal_grate_3_200x148.png" /></a></div></div>

<h3 id="bistro-set">Bistro Set</h3>

<p>This first image shows two models that are part of what’s internally called the Bistro Set.
I’ve not noticed them in any of the pre-release material I’ve looked at, so it’s not outside the realm of possibility that these were introduced later in development.</p>

<p><a href="/assets/2026/infinite/table_chair.png"><img src="/assets/2026/infinite/table_chair_thumb.png" alt="Screenshot showing the table and chair model." /></a></p>

<p>Something about their texture sheet also generally looks a lot cleaner to me than some of the other models we’ve looked at until now, which also makes me doubly unsure if these weren’t just added later in development.</p>

<p>These bastards were also responsible for sending me down this rabbit hole in the first place. <em>Shakes fist angrily at them.</em></p>

<h2 id="the-end">The End</h2>

<p>Oh my goodness, is it over?
This was supposed to be a short piece!
But it just kept going and going.</p>

<p>One thing I most certainly did not do, particularly because it probably would’ve driven me rather insane, is checking out all the textures in the game.
This is a bit beyond my patience.
But I’ve no doubt there’s probably more to see there.</p>

<p>Anyway, if you’ve enjoyed this, do let me know!
If you want to submit any feedback, you can either do so via the comments section below or you can join our <a href="https://discord.gg/bJKBRNagfA">Discord server</a> and share your thoughts there.</p>

<p>Until next time, farewell…</p>

<p><img src="/assets/gifs/flanders-carry.gif" alt="Flanders being carried away." /></p>]]></content><author><name>Mark Sowden</name></author><category term="Game" /><category term="Game/BioShock Infinite" /><category term="2026" /><category term="BioShock Infinite" /><category term="Travellers Tales" /><summary type="html"><![CDATA[I’d recently started another playthrough of BioShock Infinite on PlayStation 3 (don’t ask), when a particular model caught my eye. “Huh, I wonder how many more are still lingering in the game,” I said to myself, in an empty room with the curtains closed. The answer? More than I expected. Noting the lack of documentation on this and desiring to take a bit of a break from what I’ve been working on, a fixation took upon me, and I did an incredibly normal sane thing; I took a look at every single model in the game, to see which potentially carried over from an earlier point of development, when the game used a different art style altogether, and provided supporting evidence where possible.]]></summary></entry><entry><title type="html">Poking and Prodding Haven</title><link href="https://talonbrave.info/2025/09/10/haven.html" rel="alternate" type="text/html" title="Poking and Prodding Haven" /><published>2025-09-10T00:00:00+00:00</published><updated>2025-09-10T00:00:00+00:00</updated><id>https://talonbrave.info/2025/09/10/haven</id><content type="html" xml:base="https://talonbrave.info/2025/09/10/haven.html"><![CDATA[<p>Haven: Call of the King, or simply Haven, as I’ll refer to it, is a game developed by the <a href="https://youtu.be/I8KSAtos-dk?t=24">British</a> video-game development studio, <a href="https://en.wikipedia.org/wiki/Traveller%27s_Tales">Traveller’s Tales</a>. 
It’s quite technically impressive, and certainly ambitious, as far as PlayStation 2 games go.</p>

<!-- more -->

<p>To be clear, we’re only going to be diving into the technical side of the game for this one, so if that’s not your sort of thing then hopefully the next article will be more to your satisfaction.</p>

<p>Also, got to point out, I am (unfortunately) human, I make mistakes and every day is a learning experience.
Not to mention going over all this turned my brain into mushed peas.
If I’ve made an error anywhere, let me know about it, so I can correct it, rather than immediately blasting me into orbit!</p>

<h2 id="dvd-contents-">DVD Contents 📀</h2>

<p>Okay, so let’s start getting our hands dirty.</p>

<p><img src="/assets/gifs/cat-digging.gif" alt="Cat digging gif." /></p>

<p>I’d give you a complete listing of all the content as I’ve done before, but there’s a far too much for that, so we’ll just go over the things that matter.</p>

<p>I’m going to be looking at the final EU release of the game here, so SLES-51209 specifically.
There is a pre-release build out there, <a href="https://hiddenpalace.org/Haven:_Call_of_the_King_(Aug_15,_2002_prototype)">from August 2002</a>, which I’ve looked at too - but we’ll save more details about that for a future article.
For now I’ll just reference it in passing where it makes sense to do so.</p>

<p>In the root directory of the ISO we can see the following.</p>

<p><img src="/assets/2025/haven-folder-prev.png" alt="Root directory of an extracted ISO" /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Oct 14  2002 DATA           ; where the game data is stored
Oct 14  2002 DLL.DAT        ; package with a collection of .rel files
Sep 27  1999 DUMMY.DAT      ; null padding file
Oct 14  2002 SLES_512.09    ; main game executable
Oct 14  2002 SYS250         ; system libraries
Oct  9  2002 SYSTEM.CNF     ; typical PS2 config file
</code></pre></div></div>

<p>As indicated in my notes above, the <code class="language-plaintext highlighter-rouge">DLL.DAT</code> file is actually using a package format which we’ll need to deal with.
Fortunately, unlike some other formats I’ve looked at of late, the filenames are not hashed.
That said, they <em>are</em> stored in a rather unusual way, as we’ll find out.
Nothing’s ever simple… 😒</p>

<p>As also indicated in my notes, the <code class="language-plaintext highlighter-rouge">DUMMY.DAT</code> file is used for nothing more than padding the DVD and contains nothing interesting.
Given the modification timestamp on it, I’d guess this exact same file may have been used for padding in some of their other titles, heh.</p>

<p>Next looking under the <code class="language-plaintext highlighter-rouge">DATA</code> directory, we can see the following.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Oct 14  2002 BIN            ; contains content used for each of the planets in the game
Oct 14  2002 CSFX1          ; various sound effects using the common PS2 VAG format
Oct 14  2002 ENGLISH        ; english dialogue audio as PS2 VAG format
Oct 14  2002 FRENCH         ; ditto but for french
Oct 14  2002 GERMAN         ; ditto but for german
Oct 14  2002 GSFX1          ; various sound effects using the common PS2 VAG format
Oct 14  2002 HSFX1          ; various sound effects using the common PS2 VAG format
Oct 14  2002 HSFX2          ; various sound effects using the common PS2 VAG format
Oct 10  2002 INTRO_25.PSS   ; one of the pre-rendered cutscenes featured in the game
Oct 14  2002 ITALIAN        ; italian dialogue audio
Oct 14  2002 LSFX1          ; various sound effects using the common PS2 VAG format
Oct 14  2002 LSFX2          ; various sound effects using the common PS2 VAG format
Oct 14  2002 LSFX3          ; various sound effects using the common PS2 VAG format
Oct 14  2002 LSFX4          ; various sound effects using the common PS2 VAG format
Oct 14  2002 MUSIC          ; VAG music
Oct 14  2002 MUSIC2         ; VAG music
Oct 14  2002 MUSIC3         ; VAG music
Oct 14  2002 PSFX1          ; various sound effects using the common PS2 VAG format
Oct 14  2002 RWLDS.DAT      ; another package, this holding models, scripts and other data
Oct 14  2002 SPANISH        ; spanish dialogue audio
Oct 11  2002 VID_PAL.PSS    ; a making-of video, which curiously shows an older build of the game
Oct 14  2002 VSFX1          ; various sound effects using the common PS2 VAG format
Oct 14  2002 VSFX2          ; various sound effects using the common PS2 VAG format
Oct 14  2002 WSFX1          ; various sound effects using the common PS2 VAG format
</code></pre></div></div>

<p>Lots of audio!
The VAG format is a common audio format used for PlayStation 2 games.
You can find a handy tool to convert them to WAV <a href="https://github.com/eurotools/es-ps2-vag-tool">here</a>.</p>

<p>As I also indicated in the list above, the <code class="language-plaintext highlighter-rouge">VID_PAL.PSS</code> video is actually one of those “making of” videos titled “Beneath the Surface”.
Anyway what’s rather curious about it is that it seems to show a build even older than the August 2002 build mentioned earlier, as the HUD has a different design.</p>

<p><img src="/assets/2025/haven-old-video-01.png" alt="Screenshot from the VID_PAL.PSS video." /></p>

<p>An area shown in the video, which I’ve captured in the screenshot above, also appears to be the same starting area as the August 2002 build (with some of the same quirks), though we’ll talk about that more in a future piece.</p>

<p>We’ve also got yet another package, this time <code class="language-plaintext highlighter-rouge">RWLDS.DAT</code>. It’s much bigger than that last one we looked at, and seems to hold the bulk of the game data.</p>

<p>If you’re curious about its name, this is seemingly an abbreviation of “realworlds” which was possibly a working name for the game, or the technology.
We’ll touch on it a little more soon.</p>

<p>Alright, next up we’ll have a look under the <code class="language-plaintext highlighter-rouge">BIN</code> directory before looking at the contents of the <code class="language-plaintext highlighter-rouge">RWLDS.DAT</code> package.
“Bin” is a pretty classic vague name for a place to throw binary files.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Oct 14  2002 AURIA          ; folder for planet-specific data (see below)
Feb  8  2001 CUBE.BIN       ; ...
Oct 14  2002 EXARCO         ; folder for planet-specific data (see below)
Oct 14  2002 FERRA          ; folder for planet-specific data (see below)
Oct 14  2002 FERVEN         ; folder for planet-specific data (see below)
Sep  2  2002 MKSPACE.DAT    ; package containing .pnt (texture) files
Oct 14  2002 NEQUAM         ; folder for planet-specific data (see below)
Oct  8  2002 SYSTEM.RW3     ; some unknown data + planet textures

./AURIA:
Oct  8  2002 HEIGHTS.RW3    ; presumably height information for the planet terrain?
Aug  9  2002 NOISE.PNT      ; noise... :)
Oct  9  2002 PATCHES.RW3    ; unsure
Oct  9  2002 PLANET.RW3     ; some unknown data + planet textures
Oct  9  2002 TEX004.RW3     ; holds textures for the planet in specific sizes (4x4)
Oct  9  2002 TEX008.RW3     ; holds textures for the planet in specific sizes (8x8)
Oct  9  2002 TEX016.RW3     ; holds textures for the planet in specific sizes (16x16)
Oct  9  2002 TEX032.RW3     ; holds textures for the planet in specific sizes (32x32)
Oct  9  2002 TEX064.RW3     ; holds textures for the planet in specific sizes (64x64)
Oct  9  2002 TEX128.RW3     ; holds textures for the planet in specific sizes (128x128)
Oct  9  2002 TEXNDX.RW3     ; unsure

[As the types of files under each sub-dir are the same, I've ommitted the rest]
</code></pre></div></div>

<p>So as far as these immediate folders go, I think we’re done for now with that.
Before we jump into anything else, let’s first take a quick look at the executable (<code class="language-plaintext highlighter-rouge">SLES_512.09</code>) which we saw in the initial root directory.</p>

<h2 id="disassembling-">Disassembling 🔍</h2>

<p>One of the first things I’ll typically check for are debug symbols, or at the least, symbol names, which can sometimes help the poking and prodding process.</p>

<p>It turned out that there’s an interesting quirk with the executable in this game; the main executable is actually just an unpacker, and the real executable is contained within, compressed.</p>

<p>They used RNC ProPack for the compression, for which TT’s Nu2 framework (often described as an engine, but I’d personally call it a framework) provides the methods for decompressing the contained executable into memory when the game launches.
I’d ended up using <a href="https://github.com/lab313ru/rnc_propack_source">this</a> to decompress it.</p>

<p>You seemingly can make the game run just fine directly from the resulting executable once done, so besides providing an unpacking solution, it doesn’t seem that the main executable was doing anything else particularly special.</p>

<p>Why did they do this?
I’ve got no idea.
Originally I’d assumed it was some sort of crude anti-tamper method, but that doesn’t make any sense.</p>

<p>This allows us to open the resulting unpacked executable in <a href="https://github.com/NationalSecurityAgency/ghidra/releases">Ghidra</a>, a popular reverse engineering tool (well, with the necessary <a href="https://github.com/chaoticgd/ghidra-emotionengine-reloaded">extensions</a>.)
Much to our joy, there are at least symbol names but nothing too extensive as far as debug data, but hey, better than nothing.</p>

<p>It’s rather interesting to see the structure of the project, which was broken up between the Nu2 libraries (such as <code class="language-plaintext highlighter-rouge">nucore</code>, <code class="language-plaintext highlighter-rouge">nusound2</code>, <code class="language-plaintext highlighter-rouge">numath</code>, <code class="language-plaintext highlighter-rouge">nu3d</code>, <code class="language-plaintext highlighter-rouge">nups2</code>, <code class="language-plaintext highlighter-rouge">gamelib</code>. <code class="language-plaintext highlighter-rouge">mp2play</code>, <code class="language-plaintext highlighter-rouge">edtools</code> and <code class="language-plaintext highlighter-rouge">coblib</code>), a library called <code class="language-plaintext highlighter-rouge">realwlds</code> and finally of course the game itself. The primary libraries, <code class="language-plaintext highlighter-rouge">nu2</code> and <code class="language-plaintext highlighter-rouge">realwlds</code>, seem to be post-fixed with the target platform, so in this case <code class="language-plaintext highlighter-rouge">.ps2</code> (for instance, <code class="language-plaintext highlighter-rouge">..\nu2.ps2\nu3d\nuscene.c</code> and <code class="language-plaintext highlighter-rouge">..\realwlds.ps2\frcube.c</code>).</p>

<p>The <code class="language-plaintext highlighter-rouge">realwlds</code> library seems to manage all the systems behind the planets and their terrain. In the <a href="https://www.mobygames.com/game/14049/haven-call-of-the-king/credits/ps2/?autoplatform=true">credits</a>, it seems to be referred to as the “Fractal Render Engine” and is credited to Richard Taylor and Alistair Crowe (the latter of whom appears to still work at Traveller’s Tales).</p>

<p>The fact that it’s developed into a separate library makes me wonder if there was perhaps an intention to use this technology for other titles.
No doubt something that Haven’s sequel, which never materialised, would’ve retained.</p>

<p>And unsurprisingly all of this was written in C (<a href="https://youtu.be/G7LJC9vJluU">queue song</a>).</p>

<p>With Ghidra at our aid, there are a great many additional things we can discover.
Such as a selection of command-line arguments that can be passed to the game!</p>

<p>But unfortunately these arguments are actually hardcoded in CD/DVD builds of the game (like the ones we have), however if you open the ELF in your favourite hex editor and go to address <code class="language-plaintext highlighter-rouge">0x507100</code>, you can change at least a few of them to anything of your preference.</p>

<p>So for instance, in the PAL version of the game, there’s a <code class="language-plaintext highlighter-rouge">pal</code> argument which switches the game into a 50hz mode, but if you null it out, you’ll be able to run the PAL version at 60hz instead.</p>

<p>Or, if you null out <code class="language-plaintext highlighter-rouge">startpoint</code>, you’ll get a level select screen!</p>

<p><img src="/assets/2025/haven-level-select.png" alt="Level select screen." /></p>

<p>This is very similar to what you’ll see in the August build, only with a lot more options available.</p>

<p>Alternatively, if you leave the <code class="language-plaintext highlighter-rouge">startpoint</code> argument as is, the argument that proceeds it is actually the argument for <code class="language-plaintext highlighter-rouge">startpoint</code>, which by default is <code class="language-plaintext highlighter-rouge">jp_startgame</code>.
You can change this to some hardcoded points such as <code class="language-plaintext highlighter-rouge">sp_bonus8</code>, to spawn at a point later in the game.</p>

<p>All the start points, however, are available via the level select screen, so I’d recommend just going with that instead.</p>

<p>With the ELF unpacked, we can also use it alongside PCSX2 to see all the globals available that we can tweak to our amusement.</p>

<p><img src="/assets/2025/haven-pcsx2-debugger.png" alt="PCSX2 debugger." /></p>

<p>So, as you can tell, this is just scratching the surface.
I’ll leave the rest of that to someone with a lot more time on their hands though.</p>

<h2 id="dat-package-format-">DAT Package Format 📦</h2>

<p>So before we can get at anything else, we’ll need to figure out how everything is packaged.</p>

<p>I’m not going to lie, this one gave me a little bit of a headache.</p>

<p>First, within the package, the file table is stored at the <em>end</em> of the file rather than the start.
This is followed by a table of null-terminated strings, which are the folder and filenames.
The first four bytes of the package indicate the offset of the file table.
There’s no filename hashing or compression, so that makes things very easy here.</p>

<p>Seems simple enough so far.</p>

<p>But there’s a catch (of course there is).
Strings for each file are not handled quite in the way you might expect, and instead it’s organized as some sort of tree?
For each entry in the string table, there’s an offset into the end of the file, where the strings are located, but then two 16-bit integers that seem to indicate the relationship per file and directory.</p>

<p>That basically gives us a structure like this.
This is in the binary template format supported by <a href="https://rehex.solemnwarning.net/">REHex</a>.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">TTHavenDatHeader</span>
<span class="p">{</span>
	<span class="kt">uint32_t</span> <span class="n">tocOffset</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">tocAllocSize</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="nc">TTHavenDatIndex</span>
<span class="p">{</span>
	<span class="kt">uint32_t</span> <span class="n">offset</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">size</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">compressedSize</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">flags</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="nc">TTHavenDatTocHeader</span>
<span class="p">{</span>
	<span class="kt">uint32_t</span> <span class="n">unknown</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">numFiles</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="nc">TTHavenDatHeader</span> <span class="n">header</span><span class="p">;</span>

<span class="n">FSeek</span><span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">tocOffset</span> <span class="p">);</span>
<span class="k">struct</span> <span class="nc">TTHavenDatTocHeader</span> 	<span class="n">tocHeader</span><span class="p">;</span>
<span class="k">struct</span> <span class="nc">TTHavenDatIndex</span> 		<span class="n">files</span><span class="p">[</span> <span class="n">tocHeader</span><span class="p">.</span><span class="n">numFiles</span> <span class="p">];</span>

<span class="k">struct</span> <span class="nc">TTHavenDatTreeIndex</span>
<span class="p">{</span>
	<span class="kt">uint32_t</span> 	<span class="n">offset</span><span class="p">;</span>
<span class="c1">// these are awful...</span>
	<span class="kt">int16_t</span> 	<span class="n">l</span><span class="p">;</span><span class="c1">// if positive, followed by directory, next index before pop</span>
	<span class="kt">int16_t</span> 	<span class="n">r</span><span class="p">;</span><span class="c1">// if zero, indicates directory</span>
<span class="p">};</span>

<span class="kt">uint32_t</span> <span class="n">numTreeEntries</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">numB</span><span class="p">;</span>

<span class="k">struct</span> <span class="nc">TTHavenDatTreeIndex</span> <span class="n">tree</span><span class="p">[</span> <span class="n">numTreeEntries</span> <span class="p">];</span>
</code></pre></div></div>

<p>I couldn’t quite figure it out but came up with something that seems to be <em>good enough</em>, but certainly isn’t anywhere close to how you’re supposed to be handling these.</p>

<p>Someone with a brain, unlike myself, could probably do a better job at figuring out the correct way to deal with this.
Do let me know if you do!</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">dirDepth</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">DirIndex</span> <span class="n">dirStack</span><span class="p">[</span> <span class="n">STACK_SIZE</span> <span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
	<span class="c1">// i = 1 because first is an empty dir, indicating root, </span>
	<span class="c1">// so we'll just skip for simplicity...</span>
	<span class="k">for</span> <span class="p">(</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numStrings</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span> <span class="p">{</span>
		<span class="c1">// current string</span>
		<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span> <span class="n">tree</span><span class="p">[</span> <span class="n">i</span> <span class="p">].</span><span class="n">offset</span> <span class="p">];</span>

		<span class="c1">// last string is a special weird case... meh</span>
		<span class="k">if</span> <span class="p">(</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numStrings</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">)</span> <span class="p">{</span>
			<span class="c1">// if r is zero, seems to be a directory</span>
			<span class="k">if</span> <span class="p">(</span> <span class="n">tree</span><span class="p">[</span> <span class="n">i</span> <span class="p">].</span><span class="n">r</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">)</span> <span class="p">{</span>
				<span class="n">dirStack</span><span class="p">[</span> <span class="n">dirDepth</span> <span class="p">].</span><span class="n">string</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span>
				<span class="n">dirStack</span><span class="p">[</span> <span class="n">dirDepth</span> <span class="p">].</span><span class="n">index</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
				<span class="n">dirDepth</span><span class="o">++</span><span class="p">;</span>
				<span class="k">continue</span><span class="p">;</span>
			<span class="p">}</span>
		<span class="p">}</span>

		<span class="c1">// build up the string here based on stack ...</span>

		<span class="c1">// if r isn't equal to our index, and it's not zero, </span>
		<span class="c1">// it's probably the index we want to pop back to</span>
		<span class="k">if</span> <span class="p">(</span> <span class="n">tree</span><span class="p">[</span> <span class="n">i</span> <span class="p">].</span><span class="n">r</span> <span class="o">!=</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span>
			<span class="k">for</span> <span class="p">(</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">dirDepth</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span> <span class="p">)</span> <span class="p">{</span>
				<span class="k">if</span> <span class="p">(</span> <span class="n">dirStack</span><span class="p">[</span> <span class="n">j</span> <span class="p">].</span><span class="n">index</span> <span class="o">==</span> <span class="n">tree</span><span class="p">[</span> <span class="n">i</span> <span class="p">].</span><span class="n">r</span> <span class="p">)</span> <span class="p">{</span>
					<span class="n">dirDepth</span> <span class="o">=</span> <span class="n">j</span><span class="p">;</span>
					<span class="k">break</span><span class="p">;</span>
				<span class="p">}</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>With that out of the way, we can pretty much get everything extracted from the package with a good majority of the filenames intact, as far as I can tell.</p>

<p>You can find my implementation of a loader for the format <a href="https://github.com/QuartermindGames/hei/blob/3d3a2e3c93056ea10e6666462ae2eb11ac3af505/extras/package/package_haven_dat.c">here</a>, to give you a more complete idea if the above isn’t clear enough.
Just mind it’s a bit of a mess, as it just needed to extract the packages to satisfy my own curiosity and nothing more.</p>

<p>So, the time has come!
Tally-ho!</p>

<p><img src="/assets/gifs/cat-jump-box.gif" alt="A cat jumping into a box" /></p>

<p>What’s that left us with?
Hm, well with the <code class="language-plaintext highlighter-rouge">RWLDS.DAT</code> package extracted, let’s take a look.</p>

<p><img src="/assets/2025/haven-rwlds-extracted.png" alt="Root directory of an extracted ISO" /></p>

<p>There’s a lot here! Just what I like to see. 🤤</p>

<p>We’re not going to go over <em>everything</em> today, but let’s make a start taking a look around.</p>

<h2 id="game-flow-">Game Flow 🌊</h2>

<p>First, in the root directory, we’ve got a collection of <code class="language-plaintext highlighter-rouge">.flo</code> files.
These are used for a number of things, from specifying areas of the game to load, transitions betweens areas of the game, cutscenes and more. Generally, they control various parts of the games flow, through commands. Probably hence the extension, <em>flo</em>.</p>

<p>Internally, perhaps unsurprisingly based on what I just said, this is referred to as the <em>Game Flow</em>.</p>

<p>Per <code class="language-plaintext highlighter-rouge">haven.flo</code>, which is the main script that’s loaded on startup, we’ve actually got some documentation on the various supported commands and what they do (and if it’s not obvious, a semicolon is interpreted as a comment).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;-----------------------------------------------------------------------------------------------
; Reserved System Commands;
;-----------------------------------------------------------------------------------------------
;
; Traditional Semaphore support
;
; AddSem    &lt;sem&gt; &lt;val&gt; &lt;rng&gt;   - 
; ResetSem  &lt;sem&gt;       	- state[sem] = 0
; SetSem    &lt;sem&gt; &lt;val&gt; 	- state[sem] = val
; SignalSem &lt;sem&gt;       	- state[sem] = state[sem] + 1
; WaitSem   &lt;sem&gt;		- while(state[sem] = 0); state[sem] = state[sem] - 1
; WaitSemMulti &lt;sem0...sem31&gt;   - for n=1 to numsem; if state[sem] != 0 state[sem] - 1, break; next
;
; Using Semaphores as crude variables
;
; WaitSemEQ &lt;sem&gt; &lt;val&gt;		- while(state[sem] != val);
; WaitSemLT &lt;sem&gt; &lt;val&gt;		- while(state[sem] &gt;= val);      
; WaitSemGT &lt;sem&gt; &lt;val&gt;		- while(state[sem] &lt;= val);      
; WaitSemNE &lt;sem&gt; &lt;val&gt;		- while(state[sem] == val);      
; IfSemEQ   &lt;sem&gt; &lt;val&gt; &lt;lab&gt;	- if (state[sem] = val)  pc = lab
; IfSemLT   &lt;sem&gt; &lt;val&gt; &lt;lab&gt;	- if (state[sem] &lt; val)  pc = lab
; IfSemGT   &lt;sem&gt; &lt;val&gt; &lt;lab&gt;	- if (state[sem] &gt; val)  pc = lab
; IfSemNE   &lt;sem&gt; &lt;val&gt; &lt;lab&gt;	- if (state[sem] != val) pc = lab
;
; Flow Control / Debugging
;
; GoTo	    &lt;lab&gt;       	- pc = lab
; GoSub	    &lt;lab&gt;		- stack[sp++] = pc; pc = lab
; Return			- pc = stack[--sp]
; Pop				- --sp
; PopAll			- sp = 0
; Break				- temporarily releases program flow 
; Halt				- suspends program flow indefinitely
; DebugMsg &lt;message&gt;		- type message to tty
; BeginGuard &lt;name&gt; &lt;lab&gt;       - if the named guard is set, jumps to label, otherwise sets the named guard
; EndGuard &lt;name&gt;               - clears the named guard
;
;-----------------------------------------------------------------------------------------------
; Haven Specific Commands;
;-----------------------------------------------------------------------------------------------
; --FRACTAL STUFF--
; RWInitLevel	&lt;level&gt;
; RWBuffer	&lt;bufftype - HIGH/LOW&gt; [&lt;buffsize&gt;] - omitting buffsize will cause either the current planets buffer size or the current size of the rw buffer to be used - set initlevel to 2 to release it
; RWPlanetMesh	&lt;ON/OFF&gt;
; RWPlanetRender&lt;ON/OFF&gt;
; RWProcessEnable &lt;arg&gt; - arg 0 - processing disabled, arg 1 - processing enabled, -ve - processing disabled in -ve frames
; RWTextures	&lt;ON/OFF&gt;
; RWDontRenderPlate &lt;plateid or -1&gt;
; SkyVisible &lt;ON/OFF&gt; - allows level with sky off to be overridden
; SeaVisible &lt;ON/OFF&gt; - allows level with sea off to be overridden
; RWSetPlanetTime &lt;planet name&gt; &lt;time of day 0-24&gt;
;
; --LEVCON STUFF--
; StopLogic	  &lt;flags&gt;   - stops internal logic from running - flags are a combination of WORLD ISLAND or LEVEL - no flags equates to all flags being present
; SyncLogic	  &lt;flags&gt;   - brings internal logic into sync with current state, allows logic to run if its been knocked out - flags are a combination of WORLD ISLAND or LEVEL - no flags equates to all flags being present
; ShowLevel	  &lt;levname&gt; - allows a loaded level to draw
; HideLevel	  &lt;levname&gt; - stops a level being drawn
; PreLoadLevel	  &lt;levname&gt; - loads a level without activating it
; LoadLevel	  &lt;levname&gt; - loads and activates a level (just activates if already loaded)
; PreLoadLevelNB  &lt;levname&gt; - loads a level without activating it - non blocking variant - script continues
; LoadIsland	  &lt;islandname&gt;
; LoadWorld	  &lt;worldname&gt;
; IfLevelProgress &lt;levname&gt; &lt;bit ix&gt; &lt;label&gt;
; IfNotLevelProgress &lt;levname&gt; &lt;bit ix&gt; &lt;label&gt;
; SetLevelProgress &lt;levname&gt; &lt;bit ix&gt;
; SetSpecialVis    &lt;levname&gt; &lt;specialname&gt; &lt;ON/OFF&gt; - sets the visibility state of the specified special object - setting is lost in the event of a reload
;
; --CONFLICTS--
; ResetConflict &lt;conname&gt; - calls the Reset() function of the conflict
; LoadConflict  &lt;conname&gt; - loads the named conflict
; DumpConflict  &lt;conname&gt; - requests the dumping of the named conflict
; DumpBuffer	&lt;LOW/SUB/HIGH&gt; - requests a dump from the specified buffer
; ConflictSpawnDelay &lt;delay&gt; - prevents auto spawning conflicts (space, sky etc) from occurring for &lt;delay&gt; frames, -1 stops them happening ad-infinitum
;
; --CUTSCENES--
; PlayLevelCutScene &lt;levname&gt; &lt;cutname&gt; - starts the cutscene - cutname, in level levname
; WaitLevelCutScene &lt;levname&gt; &lt;cutname&gt; [time offset] - waits for the cutscene - cutname, in level levname to finish - the optional time offset allows you to wait for a point n frames into the cutscene (+ve) or n frames from the end of the cutscene (-ve)
; Fade &lt;out/in&gt; [&lt;black/white&gt;] - fades the game to black or white - black by default
; WaitFade                      - waits for fade command to complete fade in or out
; PlayFMV &lt;fmvname&gt; &lt;HIGH/SUB/SUBLOW/SUBHIGH/LOW&gt; - plays an fmv file then waits for it to finish
; TexAnimSetSignal   &lt;sigid&gt;            - sets the texture animation signal identified by sigid to ON
; TexAnimResetSignal &lt;sigid&gt;            - sets the texture animation signal identified by sigid to OFF
; OnCutEvent         &lt;val&gt; &lt;label&gt;      - adds an event handler for the "FlowEvent" locator in cutscenes. When the animatable parameter is set to &lt;val&gt; and the locator is made visible, &lt;label&gt; will be called
;
; --GAMEPLAY--
; PlacePlayer &lt;x&gt; &lt;y&gt; &lt;z&gt; &lt;ay&gt; - puts the player at a specific location and resets the game camera
; PlacePlayerAtLevelStart &lt;level&gt; - puts the player at the setup start location for the named level
; SetCheckPoint [&lt;x&gt; &lt;y&gt; &lt;z&gt; &lt;ay&gt;] - sets a checkpoint at the current player position / orientation - if the optional parameters are specified, these are used to place the checkpoint instead
; IfPickupEQ &lt;pikname&gt; &lt;val&gt; &lt;label&gt; - if the pickup - pikname - equals val, then jump to label - valid piknames are "BLUEFEATHER","REDFEATHER","ANTIDOTE","GOLDCOG","HEROSTONE"
; IfPickupNE &lt;pikname&gt; &lt;val&gt; &lt;label&gt; - if the pickup - pikname - doesn't equal val, then jump to label - valid piknames are "BLUEFEATHER","REDFEATHER","ANTIDOTE","GOLDCOG","HEROSTONE"
; IfPickupGE &lt;pikname&gt; &lt;val&gt; &lt;label&gt; - if the pickup - pikname - is greater than or equal to val, then jump to label - valid piknames are "BLUEFEATHER","REDFEATHER","ANTIDOTE","GOLDCOG","HEROSTONE"
; IfPickupGT &lt;pikname&gt; &lt;val&gt; &lt;label&gt; - if the pickup - pikname - is greater than val, then jump to label- valid piknames are "BLUEFEATHER","REDFEATHER","ANTIDOTE","GOLDCOG","HEROSTONE"
; IfPickupLE &lt;pikname&gt; &lt;val&gt; &lt;label&gt; - if the pickup - pikname - is less than or equal to val, then jump to label - valid piknames are "BLUEFEATHER","REDFEATHER","ANTIDOTE","GOLDCOG","HEROSTONE"
; IfPickupLT &lt;pikname&gt; &lt;val&gt; &lt;label&gt; - if the pickup - pikname - is less than val, then jump to label - valid piknames are "BLUEFEATHER","REDFEATHER","ANTIDOTE","GOLDCOG","HEROSTONE"
; JonSave &lt;startpoint&gt;               - saves a startpoint
;
; --VEHICLES--
; EnterVehicle &lt;vehicle name&gt; [USECOBPOS] - puts player into vehicle - only works with VEHCRAFT type at the moment - usecobpos makes the vehicle use its cobs current matrix rather than what's in its savegame structure
; LeaveVehicle &lt;vehicle name&gt; - [NOJUMP/CUTCON] takes player out of vehicle - only works with VEHCRAFT type at the moment - options not really supported at the mo.
; VehicleCameraEnable - Activates the vehicle camera - needs Chris to explain this
; SetNavigator &lt;wldname&gt; &lt;islename&gt; &lt;x y z&gt; - sets the location of the navigation beacon - wldname or islename can be substituted with none
;
; --MISC--
; BizarreFlags &lt;flag&gt; &lt;val&gt; - Bizarre flags are app specific variables that can be set and easily picked up by the application and used for any purpose.
; Currently assigned are;
; 0 - if 1 - sea status is overriden and the sea is drawn
;
;-----------------------------------------------------------------------------------------------
; Haven Specific Semaphores;
;-----------------------------------------------------------------------------------------------
; edit_mode        - set to 1 if running in edit mode otherwise 0 - reference this if a levels script needs to 
;                    behave differently in edit mode 
;		   
; rw_init_level    - set to reflect the current state of rw_init_level - can be used to change behaviour conditionally
;                    on the current state of the fractal
;
; in_craft_biplane - if 1, player is in the biplane
;
; in_craft_wasp    - if 1, player is in the wasp
;
; in_craft_shuttle - if 1, player is in the shuttle
;
; in_craft         - if 1, player is in one of the craft

;-----------------------------------------------------------------------------------------------
; Haven Reserved Labels;
;-----------------------------------------------------------------------------------------------
; Start - Initial entry point when starting a game
; Restart - Position in script that we jump back to when restarting the game
</code></pre></div></div>

<p>This pleases me.
The glorious bastards even kept this documentation updated between the August and final build.</p>

<p><img src="/assets/gifs/doctor-grin.gif" alt="Grinning Tom Baker." /></p>

<p>That said, there are actually three additional commands that aren’t documented.
The <code class="language-plaintext highlighter-rouge">#include</code> at the bottom of <code class="language-plaintext highlighter-rouge">haven.flo</code>, for instance; these look like pre-processor macros you might see in C/C++ but they’re actually handled through the same command system as everything else.</p>

<p>And then there are two others that are undocumented,
<code class="language-plaintext highlighter-rouge">#strict</code> and <code class="language-plaintext highlighter-rouge">#sloppy</code>. These actually work together; the <code class="language-plaintext highlighter-rouge">#sloppy</code> command just disables the <code class="language-plaintext highlighter-rouge">#strict</code> state.</p>

<p>But what <em>is</em> the strict state?
If it’s enabled, and the parser encounters an invalid number of arguments for a command, it’ll throw an error rather than silently spitting out a little message that might be missed.
As far as I can tell, that’s it.</p>

<p><code class="language-plaintext highlighter-rouge">#strict</code> is unused in Haven, and <code class="language-plaintext highlighter-rouge">#sloppy</code> is explicitly set right at the start of execution per <code class="language-plaintext highlighter-rouge">haven.flo</code>.
This is despite the fact that sloppy seems to be the default state?
Though perhaps that was different for debug builds, or used to be the case but changed before shipping.</p>

<p>You probably noticed there’s an <code class="language-plaintext highlighter-rouge">edit_mode</code> semaphore that’s mentioned.
This toggles a global variable that’s in memory called <code class="language-plaintext highlighter-rouge">game_editmode</code>, which actually does a lot of different things that’s a little hard for me to summarise, but it will for instance give you a different (more primitive) start menu with a screenshot option.</p>

<p><img src="/assets/2025/haven-edit-start.png" alt="Simple start menu in edit mode." /></p>

<p>And otherwise it’ll skip a lot of the rendering, and particularly skip much of the world rendering and simulation.
So you’ll still be able to load into levels and run around, but otherwise anything involving the fractal rendering will be invisible to you.</p>

<p>I’d not had the time to verify it to the same extent but as far as it appears, this works much the same in the August build.</p>

<p>When looking at things in Ghidra, we can see that all the custom Haven commands are registered per an <code class="language-plaintext highlighter-rouge">InitGameFlow</code> method which is called on startup.
This seems to register each command through a handy little method called <code class="language-plaintext highlighter-rouge">gfcAddCommandType</code>, while each semaphore (variable) is registered via a <code class="language-plaintext highlighter-rouge">gfcAddSem</code> method.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* gfsupp.c */</span>

<span class="n">undefined4</span>
<span class="nf">InitGameFlow</span><span class="p">(</span><span class="n">undefined4</span> <span class="n">param_1</span><span class="p">,</span><span class="n">undefined4</span> <span class="n">param_2</span><span class="p">,</span><span class="n">undefined4</span> <span class="n">param_3</span><span class="p">,</span><span class="n">undefined4</span> <span class="n">param_4</span><span class="p">,</span>
            <span class="n">undefined4</span> <span class="n">param_5</span><span class="p">,</span><span class="n">undefined4</span> <span class="n">param_6</span><span class="p">,</span><span class="n">undefined4</span> <span class="n">param_7</span><span class="p">,</span><span class="kt">long</span> <span class="n">param_8</span><span class="p">,</span><span class="kt">int</span> <span class="o">*</span><span class="n">param_9</span><span class="p">,</span>
            <span class="n">undefined8</span> <span class="n">param_10</span><span class="p">)</span>
<span class="p">{</span>
  <span class="p">[...]</span>
  
  <span class="n">gfcInit</span><span class="p">();</span>
  <span class="n">sem_edit_mode</span> <span class="o">=</span> <span class="p">(</span><span class="n">undefined</span> <span class="o">*</span><span class="p">)</span><span class="n">gfcAddSem</span><span class="p">(</span><span class="mh">0x603f28</span><span class="p">,</span><span class="n">game_editmode</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>

  <span class="p">[...]</span>
  
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f38</span><span class="p">,</span><span class="n">executeResetConflict</span><span class="p">,</span><span class="n">parseResetConflict</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f48</span><span class="p">,</span><span class="n">executeLoadConflict</span><span class="p">,</span><span class="n">parseLoadConflict</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603048</span><span class="p">,</span><span class="n">executeDumpConflict</span><span class="p">,</span><span class="n">parseDumpConflict</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f58</span><span class="p">,</span><span class="n">executeSkyVisible</span><span class="p">,</span><span class="n">parseSkyVisible</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f68</span><span class="p">,</span><span class="n">executeSeaVisible</span><span class="p">,</span><span class="n">parseSeaVisible</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f78</span><span class="p">,</span><span class="n">executeDumpBuffer</span><span class="p">,</span><span class="n">parseDumpBuffer</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f88</span><span class="p">,</span><span class="n">executeRWInitLevel</span><span class="p">,</span><span class="n">parseRWInitLevel</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603f98</span><span class="p">,</span><span class="n">executeRWBuffer</span><span class="p">,</span><span class="n">parseRWBuffer</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x603fa8</span><span class="p">,</span><span class="n">executeRWGetBuffer</span><span class="p">,</span><span class="n">parseRWGetBuffer</span><span class="p">);</span>
  <span class="p">[...]</span>
</code></pre></div></div>

<p>And all the system commands are registered in the <code class="language-plaintext highlighter-rouge">gfcInit</code> method that we can see called via <code class="language-plaintext highlighter-rouge">InitGameFlow</code> just before the Haven commands are registered.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* gameflow.c */</span>

<span class="kt">void</span> <span class="nf">gfcInit</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">gfc_num_cmd_types</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">gfc_num_sems</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">gfc_num_guards</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">gfc_global_data_ptr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c30</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">gfparseStrict</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c38</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">gfparseSloppy</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c40</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">gfparseInclude</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c50</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">gfparseAddSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c58</span><span class="p">,</span><span class="n">gfexecuteResetSem</span><span class="p">,</span><span class="n">gfparseResetSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c68</span><span class="p">,</span><span class="n">gfexecuteSetSem</span><span class="p">,</span><span class="n">gfparseSetSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c70</span><span class="p">,</span><span class="n">gfexecuteSignalSem</span><span class="p">,</span><span class="n">gfparseSignalSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c80</span><span class="p">,</span><span class="n">gfexecutePollSem</span><span class="p">,</span><span class="n">gfparsePollSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c88</span><span class="p">,</span><span class="n">gfexecuteWaitSem</span><span class="p">,</span><span class="n">gfparseWaitSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602c90</span><span class="p">,</span><span class="n">gfexecuteWaitSemMulti</span><span class="p">,</span><span class="n">gfparseWaitSemMulti</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602ca0</span><span class="p">,</span><span class="n">gfexecuteWaitSemEQ</span><span class="p">,</span><span class="n">gfparseWaitSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602cb0</span><span class="p">,</span><span class="n">gfexecuteWaitSemLT</span><span class="p">,</span><span class="n">gfparseWaitSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602cc0</span><span class="p">,</span><span class="n">gfexecuteWaitSemGT</span><span class="p">,</span><span class="n">gfparseWaitSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602cd0</span><span class="p">,</span><span class="n">gfexecuteWaitSemNE</span><span class="p">,</span><span class="n">gfparseWaitSem</span><span class="p">);</span>
  <span class="n">gfcAddCommandType</span><span class="p">(</span><span class="mh">0x602ce0</span><span class="p">,</span><span class="n">gfexecuteIfSemEQ</span><span class="p">,</span><span class="n">gfparseIfSem</span><span class="p">);</span>
  <span class="p">[...]</span>
</code></pre></div></div>

<p>There are two callbacks passed into the <code class="language-plaintext highlighter-rouge">gfcAddCommandType</code> method, one for handling the actual execution of the command and another for parsing the arguments for that command.
The first argument is of course the name of the command (Ghidra is just showing the addresses to those strings here, rather than the strings themselves).</p>

<p>You can see that nothing is provided for the execution callback for the first four commands above, under <code class="language-plaintext highlighter-rouge">gfcInit</code>, and this is because they’ll set up what they need through the parse callback instead.</p>

<p>So, without going over the entire implementation of the damn thing and spending another 1,000 words explaining everything as I’d be so tempted to do; it’s all quite powerful but all exposed through a fairly tidy API.
Unfortunately, there are several other types of scripts, and each has its own way of doing things. <em>Sigh…</em></p>

<p>Their script syntax here is actually case-insensitive, so for example the documentation above specifies <code class="language-plaintext highlighter-rouge">IfSemEQ</code> but you’ll more typically see it written as <code class="language-plaintext highlighter-rouge">ifsemeq</code> instead. As a matter of fact, it looks like even labels are case-insensitive too… Wouldn’t have been my choice, but hey.</p>

<p>As mentioned earlier, when the game starts up, it will execute from the start of <code class="language-plaintext highlighter-rouge">haven.flo</code> as that’s the primary script that’s explicitly loaded by the game.
From there, all the commands up until the <code class="language-plaintext highlighter-rouge">Halt</code> are executed, but the rest of the script will continue to be parsed.</p>

<p>At the end of that same file, you’ll see includes for the other <code class="language-plaintext highlighter-rouge">.flo</code> files we also saw in the root directory.
Each of these are additional scripts that consolidate everything for each given planet, so the <code class="language-plaintext highlighter-rouge">ferra.flo</code> script contains everything for the first planet in the game, Ferra.</p>

<p>A little extra tidbit is that there is a <code class="language-plaintext highlighter-rouge">Start</code> label just above the <code class="language-plaintext highlighter-rouge">Restart</code> label near the beginning of the file, but as far as I can tell it’s not actually referenced (instead, only the latter is used), which is just as well as both of them will result in the same thing.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Start:


Restart:
	SetNavigator none none 0 0 0

	ResetSem  LevShaftCutSceneEnded 
	ResetSem  guard_stationactivate 
	ResetSem  guard_testsiteend     
	ResetSem  guard_volctop	        
	ResetSem  ferry_loading
	Resetsem  newhilltotopguard     
	ResetSem  in_craft_biplane
	ResetSem  in_craft_wasp
	ResetSem  in_craft_shuttle
	ResetSem  in_craft
	ResetSem  hometownLoadScript_sem

	ResetSem  DenyInputVehicles
	ResetSem  guard_puffdragon
	
	ResetSem  MineEntSem
	ResetSem  inhibit_ml_mos
	ResetSem  inhibit_ml_ref

	ResetAllGuards

	TexAnimResetSignal 0
	TexAnimResetSignal 1
	TexAnimResetSignal 2
	TexAnimResetSignal 3
	TexAnimResetSignal 4
	TexAnimResetSignal 5
	TexAnimResetSignal 6
	TexAnimResetSignal 7
	TexAnimResetSignal 8
	TexAnimResetSignal 9
	TexAnimResetSignal 10
	TexAnimResetSignal 11
	TexAnimResetSignal 12
	TexAnimResetSignal 13
	TexAnimResetSignal 14
	TexAnimResetSignal 15

	;; Hack

	BizarreFlags 0 0	; clear frig-o-sea enable
	BizarreFlags 1 0	; tailgun hold off flag
	BizarreFlags 2 0	; level/atmosphere sorting control

	ConflictSpawnDelay 0	; reset spawn system
;;	SetLevelProgress village 2
;	SignalSem  no_sky_battle	; uncomment this line to disable the sky battle - for free flight around the planet with no attackers

; Main idle point. Script sits here most of the time waiting for subroutines to happen.
	Halt
</code></pre></div></div>

<p>I’d guess at some stage <code class="language-plaintext highlighter-rouge">Start</code> was used and executed some additional commands that were since scrapped, and they stopped bothering to use the label at all.</p>

<p>We could probably keep going, but hopefully you’ve got the idea on how these work now.</p>

<h2 id="dialog-script-">Dialog Script 🗣</h2>

<p>So for this one, we’ve only got the one script which is <code class="language-plaintext highlighter-rouge">dialog.txt</code>. There’s no fancy file extension, so no fun for us there.
That said, there’s some very limited documentation provided which makes things a little easier for us to understand.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;;
;; Commands
;;
;;	ACTOR id name colourcode eyelevel
;;	BEGINSCRIPT id
;;	  ORIGIN x y z
;; 	  RANGE n
;;	  ONCE  
;;
;;	  SETPOS actor pos x y z face actor 
;;	  CAMPOS actor shot[back|side|front] transition[cut|pan] nframes
;;        CAMTRG actor
;;	  TEXT actorname "text"
;;	ENDSCRIPT
;;
;; The actor and script id's are used directly by the code 
;;  to setup the dialog so changed to these will require
;;  changes in the code. Everything else can be changed freely
;;  
</code></pre></div></div>

<p>This isn’t <em>everything</em> though, but it’s a start.
There are actually a couple extra commands available.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">move &lt;actor&gt; &lt;x y z&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">face &lt;actor&gt; &lt;actor&gt;</code></li>
</ul>

<p>The following is then provided at the start of the dialog file, just after the documentation, essentially outlining the details of each actor to be used in a conversation.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ACTOR  1  Teque  "Haven"  "#0#"  0.7
ACTOR  2  Bloke2 "C2"	  "#13#" 0.9
ACTOR  3  Bloke3 "C3"     "#13#" 0.9
ACTOR  4  Bloke4 "C4"     "#13#" 0.9
ACTOR  5  Bloke5 "C5"     "#13#" 0.9
ACTOR  6  Bloke6 "C6"     "#13#" 0.9
ACTOR  7  Bloke7 "C7"     "#13#" 0.9
ACTOR  8  Bloke8 "C8"     "#13#" 0.9
</code></pre></div></div>

<p>If you refer to the documentation from earlier, you’ll notice it doesn’t <em>quite</em> align with it, as there’s actually two names provided here, but the first appears to work essentially like an alias and then the second name is what will actually be displayed.</p>

<p>Evidently given this and the two other missing commands, the documentation at the start of the file is a little out of date.</p>

<p>You may have also noticed that Haven (which if you hadn’t guessed it, is the name of the main character) is referenced as <em>Teque</em> here.</p>

<p>This is quite likely an earlier name for the main character, as this name is used in a some other spots too (for instance, Haven’s house is referenced to as <code class="language-plaintext highlighter-rouge">teqhouse</code>, and a second copy of Haven’s model is stored under <code class="language-plaintext highlighter-rouge">lowteq</code>).</p>

<p>There are a number of test and placeholder conversations left in here.</p>

<p>So for instance, here’s an entry with an ID of zero, clearly to catch any instances where the ID provided for a conversation somewhere else has been left unset.
This was quite likely done so either the developers or QA would notice and remedy it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;;;;;;;;;;;;
;; no script
BEGINSCRIPT 0
;;  FACE    teque nobody
  
  CAMPOS  scene side pan 30
  CAMTRG  scene
  TEXT	  scene "My dialog script id has not been set"
  TEXT    teque "Oh dear"
  TEXT	  scene "Please find someone to set my dialog script id"
  TEXT	  teque "OK"
ENDSCRIPT
</code></pre></div></div>

<p>And then we’ve got another test or placeholder script following that.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;;;;;;;;;;;;;;
;; start bloke
BEGINSCRIPT 2
  ;; Script global settings
  ORIGIN  -546.5 19.2.5 117.2
  RANGE   1

  ;; Script dialog &amp; camera sequence  

  FACE    teque bloke2

  CAMPOS  teque back pan 30
  CAMTRG  bloke2
  TEXT	  bloke2 "Welcome to Hero Island"
  
  CAMPOS  scene side pan 30
  CAMTRG  scene
  TEXT    teque  "Hello, my name is Haven"
  TEXT    bloke2 "You have no nose"
  TEXT    teque  "Dont say it"
  
  CAMPOS  bloke2 front cut
  CAMTRG  bloke2
  TEXT    bloke2 "How do you smell"
  
  CAMTRG  teque
  TEXT    teque  "AARRRGGG"
  
ENDSCRIPT
</code></pre></div></div>

<p>This one’s rather curious as the “bloke” in the conversation suggests that Haven doesn’t have a nose, which may be a reference to an older design featured in the video by Jon Burton <a href="https://www.youtube.com/watch?v=R__HquoudVc">here</a>.</p>

<p><img src="/assets/2025/haven-old-no-nose.png" alt="Old alien Haven design." /></p>

<p>Or it’s nothing more than a silly joke, and I’m reading too much into it.</p>

<p>The entirety of this file is filled with unused, placeholder dialog, some of which was still in use in the August prototype but is otherwise left unused in the final game.
And with that said, as far as I can tell, this entire dialog system actually goes <strong>completely unused</strong> in the final game, and instead it uses cutscenes (an entirely separate system, internally) to convey dialog and story to the player.</p>

<p>It’s curious why this is.
Perhaps it was decided having cutscenes for these situations simply looked better.
Perhaps originally the game was to feature more dialog, and that ended up not being the case?
Who knows, but it’s surprising that it seems it was cut fairly late in production given that cutscenes likely would’ve taken more work.</p>

<p>The dialog system is entirely bespoke to Haven, but the system used for cutscenes is actually a system Nu2 appears to provide (though Haven has its own intermediate scripting language for cutscenes which we’ll briefly cover later).</p>

<p>If you’re curious what the dialog system looked like in action however, you can see it <a href="https://youtu.be/TFoSzh3hIPc?t=2004">here</a> per a video on our YouTube channel.</p>

<h2 id="placements">Placements</h2>

<p>Amongst the files, there’s a <code class="language-plaintext highlighter-rouge">placements.pmt</code> file.
This can be found in the root of the extracted data.
It’s likely <code class="language-plaintext highlighter-rouge">.pmt</code> is just an abbreviation for <em>placement</em>.</p>

<p>There isn’t much to it, so I can just give you the contents below.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"SamSite"
"Fuel"
"Waypoint"
"mosshield"
"fervenshield"
"restart"
</code></pre></div></div>

<p>In the earlier August build, you can access the placement editor that lists the contents of this file, though it’s not really immediately obvious to me what they are and what they should do.</p>

<p>You can see some footage of the placement editor <a href="https://youtu.be/EMzQUuCoHF4?t=3789">here</a>.</p>

<p>Curiously, the last three, <code class="language-plaintext highlighter-rouge">mosshield</code>, <code class="language-plaintext highlighter-rouge">fervenshield</code> and <code class="language-plaintext highlighter-rouge">restart</code> were added sometime after the aforementioned August build, but as far as I can tell they’re not referenced anywhere?</p>

<p>Again, like the dialog system, the placements system seems bespoke to Haven. That said, as far as I can tell these are only really relevant to the placement editor? And even then I’m not entirely sure if I’m understanding what these actually do from there.</p>

<p>From how the editor displays them, depending on what you do, they look like waypoints.
Anyway, let’s move on.</p>

<h2 id="randomisation-">Randomisation 🔢</h2>

<p>Under the extracted content, there’s a <code class="language-plaintext highlighter-rouge">bin/rand.dat</code> file.
This appears to be unused by the game, and instead the game uses a table in memory, which matches the <code class="language-plaintext highlighter-rouge">rand.dat</code> data.</p>

<p>I’m not really sure if I entirely understand the purpose of having a fixed table for randomisation in this instance.
Why they didn’t just do this algorithmically is a bit strange?</p>

<p>Just to explain a bit further, essentially there is a pointer initially set to the start of the table in memory.
At the start of each frame, it’ll check to make sure that the position has not moved out of scope of the table.
This is then used to produce “random” numbers in the game, as the position in the table is gradually incremented at points during the frame.</p>

<p>It’s very possible there’s something I’m missing, but it almost wouldn’t surprise me if the position could or does go out of scope of the table, given the check appears to only be at the start of each frame and the position is incremented at multiple points during the frame.</p>

<p><img src="/assets/gifs/scared-cat.gif" alt="A rather concerned cat gif." /></p>

<p>Probably a calculated risk…
Anyway, let’s not worry about that.
Nothing to see here.</p>

<h2 id="universe-">Universe 🌌</h2>

<p>We’ll take a quick look at the <code class="language-plaintext highlighter-rouge">new.uni</code> script which is included in the root directory of the extracted data.
The extension for this format, if you couldn’t figure it out, seems to be an abbreviation of <em>universe</em>.</p>

<p>These are a lot more complicated to explain than the game flow scripts we looked at. They outline each available “world”, each of the “islands” on that world, then the “levels” on each island.</p>

<p>There’s at least some basic documentation provided within the script itself via the <code class="language-plaintext highlighter-rouge">;</code> comments.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RWCONFIG HIGH 	9000000							; Assign a buffer to the fractal - global setting, may also be done per world
TEXANIMFILE 	"texanim.cfg"	40960					; name of texanim cfg file and buffer size for texanims

[...]

;;BEGINWORLD testworld
;;	RWCONFIG HIGH 	9000000						; Assign a buffer to the fractal - may ultimately be done on a per level or world basis
;;	PATH		world1						; where to look for bits specific to the world
;;	WBSCENE		clouds						; scene containins atmos,clouds,sea etc....
;;;	RWNAME		forest forest5					; name of realworld data set to load - 2nd parameter is wacky directory override
;;
;;	BEGINISLAND testisle
;;		PATH		island3					; where to look for bits specific to the island
;;		LOSCENE	lo_isl2						; name of low res scene
;;;;		PLATEID 	863					; fractal plate we're sat on
;;		PLATEID 	852					; fractal plate we're sat on
;;		PLATEPOS 	0,1410,0                		; origin offset on the plate
;;		ACTIVERANGE	1000					; range at which to activate island
;;		LOADTRIGGERS	island3					; loadtriggers file for this island
;;
;;;;		BEGINLEVEL boattest BUFFER high 5000000	
;;;;			PATH		boattest			; where to look for bits specific to the level
;;;;			HISCENE	boattest				; name of scene/terrain/etc file to load
;;;;;;			POS		0,0,0				; location of level - used by visibility guff
;;;;			STARTPOS	-66.4 5.26 -15			; starting position for player
;;;;			PARTICLES   particle
	
[...]
</code></pre></div></div>

<p>This sets everything up in conjunction with their fractal rendering engine, and this involves multiple different formats, which we would need to look at individually.</p>

<p>One of these formats is at least <code class="language-plaintext highlighter-rouge">.gsc</code>. These seem to contain textures, geometry and other data. They’re usually used to provide the main geometry for a level.</p>

<p>And another is <code class="language-plaintext highlighter-rouge">.rw3</code>. They’re found under <code class="language-plaintext highlighter-rouge">DATA/BIN/</code>, with each subdirectory then providing the data for each respective world. They contain textures and other data. The <code class="language-plaintext highlighter-rouge">TEX*.RW3</code> files provide different sized textures for the terrain, which presumably are streamed in and out.</p>

<p>There are several others, but we’ll skip those for the sake of time.</p>

<p>It’s possible to edit the universe script, as I’ll demonstrate <a href="#exploring-the-unseen-">later on</a>, to load in other locations but can, probably unsurprisingly, crash the game if you’re not careful.</p>

<p>Curiously, the way this is implemented suggests there was perhaps going to be support for loading other universe scripts? Or they just wanted to keep things flexible.</p>

<p>There’s only one of these scripts in the final version of the game, which is the rather curiously named <code class="language-plaintext highlighter-rouge">new.uni</code>.
The August build has a few alternate revisions of that script.
Some warning messages seem to indicate it may have been called <code class="language-plaintext highlighter-rouge">game.uni</code> at some point.</p>

<p>There is a start-up argument, <code class="language-plaintext highlighter-rouge">uni</code>, which will allow you to override which universe script is loaded on startup, but as we covered before, these arguments are hardcoded in the builds we’ve got.</p>

<p>You can see the end result of a lot of this working together, <a href="https://youtu.be/7j2ivxJfX8w?t=1251">here</a>.
The player is able to approach a world from space without any loading screens, different main areas of the game can be seen which are the islands, represented by their <code class="language-plaintext highlighter-rouge">LOSCENE</code> geometry, and then once a player lands they move into the actual level instance itself.</p>

<p>Apologies this is a little vague.
I’d failed to document my findings when editing these, so for the sake of time, I’ve decided to skip on a lot of this for now, and there’s so much to cover that it would probably be better served as its own article.</p>

<h2 id="models">Models</h2>

<p>Model used by the game have a <code class="language-plaintext highlighter-rouge">.ghg</code> extension. They are all stored under their own subdirectories alongside their animations, under the <code class="language-plaintext highlighter-rouge">chars</code> directory, within the <code class="language-plaintext highlighter-rouge">RWLDS.DAT</code> package.</p>

<p>From what I’ve understood from looking around a little bit, there’s a variations of this format used for other titles using the Nu2 framework, but it doesn’t look like there was any work done towards the version used in Haven specifically.</p>

<p>I’d unfortunately not finished entirely reversing them, and don’t think I’ll do so for the time otherwise we’ll be here forever, but I did learn some things to share.</p>

<p>Below is a rough outline of the initial structure of the files.
This is in the binary template format supported by <a href="https://rehex.solemnwarning.net/">REHex</a>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Trike, 18 joints?</span>

<span class="c1">// Texture block 672</span>
<span class="c1">// Pal 349 / 289~</span>
<span class="c1">// Header is 144 for texture? NOPE! Size isn't fixed...</span>
<span class="c1">// W &amp; H are 80 bytes into the header? NOPE! Not fixed either...</span>

<span class="k">struct</span> <span class="n">Nu2TextureHeader</span>
<span class="p">{</span>
    <span class="kt">uint8_t</span>     <span class="n">unk0</span><span class="p">[</span> <span class="mi">80</span> <span class="p">];</span>
    <span class="kt">uint32_t</span>    <span class="n">width</span><span class="p">;</span>
    <span class="kt">uint32_t</span>    <span class="n">height</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">Nu2GHGHeader</span>
<span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">fileSize</span><span class="p">;</span>   <span class="c1">//?</span>
    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>    <span class="c1">// 0 per Haven</span>
    <span class="kt">uint32_t</span> <span class="n">numTextures</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">textureTableOffset</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">numMaterials</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">materialsOffset</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">unkNum0</span><span class="p">;</span>       <span class="c1">// ? 12 per Trike</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">Nu2GHGHeader</span> <span class="n">header</span><span class="p">;</span>

<span class="n">FSeek</span><span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">textureTableOffset</span> <span class="p">);</span>
<span class="kt">uint32_t</span> <span class="n">textureOffsets</span><span class="p">[</span> <span class="n">header</span><span class="p">.</span><span class="n">numTextures</span> <span class="p">];</span>

<span class="k">for</span> <span class="p">(</span> <span class="n">local</span> <span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">header</span><span class="p">.</span><span class="n">numTextures</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span>
<span class="p">{</span>
    <span class="n">SetComment</span><span class="p">(</span> <span class="n">textureOffsets</span><span class="p">[</span> <span class="n">i</span> <span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"Texture"</span> <span class="p">);</span>
<span class="p">}</span>

<span class="n">SetComment</span><span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">unkOffset0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"Unknown data 0"</span> <span class="p">);</span>
</code></pre></div></div>

<p>As you can tell, there’s still a lot to figure out here, as this is only for the header.</p>

<p>But these files not only contain the mesh and skeleton, but the textures too.
For instance, if we take a look at <code class="language-plaintext highlighter-rouge">chars/lowteq/rest.ghg</code>, we can find it contains textures for Haven’s main character, Haven, including a texture for his beautiful face.</p>

<p><img src="/assets/2025/haven-ghg-texture.png" alt="Haven's face texture, pulled from a ghg." /></p>

<p>In this instance, these textures use a palette that’s included in the header of each texture contained within the file, but it appears they can be different formats besides that too.</p>

<p>Alas, that’s all I’d had time for.
Though it would be nice to revisit these in the future.</p>

<h2 id="strings-">Strings 🧵</h2>

<p>Going to pass on this one quickly.
There’s a set of <code class="language-plaintext highlighter-rouge">strings.*</code> files in the root of the extracted data, with the <code class="language-plaintext highlighter-rouge">*</code> being an abbreviated name of a country.</p>

<p>Each one of these contain a list of strings with an index so the correct text can be used depending on whichever language you want to play in.</p>

<p>Under <code class="language-plaintext highlighter-rouge">strings.uk</code>, you can find the following rather amusing set of strings from a seemingly distressed developer.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>400 "task 0  Find level programmers"
401 "task 1  Ask them how the levels work"
402 "task 2  Add new level progress strings to the strings.uk file"
403 "task 3  Add level progress info to level.hlp file"
404 "task 4  Find out it doesn't quite work"
405 "task 5  Look for level programmers who have now gone home"
406 "task 6  Panic about what to do cos it was all left to the last minute"
407 "task 7  Bodge bodge bodge bodge bodge bodge bodge bodge bodge bodge"
408 "task 8  Shout across the office at who ever come in next"
409 "task 9  The solution to all problems is more hours"
</code></pre></div></div>

<h2 id="more-scripts-because-sure-why-not-">More Scripts, Because Sure, Why Not? 😩</h2>

<p>Didn’t we already cover some other types of scripts already?
Yes.
Yes we did.
And there are still more.</p>

<p><img src="/assets/gifs/jake-agony.gif" alt="Jake, from Adventure Time, yelling in agony gif." /></p>

<p>Why you would have <em>this</em> many different unique script types, each with their own interpreter, loader and everything in between is beyond me, but perhaps this is the result of each developer having their own area they were in charge of and going in their own direction with that.
Either way, this is pain.</p>

<p>Let’s go over this quickly.</p>

<p>There are two others I’ll just quickly touch on.</p>

<h3 id="scp-scripts-">SCP Scripts 🗒</h3>

<p>For a change, these are scripts that aren’t bespoke to Haven and are actually implemented under their Nu2 framework.
Which implies other games they’ve developed probably use these.
Why they implemented the game flow scripts rather than utilising this existing scripting language for the same purpose, I don’t know.</p>

<p>If it wasn’t capable of things they needed it for, I don’t understand why they didn’t just extend it.
Am I grumpy about this?
Yes, give me a time machine, so I can correct it.</p>

<p>The <code class="language-plaintext highlighter-rouge">.scp</code> extension is probably an abbreviation of script?
You know what, I don’t even care at this point.</p>

<p>Here’s an example of what one of these looks like.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SCRIPT cgate1
	IFPARAM 2 0
		STARTCUTSCENEINST cgate2
		APPCOMMAND SoundFX "sylex door"
		SETPARAM 2 1
	ELSE
		STARTCUTSCENEINST cgate1
		APPCOMMAND SoundFX "sylex door"
	ENDIF
ENDSCRIPT
</code></pre></div></div>

<p>Once again, the commands used here are case-insensitive.</p>

<p>The <code class="language-plaintext highlighter-rouge">APPCOMMAND</code> appears to allow you to call a custom command that the game has registered.
So for instance, Haven implements a <code class="language-plaintext highlighter-rouge">GameFlowCall</code> app command that allows you to execute from a label in a <code class="language-plaintext highlighter-rouge">.flo</code> script.
So a method was essentially exposed here from Nu2 to allow games to extend the abilities of these scripts without actually requiring changes to Nu2 itself, which is nice.</p>

<p>Otherwise, internally these scripts are parsed by a <code class="language-plaintext highlighter-rouge">NuScriptLoadCommands</code> which has a fairly nasty nested structure…
Though altogether we can see it supports all the following commands.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">SCRIPT</code></li>
  <li><code class="language-plaintext highlighter-rouge">STARTCUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">PLAYCUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">CHAINCUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">REPEATCUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">RESETCUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">PAUSECUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">UNPAUSECUTSCENEINST</code></li>
  <li><code class="language-plaintext highlighter-rouge">SETTRIGGERFLAG</code></li>
  <li><code class="language-plaintext highlighter-rouge">RESETTRIGGER</code></li>
  <li><code class="language-plaintext highlighter-rouge">ENABLETRIGGER</code></li>
  <li><code class="language-plaintext highlighter-rouge">IFTRIGGERPULLED</code></li>
  <li><code class="language-plaintext highlighter-rouge">IFTRIGGERNOTPULLED</code></li>
  <li><code class="language-plaintext highlighter-rouge">IFPARAM</code></li>
  <li><code class="language-plaintext highlighter-rouge">SETPARAM</code></li>
  <li><code class="language-plaintext highlighter-rouge">ELSE</code></li>
  <li><code class="language-plaintext highlighter-rouge">APPCOMMAND</code></li>
  <li><code class="language-plaintext highlighter-rouge">SETVISIBILITY</code></li>
  <li><code class="language-plaintext highlighter-rouge">ENDSCRIPT</code></li>
</ul>

<p>Just mind a lot of these are contextual, so obviously you wouldn’t use an <code class="language-plaintext highlighter-rouge">ELSE</code> command on its own, you’d need to match it with an <code class="language-plaintext highlighter-rouge">IF*</code>, and then end it with an <code class="language-plaintext highlighter-rouge">ENDIF</code>; although I don’t really want to start getting into how structured programming works.</p>

<p>Let’s move on.</p>

<h3 id="csc-scripts-">CSC Scripts 🎥</h3>

<p>Unlike the above, these are again bespoke to Haven.
These essentially pick the actual <code class="language-plaintext highlighter-rouge">.cut</code> file to load and setup some states for that.</p>

<p>What’s a <code class="language-plaintext highlighter-rouge">.cut</code> file?
They’re binary blobs for the cutscenes silly.
I’ve not had the motivation or time to look at those in depth.</p>

<p>Curiously, the <code class="language-plaintext highlighter-rouge">PLAYSTREAM</code> command, which streams audio from a VAG file, goes unused and <code class="language-plaintext highlighter-rouge">PLAYSTEREO</code>, which is the same but allows you to specify and left and right VAG file to use for audio, is used instead.</p>

<p>As far as I’ve understood, the VAG format doesn’t typically support stereo audio, so I guess the developers worked around this limitation by just producing these L and R files instead, and then streaming them into the respective audio channels.
You can see all the usages of <code class="language-plaintext highlighter-rouge">PLAYSTREAM</code> commented out, to be replaced by the latter.</p>

<p>The requested VAG will get mapped to a directory matching the current language, so for instance <code class="language-plaintext highlighter-rouge">CUTSND3L\eLcityD.vag</code> will get mapped to <code class="language-plaintext highlighter-rouge">english\CUTSND3L\eLcityD.vag</code> if your chosen language is English.</p>

<h2 id="exploring-the-unseen-">Exploring the Unseen 👀</h2>

<p>Okay, this isn’t actually <strong>all</strong> the unseen elements in the game, but a selection of items you might find interesting.
This is probably what <em>most</em> of you are going to be interested in.</p>

<p>I’d mentioned earlier that there’s a pre-release build from August 2003.
Surprisingly there’s actually very little in that build, as far as unused content goes, which isn’t actually also left in the final game.
Though the build from August does certainly make it a lot easier to view in-engine so that’s what I’d used to take these screenshots.</p>

<p>The majority of these were accessed by simply modifying the <a href="#universe-">universe script</a> we discussed earlier.
I believe there was one which required me to amend one of the game flow scripts, but in my great wisdom I didn’t document that (mostly as a result of me flailing around figuring out how everything worked).</p>

<p>Mind all of these are found within the <code class="language-plaintext highlighter-rouge">RWLDS.DAT</code> package.</p>

<h4 id="hometown">Hometown</h4>

<p>I’ll start off looking at the hometown at the start of the game as this one will be a little confusing without more context.
This area was completely overhauled in the final game, and is much more linear there, but originally was intended to be a much more open area.</p>

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:7vt3ziicjzgemumsoxad2sdb/app.bsky.feed.post/3lu3m6fp6ls2w" data-bluesky-cid="bafyreiajia5i3qhlupluy2emzib4ygmrvmgyveudp5gipgtoesntwmrvx4" data-bluesky-embed-color-mode="system"><p lang="en">So here&#x27;s a fun little thing. In the August &#x27;02 Haven proto, you start in an version of the hometown which was overhauled before released, but it turns out there&#x27;s an older version of the area in the files with different plants (and without the messed up geom).

(left is old, right is new)<br /><br /><a href="https://bsky.app/profile/did:plc:7vt3ziicjzgemumsoxad2sdb/post/3lu3m6fp6ls2w?ref_src=embed">[image or embed]</a></p>&mdash; hogsy (<a href="https://bsky.app/profile/did:plc:7vt3ziicjzgemumsoxad2sdb?ref_src=embed">@hogsy.me</a>) <a href="https://bsky.app/profile/did:plc:7vt3ziicjzgemumsoxad2sdb/post/3lu3m6fp6ls2w?ref_src=embed">July 16, 2025 at 3:16 PM</a></blockquote>
<script async="" src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

<p>Essentially, there’s a <code class="language-plaintext highlighter-rouge">dayht.gsc</code> file under <code class="language-plaintext highlighter-rouge">ferra/isl1home/hometown</code>, which is in both the August build and final, which seems to feature an older(?) version of that area.</p>

<p>You can find some footage I recorded showing this area in more detail <a href="https://youtu.be/TFoSzh3hIPc?t=3512">here</a> while also comparing it against the one used in the August build.
We’ll probably talk a little more about this in future.</p>

<h4 id="dhone">dhone</h4>

<p>This is found in the files under <code class="language-plaintext highlighter-rouge">ferra/isl1home/tempscn/dhone</code>.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2025/dhone_01.png"><img src="/_thumbs/assets/2025/dhone_01_200x150.png" /></a><a href="/assets/2025/dhone_02.png"><img src="/_thumbs/assets/2025/dhone_02_200x150.png" /></a></div></div>

<p>There’s another version of this.
Not entirely sure how they differ compared to the final version?</p>

<h4 id="oxtest1">oxtest1</h4>

<p>This is found in the files under <code class="language-plaintext highlighter-rouge">ferra/isl1home/tempscn/oxtest1</code>.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2025/oxtest1_01.png"><img src="/_thumbs/assets/2025/oxtest1_01_200x150.png" /></a><a href="/assets/2025/oxtest1_02.png"><img src="/_thumbs/assets/2025/oxtest1_02_200x150.png" /></a></div></div>

<p>An appearance from our friend Crash! 😮</p>

<p>This is actually a huge area, unfortunately untextured. Crash is about 2 pixels in size in the second image, which hopefully gives you an idea of its scale!</p>

<h4 id="tempscn">tempscn</h4>

<p>This is found in the files under <code class="language-plaintext highlighter-rouge">ferra/isl1home/tempscn/tempscn</code>.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2025/tempscn_01.png"><img src="/_thumbs/assets/2025/tempscn_01_200x150.png" /></a><a href="/assets/2025/tempscn_02.png"><img src="/_thumbs/assets/2025/tempscn_02_200x150.png" /></a><a href="/assets/2025/tempscn_03.png"><img src="/_thumbs/assets/2025/tempscn_03_200x150.png" /></a><a href="/assets/2025/tempscn_04.png"><img src="/_thumbs/assets/2025/tempscn_04_200x150.png" /></a></div></div>

<p>Appears to be a huge unfinished area.
Fairly sure this did make it into the final game, though I believe it looks a little bit different to this?
Well, besides being finished.</p>

<p>The last image shows the camera pulled really far back, showing how massive the area is, for some reason.</p>

<h4 id="test2">test2</h4>

<p>This is found in the files under <code class="language-plaintext highlighter-rouge">ferra/isl1home/tempscn/test2</code>.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-2"><a href="/assets/2025/test2_01.png"><img src="/_thumbs/assets/2025/test2_01_200x150.png" /></a><a href="/assets/2025/test2_02.png"><img src="/_thumbs/assets/2025/test2_02_200x150.png" /></a></div></div>

<p>The other test level, if you’re wondering, just features the same geometry as this one.</p>

<h4 id="lightbox">lightbox</h4>

<p>This is found in the files under <code class="language-plaintext highlighter-rouge">ferra/isl1home/tempscn/lightbox</code>.</p>

<p><img src="/assets/2025/haven-test-lighting.png" alt="Lightbox test map screenshot" /></p>

<h4 id="tek">tek</h4>

<p>This is found in the files under <code class="language-plaintext highlighter-rouge">ferra/isl1home/teqhouse/tek</code>.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2025/teqhouse_01.png"><img src="/_thumbs/assets/2025/teqhouse_01_200x150.png" /></a><a href="/assets/2025/teqhouse_02.png"><img src="/_thumbs/assets/2025/teqhouse_02_200x150.png" /></a><a href="/assets/2025/teqhouse_03.png"><img src="/_thumbs/assets/2025/teqhouse_03_200x150.png" /></a><a href="/assets/2025/teqhouse_04.png"><img src="/_thumbs/assets/2025/teqhouse_04_200x150.png" /></a></div></div>

<p>As mentioned <a href="#dialog-script-">earlier</a>, Teque seems to be an older name for Haven (the character, not the game).
And that’s often abbreviated as just <em>Teq</em>.
Though weirdly the actual files for the scene itself (<code class="language-plaintext highlighter-rouge">.gsc</code> and <code class="language-plaintext highlighter-rouge">.ter</code>) are named “tek” instead…
Probably a mistake.</p>

<p>I’m also probably mistaken myself, but I don’t believe any of this is used in the final game?
Haven’s house looks entirely different to the one featured in the final game, for instance.
And an appearance by the tower in the last screenshot is also replaced by a pre-rendered cutscene in the final game, as far as I’m aware.</p>

<p>Scenes from here do at least appear in a trailer for the game, which you can find <a href="https://youtu.be/9D71DIvyP-Y?t=27">here</a>.</p>

<h4 id="one-more-crash">One more Crash</h4>

<p>And finally, this is under <code class="language-plaintext highlighter-rouge">fonts/testfont.bmp</code>.</p>

<p><img src="/assets/2025/haven-testfont.bmp" alt="Haven test font bmp." /></p>

<p>I’m fairly sure this same font sheet was documented as left-over in some other TT game over on The Cutting Room Floor wiki.</p>

<hr />

<p>This is far from everything, and I’ll probably leave that for the future piece on this game, or I’ll just encourage you to try checking it out for yourself!
There’s only so much I’m going to have time to look at.</p>

<p>One other thing that was noteworthy, but I didn’t have time to properly document, is that under <code class="language-plaintext highlighter-rouge">ferra/island3/doomroom</code>, there are actually multiple revisions of this area available, each one featuring slightly different designs.
These exist in both the August build and final release.</p>

<h2 id="revision-history-">Revision History 📜</h2>

<p>One thing that stood out to me upon extracting all the data was a <code class="language-plaintext highlighter-rouge">cvs</code> directory; CVS is… Er, was, a fairly popular version control solution <em>back in the day</em>, and it stands for <a href="https://cvs.nongnu.org/">Concurrent Versions System</a>.
It basically lets you keep track of your changes.</p>

<p>This has all since been superseded by things like <a href="https://en.wikipedia.org/wiki/Git">Git</a> and <a href="https://en.wikipedia.org/wiki/Perforce#P4">Perforce</a>.</p>

<p>This directory here basically exists because the content was checked out from their remote CVS repository, and someone forgot to delete this particular folder.
Looking inside we can see three files leftover here.</p>

<p>In the <code class="language-plaintext highlighter-rouge">cvs/root</code> file, we can see a <code class="language-plaintext highlighter-rouge">ddootson</code> mentioned, which is David Dootson, the head programmer on the game.
Perhaps it’s him to blame for this folder being left over?</p>

<p>Anyway, ignoring all that, we’re rather fortunate that some of the files here actually include the revision history in their header.
Specifically, the <code class="language-plaintext highlighter-rouge">.flo</code>, <code class="language-plaintext highlighter-rouge">.uni</code> and strings files.</p>

<p>I’m not going to paste the entire revision history for each one directly here for obvious reasons, but I’ve linked each file below for you to check out if you’re interested in that.</p>

<ul>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/auria.flo">auria.flo</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/exarco.flo">exarco.flo</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/ferra.flo">ferra.flo</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/ferven.flo">ferven.flo</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/haven.flo">haven.flo</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/nequam.flo">nequam.flo</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/cf72c83c4fd9ea01d744114a835d2b42c213fd92/DATA/RWLDS.DAT_extracted/new.uni">new.uni</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/3e7ff03654bfe77a353893b4d0c8a2490125c298/DATA/RWLDS.DAT_extracted/strings.uk">strings.uk</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/3e7ff03654bfe77a353893b4d0c8a2490125c298/DATA/RWLDS.DAT_extracted/strings.sp">strings.sp</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/3e7ff03654bfe77a353893b4d0c8a2490125c298/DATA/RWLDS.DAT_extracted/strings.it">strings.it</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/3e7ff03654bfe77a353893b4d0c8a2490125c298/DATA/RWLDS.DAT_extracted/strings.fr">strings.fr</a></li>
  <li><a href="https://codeberg.org/TalonBraveInfo/tt-haven-dump/src/commit/3e7ff03654bfe77a353893b4d0c8a2490125c298/DATA/RWLDS.DAT_extracted/strings.de">strings.de</a></li>
</ul>

<p>Curious as to who’s who in the revision history?
Well, in no particular order…</p>

<table>
  <thead>
    <tr>
      <th><strong>Username</strong></th>
      <th><strong>Real name</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>rtaylor</td>
      <td>Richard Taylor</td>
    </tr>
    <tr>
      <td>cstanforth</td>
      <td>Chris Stanforth</td>
    </tr>
    <tr>
      <td>smonks</td>
      <td>Steven Monks</td>
    </tr>
    <tr>
      <td>leonwarren</td>
      <td>Leon Warren</td>
    </tr>
    <tr>
      <td>gscragg</td>
      <td>Glynn Scragg</td>
    </tr>
    <tr>
      <td>ddootson</td>
      <td>David Dootson</td>
    </tr>
    <tr>
      <td>aparsons</td>
      <td>Arthur Parsons</td>
    </tr>
    <tr>
      <td>jburton</td>
      <td>Jon Burton</td>
    </tr>
    <tr>
      <td>ralph</td>
      <td>Ralph Ferneyhough</td>
    </tr>
    <tr>
      <td>acrowe</td>
      <td>Alistair Crowe</td>
    </tr>
  </tbody>
</table>

<p>If any names above are written incorrectly, blame autocorrect!
If I’ve missed anyone out, well blame me (and preferably let me know.)</p>

<p>An article published <a href="https://hexus.net/gaming/features/industry/1348-travellers-tales-team-profile/">here</a> a couple of years after Haven shipped seems to feature a few of the developers mentioned above.</p>

<h2 id="finishing-up">Finishing Up</h2>

<p>So that’ll probably do for now, on the technical side.
Probably.
There’s actually still a lot I didn’t cover here but if I did this would’ve gone on for a long time, and there’s always the possibility of revisiting this in the future.</p>

<p>Working on this has made me realise that in future it may be a little more wise to just pick one aspect to focus on, rather than trying to work through everything all at once.
As that certainly burnt me out a bit.</p>

<p>If you’ve made it to the end, with your sanity intact, and you enjoyed it, I’ve previously done pieces on <a href="https://talonbrave.info/2024/06/14/poking-titanae.html">Titan A.E.</a>, <a href="https://talonbrave.info/2023/11/27/wild-water-adrenaline.html">Wild Water Adrenaline</a> and <a href="https://talonbrave.info/2018/10/27/exploring-the-deep-with-bullfrogs-creation.html">Creation</a>, which you may also enjoy.</p>

<p>And as usual, if you want to keep up with what we’re doing, want to get involved, or have things to share, then feel free to join our <a href="https://discord.gg/bJKBRNagfA">Discord server</a>.</p>]]></content><author><name>Mark Sowden</name></author><category term="Reverse Engineering" /><category term="Game" /><category term="Game/Haven: Call of the King" /><category term="2025" /><category term="Haven: Call of the King" /><category term="Travellers Tales" /><category term="Reverse Engineering" /><summary type="html"><![CDATA[Haven: Call of the King, or simply Haven, as I’ll refer to it, is a game developed by the British video-game development studio, Traveller’s Tales. It’s quite technically impressive, and certainly ambitious, as far as PlayStation 2 games go.]]></summary></entry><entry><title type="html">What’s Beyond?</title><link href="https://talonbrave.info/2025/02/01/beyond.html" rel="alternate" type="text/html" title="What’s Beyond?" /><published>2025-02-01T00:00:00+00:00</published><updated>2025-02-01T00:00:00+00:00</updated><id>https://talonbrave.info/2025/02/01/beyond</id><content type="html" xml:base="https://talonbrave.info/2025/02/01/beyond.html"><![CDATA[<p>My first encounter with Beyond was through a prototype added to the <a href="https://hiddenpalace.org/Beyond_(Sep_12,_2005_prototype)">Hidden Palace website</a> some time ago now, which was more specifically released by <a href="https://hiddenpalace.org/CVLT_OF_OSIRIS">CVLT OF OSIRIS</a>.</p>

<p>It seemed odd that there was so little information available about the game in the wild, and absolutely no information that I could find explaining why it was cancelled.</p>

<p>Back in 2021, I was very fortunate to be able to get in touch with producer Goran Milisavljevic, 3D artist Aleksandar Novta, and programmer Marko Djurovic, to get some more information about the title which I’m finally sharing here. Unfortunately, due to how long it’d been, there was only so much they could share.</p>

<!-- more -->

<p>Additionally, as I couldn’t find any existing footage on YouTube of the prototype, I’ve provided some gameplay footage below recorded on real hardware.</p>

<div style="position: relative; margin: auto; width: 1280px; max-width: 100%;">
<div style="padding-top: 56.25%;"></div>
<iframe style="position: absolute; top:0px; left: 0px; width: 100%; height: 100%;" src="https://www.youtube.com/embed/Yu7oEEFRSMs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<p class="tb-img-subtitle">The video above may be blocked due to copyrighted music used in the prototype, in which case I’ll be putting the video up elsewhere soon.</p>

<p><small>edit: turns out that <a href="https://www.youtube.com/@PtoPOnline">PtoPOnline</a> had already captured footage, so an alternate capture is available <a href="https://youtu.be/N8512MS3vO4">here</a>.</small></p>

<hr />

<p>These answers were kindly provided by Goran Milisavljevic, the producer on the game, based on a series of questions I’d provided.
I’ve laid them out below in a Q&amp;A form.</p>

<p><strong>How far along were you in developing the game before ceasing development?</strong></p>

<blockquote>
  <p>We developed a working prototype for the Xbox platform. Besides the prototype that contained one complete medium size mission, we also had a well-developed game design document with detailed description for the complete game, covering all levels, enemies and collectables. Also, concept art was around 75% finished.</p>
</blockquote>

<p><strong>Did the game use a custom engine, or is it based on something else (i.e. RenderWare)?</strong></p>

<blockquote>
  <p>We were accepted to Microsoft’s Xbox Registered Developers programme, so we received two Xbox dev consoles and were using Microsoft’s XDK. Besides this, we were using RenderWare for rendering, FMod for sound, Bullet for physics and everything else was developed in-house.</p>
</blockquote>

<p><strong>How big was the team working on the game?</strong></p>

<blockquote>
  <p>The team had 4 programmers, 5 artists and 2 game designers.</p>
</blockquote>

<p><strong>Were there any plans to bring it to other platforms, or was it being developed specifically for the Xbox only? If so, what were the motivations?</strong></p>

<blockquote>
  <p>We were misled by a “big name” consultant who insisted that we should develop for Xbox exclusively. At GDC 2005, we found out that this decision was fatal for the company, since the publishers we had contacts with showed us that their plans for the next 5 years didn’t include any Xbox titles, only titles for the next gen consoles (Xbox 360 and PS3).</p>

  <p>Based on this prototype at GDC 2005, people from Sony offered us to develop for them a PS3 game based on the “Starship Trooper” movie. However, our investor declined this offer and this meant the end of the road for us.</p>
</blockquote>

<hr />

<p>Marko Djurovic, a programmer on the game, also recounted details behind the development of the game and the prototype.</p>

<blockquote>
  <p>The idea was that Beyond should be a space TPS.</p>

  <p>The initial idea was that NPCs have some kind of primitive AI, but later that idea was abandoned, and in demo, the NPCs were script driven.</p>
</blockquote>

<p>I’m not entirely sure what was meant by script driven here, as the NPCs in the prototype seem to have some basic level of AI. However, this may explain why internally in the demo there’s a <code class="language-plaintext highlighter-rouge">BStupidNPC</code> type?
Though this wasn’t something I was able to confirm.</p>

<blockquote>
  <p>Target platform was the original Xbox.</p>

  <p>I can’t remember the full story now, but the team spent months developing the story behind the game. The engine was a mix of an in-house engine with some commercial engine (can’t remember name). For animations, we used motion capture with the help of professional models.</p>

  <p>Sound and music was made by professional musicians.</p>
</blockquote>

<p>The commercial engine referred to here was likely RenderWare, as mentioned by Goran Milisavljevic.</p>

<p>As far as I’m aware, we unfortunately don’t get to hear any of the custom music that was produced for the game in the prototype. Much of the music there seems to be pulled from the Animatrix and The Matrix Revolutions, very likely as placeholders.</p>

<blockquote>
  <p>I think at that time members of the team were the best game developers in the region. Unfortunately, after a successful demo, we ran out of funds (through the fault of the CEO).</p>

  <p>Sorry this was a long time ago, so I forgot many things, but I can remember it was hell of the ride. We were young and often working 40 hours without a pause.</p>
</blockquote>

<hr />

<p>I originally provided much of this for a since unlisted video produced by <a href="https://www.youtube.com/@TechmoSnowspark">Techmo Snowspark</a>; hence why there’s been a wait on getting these published on the site.</p>

<p>Big thanks to them for answering my questions about the game.
This provides some more context for the prototype and its fate.</p>

<p>Until next time! 👋</p>

<p><strong>Update:</strong> Andrew Borman, a director at The Strong National Museum of Play, kindly alerted me to the fact that the developers released two design documents related to the game online at some point in the past. I’ve now mirrored these <a href="https://archive.thedatadungeon.com/beyond_cancelled/documents/gametimeline.pdf">here</a> and <a href="https://archive.thedatadungeon.com/beyond_cancelled/documents/missions.pdf">here</a>.</p>]]></content><author><name>Mark Sowden</name></author><category term="Game" /><category term="Game/Beyond (Cancelled)" /><category term="Interview" /><category term="2025" /><category term="Beyond" /><summary type="html"><![CDATA[My first encounter with Beyond was through a prototype added to the Hidden Palace website some time ago now, which was more specifically released by CVLT OF OSIRIS. It seemed odd that there was so little information available about the game in the wild, and absolutely no information that I could find explaining why it was cancelled. Back in 2021, I was very fortunate to be able to get in touch with producer Goran Milisavljevic, 3D artist Aleksandar Novta, and programmer Marko Djurovic, to get some more information about the title which I’m finally sharing here. Unfortunately, due to how long it’d been, there was only so much they could share.]]></summary></entry><entry><title type="html">Shadow Man: 1999-03-26</title><link href="https://talonbrave.info/2024/12/24/shadow-man-99.html" rel="alternate" type="text/html" title="Shadow Man: 1999-03-26" /><published>2024-12-24T00:00:00+00:00</published><updated>2024-12-24T00:00:00+00:00</updated><id>https://talonbrave.info/2024/12/24/shadow-man-99</id><content type="html" xml:base="https://talonbrave.info/2024/12/24/shadow-man-99.html"><![CDATA[<p>Today we’re showing you a version of Shadow Man from March 26th, 1999. This was kindly provided to us to study much in the same way we studied an <a href="https://talonbrave.info/2021/01/01/a-quick-look-at-the-shadow-man-97-prototype.html">earlier version of the game from 1997</a>.</p>

<!-- more -->

<p>We’ll be publishing more about this version of the game on our blog in the new year.</p>

<p>As mentioned in the video, some modifications have been made just to make it easier to capture. A cap for the frame rate was introduced. Some issues that resulted in crashes were also resolved, though otherwise much of the rest has been left as-is to remain as an accurate representation of the game at the time.</p>

<p>If you have any questions, feel free to join our Discord server, <a href="https://discord.com/invite/bJKBRNagfA">here</a>.</p>

<p>Hope you all have a great Christmas and new year!</p>

<div style="position: relative; margin: auto; width: 1280px; max-width: 100%;">
<div style="padding-top: 56.25%;"></div>
<iframe style="position: absolute; top:0px; left: 0px; width: 100%; height: 100%;" src="https://www.youtube.com/embed/35-5dguWUfQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>]]></content><author><name>Mark Sowden</name></author><category term="Game" /><category term="Game/Shadow Man (1999)" /><category term="Video" /><category term="2024" /><category term="Shadow Man" /><category term="Video" /><summary type="html"><![CDATA[Today we’re showing you a version of Shadow Man from March 26th, 1999. This was kindly provided to us to study much in the same way we studied an earlier version of the game from 1997.]]></summary></entry><entry><title type="html">Halo CE &amp;amp; Legend of Alon D’ar Source Trees</title><link href="https://talonbrave.info/2024/12/02/halo-legend.html" rel="alternate" type="text/html" title="Halo CE &amp;amp; Legend of Alon D’ar Source Trees" /><published>2024-12-02T00:00:00+00:00</published><updated>2024-12-02T00:00:00+00:00</updated><id>https://talonbrave.info/2024/12/02/halo-legend</id><content type="html" xml:base="https://talonbrave.info/2024/12/02/halo-legend.html"><![CDATA[<p>There are some more fleshed out things on the way, slowly but surely, but until then we’ve recently made some more source tree reproductions available for those that like seeing that sort of thing (probably just me.)</p>

<p>This time we’ve got <a href="https://github.com/TalonBraveInfo/halo-source-tree">Halo: Combat Evolved</a>, specifically generated from a pre-release build found <a href="https://dg.getrektby.us/_files/Xbox/%5BXBOX%5D%20Halo%20Combat%20Evolved%20%5B2001-01-14%5D%20(PDB).7z">here</a>, and <a href="https://github.com/TalonBraveInfo/legend-source-tree">Legend of Alon D’ar</a>, which was produced from symbols included in the retail US release (oops).</p>

<p>As you can probably see, and might have known already, much of the original Halo was written in C.</p>

<p>As for The Legend of Alon D’ar, it seems much of that was written in C++.
The latter is referred to as FireFly, which was apparently an early working title (at least according to MobyGames, which provides no citation, because of course). Another working title was <a href="https://www.ign.com/articles/2000/05/12/e3-2000-role-players-unite-behind-eternal-blade">Eternal Blade</a>.</p>

<p>Enjoy!</p>]]></content><author><name>Mark Sowden</name></author><category term="Source Tree" /><category term="Game" /><category term="Game/Halo" /><category term="2024" /><category term="Halo" /><category term="Source Tree" /><category term="Reverse Engineering" /><summary type="html"><![CDATA[There are some more fleshed out things on the way, slowly but surely, but until then we’ve recently made some more source tree reproductions available for those that like seeing that sort of thing (probably just me.) This time we’ve got Halo: Combat Evolved, specifically generated from a pre-release build found here, and Legend of Alon D’ar, which was produced from symbols included in the retail US release (oops). As you can probably see, and might have known already, much of the original Halo was written in C. As for The Legend of Alon D’ar, it seems much of that was written in C++. The latter is referred to as FireFly, which was apparently an early working title (at least according to MobyGames, which provides no citation, because of course). Another working title was Eternal Blade. Enjoy!]]></summary></entry><entry><title type="html">Primal Source Tree</title><link href="https://talonbrave.info/2024/06/24/primal-source-tree.html" rel="alternate" type="text/html" title="Primal Source Tree" /><published>2024-06-24T00:00:00+00:00</published><updated>2024-06-24T00:00:00+00:00</updated><id>https://talonbrave.info/2024/06/24/primal-source-tree</id><content type="html" xml:base="https://talonbrave.info/2024/06/24/primal-source-tree.html"><![CDATA[<p>As <a href="https://talonbrave.info/2021/02/11/final-fantasy-xv-source-tree.html">before</a>, I’ve made the source tree for <a href="https://en.wikipedia.org/wiki/Primal_(video_game)">Primal</a> available <a href="https://github.com/TalonBraveInfo/primal-source-tree/tree/master"><strong>here</strong></a> for you to nose through to your content.</p>

<p>Keep in mind that <strong>there is no actual code here</strong>, but just the tree representing how everything, or more specifically the source code, was laid out during development. This might give you some ideas, clues, or just generally, hopefully, be interesting for you to see—personally I’ve found some of these rather inspiring when deciding how to set out my own projects.</p>

<!-- more -->

<p>I should also emphasize that this is derived from an early debug build released <a href="https://hiddenpalace.org/Primal_(Dec_21,_2001_prototype)">here</a>, so it’s likely things may have changed as the game continued development.</p>

<p>That said, you can already see how the developers had divided the code in a tidy manner between the game and the engine; much of the tech behind this game ended up getting used for other titles by the same studio, SCE Cambridge, such as <a href="https://en.wikipedia.org/wiki/Ghosthunter_(video_game)">Ghosthunter</a> and <a href="https://en.wikipedia.org/wiki/24:_The_Game">24: The Game</a>.</p>

<p>If you’d like to see what that early debug build looks like (without having to launch it up yourself), I produced a video covering some of it a while ago, which you can find below.</p>

<div style="position: relative; margin: auto; width: 640px; max-width: 100%;">
<div style="padding-top: 56.25%;"></div>
<iframe style="position: absolute; top:0px; left: 0px; width: 100%; height: 100%;" src="https://www.youtube.com/embed/ZW5qLfxqN5s" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>

<p>A version of the video with some rather questionable commentary is also available <a href="https://www.youtube.com/watch?v=Ro11PWtXHow">here</a> if you want to hear my boring voice.</p>

<p>As you can see from the source tree I’ve uploaded, the project was internally referred to as “Fable”. Given Lionhead’s Fable was called “Project Ego” at the time Primal was announced, it seems unlikely the name was changed to avoid conflicts there, so it may have simply been a placeholder.</p>

<p>This earlier title or internal name used for the game can actually be seen in some public pre-release material too, such as these Maya screenshots (note the “Fable Map Objects” menu along the top and, again, the “fable” directory).</p>

<p><a href="/assets/2024/primal/MAYAAQUI.jpg"><img src="/assets/2024/primal/MAYAAQUI.jpg" alt="Maya Screenshot A" /></a></p>

<p><a href="/assets/2024/primal/MAYACOLU.jpg"><img src="/assets/2024/primal/MAYACOLU.jpg" alt="Maya Screenshot B" /></a></p>

<p>After many attempts to play through the game since it was released, I’ve recently been working my way through Primal and finally find myself close to completing it. I’ve really enjoyed and admired just how unique and refreshing the universe the game takes place in is. Very impressive just how far the developers pushed the PlayStation 2 hardware.</p>

<p>Enjoy!</p>]]></content><author><name>Mark Sowden</name></author><category term="Source Tree" /><category term="Game" /><category term="Game/Primal" /><category term="2024" /><category term="Primal" /><category term="Source Tree" /><category term="Reverse Engineering" /><summary type="html"><![CDATA[As before, I’ve made the source tree for Primal available here for you to nose through to your content. Keep in mind that there is no actual code here, but just the tree representing how everything, or more specifically the source code, was laid out during development. This might give you some ideas, clues, or just generally, hopefully, be interesting for you to see—personally I’ve found some of these rather inspiring when deciding how to set out my own projects.]]></summary></entry><entry><title type="html">Poking and Prodding Titan A.E.</title><link href="https://talonbrave.info/2024/06/14/poking-titanae.html" rel="alternate" type="text/html" title="Poking and Prodding Titan A.E." /><published>2024-06-14T00:00:00+00:00</published><updated>2024-06-14T00:00:00+00:00</updated><id>https://talonbrave.info/2024/06/14/poking-titanae</id><content type="html" xml:base="https://talonbrave.info/2024/06/14/poking-titanae.html"><![CDATA[<p><em>I peer amongst my assorted collection of unfinished works, noticing one in particular that’s been sitting for quite some time. 
Letting off a sigh, I grab it and proceed to blow the dust off it; an immediate coughing fit follows, and shock, seeing the only word on the page is nothing more than “The”.</em></p>

<p>Oops. This has only been waiting <strong>two years</strong>. 
Alright, I guess it’s about time to get this out the door.</p>

<!-- more -->

<p>Titan A.E., if you didn’t know already, was a game that was being developed by Blitz Games. 
I’m not going to go too much into the history and details of the game itself, as there’s been a fantastic amount of research into this already <a href="https://www.amazon.com/Ice-Planet-Lost-Worlds-Titan-ebook/dp/B01KP6KABW/">published here</a> by Edward Kirk.</p>

<p>Anyway, for this reason, I decided to focus on more so on the technical side of things, rather than the game generally.
This tends to be my usual trend anyway.</p>

<h2 id="lets-dive-in">Let’s Dive In!</h2>

<p class="tb-img-subtitle"><img src="/assets/gifs/cat-exploring-box.gif" alt="Into the box we go" />
Into the box of adventure we go!</p>

<p>So we’re taking a look at the demo for Titan A.E. that was released in 2000, around early June? 
I couldn’t find a date specified online, but judging from the modification timestamps amongst the content on the CD, it seems likely that’s when it was printed. 
The debug menu says “May 31 2000” in the top corner, which is probably when it was compiled.
Regardless, not too far off before the game was cancelled <a href="https://www.ign.com/articles/2000/07/27/titan-ae-canned">near the end of July</a>.</p>

<p>I’m not going to go too much into the game itself as mentioned already, but you can find a series of videos via the playlist below from our <a href="https://youtube.com/TalonbraveInfo">YouTube channel</a>.</p>

<div align="center">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/videoseries?si=6TU_VD3E77eOWjLr&amp;list=PLJEc7wv0Jvd0FEuEupaLJI8NfZF_iyeUa" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>
</div>

<p>The additional levels were accessed using the debug menu which was activated using the GameShark codes available <a href="https://tcrf.net/Titan_A.E.">here</a>.
I’ve also provided them below.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D006F6A8 0001
3007066E 000D
D006F6A8 0001
30070324 0001
</code></pre></div></div>

<p>With the level select, there’s a <strong>lot</strong> to explore!</p>

<p>When recording footage for YouTube I was a little concerned the music might get flagged (or grate a little as it’s fairly repetitive), so I’d disabled the music by hex editing the executable. 
That said, if you want the music, you can grab it below.</p>

<audio controls="" src="/assets/downloads/titan-demo-music-ogg/FLYING_0.ogg"></audio>
<p><a href="/assets/downloads/titan-demo-music-ogg/FLYING_0.ogg"><strong>Download</strong></a></p>

<audio controls="" src="/assets/downloads/titan-demo-music-ogg/LEVELS1_0.ogg"></audio>
<p><a href="/assets/downloads/titan-demo-music-ogg/LEVELS1_0.ogg"><strong>Download</strong></a></p>

<audio controls="" src="/assets/downloads/titan-demo-music-ogg/LEVELS1_1.ogg"></audio>
<p><a href="/assets/downloads/titan-demo-music-ogg/LEVELS1_1.ogg"><strong>Download</strong></a></p>

<audio controls="" src="/assets/downloads/titan-demo-music-ogg/LEVELS1_2.ogg"></audio>
<p><a href="/assets/downloads/titan-demo-music-ogg/LEVELS1_2.ogg"><strong>Download</strong></a></p>

<audio controls="" src="/assets/downloads/titan-demo-music-ogg/TESTALL_0.ogg"></audio>
<p><a href="/assets/downloads/titan-demo-music-ogg/TESTALL_0.ogg"><strong>Download</strong></a></p>

<h3 id="reviewing-the-cd-contents">Reviewing The CD Contents</h3>

<p>After extracting the contents of the CD, we’ve got the following directory tree.</p>

<pre>
<font color="#12488B"><b>.</b></font>
├── BOOT.EXE
├── SLUS_900.82
├── SYSTEM.CNF
├── TITAN.DAT
├── TITAN.MAP
├── <font color="#12488B"><b>VIDEO</b></font>
│   ├── BLITZ1N.STR
│   ├── FOX_1_N.STR
│   ├── GAMEDEMO.STR
│   ├── ROLDEM_N.STR
│   └── TITAN_N.STR
└── <font color="#12488B"><b>XA</b></font>
    ├── FLYING.XA
    ├── LEVELS1.XA
    └── TESTALL.XA
</pre>

<p>I’m not entirely sure what <em>BOOT.EXE</em> does. 
From looking at the strings within, it certainly sounds like some sort of bootstrapper? 
Though the main executable, <em>SLUS_900.82</em>, doesn’t appear to make any references to it, so it might be unused? 
Regardless, not too interested in that for now.</p>

<p>The <em>TITAN.MAP</em> file is a linker map, which was probably unintentionally included on the CD. 
It provides all the addresses for the various sections and functions, etc.</p>

<div style="overflow-y: scroll; height:400px;">

<pre>

    Start     Stop   Length      Obj Group            Section name
 00018000 0001B8E3 000038E4 00018000 text             .rdata
 0001B8E4 000655CF 00049CEC 0001B8E4 text             .text
 000655D0 0006D68B 000080BC 000655D0 text             .data
 0006D68C 0006D733 000000A8 0006D68C text             .sdata
 0006D738 0006F5CF 00001E98 0006D738 text             incbins
 0006F5D0 0006F5D3 00000004 1F800000 dcache           cachedata
 0006F5D4 0006F643 00000070 0006F5D4 bss              .sbss
 0006F644 0007D6C3 0000E080 0006F644 bss              .bss
 0007D6C4 0007D6C7 00000004 0007D6C4 explore          explore$SN_OVL_explore
 0007D6C8 0007FB63 0000249C 0007D6C8 explore          explore.rdata
 0007FB64 000CD177 0004D614 0007FB64 explore          explore.text
 000CD178 000CD8E3 0000076C 000CD178 explore          explore.data
 000CD8E4 000CFE9F 000025BC 000CD8E4 explore          explore.bss
 
[...]

 00020F8C MATHS_SqrtFull
 00020FC4 MATHS_ATanFast
 00021138 MATHS_AngleDifference
 0002116C MATHS_Magnitude
 0002124C MATHS_MagnitudeS
 0002132C MATHS_Magnitude2D
 000213E0 MATHS_Rotate
 00021490 MATHS_ProjectPointForward
 00021544 MATHS_ProjectPointDown
 000215F8 MATHS_ProjectPointRight
 000216AC MATHS_GetPointDistanceFromLineSegment3D
 00021AD0 MATHS_GetPointDistanceFromLineSegment2D
 00021D04 MATHS_Get2DLineSegmentsIntersectPoint
 00022164 MATHS_LineSegmentsIntersect
 00022214 MATHS_ShortAntiClockWise
 00022278 MATHS_VECTORCrossProduct
 0002233C MATHS_TSVECTORSafeCrossProduct
 00022400 MATHS_NormaliseVECTOR
 000224FC MATHS_NormaliseTVECTOR
 000225D4 MATHS_NormaliseTSVECTOR
 000226E4 MATHS_NormaliseTSVECTOR2D
 000227BC MATHS_LineSegmentIntersectPlane
 
[...]

</pre>

</div>

<p>The files under XA and VIDEO use fairly standard formats as far as PSX games are concerned, and you can view/extract them using <a href="https://github.com/m35/jpsxdec">existing tools</a>.</p>

<p>The file we’re most interested in is <em>TITAN.DAT</em>, as this is what holds the bulk of the data used by the game.</p>

<p>Turns out this <em>DAT</em> container is a common format amongst some other games Blitz developed at the time, such as <a href="https://en.wikipedia.org/wiki/Chicken_Run_(video_game)">Chicken Run</a> and <a href="https://www.mobygames.com/game/140001/action-man-destruction-x/">Action Man: Destruction X</a>.
There are probably others, but I’d confirmed those two at least.</p>

<p>I’ve documented the general structure as the following <a href="https://github.com/hogsy/formats/blob/main/templates/blitz/blitz_dat.bt">here</a>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span>
<span class="p">{</span>
	<span class="kt">int32_t</span> <span class="n">magic</span><span class="p">;</span><span class="c1">// '78563412'</span>
	<span class="kt">int32_t</span> <span class="n">numFiles</span><span class="p">;</span>
	<span class="k">struct</span>
	<span class="p">{</span>
		<span class="kt">int32_t</span> <span class="n">hash</span><span class="p">;</span>
		<span class="kt">int32_t</span> <span class="n">block</span><span class="p">;</span>
		<span class="kt">int32_t</span> <span class="n">length</span><span class="p">;</span>
	<span class="p">}</span> <span class="n">files</span><span class="p">[</span> <span class="n">numFiles</span> <span class="p">];</span>
<span class="p">}</span> <span class="n">header</span><span class="p">;</span>

<span class="n">local</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">struct</span> <span class="nf">Data</span><span class="p">(</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">i</span> <span class="p">)</span>
<span class="p">{</span>
	<span class="n">FSeek</span><span class="p">(</span> <span class="mi">16384</span> <span class="o">+</span> <span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">files</span><span class="p">[</span> <span class="n">i</span> <span class="p">].</span><span class="n">block</span> <span class="o">*</span> <span class="mi">2048</span> <span class="p">)</span> <span class="p">);</span>
	<span class="kt">char</span> <span class="n">chunk</span><span class="p">[</span> <span class="n">header</span><span class="p">.</span><span class="n">files</span><span class="p">[</span> <span class="n">i</span> <span class="p">].</span><span class="n">length</span> <span class="p">];</span>
	<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">Data</span> <span class="n">data</span><span class="p">[</span> <span class="mi">0</span> <span class="p">];</span>
<span class="n">ArrayResize</span><span class="p">(</span> <span class="n">data</span><span class="p">,</span> <span class="n">header</span><span class="p">.</span><span class="n">numFiles</span><span class="p">,</span> <span class="n">i</span> <span class="p">);</span>
</code></pre></div></div>

<p>This format is heavily oriented around streaming from a PSX CD, which has sectors sized at 2,048 bytes each—so to get the file offset, it’s 16384 + (block * 2048).</p>

<p>That doesn’t seem so bad, right? But hang on, there’s a problem…</p>

<p><img src="/assets/gifs/cat-angry-small.gif" alt="Angry cat" /></p>

<h2 id="hashed-filenames">Hashed Filenames</h2>

<p>If we were to extract the data right now, we wouldn’t know what any of the files are actually called as they’re identified by a hash.
I’m not sure how many formats I’ve come across now that feature hashed filenames, but it’s one too many.</p>

<p>So the first problem to solve here is the hashing algorithm that was used. 
On this occasion I got a little lazy and took a quick look at the <a href="https://archive.thedatadungeon.com/chicken_run_2000/distributions/2000-08-31_source_code/chicken_run_source.rar">Chicken Run code</a> for clues, and it turns out it can be found under <code class="language-plaintext highlighter-rouge">islutil.c</code> per <code class="language-plaintext highlighter-rouge">utilStr2CRC</code>.
Essentially, this code generates a hash based on a pre-existing table (likely done for the sake of speed).</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">utilStr2CRC</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">register</span> <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span>
	<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">CRCaccum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">for</span> <span class="p">(</span><span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">size</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span>
		<span class="p">{</span>
		<span class="n">i</span> <span class="o">=</span> <span class="p">((</span><span class="kt">int</span><span class="p">)(</span><span class="n">CRCaccum</span><span class="o">&gt;&gt;</span><span class="mi">24</span><span class="p">)</span><span class="o">^</span><span class="p">(</span><span class="o">*</span><span class="n">ptr</span><span class="o">++</span><span class="p">))</span><span class="o">&amp;</span><span class="mh">0xff</span><span class="p">;</span>
		<span class="n">CRCaccum</span> <span class="o">=</span> <span class="p">(</span><span class="n">CRCaccum</span><span class="o">&lt;&lt;</span><span class="mi">8</span><span class="p">)</span><span class="o">^</span><span class="n">CRCtable</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
		<span class="p">}</span>
	<span class="k">return</span> <span class="n">CRCaccum</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After studying the code a bit, I’d ended up writing my own variation of it to use for matching names.</p>

<p>The next thing to do was to determine all the original filenames, which is uh, a little trickier. 
In the end, through a combination of scraping the executable, scraping through much of the extracted content and guessing based on naming patterns, I’d managed to match a reasonable amount, though sadly not everything.</p>

<blockquote>
  <p>51.53% matched (675/1310) for Blitz package “TITAN.DAT”</p>
</blockquote>

<p>I don’t think I’ve got more in me for now. 
At times, it felt like a game of battleship. 
You can find all the matched names <a href="https://raw.githubusercontent.com/OldTimes-Software/hei/master/extras/blitz/blitz_titan_files.h">here</a>.
Mind that the last section in that list is for <em>SPT</em> files, which we’ll talk about later.</p>

<h2 id="reviewing-the-dat-contents">Reviewing The DAT Contents</h2>

<p>Right so we’ve first gone over the files on the CD and found where the bulk of the game data was stored, spent a silly amount of time trying to determine the filenames, and now we’re here. 
Your secrets can’t hide from me!</p>

<p class="tb-img-subtitle"><img src="/assets/2024/titanae-dir-overview.png" alt="Extracted assets overview" />
Oh look, folders…</p>

<p>As you can see, we’ve got a number of files that still don’t have valid names—we’ll ignore those for now.
But hey, we’ve also got a number of folders here too.</p>

<ul>
  <li><em>BIN</em>
    <ul>
      <li>This contains different binaries of logic for different areas of the game which are loaded as needed. Likely to save on memory, they are separate from the main executable, which is not all uncommon for PSX games.</li>
    </ul>
  </li>
  <li><em>EXPLORE</em>
    <ul>
      <li>This contains all the assets specific to the <em>explore</em> mode, i.e., when we’re not flying a spaceship. This is, unsurprisingly, given the number of levels for this mode, quite a significant amount of data.</li>
    </ul>
  </li>
  <li><em>FLYING</em>
    <ul>
      <li>All the assets specific to the parts of the game involving flying spaceships along a fixed track.</li>
    </ul>
  </li>
  <li><em>GRAPHICS</em>
    <ul>
      <li>Graphics that I’d figure are shared between both modes of gameplay, including assets for the frontend.</li>
    </ul>
  </li>
  <li><em>MODELS</em>
    <ul>
      <li>Fairly self-explanatory as to what this contains. Many of these are actually specific to the <em>explore</em> mode.</li>
    </ul>
  </li>
  <li><em>SOUND</em>
    <ul>
      <li>Holds sound banks used by the game, for, well, sounds.</li>
    </ul>
  </li>
  <li><em>STRTMENU</em>
    <ul>
      <li>More frontend assets.</li>
    </ul>
  </li>
</ul>

<p>There are additional folders under some of these for further organisation.</p>

<p>There are also a number of different formats used by the game, all proprietary, of course. 
The below doesn’t even cover everything.</p>

<h3 id="psi">PSI</h3>

<p>These are the models used by the game and also appear to be a common format used by some other Blitz titles.
It was my plan to write a tool to convert these in time for this piece, but it kept tumbling down my list.</p>

<p>You can find a rather limited write-up of the spec below.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">RGB</span>
<span class="p">{</span>
	<span class="kt">uint8_t</span> <span class="n">r</span><span class="p">;</span>
	<span class="kt">uint8_t</span> <span class="n">g</span><span class="p">;</span>
	<span class="kt">uint8_t</span> <span class="n">b</span><span class="p">;</span>
	<span class="kt">uint8_t</span> <span class="n">padding</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span>
<span class="p">{</span>
	<span class="kt">int32_t</span> <span class="n">magic</span><span class="p">;</span>  <span class="c1">// 'PSI\0'</span>
	<span class="kt">int32_t</span> <span class="n">version</span><span class="p">;</span><span class="c1">// 1</span>
	<span class="kt">int32_t</span> <span class="n">flags</span><span class="p">;</span>
	<span class="kt">char</span>    <span class="n">name</span><span class="p">[</span> <span class="mi">32</span> <span class="p">];</span><span class="c1">// typically seems to be blank...</span>

	<span class="kt">int32_t</span> <span class="n">numMeshes</span><span class="p">;</span>
	<span class="kt">int32_t</span> <span class="n">numVertices</span><span class="p">;</span>

	<span class="kt">int32_t</span> <span class="n">numPolygons</span><span class="p">;</span>
	<span class="kt">int32_t</span> <span class="n">polygonOffset</span><span class="p">;</span>

	<span class="kt">uint16_t</span> <span class="n">firstFrame</span><span class="p">;</span>
	<span class="kt">uint16_t</span> <span class="n">lastFrame</span><span class="p">;</span>

	<span class="kt">int32_t</span> <span class="n">numSegments</span><span class="p">;</span>
	<span class="kt">int32_t</span> <span class="n">segmentOffset</span><span class="p">;</span>

	<span class="kt">int32_t</span> <span class="n">numTextures</span><span class="p">;</span>
	<span class="kt">int32_t</span> <span class="n">textureOffset</span><span class="p">;</span>

	<span class="kt">int32_t</span> <span class="n">meshOffset</span><span class="p">;</span>

	<span class="kt">int32_t</span> <span class="n">radius</span><span class="p">;</span>
<span class="p">}</span> <span class="n">header</span><span class="p">;</span>

<span class="c1">// textures</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">numTextures</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">)</span>
<span class="p">{</span>
	<span class="n">FSeek</span><span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">textureOffset</span> <span class="p">);</span>
	<span class="k">struct</span>
	<span class="p">{</span>
		<span class="kt">char</span> <span class="n">name</span><span class="p">[</span> <span class="mi">32</span> <span class="p">];</span>
	<span class="p">}</span> <span class="n">textures</span><span class="p">[</span> <span class="n">header</span><span class="p">.</span><span class="n">numTextures</span> <span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There’s more <a href="https://github.com/hogsy/formats/blob/main/templates/blitz/blitz_psi.bt">here</a> but not much.
At some stage I’d like to look at these again.</p>

<h3 id="xed">XED</h3>

<p>Not sure how you would pronounce this one in the real world. 
Zed?</p>

<p>Anyway, these appear to be scripts that outline the objects that are placed in the world and their properties. 
There is one for each level.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// MAP: Drej_3</span>

<span class="n">SEGMENT</span>
<span class="p">{</span>
	<span class="n">INDEX</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
	<span class="n">POLYGONS</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
	<span class="n">VERTICES</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
	<span class="n">TYPE</span> <span class="p">{</span><span class="n">LANDSCAPE</span><span class="p">}</span>
	<span class="n">NAME</span> <span class="p">{</span><span class="n">NAVROOM</span><span class="p">}</span>
	<span class="n">POS</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}</span>
	<span class="n">ANGLE</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}</span>
	<span class="n">SIZE</span> <span class="p">{</span><span class="mi">4096</span><span class="p">,</span> <span class="mi">4096</span><span class="p">,</span> <span class="mi">4096</span><span class="p">}</span>
<span class="p">}</span>

<span class="p">[...]</span>

<span class="n">LIGHT</span>
<span class="p">{</span>
	<span class="n">POS</span> <span class="p">{</span><span class="mi">2880</span><span class="p">,</span> <span class="o">-</span><span class="mi">600</span><span class="p">,</span> <span class="mi">6401</span><span class="p">}</span>
	<span class="n">VAL</span> <span class="p">{</span><span class="mi">158</span><span class="p">,</span> <span class="mi">69</span><span class="p">,</span> <span class="mi">194</span><span class="p">}</span>
	<span class="n">RANGE</span> <span class="p">{</span><span class="mi">2481</span><span class="p">}</span>
	<span class="n">AMBIENCE</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
	<span class="n">BOOST</span> <span class="p">{</span><span class="mi">2500</span><span class="p">}</span>
	<span class="n">INFINITE</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
<span class="p">}</span>

<span class="p">[...]</span>

<span class="n">SWITCH</span>
<span class="p">{</span>
	<span class="n">TAG</span> <span class="p">{</span><span class="n">T34SWTCH</span><span class="p">}</span>
	<span class="n">TAG_CRC</span> <span class="p">{</span><span class="mi">488158572</span><span class="p">}</span>
	<span class="n">NAME</span> <span class="p">{</span><span class="n">CSWITCH</span><span class="p">}</span>
	<span class="n">POS</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">125</span><span class="p">,</span> <span class="o">-</span><span class="mi">1251</span><span class="p">}</span> <span class="c1">// x,y,z</span>
	<span class="n">ANGLE</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2048</span><span class="p">,</span> <span class="mi">0</span><span class="p">}</span> <span class="c1">// 0-&gt;4095</span>
	<span class="n">LINK</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
	<span class="n">STATE</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
<span class="p">}</span>

<span class="n">DOOR</span>
<span class="p">{</span>
	<span class="n">NAME</span> <span class="p">{</span><span class="n">CONDUIT</span><span class="p">}</span>
	<span class="n">TAG</span> <span class="p">{</span><span class="n">TP34</span><span class="p">}</span>
	<span class="n">TAG_CRC</span> <span class="p">{</span><span class="o">-</span><span class="mi">1266880023</span><span class="p">}</span>
	<span class="n">POS</span> <span class="p">{</span><span class="mi">683</span><span class="p">,</span> <span class="o">-</span><span class="mi">220</span><span class="p">,</span> <span class="o">-</span><span class="mi">268</span><span class="p">}</span> <span class="c1">// x,y,z</span>
	<span class="n">ANGLE</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">0</span><span class="p">}</span> <span class="c1">// 0-&gt;4095</span>
	<span class="n">ID</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
	<span class="n">STATE</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
<span class="p">}</span>

<span class="n">AUTO_DOOR</span>
<span class="p">{</span>
	<span class="n">NAME</span> <span class="p">{</span><span class="n">CONDUIT</span><span class="p">}</span>
	<span class="n">TAG</span> <span class="p">{</span><span class="n">EMITTER3</span><span class="p">}</span>
	<span class="n">TAG_CRC</span> <span class="p">{</span><span class="o">-</span><span class="mi">1416931696</span><span class="p">}</span>
	<span class="n">POS</span> <span class="p">{</span><span class="mi">2831</span><span class="p">,</span> <span class="o">-</span><span class="mi">491</span><span class="p">,</span> <span class="mi">6867</span><span class="p">}</span>
	<span class="n">ANGLE</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}</span> <span class="c1">// 0-&gt;4095</span>
	<span class="n">RADIUS</span> <span class="p">{</span><span class="mi">10</span><span class="p">}</span>
	<span class="n">STATE</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
<span class="p">}</span>

<span class="p">[...]</span>
</code></pre></div></div>

<p>The <em>SEGMENT</em> type appears to be referencing entries in the <em>LND</em> files, so my guess is that these are specifying what chunks of geometry are being used for the level and where. 
Otherwise, many of the other chunks are somewhat self-explanatory.</p>

<h3 id="fuk">FUK</h3>

<p>These are located under the <em>EXPLORE/EVENTS</em> directory. 
There is <em>usually</em> one per level, but some levels don’t appear to have one at all.</p>

<p>They’re similar in syntax to the <a href="#xed"><em>XED</em></a> files, however, these are used for adding in some basic logic for handling level-specific events. 
They also have a rather unfortunate file extension.</p>

<p>At the start of the file, the number of events and atoms (variables) are specified like so.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">TOTALEVENTS</span>	<span class="p">{</span><span class="mi">35</span><span class="p">}</span>
<span class="n">TOTALATOMS</span>	<span class="p">{</span><span class="mi">152</span><span class="p">}</span>
</code></pre></div></div>

<p>Following this, there’s then an entry for each event, such as seen below.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[...]</span>

<span class="n">EVENT</span>
<span class="p">{</span>
	<span class="n">NAME</span>	<span class="p">{</span><span class="n">STARTUP</span><span class="p">}</span>	<span class="c1">//sets poly colours for emergency light</span>

	<span class="n">ONESHOT</span>

	<span class="n">TRIGGER</span>
	<span class="p">{</span>
		<span class="n">NUMATOMS</span> <span class="p">{</span><span class="mi">1</span><span class="p">}</span>

		<span class="n">OR</span> <span class="p">{</span> <span class="n">TRUE</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span> <span class="p">}</span> <span class="c1">// TRUE. ALWAYS FIRES.</span>
	<span class="p">}</span>

	<span class="n">SCRIPT</span>
	<span class="p">{</span>
		<span class="n">NUMATOMS</span> <span class="p">{</span><span class="mi">2</span><span class="p">}</span>

		<span class="c1">// - set flatcolours</span>
		<span class="n">DO</span> <span class="p">{</span><span class="n">FLATCOL0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">3</span> <span class="p">}</span>
		<span class="n">DO</span> <span class="p">{</span><span class="n">FLATCOL1</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">3</span><span class="p">}</span>
		
		<span class="c1">// - cheats</span>
		<span class="c1">//DO { WRITETOK, 944488308, 1, 0, 0} //frank</span>
		<span class="c1">//DO { WRITETOK, 1519763642, 1, 0, 0} //dave</span>
		<span class="c1">//DO { WRITETOK, 725827994, 1, 0, 0} //geoff</span>
		<span class="c1">//DO { WRITETOK, 1245070714, 1, 0, 0} //wayne</span>
		<span class="c1">//DO { WRITETOK, 2059165261, 0, 0, 0} //titan code off</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="p">[...]</span>
</code></pre></div></div>

<p>Given these looks like they’re created by hand, I’d wager it must’ve been a pain keeping the <code class="language-plaintext highlighter-rouge">TOTALEVENTS</code> and <code class="language-plaintext highlighter-rouge">TOTALATOMS</code> updated. 
I’d figure that these are provided so that the script interpreter can pre-allocate storage for the required events and variables up-front for speed.</p>

<h3 id="spt">SPT</h3>

<p>These are containers for either a single image, or multiple.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="n">SptFlags</span>
<span class="p">{</span>
	<span class="n">SPT_FLAGS_END</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>      <span class="c1">// indicates that it's the last image</span>
	<span class="n">SPT_FLAGS_8BPP</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>     <span class="c1">// if this flag isn't set, it seems to be 4bpp</span>
	<span class="n">SPT_FLAGS_ALPHA</span> <span class="o">=</span> <span class="mi">16</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">SptHeader</span>
<span class="p">{</span>
	<span class="kt">uint32_t</span> <span class="n">dataOffset</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">paletteOffset</span><span class="p">;</span>
	<span class="kt">uint8_t</span>  <span class="n">width</span><span class="p">;</span>
	<span class="kt">uint8_t</span>  <span class="n">height</span><span class="p">;</span>
	<span class="kt">int32_t</span>  <span class="n">unk0</span><span class="p">;</span>
	<span class="kt">uint16_t</span> <span class="n">flags</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">hash</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>You’ll notice the last variable is our friend, Mr. Hash. 
Fortunately, this uses the same hashing algorithm we looked at earlier, though we still need to put in some work to figure out what the names were, so we can match them.</p>

<p>In the end, I’d managed to match a number of them by scraping the texture names mentioned in the PSI files. You can find the end result at the bottom of <a href="">here</a>.</p>

<p>As mentioned, the <em>SPT</em> format doesn’t contain just one image but multiple—the last is denoted by an end flag. 
An example of how you could iterate over each of these is provided below.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">struct</span> <span class="n">SptHeader</span> <span class="n">header</span><span class="p">;</span>
	<span class="k">do</span> <span class="p">{</span>
		<span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">parse_header</span><span class="p">(</span> <span class="n">file</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">header</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span>
			<span class="k">break</span><span class="p">;</span>
		<span class="p">}</span>

		<span class="c1">// save this so we can restore our position after</span>
		<span class="n">PLFileOffset</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">gInterface</span><span class="o">-&gt;</span><span class="n">GetFileOffset</span><span class="p">(</span> <span class="n">file</span> <span class="p">);</span>

		<span class="c1">// code here for parsing and converting the image goes here...</span>

		<span class="n">gInterface</span><span class="o">-&gt;</span><span class="n">FileSeek</span><span class="p">(</span> <span class="n">file</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">PL_SEEK_SET</span> <span class="p">);</span>
	<span class="p">}</span> <span class="k">while</span> <span class="p">(</span> <span class="o">!</span><span class="p">(</span> <span class="n">header</span><span class="p">.</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">SPT_FLAGS_END</span> <span class="p">)</span> <span class="p">);</span>
</code></pre></div></div>

<p>There appear to be two possible formats an <em>SPT</em> can be - 4bpp or 8bpp, which is denoted by a flag (2) - if the flag isn’t present, the image is 4bpp but otherwise 8bpp.</p>

<p>You can then convert it like so. 
Just keep in mind I’m not currently handling the alpha channel here as I’d encountered some oddities there.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define AAA5( x ) ( ( ( x ) &lt;&lt; 3 ) | ( ( x ) &gt;&gt; 1 ) )
#define AAA6( x ) ( ( ( x ) &lt;&lt; 2 ) | ( ( x ) &gt;&gt; 4 ) )
</span>	<span class="n">PLColour</span> <span class="o">*</span><span class="n">pColour</span> <span class="o">=</span> <span class="p">(</span> <span class="n">PLColour</span> <span class="o">*</span> <span class="p">)</span> <span class="o">&amp;</span><span class="n">image</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span> <span class="mi">0</span> <span class="p">][</span> <span class="mi">0</span> <span class="p">];</span>
	<span class="k">if</span> <span class="p">(</span> <span class="n">format</span> <span class="o">==</span> <span class="n">SPT_FORMAT_8BPP</span> <span class="p">)</span> <span class="p">{</span>
		<span class="k">for</span> <span class="p">(</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span> <span class="p">{</span>
			<span class="kt">uint16_t</span> <span class="n">c</span> <span class="o">=</span> <span class="n">pal</span><span class="p">[</span> <span class="n">img</span><span class="p">[</span> <span class="n">i</span> <span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x0F</span> <span class="p">];</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">=</span> <span class="n">AAA5</span><span class="p">(</span> <span class="n">c</span> <span class="o">&amp;</span> <span class="mh">0x1F</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">=</span> <span class="n">AAA6</span><span class="p">(</span> <span class="p">(</span> <span class="n">c</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span> <span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3E</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">=</span> <span class="n">AAA5</span><span class="p">(</span> <span class="n">c</span> <span class="o">&gt;&gt;</span> <span class="mi">10</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">a</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
			<span class="o">++</span><span class="n">pColour</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="k">for</span> <span class="p">(</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span> <span class="p">{</span>
			<span class="kt">uint16_t</span> <span class="n">c</span> <span class="o">=</span> <span class="n">pal</span><span class="p">[</span> <span class="n">img</span><span class="p">[</span> <span class="n">i</span> <span class="p">]</span> <span class="o">&amp;</span> <span class="mh">0x0F</span> <span class="p">];</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">=</span> <span class="n">AAA5</span><span class="p">(</span> <span class="n">c</span> <span class="o">&amp;</span> <span class="mh">0x1F</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">=</span> <span class="n">AAA6</span><span class="p">(</span> <span class="p">(</span> <span class="n">c</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span> <span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3E</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">=</span> <span class="n">AAA5</span><span class="p">(</span> <span class="n">c</span> <span class="o">&gt;&gt;</span> <span class="mi">10</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">a</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
			<span class="o">++</span><span class="n">pColour</span><span class="p">;</span>

			<span class="n">c</span> <span class="o">=</span> <span class="n">pal</span><span class="p">[</span> <span class="n">img</span><span class="p">[</span> <span class="n">i</span> <span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span> <span class="p">];</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">=</span> <span class="n">AAA5</span><span class="p">(</span> <span class="n">c</span> <span class="o">&amp;</span> <span class="mh">0x1F</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">=</span> <span class="n">AAA6</span><span class="p">(</span> <span class="p">(</span> <span class="n">c</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span> <span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3E</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">=</span> <span class="n">AAA5</span><span class="p">(</span> <span class="n">c</span> <span class="o">&gt;&gt;</span> <span class="mi">10</span> <span class="p">);</span>
			<span class="n">pColour</span><span class="o">-&gt;</span><span class="n">a</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
			<span class="o">++</span><span class="n">pColour</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>Once that is all out of the way, we end up with this.</p>

<p><img src="/assets/2024/titanae-spt-overview.png" alt="Dumped SPT" /></p>

<p>There were a couple interesting things I’d noticed once everything was converted for easy viewing.</p>

<p>For instance, under D72B9099 (hashed value as I didn’t find a match), there’s what appears to be an early wrench icon?</p>

<p><img src="/assets/2024/titanae-7900697.png" alt="Old wrench icon" /></p>

<p>And then under FC3B0030, we find the same wrench icon alongside what appears to be an old icon for a gun, health and ammo.</p>

<p><img src="/assets/2024/titanae-2DFE30F5.png" alt="" /></p>

<p><img src="/assets/2024/titanae-499633EA.png" alt="" /></p>

<p><img src="/assets/2024/titanae-BA5FC303.png" alt="" /></p>

<p>All the converted results can be downloaded <a href="/assets/2024/titanae-dump-spts.7z">here</a> for you to do your own sleuthing.</p>

<h3 id="raw">RAW!</h3>

<p><img src="/assets/gifs/raw.gif" alt="It's f****** raw!" /></p>

<p>The <em>RAW</em> files are images that seem to all be displayed directly on the screen. There’s no header or anything fancy to them. 512x256 and RGB555 format.</p>

<p>I ended up implementing the following to convert them.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">static</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">RAW_WIDTH</span> <span class="o">=</span> <span class="mi">512</span><span class="p">;</span>
	<span class="k">static</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">RAW_HEIGHT</span> <span class="o">=</span> <span class="mi">256</span><span class="p">;</span>
	<span class="k">static</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">RAW_SIZE</span> <span class="o">=</span> <span class="n">RAW_WIDTH</span> <span class="o">*</span> <span class="n">RAW_HEIGHT</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>

	<span class="p">[...]</span>

	<span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">shiftR</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">shiftG</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
	<span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">shiftB</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>

	<span class="n">PLColour</span> <span class="o">*</span><span class="n">colour</span> <span class="o">=</span> <span class="p">(</span> <span class="n">PLColour</span> <span class="o">*</span> <span class="p">)</span> <span class="o">&amp;</span><span class="n">image</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span> <span class="mi">0</span> <span class="p">][</span> <span class="mi">0</span> <span class="p">];</span>
	<span class="k">for</span> <span class="p">(</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span> <span class="p">{</span>
		<span class="kt">uint16_t</span> <span class="n">c</span> <span class="o">=</span> <span class="n">htole16</span><span class="p">(</span> <span class="n">buf</span><span class="p">[</span> <span class="n">i</span> <span class="p">]</span> <span class="p">);</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">=</span> <span class="p">(</span> <span class="p">(</span> <span class="n">c</span> <span class="o">&amp;</span> <span class="p">(</span> <span class="mi">31</span> <span class="o">&lt;&lt;</span> <span class="n">shiftR</span> <span class="p">)</span> <span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">shiftR</span> <span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">=</span> <span class="p">(</span> <span class="p">(</span> <span class="n">c</span> <span class="o">&amp;</span> <span class="p">(</span> <span class="mi">31</span> <span class="o">&lt;&lt;</span> <span class="n">shiftG</span> <span class="p">)</span> <span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">shiftG</span> <span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">=</span> <span class="p">(</span> <span class="p">(</span> <span class="n">c</span> <span class="o">&amp;</span> <span class="p">(</span> <span class="mi">31</span> <span class="o">&lt;&lt;</span> <span class="n">shiftB</span> <span class="p">)</span> <span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">shiftB</span> <span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">|=</span> <span class="n">colour</span><span class="o">-&gt;</span><span class="n">r</span> <span class="o">&gt;&gt;</span> <span class="mi">5</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">|=</span> <span class="n">colour</span><span class="o">-&gt;</span><span class="n">g</span> <span class="o">&gt;&gt;</span> <span class="mi">5</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">|=</span> <span class="n">colour</span><span class="o">-&gt;</span><span class="n">b</span> <span class="o">&gt;&gt;</span> <span class="mi">5</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">-&gt;</span><span class="n">a</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
		<span class="n">colour</span><span class="o">++</span><span class="p">;</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>You can find the converted results below, though these are just the identified files I’ve converted here—there may be more.</p>

<h4 id="strtmenu">STRTMENU</h4>

<p>The first four of these are displayed when the player dies or the level ends.
The one showing Cale on the floor, dubbed <em>GAMEOVER.RAW</em>, is possibly unused (the game references it, but I do not recall seeing it).</p>

<p>The screens with the controls are shown when starting to play either of the exploration or flying levels from the main menu.</p>

<p>The very last is used as the background for the main menu.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2024/COMING1.RAW.png"><img src="/_thumbs/assets/2024/COMING1.RAW_200x100.png" /></a><a href="/assets/2024/COMING2.RAW.png"><img src="/_thumbs/assets/2024/COMING2.RAW_200x100.png" /></a><a href="/assets/2024/COMING3.RAW.png"><img src="/_thumbs/assets/2024/COMING3.RAW_200x100.png" /></a><a href="/assets/2024/COMING4.RAW.png"><img src="/_thumbs/assets/2024/COMING4.RAW_200x100.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2024/CSELECT.RAW.png"><img src="/_thumbs/assets/2024/CSELECT.RAW_200x100.png" /></a><a href="/assets/2024/ESETUP.RAW.png"><img src="/_thumbs/assets/2024/ESETUP.RAW_200x100.png" /></a><a href="/assets/2024/FSETUP.RAW.png"><img src="/_thumbs/assets/2024/FSETUP.RAW_200x100.png" /></a><a href="/assets/2024/GAMEOVER.RAW.png"><img src="/_thumbs/assets/2024/GAMEOVER.RAW_200x100.png" /></a></div><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2024/STRTMENU.RAW.png"><img src="/_thumbs/assets/2024/STRTMENU.RAW_200x100.png" /></a></div></div>

<h4 id="graphicsfrontend">GRAPHICS/FRONTEND</h4>

<p>This gets used as the background for the debug menu.</p>

<p><img src="/assets/2024/MENU.RAW.png" alt="MENU.RAW" /></p>

<h4 id="flyingbackdrps">FLYING/BACKDRPS</h4>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/2024/ENDSCN1.RAW.png"><img src="/_thumbs/assets/2024/ENDSCN1.RAW_200x100.png" /></a><a href="/assets/2024/ENDSCN2.RAW.png"><img src="/_thumbs/assets/2024/ENDSCN2.RAW_200x100.png" /></a><a href="/assets/2024/ENDSCN3.RAW.png"><img src="/_thumbs/assets/2024/ENDSCN3.RAW_200x100.png" /></a><a href="/assets/2024/VOLCANO.RAW.png"><img src="/_thumbs/assets/2024/VOLCANO.RAW_200x100.png" /></a></div></div>

<p>The last image, <em>VOLCANO.RAW</em>, might just be a placeholder as it matches an image used for many of the other exploration levels.</p>

<h4 id="explore">EXPLORE</h4>

<p>Most of these are just duplicates, so I’ve only included the first unique ones.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/2024/BAN_1.RAW.png"><img src="/_thumbs/assets/2024/BAN_1.RAW_200x100.png" /></a><a href="/assets/2024/DERELIC1.RAW.png"><img src="/_thumbs/assets/2024/DERELIC1.RAW_200x100.png" /></a><a href="/assets/2024/TAU14_1.RAW.png"><img src="/_thumbs/assets/2024/TAU14_1.RAW_200x100.png" /></a></div></div>

<h2 id="bonus-section">Bonus Section</h2>

<p>These are just some minor things I wasn’t really sure where else to throw.</p>

<h3 id="source-code">Source Code</h3>

<p>We’re rather fortunate that another game by Blitz, Chicken Run, had its source code uploaded to the web a while back which can be found <a href="https://archive.thedatadungeon.com/chicken_run_2000/distributions/2000-08-31_source_code/chicken_run_source.rar">here</a> (side-note: if you know who/where it was uploaded it originally, let me know, so I can provide a citation on our archive).</p>

<p>By using the linker map as a reference, we can see some common ancestry between Chicken Run and Titan A.E.</p>

<p>Overall, judging from the Chicken Run code, at least, I wouldn’t really say Blitz had an <em>engine</em> at this time but rather some common libraries/code their games shared. Not all uncommon for the time. Eventually, they would go on to develop <a href="">Blitz Tech</a> but that seems to have come much later (curiously, the <a href="https://www.pcgamingwiki.com/wiki/Engine:BlitzTech">PC Gaming Wiki</a> suggests their technology was called Babel Engine prior to 2009, but there’s no citation, and I’d wager that still likely postdates Titan A.E. and Chicken Run.)</p>

<h3 id="shadows">Shadows</h3>

<p>I’d posted about these on <a href="https://mastodon.social/@hogsy/112226446140883055">Mastodon</a> back in April.</p>

<div align="center">
<iframe src="https://mastodon.social/@hogsy/112226446140883055/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"></iframe><script src="https://mastodon.social/embed.js" async="async"></script>
</div>

<p>They just use sprites essentially projected to the ground plane from each part of the model, so it’s a really simple system but not something I’ve seen very commonly on the PSX.
At least as of the code recycled in Chicken Run, there can be up to five shadows projected that are emitted from lights marked to cast shadows.</p>

<h3 id="loading-screens">Loading Screens</h3>

<p>The screen displayed when loading a level is denoted by its corresponding <em>RAW</em> file; for instance, you load up the Shooting Gallery level, and it’ll use <em>EXPLORE/SHOOTGAL.RAW</em> for the loading screen. 
Despite this, most of the levels unfortunately just use the same duplicate image in this build of the game.
It’s likely this probably would’ve changed had the game continued development.</p>

<h2 id="the-end">The End</h2>

<p>That concludes this poking and prodding session for now.
I’m hopeful that at some stage we might see more show up in relation to this game.</p>

<p>If you want to support what we do, we’re not accepting donations at this time, but any feedback is appreciated. 
Feel free to join our <a href="https://discord.gg/bJKBRNagfA">Discord</a> or leave a comment letting us know what you think! 
Alternatively, you can follow me on <a href="https://mastodon.social/@hogsy">Mastodon</a> for any of my general rambling.</p>

<p>In the meantime, I shall vanish back into my hiding place.</p>

<p><img src="/assets/gifs/homer-simpson-bye.gif" alt="bye" /></p>

<h2 id="resources">Resources</h2>

<p>Some of these are more specific to the film than the game itself, but I thought they were still interesting, and so I’ve included them here.</p>

<ul>
  <li><a href="https://www.amazon.com/Ice-Planet-Lost-Worlds-Titan-ebook/dp/B01KP6KABW/">Ice Planet: The Lost Worlds of the Titan A.E. Game</a></li>
  <li><a href="http://www.thomasmadams.com/titan-ae.html">Thomas M. Adams Portfolio</a> (includes some very nice screenshots of the tools used to develop the game)</li>
  <li><a href="https://archive.org/details/titan-a.-e.-usa-demo-patched">Titan A. E. ( USA) ( Demo) (patched)</a> (a patched copy of the demo that makes more content accessible)</li>
  <li><a href="https://www.unseen64.net/2023/09/23/titan-ae-psx-pc-cancelled/">Unseen64: Titan A.E.</a></li>
  <li><a href="https://www.unseen64.net/2010/10/20/the-cancelled-titan-a-e-game-is-almost-restored/">Unseen64: The cancelled Titan A.E. game is almost restored</a></li>
  <li><a href="http://titanae.free.fr/en/index.php">Titan AE : the Wake Angels</a> (a fan-site that covers the film and other media)</li>
  <li><a href="http://livlily.blogspot.com/2012/03/titan-ae-2000-concept-art_04.html">Living Lines Library: Titan A.E. (2000) - Concept Art </a></li>
  <li><a href="http://www.gflanimationstudios.com/p/titan-ae-production-designs-and.html">GFL Animation Studios: Titan AE</a> (concept art produced for the movie)</li>
  <li><a href="https://comics.ha.com/itm/animation-art/concept-art/titan-ae-concept-painting-by-winston-aquino-don-bluth-fox-2000-/a/7292-19558.s?ic16=ViewItem-BrowseTabs-Auction-Archive-ThisAuction-120115">Heritage Auctions</a> (a collection of concept art that was sold in 2022, navigate along the top to see more)</li>
</ul>]]></content><author><name>Mark Sowden</name></author><category term="Reverse Engineering" /><category term="Game" /><category term="Game/Titan A.E." /><category term="2024" /><category term="Titan A.E." /><category term="Reverse Engineering" /><summary type="html"><![CDATA[I peer amongst my assorted collection of unfinished works, noticing one in particular that’s been sitting for quite some time. Letting off a sigh, I grab it and proceed to blow the dust off it; an immediate coughing fit follows, and shock, seeing the only word on the page is nothing more than “The”. Oops. This has only been waiting two years. Alright, I guess it’s about time to get this out the door.]]></summary></entry><entry><title type="html">Poking and Prodding Wild Water Adrenaline</title><link href="https://talonbrave.info/2023/11/27/wild-water-adrenaline.html" rel="alternate" type="text/html" title="Poking and Prodding Wild Water Adrenaline" /><published>2023-11-27T00:00:00+00:00</published><updated>2023-11-27T00:00:00+00:00</updated><id>https://talonbrave.info/2023/11/27/wild-water-adrenaline</id><content type="html" xml:base="https://talonbrave.info/2023/11/27/wild-water-adrenaline.html"><![CDATA[<p>This isn’t going to be the most in-depth piece I’ve ever written, as my interest only went so far with this one (I’m sure you can understand if you’re familiar with the title in question), but I thought I’d throw some of my findings out there just on the off-chance it’s useful.</p>

<!-- more -->

<p>And uh, yeah, some mysteries here could be solved by simply disassembling the game, however, I felt lazy and just poked at the files on this one—as I said, maybe this will spark something for someone a little more interested than I was, heh.</p>

<p>If you’re <em>not</em> familiar with Wild Water Adrenaline, it’s a uh, not fantastically well-received PlayStation 2 game produced by <a href="https://www.fresh3d.com/">Fresh3D</a>; essentially the guys that were involved in working on Outcast 2 (which, I’m not going to lie, is the sole reason for my interest).</p>

<p>While Fresh3D still seems to exist in some capacity, it turned out Franck Sauer, who is listed on it’s website, no longer actually works there, leaving just Yann Robert I guess. Unfortunately, I did not have any luck getting in touch with Yann, but it seems they’ve since moved on to work for Epic Games.</p>

<p>So that’s another reason why this piece is so short, or another excuse.</p>

<div align="center">

<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Vgh7OQ3flDs?si=FOUapu401PumykqP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>

</div>

<p>I’ll confess, though, as I’m sure you can appreciate from the footage above, Wild Water Adrenaline is absolutely gorgeous for a PlayStation 2 title. Though that makes it all the more disappointing that the tech behind it didn’t get used to produce something a little more compelling (you know, like Outcast 2…)</p>

<p>You can read more about Wild Water Adrenaline’s development <a href="https://web.archive.org/web/20150512205341/http://francksauer.com/index.php/games/test/15-games/published-games/24-wild-water-adrenaline">here</a>, as written up by one of the developers involved with the game.</p>

<p>If you’d like to learn more about the ill-fated Outcast 2, there’s some great information available <a href="https://web.archive.org/web/20150815235718/https://francksauer.com/index.php/16-games/unpublished-prototypes/29-outcast-2-the-lost-paradise">here</a> written up by the same developer. That said, I’ll throw a <strong>spoiler warning</strong> in there as I understand this was later removed from their current website as elements of this earlier design are being used in the upcoming <a href="https://www.outcastthegame.com/">Outcast: A New Beginning</a> title.</p>

<p>Anyway, I digress, let’s get on with it.</p>

<p>When viewing the contents of the disc for the game, we see the following.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
├── DATA.BIN
├── PS2
│   ├── IRX300
│   │   ├── IOPRP300.IMG
│   │   ├── LIBSD.IRX
│   │   ├── MCMAN.IRX
│   │   ├── MCSERV.IRX
│   │   ├── MTAPMAN.IRX
│   │   ├── PADMAN.IRX
│   │   ├── SDRDRV.IRX
│   │   ├── SIO2MAN.IRX
│   │   └── STREAM.IRX
│   └── NAMES.BIN
├── SLES_535.95
└── SYSTEM.CNF
</code></pre></div></div>

<p>Can you guess where the actual data for the game is stored? If you guessed <code class="language-plaintext highlighter-rouge">DATA.BIN</code> then you guessed correctly!</p>

<h2 id="databin">DATA.BIN</h2>

<p>It’s a pretty uninteresting package format, and the content within is aligned to blocks of 2,048 bytes, which was quite likely done for more effectively streaming the data off the disc.</p>

<p><a href="/assets/wild-water-adrenaline/wwa-data-bin-overview.png"><img src="/assets/wild-water-adrenaline/wwa-data-bin-overview.png" alt="Overview of DATA.BIN in Rehex" /></a></p>

<p>The header of the package is as follows.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Header</span>
<span class="p">{</span>
	<span class="kt">char</span> <span class="n">ident</span><span class="p">[</span> <span class="mi">8</span> <span class="p">];</span>
	<span class="kt">int32_t</span> <span class="n">unk0</span><span class="p">[</span> <span class="mi">2</span> <span class="p">];</span> <span class="c1">// &lt;- these are 0, so I've no idea</span>
	<span class="kt">uint32_t</span> <span class="n">numFiles</span><span class="p">;</span>
	<span class="n">Index</span> <span class="n">indices</span><span class="p">[</span><span class="n">numFiles</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<p>And each <code class="language-plaintext highlighter-rouge">Index</code> is the following.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Index</span>
<span class="p">{</span>
	<span class="kt">char</span> <span class="n">name</span><span class="p">[</span> <span class="mi">12</span> <span class="p">];</span>
	<span class="kt">uint32_t</span> <span class="n">blockNum</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">size</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">blockNum</code> variable confused me for a little bit before I realised it’s just simply how many blocks of 2,048 bytes to reach the offset of the file in question.</p>

<p>Oh, and note that the names are not <em>null</em> terminated but instead <em>space</em> terminated, which you’ll want to account for if you decide to write your own tools to dump/repack the files.</p>

<p>If this isn’t sufficient for you to understand the format, for whatever reason, I’d thrown together an implementation <a href="https://github.com/OldTimes-Software/hei/blob/9c039cffc5a10e6f4ea3db32768a5fb6a7354dfc/plcore/package/package_format_fresh.c">here</a>.</p>

<p>We’re finally left with this.</p>

<p><a href="/assets/wild-water-adrenaline/wwa-data-bin-filled.png"><img src="/assets/wild-water-adrenaline/wwa-data-bin-filled.png" alt="With binary template loaded into Rehex" /></a></p>

<p>Right, so we’ve got that out the way and extracted the <code class="language-plaintext highlighter-rouge">DATA.BIN</code> file - what are we left with?</p>

<p>A big ol’ blob o’ more files… (click <a href="#namesbin">here</a> if you want to skip)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
├── 111.DAT
├── 112.DAT
├── 114.DAT
├── 211.DAT
├── 213.DAT
├── 214.DAT
├── 222.DAT
├── A_CA_1.MIC
├── A_FR_1.MIC
├── ALTISS.PSS
├── AMBIENT.LUA
├── ANIM.LUA
├── ANIMS.PAK
├── A_NZ_1.MIC
├── ASTICK.PAK
├── A_US_1.MIC
├── AUTOEXEC.LUA
├── BONUS.LUA
├── BONUS.PAK
├── BOOT.TGA
├── CA_BACK.PAK
├── CA_BONUS.GAM
├── CA_CAM.PAK
├── CA_COLL.PAK
├── CA.CPS
├── CAMTRIG.LUA
├── CA_OCCL.PAK
├── CA.PAK
├── CA_SFX.PAK
├── CA_SNOW.PAK
├── CA_STREA.GAM
├── CA.TEX
├── CA_TRIG.GAM
├── CD.LUA
├── CHARS.TEX
├── CREDIT.MIC
├── CREDITS.LUA
├── CURVE.PAK
├── E_CREDIT.VAG
├── EFFECTS.LUA
├── ELEC1.MIC
├── ELEC2.MIC
├── EMITTER.ANM
├── ENDRACE.LUA
├── ENGLISH.LUA
├── E_TIME.VAG
├── F3DSPLAS.ANM
├── F3DSPLAS.PAK
├── F3DSPLAS.TEX
├── FEMA_M.PAK
├── FEMA.PAK
├── FEMB_M.PAK
├── FEMB.PAK
├── FEMC_M.PAK
├── FEMC.PAK
├── FEMD_M.PAK
├── FEMD.PAK
├── FIELD.ANM
├── FINISH.VAG
├── FLAGS.TEX
├── FLUID.PSS
├── FONTS.PAK
├── FONTS.TEX
├── FR_BACK.PAK
├── FR_BONUS.GAM
├── FR_CAM.PAK
├── FR_CLOUD.PAK
├── FR_COLL.PAK
├── FR.CPS
├── FRENCH.LUA
├── FRESH3D.LUA
├── FRESHENG.LUA
├── FRESH.LUA
├── FRESH.MIC
├── FRESH.PAK
├── FRESH.TEX
├── FR_FLARE.PAK
├── FR_OCCL.PAK
├── FR.PAK
├── FR_PART.PAK
├── FR_SFX.PAK
├── FR_STREA.GAM
├── FR.TEX
├── FR_TRIG.GAM
├── GAME.LUA
├── GAMEPLAY.LUA
├── GERMAN.LUA
├── GETREADY.LUA
├── GLOBALS.LUA
├── HELMET.PAK
├── HELP.LUA
├── HOMA_M.PAK
├── HOMA.PAK
├── HOMB_M.PAK
├── HOMB.PAK
├── HOMC_M.PAK
├── HOMC.PAK
├── HUD.LUA
├── I_BALISE.VAG
├── I_BAR.VAG
├── I_CANCEL.VAG
├── ICON.SYS
├── I_NAV.VAG
├── INDIE.PSS
├── INTERF1.TEX
├── INTERF2.TEX
├── INTERF.PAK
├── ITALIAN.LUA
├── I_TEXT.VAG
├── I_VALID.VAG
├── KAYAK.LUA
├── KAYAK.PAK
├── KIMPACT1.VAG
├── KIMPACT2.VAG
├── LANG.LUA
├── LOADING.PAK
├── LOADSAVE.LUA
├── MAIN.LUA
├── MEMCARD.LUA
├── MENU.COL
├── MENU.LUA
├── MENU.PAK
├── MENU.PSS
├── MENU.VAG
├── MSCREAM1.VAG
├── MSCREAM2.VAG
├── MSCREAM3.VAG
├── MSCREAM4.VAG
├── MSCREAM5.VAG
├── NAMES.BIN
├── NZ_BACK.PAK
├── NZ_BONUS.GAM
├── NZ_CAM.PAK
├── NZ_COLL.PAK
├── NZ.CPS
├── NZ_OCCL.PAK
├── NZ.PAK
├── NZ_PART.PAK
├── NZ_RAIN.PAK
├── NZ_SFX.PAK
├── NZ_STREA.GAM
├── NZ.TEX
├── NZ_TRIG.GAM
├── OCCLUDE.LUA
├── PADDLE.PAK
├── PAGAIE.PAK
├── PARTICLE.ANM
├── RAFTG.PAK
├── RAFTKAYA.TEX
├── RAFT.LUA
├── RAFT.PAK
├── RANKING.LUA
├── RECORDER.LUA
├── RECT.LUA
├── RIMPACT1.VAG
├── RIMPACT2.VAG
├── RKMENUS.LUA
├── ROCK1.MIC
├── ROCK2.MIC
├── ROOT1.MIC
├── ROOT2.MIC
├── ROW.VAG
├── RWATER1.VAG
├── RWATER2.VAG
├── SMOO1.MIC
├── SMOO2.MIC
├── SOUND.LUA
├── SPANISH.LUA
├── SPLASHG.PAK
├── SPLASHG.TEX
├── SPLASHM.PAK
├── SPLASHM.TEX
├── STICK.LUA
├── STYLES.LUA
├── TEAMMATE.LUA
├── TEXT.LUA
├── TIME_OUT.VAG
├── TRAINING.LUA
├── TR_BACK.PAK
├── TR_BONUS.GAM
├── TR_COLL.PAK
├── TR.CPS
├── TRIGGERS.LUA
├── TR.PAK
├── TR.TEX
├── UI.LUA
├── UNDWATER.VAG
├── US_BACK.PAK
├── US_BONUS.GAM
├── US_CAM.PAK
├── US_COLL.PAK
├── US.CPS
├── US_OCCL.PAK
├── US.PAK
├── US_PART.PAK
├── US_RIDE.PAK
├── US_SFX.PAK
├── US_STREA.GAM
├── US.TEX
├── US_TRIG.GAM
├── UTILS.LUA
├── VIEWER.LUA
├── VISUAL.LUA
├── WATFALLL.VAG
├── WATFALLM.VAG
├── WATFALLS.VAG
├── WSCREAM1.VAG
├── WSCREAM2.VAG
├── WSCREAM3.VAG
├── WSCREAM4.VAG
├── WSCREAM5.VAG
├── WSTREAM.LUA
├── WWA2.PSS
└── WWA.ICO
</code></pre></div></div>

<p><em>sigh</em></p>

<h2 id="namesbin">NAMES.BIN</h2>

<p>So one of the files in here actually has a duplicate stored outside under the <code class="language-plaintext highlighter-rouge">PS2</code> directory, and that’s <code class="language-plaintext highlighter-rouge">NAMES.BIN</code>. What does it do? I don’t exactly know, but it looks like a list of symbol names such as classes/structs, variables, and more. I’m not sure how much of it is actually used as some of them are nonsensical for a PlayStation 2 title, such as several Direct3D specific classes.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IDirect3D8
IDirect3DDevice8
IDirect3DResource8
IDirect3DBaseTexture8
IDirect3DTexture8
IDirect3DVolumeTexture8
IDirect3DCubeTexture8
IDirect3DVertexBuffer8
IDirect3DIndexBuffer8
IDirect3DSurface8
IDirect3DVolume8
IDirect3DSwapChain8
</code></pre></div></div>

<p>Anyway, as I was reading through the strings in this file, I’d realised something was a little bit amiss when there were references to guns, explosions, worms and other things that didn’t really make much sense for a game about rowing a boat through water (unless there’s some incredibly hardcore and well hidden bonus mode I was unaware of).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mClipSize
mBullets
mDamageType
mFireRate
mLastFire
mBulletSpeed
mTeamSoldierLifeCycle
mTeamSoldierFlee
mSoldierPos1
mSoldierPos2
mSoldier1
mSoldier2
mExploding
BULLET_IMPACT_NEAR
mIsCutterSeen
mAssaultGun
mShootSound
mHandgunBulletSpeed
mHandgunBulletDamage
mHandgunClipSize
mHandgunFireRate
mSniperBulletSpeed
mSniperBulletDamage
mSniperClipSize
mSniperFireRate
mSniperDamageType
</code></pre></div></div>

<p class="tb-img-subtitle"><img src="/assets/wild-water-adrenaline/wwa-reaction.png" alt="My reaction" />
<em>“What? Rowing boats with guns!?”</em></p>

<p>It’s actually likely these are carry-overs from the Outcast 2 prototype that can be seen below, as some of the symbol names here seem to align with some of the features we see. To support that theory too, some of the symbol names explicitly mention Cutter, the protagonist from Outcast.</p>

<div align="center">

<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Zy1lqQNcHbc?si=Ihpc030nZc2KMa1t" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>

</div>

<p>What I posted above is not everything, otherwise you’ll be scrolling forever, so I’ve just dumped all the strings to a file you can find <a href="/assets/wild-water-adrenaline/names-bin-strings.txt">here</a>. Obviously, the meaning behind some of these strings is going to be up to speculation (unless a prototype is released 👀), but some implications are rather intriguing.</p>

<p>Also, before you get <em>too</em> excited, I’ve got a hunch that none of this functionality still exists in Wild Water Adrenaline, but I’d be happy to be proven wrong.</p>

<h2 id="loadsalua-and-modding"><a href="https://en.wikipedia.org/wiki/Loadsamoney">Loadsalua</a> and Modding</h2>

<p>Oh, Lua! We know <a href="https://www.lua.org/">Lua</a>. Yeah, not too surprising when you read up the specs of the engine <a href="https://web.archive.org/web/20070114180342/http://fresh3d.com/fresh_engine/specs.php">here</a> (see Fresh Scripting); much of the game logic for this game is actually implemented through Lua which is certainly a first for me when it comes to PlayStation 2 games.</p>

<p>This lead to an interesting thought, what if we could make changes to the scripts here and then repack the game? Hehe, delightfully devilish!</p>

<p>With the files already unpacked, I proceeded to whip up a tool to just repack the data and then recreated the ISO using the following command.</p>

<p><code class="language-plaintext highlighter-rouge">mkisofs -udf -o WILDMOD.ISO ./Wild\ Water/</code></p>

<p>Aaaaand, nothing.</p>

<p>To troubleshoot, I proceeded to edit a file in the original package just to rule out that there wasn’t some sort of checksum on the package, and that seemed fine.</p>

<p>After some further fiddling and some fixes to my repacking tool, there was still no luck, so on a whim I decided to repack the files in the original order they were in (so basically making a 1:1 copy), and that worked. Okay.</p>

<p>Next I went ahead and edited one of the Lua scripts with some extra additions, but again, nothing.</p>

<p><img src="/assets/wild-water-adrenaline/wwa-confused.png" alt="Visible confusion" /></p>

<p>Long story short, it seems that if the files are any <em>larger</em>, then it doesn’t work, but if the files are smaller they will work fine. This suggests to me that the files are possibly loaded into some sort of fixed-size buffer in the engine. Or as my notes put it…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...] changing 'English' to 'Butt' worked fine...
But changing it to 'ButtButtButt' did not work, file size increased marginally.
</code></pre></div></div>

<p>Once again, this is something I probably could confirm by just checking the disasm, but I’ll leave that to someone with a little more time and motivation on their hands. You can find the source code for my repacking tool <a href="https://gist.github.com/hogsy/90ae893ded9c2b42796c5eff8788d1ba">here</a> (just be aware it depends on <a href="https://github.com/OldTimes-Software/hei">this</a>).</p>

<p>They did have documentation for Lua available on their website at some stage (seen <a href="https://web.archive.org/web/20110616030544/http://www.freshengine.net/FreshEngineCommunity/_FreshEngineDocs/FreshScripting/html/classes.html">here</a>), but much of that has unfortunately been lost.</p>

<h2 id="aight-what-else">A’ight, what else?</h2>

<h3 id="pc-version">PC Version</h3>

<p>Wild Water Adrenaline never came out on PC, as far as I could see anyway, but a game that also used the same engine, Mountain Bike Adrenaline, did. Unfortunately, though, the way the data is stored in that version of the game seems different, and again, I decided it probably wasn’t worth the time. Not sure how the PlayStation 2 version looks in that respect either, but my bet is that it’s the same as the WWA.</p>

<h3 id="paks">PAKs</h3>

<p>So many of the other formats here are proprietary, and I’d elected not to bother getting too in-depth with those, but have some theories as to how they’re structured. Though, the key one here seems to be the ‘PAK’ format.</p>

<p>Each PAK (PAK, GAM, ANM and TEX) appears to be a collection of different classes, each holding either just general data, textures, models or something else, and these are combined into the scene via the <code class="language-plaintext highlighter-rouge">merge</code> function that’s executed through the Lua scripts.</p>

<p>It seems once these are ‘merged’ into the scene, they can then be looked up to then perform whatever action as necessary. I’ve presented some examples below.</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">merge</span> <span class="p">(</span><span class="n">EFFECTS_DIR</span><span class="o">..</span><span class="s2">"splashM.pak"</span><span class="p">)</span>
<span class="n">ShaderSpriteParticlePS2</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s2">"lambert2__X"</span><span class="p">):</span><span class="n">name</span><span class="p">(</span><span class="s2">"splash2"</span><span class="p">)</span>
<span class="n">ShaderSpriteParticlePS2</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s2">"lambert3__X"</span><span class="p">):</span><span class="n">name</span><span class="p">(</span><span class="s2">"splash3"</span><span class="p">)</span>
<span class="n">ShaderHeatParticlePS2</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s2">"lambert4__X"</span><span class="p">):</span><span class="n">name</span><span class="p">(</span><span class="s2">"splash4"</span><span class="p">)</span>

<span class="p">[</span><span class="o">...</span><span class="p">]</span>

<span class="n">merge</span><span class="p">(</span><span class="n">LEVEL_DIR</span><span class="o">..</span><span class="s2">"CA_Snow.pak"</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">snow</span> <span class="o">=</span><span class="n">Transform</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s2">"SnowEffect"</span><span class="p">)</span>
<span class="n">self</span><span class="p">.</span><span class="n">rain</span> <span class="o">=</span> <span class="n">Transform</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"Snow"</span><span class="p">)</span>
<span class="n">snow</span><span class="p">:</span><span class="n">parent</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">rain</span><span class="p">)</span>
<span class="n">snow</span><span class="p">:</span><span class="n">position</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span>

<span class="p">[</span><span class="o">...</span><span class="p">]</span>

<span class="kd">local</span> <span class="n">mesh</span> <span class="o">=</span> <span class="n">SkinMesh</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s2">"Raft"</span><span class="p">)</span>
<span class="n">mesh</span><span class="p">:</span><span class="n">visible</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
<span class="n">World</span><span class="p">.</span><span class="n">getActive</span><span class="p">():</span><span class="n">remove</span><span class="p">(</span><span class="n">mesh</span><span class="p">)</span>
</code></pre></div></div>

<p>I don’t think the header for the PAK format is too complex and threw this together at a glance. Might provide a good starting point. But keep in mind it’s just an educated guess!</p>

<p><a href="/assets/wild-water-adrenaline/wwa-pak-header.png"><img src="/assets/wild-water-adrenaline/wwa-pak-header.png" alt="img.png" /></a></p>

<p>Should add, if it’s not obvious, but <code class="language-plaintext highlighter-rouge">packageClass</code> is a sized string, so the first byte determines it’s size.</p>

<h3 id="alternate-title">Alternate Title</h3>

<p>It also seems that their tooling stored the original paths for much of the content used in the game within the PAK files. Some of these even seem to suggest Wild Water Adrenaline had a different title at some point during its development.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>R:/XTeam_Rafting/Gfx/Textures/Tex_balise_bonus.tga
P:/XTremeRafting/Gfx/Textures/Tex_Raft_H.tga
</code></pre></div></div>

<p>I’d figure ‘XTeam’ was a typo there.</p>

<hr />
<p><br />
Anyway, apologies for the sudden end but that’s about all I’ve got in me, but I do hope it was informative! As usual, if you have any feedback for us, feel free to leave a comment.</p>]]></content><author><name>Mark Sowden</name></author><category term="Reverse Engineering" /><category term="2023" /><category term="Wild Water Adrenaline" /><category term="Reverse Engineering" /><summary type="html"><![CDATA[This isn’t going to be the most in-depth piece I’ve ever written, as my interest only went so far with this one (I’m sure you can understand if you’re familiar with the title in question), but I thought I’d throw some of my findings out there just on the off-chance it’s useful.]]></summary></entry><entry><title type="html">Welcome to the Library!</title><link href="https://talonbrave.info/2023/11/19/library-announcement.html" rel="alternate" type="text/html" title="Welcome to the Library!" /><published>2023-11-19T00:00:00+00:00</published><updated>2023-11-19T00:00:00+00:00</updated><id>https://talonbrave.info/2023/11/19/library-announcement</id><content type="html" xml:base="https://talonbrave.info/2023/11/19/library-announcement.html"><![CDATA[<p>If you’re on our <a href="https://discord.gg/EdmwgVk">Discord server</a>, you’re probably already aware of this, but we’re happy to reveal a new section of The Data Dungeon, creatively dubbed the “Library”.</p>

<p>For the library, we’ll be making various pieces of documentation and more, easily accessible for anyone to read and search online. Right now, you can find copies of earlier DirectX and MSDN documentation available, and we’ll aim to add and expand on it over time.</p>

<p>You can find the new section <a href="https://library.thedatadungeon.com/">here</a>. We haven’t yet updated the homepage of The Data Dungeon to link to the library yet but will be doing so soon.</p>

<p>As usual, if you have any feedback, you can submit it in the dedicated channel in our server.</p>]]></content><author><name>Mark Sowden</name></author><category term="Announcements" /><summary type="html"><![CDATA[If you’re on our Discord server, you’re probably already aware of this, but we’re happy to reveal a new section of The Data Dungeon, creatively dubbed the “Library”. For the library, we’ll be making various pieces of documentation and more, easily accessible for anyone to read and search online. Right now, you can find copies of earlier DirectX and MSDN documentation available, and we’ll aim to add and expand on it over time. You can find the new section here. We haven’t yet updated the homepage of The Data Dungeon to link to the library yet but will be doing so soon. As usual, if you have any feedback, you can submit it in the dedicated channel in our server.]]></summary></entry><entry><title type="html">Black9 Lives!</title><link href="https://talonbrave.info/2023/09/15/black9-lives.html" rel="alternate" type="text/html" title="Black9 Lives!" /><published>2023-09-15T00:00:00+00:00</published><updated>2023-09-15T00:00:00+00:00</updated><id>https://talonbrave.info/2023/09/15/black9-lives</id><content type="html" xml:base="https://talonbrave.info/2023/09/15/black9-lives.html"><![CDATA[<p>Yesterday, Dink, a user on the <a href="https://discord.gg/RG723tU">Hidden Palace Discord</a>, posted a link to a build of Black9 which had seemingly been uploaded to archive.org anonymously just some days prior.
If you didn’t know already, I’d written a short piece about Black9 <a href="https://talonbrave.info/2018/09/18/black9-the-cancelled-deus-ex-inspired-rpg.html">back in 2018</a> (yikes, time flies), so it was really exciting to finally have an opportunity to see what the game actually looked and played like.</p>

<p>But nothing is ever quite that simple, is it?</p>

<!-- more -->

<p>After excitedly extracting everything from the ISO and examining it, it turned out that the ISO that was uploaded appeared to be corrupted in some way, resulting in some files to overlay others upon extracting. This is obviously a big issue as it would stop the build from running correctly on hardware and obviously puts a dent in efforts at examining the content.</p>

<p>Upon investigation, both myself and Daniel soon realised that, fortunately, it just seemed to be a matter of the offsets in the ISO being shifted back by 2048 bytes in certain (and most) cases. After some fiddling, we believe we’ve resolved this and you can find the data extracted correctly <a href="https://drive.google.com/file/d/1H6it3vvYb3SAD5kQnuw-hXSnkhmwwYj6/view?usp=drive_link">here</a> (you can find the original ISO <a href="https://archive.org/details/black-9-2226-release">here</a>).</p>

<p>As of yesterday, despite doing this, we still didn’t have any actual luck getting it running on physical hardware (even our “dev kit” with additional memory), so there’s evidently still something not quite right. That said, this <em>has</em> allowed us to view what’s here!</p>

<p><a href="/assets/black9-lives/124759.png"><img src="/assets/black9-lives/124759.png" alt="" /></a></p>

<p><a href="/assets/black9-lives/125006.png"><img src="/assets/black9-lives/125006.png" alt="" /></a></p>

<p><a href="/assets/black9-lives/125455.png"><img src="/assets/black9-lives/125455.png" alt="" /></a></p>

<p>Many of the assets should happily open under any version of Unreal Engine 2, equal or greater than version 2226, if you want to check them out yourself, though this is barring the UKX meshes/animations as it seems they might have made modifications to the animation format. Viewing those in <a href="https://www.gildor.org/en/projects/umodel">UE Viewer</a> will require you to disable the animation option.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/black9-lives/133030.png"><img src="/_thumbs/assets/black9-lives/133030_200x162.png" /></a><a href="/assets/black9-lives/133146.png"><img src="/_thumbs/assets/black9-lives/133146_200x162.png" /></a><a href="/assets/black9-lives/133204.png"><img src="/_thumbs/assets/black9-lives/133204_200x162.png" /></a></div></div>

<p>Happy spelunking! If you have any luck getting it running or want to collaborate a bit, feel free to join our <a href="https://discord.gg/bJKBRNagfA">Discord</a>.</p>]]></content><author><name>Mark Sowden</name></author><category term="Game/Black9" /><category term="Game" /><category term="Releases" /><category term="2023" /><category term="Black9" /><category term="Dump" /><category term="Taldren" /><summary type="html"><![CDATA[Yesterday, Dink, a user on the Hidden Palace Discord, posted a link to a build of Black9 which had seemingly been uploaded to archive.org anonymously just some days prior. If you didn’t know already, I’d written a short piece about Black9 back in 2018 (yikes, time flies), so it was really exciting to finally have an opportunity to see what the game actually looked and played like. But nothing is ever quite that simple, is it?]]></summary></entry></feed>