<?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>2025-09-10T22:31:11+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">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 (2002)" /><category term="2025" /><category term="Haven: Call of the King (2002)" /><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><entry><title type="html">An Odd World; Oddworld Stranger’s Wrath Prototype</title><link href="https://talonbrave.info/2023/06/25/anoddworld.html" rel="alternate" type="text/html" title="An Odd World; Oddworld Stranger’s Wrath Prototype" /><published>2023-06-25T00:00:00+00:00</published><updated>2023-06-25T00:00:00+00:00</updated><id>https://talonbrave.info/2023/06/25/anoddworld</id><content type="html" xml:base="https://talonbrave.info/2023/06/25/anoddworld.html"><![CDATA[<p>In April, in the Hidden Palace Discord server, a user with the handle ‘Sgabbit’ asked about the Oddworld Stranger’s Wrath prototype that had been shown on the YouTube channel <a href="https://www.youtube.com/watch?v=jJgY1x70tPE">PtoPOnline</a>. Unfortunately, many years later, it still wasn’t publicly available.</p>

<p>Aiming to change that, we decided to make an offer to the individual behind the channel, and after acquiring some better capture equipment and sending our Xbox off to have more RAM soldered onto the motherboard, here we are today!</p>

<!-- more -->

<p><img src="https://media.giphy.com/media/kbRWoYdw85DicjZCIK/giphy.gif" alt="Stranger is ready gif" /></p>

<p>If you’re just interested in downloading the prototype, then click <a href="#download">here</a> to skip to the end, otherwise read on!</p>

<h2 id="verifying-the-date">Verifying the Date</h2>

<p>Usually, before we get too deep in examining anything, we’ll try and verify when it’s actually from. When we checked the file timestamps on the Xbox XBEs in this prototype, we were a little puzzled as they did not align with when this particular build was supposedly thrown together. Odd. Pun not intended.</p>

<p><img src="/assets/anoddworld_xex_data.png" alt="Screenshot showing how they appear in the folder" /></p>

<p>After using a tool to further inspect the other accompanying executables alongside the Xbox XBEs, it appears this prototype is probably from around the 22nd of May 2004 - that’s at least the time the builds were actually compiled, it seems.</p>

<p><img src="/assets/anoddworld_pe_timestamp.png" alt="Screenshot showing timestamp provided in the PE executable" /></p>

<p>Because the timestamps for executable files were wrong, it casts a little bit of doubt over whether any other timestamps in this build are necessarily correct, but unfortunately, we’ve not got much else we can use to date them. Generally, we’re dubious of any timestamp going as far back as 2000 or earlier as it appears to be the general pattern.</p>

<p>To add a little bit to the confusion, when you start the prototype up, there’s a brief splash screen that appears to display ‘JANUARY 2004’ on the bottom right. (Ignore the blue box on the top left, that’s being displayed by our HD upscaler.)</p>

<p><img src="/assets/anoddworld/vlcsnap-2023-06-24-13h29m05s983.png" alt="Screenshot of splash screen" /></p>

<p>We figure there was probably some other build produced at that time, either produced for preview purposes or some internal milestone (we don’t know), and that splash screen was simply not replaced before this build. It may suggest that the build we have here is a little less <em>formal</em> than whatever was created back then.</p>

<h2 id="glorious-pdbs">Glorious PDBs!</h2>

<p>As you probably noticed from the screenshot earlier, this prototype also includes the PDBs. If you’re unfamiliar with these, they’re essentially debug symbols that contain heaps of useful information! In summary, this is going to be a massive help to anyone reverse engineering both this prototype and the final game.</p>

<p>One of the things I personally also love about PDBs, besides the fact that they’re an abundant source of information, is that they usually include references to the original files used in the source tree during compilation, so from that we can get a rough idea of how the project was set up at the time it was compiled. You can see the results of that <a href="https://github.com/TalonBraveInfo/strangerswrath-source-tree/tree/master">here</a>.</p>

<p><img src="/assets/oddworld_sourcetree_repo.png" alt="Screenshot of the source tree repo" /></p>

<p>You’ll likely note the root “exoddus” directory that Oddworld Inhabitants (OWI) used for the project, we’ll get to that shortly.</p>

<h2 id="strangers-engine">Stranger’s Engine</h2>

<p>Going into this, we actually knew next to nothing about Stranger’s Wrath, especially when it came to the technology behind the game, so this has been quite educational. As it turns out, Stranger’s Wrath uses OWI’s own proprietary game engine, which is unlike the Oddworld Munch’s Oddysee title released prior, which instead used <a href="https://en.wikipedia.org/wiki/Gamebryo">NetImmerse/Gamebryo</a> (we actually confirmed this independently ourselves too, as we’re always a little wary when no citation is provided).</p>

<p>To my knowledge, there’s no formal name provided for the engine that Stranger’s Wrath used, but we’ll refer to it as “Stranger’s engine”, which is a term Lorne Lanning, a founder of OWI, used himself.</p>

<p>The capabilities of the engine were quite respectable for their time, with support for pixel shaders, shadow maps (only from characters), level streaming, <a href="https://magogonthemarch.wordpress.com/part-4-strangers-wrath-hd-dev-diary-4/">SWF-based UI system</a>, a C-like scripting language, a skeletal animation system utilizing <a href="http://www.radgametools.com/granny.html">Granny 3D</a>, lightmaps, and more. Fancy! Additionally, as clear from nosing at the PDB, the game was written in C++ which isn’t too surprising for the time.</p>

<p>Like many others, OWI used a few third-party libraries to aid the development of the engine.</p>

<ul>
  <li><a href="http://flex.sourceforge.net/">Flex</a> + <a href="http://www.gnu.org/software/bison/">Bison</a>; these are very likely used to aid in the implementation of the scripting language.</li>
  <li><a href="http://www.stlport.org/">STLport</a>; from the terrible days when compilers didn’t provide very good <a href="https://en.wikipedia.org/wiki/C%2B%2B_Standard_Library">standard library implementations for C++</a>, and developers usually just rolled their own. This library is still available but unsurprisingly appears to have fallen out of use.</li>
  <li><a href="http://www.radgametools.com/granny.html">Granny 3D</a>; mentioned earlier, this is used to provide the skeletal animation system. Quite a common library for the time which has fallen out of use in recent years.</li>
</ul>

<p>But for the most part, excluding the Xbox SDK and other trivial libraries they might’ve used, that appears to be it and otherwise everything else is home-grown. I was especially impressed by their decision to write their own SWF player to use for the UI, which appears to have been quite ahead of the curve, as other engines later into the decade would adopt <a href="https://en.wikipedia.org/wiki/Scaleform_GFx">Scaleform</a> to support flash-based interfaces rather than rolling their own solution.</p>

<p>It’s actually a bit of a shame as it’s the <em>only</em> title OWI ended up releasing to use this technology, despite plans to use it in their follow-up ill-fated title, <em>The Brutal Ballad of Fangus Klot</em>.</p>

<blockquote>
  <p>“[…] We wanted to take Stranger’s engine and basically re-skin something more hardcore […]”<br />
<a href="https://web.archive.org/web/20230603161312/https://www.gameinformer.com/b/news/archive/2009/09/27/feature-interview-lorne-lanning-oddworld-fangus-klot-.aspx">Source</a></p>
</blockquote>

<p>Fortunately, while described as an “<a href="https://www.redbull.com/gb-en/abes-oddysee-remaking-a-legend-for-ps4">Xbox-exclusive engine</a>” during an interview, <a href="https://www.mobygames.com/company/14086/jaw-ltd/">Just Add Water Ltd.</a> eventually gave the engine a bit of love and brought Stranger’s Wrath to Sony’s PlayStation 3 in 2011, eventually followed by a <a href="https://en.wikipedia.org/wiki/Oddworld:_Stranger%27s_Wrath#HD_remaster">myriad of other platforms</a>.</p>

<h2 id="some-quick-reversing">Some quick reversing…</h2>

<p>And that leads onto a little bit of poking and prodding at some formats here. This section is a bit shorter than planned, but we’re sure the community is going to be quick to fill in the rest.</p>

<p>From comparing the final game to this build, it became apparent there were a number of additional folders that the final game didn’t include, such as the following.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">data/fonts</code></li>
  <li><code class="language-plaintext highlighter-rouge">data/geometry</code></li>
  <li><code class="language-plaintext highlighter-rouge">data/levels</code></li>
  <li><code class="language-plaintext highlighter-rouge">data/prefs</code></li>
  <li><code class="language-plaintext highlighter-rouge">data/scripts</code></li>
  <li><code class="language-plaintext highlighter-rouge">data/shaders</code></li>
  <li><code class="language-plaintext highlighter-rouge">data/ui</code></li>
</ul>

<p>I’d suspected that the prototype did not actually pull data from some of these locations, given their absence from the final game, and as it appeared a bulk of the data was packed into <code class="language-plaintext highlighter-rouge">SMB</code> files just as they also are in the final game (they’re a kind of package file, we’ll talk about them shortly).</p>

<p>We ended up confirming this is probably the case when we tried to run the prototype with the <code class="language-plaintext highlighter-rouge">levels</code> and <code class="language-plaintext highlighter-rouge">geometry</code> locations removed.</p>

<p>It’s quite likely most of these folders were left-overs from the packaging process used to produce the <code class="language-plaintext highlighter-rouge">SMB</code> packages under the <code class="language-plaintext highlighter-rouge">data/bundles</code> and <code class="language-plaintext highlighter-rouge">data/global</code> locations. Though, oddly enough there should also be a left-over directory for textures too, which appears to be missing, so that might get removed during some other stage of the process.</p>

<h3 id="smbgeo-format">SMB/GEO Format</h3>

<p>Let’s get the <em>big</em> one out the way first, I guess. ‘SMB’ here stands for Shared Memory Block, and the files are essentially a collection of separate data blobs. You could basically consider it a type of package/container file.</p>

<p>From the standpoint of reverse-engineering, the format is terrible to reverse because there doesn’t appear to be any mechanism for knowing up-front how large a given struct/class is going to be (meaning we can’t cleanly skip the ones we don’t want or don’t have reversed), but fortunately using the available PDBs, it was pretty easy to deduce elements of the format.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">OIStringBuffer</span>
<span class="p">{</span>
	<span class="kt">int32_t</span> <span class="n">size</span><span class="p">;</span>
	<span class="kt">char</span>    <span class="n">buffer</span><span class="p">[</span> <span class="n">size</span> <span class="p">];</span>
<span class="p">};</span>

<span class="cm">/**
 * There is an older version of the format that uses
 * 0x600ddea1 as it's magic instead and does not have
 * a version number (engine interprets it as version '0')
 */</span>

<span class="k">struct</span> <span class="n">SMBHeader</span>
<span class="p">{</span>
	<span class="kt">uint32_t</span> <span class="n">magic</span><span class="p">;</span><span class="c1">// 0x3a4b5c6d</span>
	<span class="k">if</span> <span class="p">(</span> <span class="n">magic</span> <span class="o">==</span> <span class="mh">0x3a4b5c6d</span> <span class="p">)</span>
	<span class="p">{</span>
		<span class="kt">int32_t</span> <span class="n">version</span><span class="p">;</span><span class="c1">// 5</span>
	<span class="p">}</span>
	<span class="k">struct</span> <span class="n">OIStringBuffer</span> <span class="n">fileName</span><span class="p">;</span>
	<span class="kt">int32_t</span>               <span class="n">fileOffset</span><span class="p">;</span>
	<span class="kt">int32_t</span>               <span class="n">headerStreamEnd</span><span class="p">;</span>

	<span class="kt">int32_t</span> <span class="n">numContiguousBytes</span><span class="p">;</span>
	<span class="kt">int32_t</span> <span class="n">numSystemBytes</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span> <span class="n">magic</span> <span class="o">==</span> <span class="mh">0x3a4b5c6d</span> <span class="p">)</span>
	<span class="p">{</span>
		<span class="c1">// if old format, both the below are the same as above</span>
		<span class="kt">int32_t</span> <span class="n">contiguousSizeUnpadded</span><span class="p">;</span>
		<span class="kt">int32_t</span> <span class="n">systemSizeUnpadded</span><span class="p">;</span>

		<span class="k">if</span> <span class="p">(</span> <span class="n">version</span> <span class="o">&gt;=</span> <span class="mi">3</span> <span class="p">)</span>
		<span class="p">{</span>
			<span class="kt">int32_t</span> <span class="n">numResources</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="p">(</span> <span class="n">version</span> <span class="o">&gt;=</span> <span class="mi">4</span> <span class="p">)</span>
		<span class="p">{</span>
			<span class="kt">int32_t</span> <span class="n">ioMode</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="kt">uint32_t</span> <span class="n">endHeaderMagic</span><span class="p">;</span><span class="c1">// 0xbeef1234</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">SMBHeader</span> <span class="n">header</span><span class="p">;</span>
</code></pre></div></div>

<p>As you can see, there are various checks for different versions of the format indicating that the format was expanded on overtime, and the team were obviously keen to retain backwards compatibility. I was a little curious if the final game (or at least the HD PC version) could load any of the SMBs from the prototype, but sadly had little success with shuffling various files around—there are many reasons why this probably didn’t work.</p>

<p>Oh, and this format gets used for models too, which use a <code class="language-plaintext highlighter-rouge">geo</code> extension instead of <code class="language-plaintext highlighter-rouge">smb</code>.</p>

<p>I’d made a slightly further effort before deciding it was a bit more than I wanted to focus my time on at the moment. Below you can find the main <code class="language-plaintext highlighter-rouge">MaterialDef</code> structure outlined, but this is then used as part of the many other subclasses such as <code class="language-plaintext highlighter-rouge">MaterialDef_Transparent</code>, <code class="language-plaintext highlighter-rouge">MaterialDef_SimpleTextured</code>, <code class="language-plaintext highlighter-rouge">MaterialDef_NormalMapped</code>, and so on and so on.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">MaterialDef</span>
<span class="p">{</span>
	<span class="kt">int32_t</span> <span class="n">version</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span> <span class="n">version</span> <span class="o">&lt;</span> <span class="mi">19</span> <span class="p">)</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">else</span>
	<span class="p">{</span>
		<span class="kt">uint32_t</span> <span class="n">flags</span><span class="p">[</span> <span class="mi">6</span> <span class="p">];</span>
	<span class="p">}</span>

	<span class="kt">float</span>    <span class="n">glareStartFade</span><span class="p">;</span>
	<span class="kt">float</span>    <span class="n">glareEndFade</span><span class="p">;</span>
	<span class="kt">float</span>    <span class="n">reflectionStrength</span><span class="p">;</span>
	<span class="kt">float</span>    <span class="n">mipmapLodBias</span><span class="p">;</span>
	<span class="kt">uint32_t</span> <span class="n">glareColor</span><span class="p">;</span>

	<span class="kt">float</span> <span class="n">lmTexelSize</span><span class="p">;</span>
	<span class="kt">float</span> <span class="n">lmCreaseAngleDegrees</span><span class="p">;</span>
	<span class="kt">float</span> <span class="n">lmNormalHardness</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">MaterialDef_NormalMapped</span>
<span class="p">{</span>
	<span class="kt">int32_t</span>            <span class="n">version</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">MaterialDef</span> <span class="n">materialDef</span><span class="p">;</span>
    <span class="c1">// i gave up here...</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">MaterialDef_*</code> classes are just one collection of many other classes that are serialised as part of the SMB, as you also have the actual geometry classes such as <code class="language-plaintext highlighter-rouge">IndexedRenderableVB</code>, <code class="language-plaintext highlighter-rouge">PBIndexedRenderableVB</code> and many others for all the various different sets of data that get serialised.</p>

<p>Something I’ll perhaps return to eventually, though I’m fairly sure the community will likely leap at this sooner and faster than I will.</p>

<h3 id="tag-format">TAG Format</h3>

<p>There are seemingly two types of TAG files here, some are in a binary form and others just plaintext. A subset of the latter format can be seen below.</p>

<p>There are multiple types of tags here, which together appear to make up everything that’s featured in the given level.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// TagFile (version: 0, comment lines: 5)
//  ** EDITS TO THIS FILE ARE NOT PRESERVED **.  Change the .mb file instead
// 
// Tags Exporter, version 0.93
//  -       File Exported as: d:/Exoddus/Build/Game/data/levels/Region_01/transition_01_1.tag
// 

<span class="nt">&lt;WorldGeometry&gt;</span>
	begin
		m_tagName: ""
		m_tagZone: -1
		m_isInProxyZone: false
		m_tagTransform: 1 [3F800000] 0 [00000000] 0 [00000000] 0 [00000000] 1 [3F800000] 0 [00000000] 0 [00000000] 0 [00000000] 1 [3F800000] 0 [00000000] 0 [00000000] 0 [00000000] 1 [3F800000] 
		m_illuminationColor: 1 [3F800000] 1 [3F800000] 1 [3F800000] 1 [3F800000] 
		m_snapToGround: false
		m_groupName: ""
		m_startsInPurgatory: false
		m_castLightmapShadows: true
		m_scriptFile: ""
		m_scriptArgs: ""
		m_scriptEmb: ""
		m_pathToken: ""
		m_levelPrefs: block
		begin
[...]
</code></pre></div></div>

<p>My guess is that the plaintext files get converted into the binary format at some stage for speed (or the plaintext files are just an older version), given the binary variations of the format seem to contain the same data within them anyway and would be more optimal to parse.</p>

<h3 id="checking-out-a-couple-models">Checking out a couple models</h3>

<p>There don’t seem to be that many ‘test’ models left hanging around, though I can’t speak for the rest of the models available (it would take someone with a bit more knowledge on Stranger’s Wrath than us to identify what is of interest).</p>

<p>There were two test models amongst the files that I was particularly interested to take a look at; <code class="language-plaintext highlighter-rouge">test_ajb.geo</code> and <code class="language-plaintext highlighter-rouge">test_dummy.geo</code>. Given the annoying complexity of parsing the file format right now I instead opted to brute-force it by just identifying the given tag and then pulling the vertex coordinates directory, though unfortunately neither of these appear to be quite as interesting as I had originally hoped.</p>

<p>This first one seen below is the <code class="language-plaintext highlighter-rouge">test_dummy.geo</code> mesh, which is hard to recognise.</p>

<p><img src="/assets/img_2.png" alt="img_2.png" /></p>

<p>The second one here is <code class="language-plaintext highlighter-rouge">test_ajb.geo</code> which looks like a collection of shapes.</p>

<p><img src="/assets/img_3.png" alt="img_3.png" /></p>

<p>There are a few other test models and other leftovers amongst the files, but they didn’t sound particularly interesting to me, so I didn’t get round to taking a look at them. And no, as far as I could tell, there don’t appear to be any particularly interesting characters or cut enemies as far as I could see, but I’m hopefully wrong!</p>

<p>It would be great to see the community whip up a model viewer which probably wouldn’t be too difficult at all with the information available in the PDBs, and such a thing could be used to more easily identify the models and any differences.</p>

<h2 id="exoddus">“Exoddus”</h2>

<p>When poking around various files, we stumbled upon references to “Exoddus”, which appears to be the internal location that Stranger’s Wrath project was developed under, at least.</p>

<p><img src="/assets/img_1.png" alt="Reference to Exoddus screenshot" /></p>

<p>But why “Exoddus”? Given our own lack of knowledge on the Oddworld franchise at the time, Abe’s Exoddus sprung to mind, which didn’t make any sense given that was a 2D game developed with an incredibly different engine, quite a significant amount of time before Stranger’s Wrath. It was doubtful Stranger’s Wrath was built upon the engine used for Abe’s Exoddus.</p>

<p>As we soon learned, there was intended to be a follow-up to Munch’s Oddysee dubbed “Munch’s Exoddus”, which didn’t see a release in the end. Initially, the only piece of information we could find was on the <a href="https://oddworld.fandom.com/wiki/Munch%27s_Exoddus">Oddworld Wiki</a> - no citations though (the administrator has since added a notice on the front page underlining the situation with dubious content on the site). We tried a few other fan sites, such as <a href="https://web.archive.org/web/20201112020817/https://oddworldlibrary.net/wiki/Oddworld_videogames">this</a>, <a href="https://web.archive.org/web/20100226183847/http://oddworld.dreamhost.com/oddworlduniverse.com/games/munchsexoddus.php">this</a>, and stumbled upon <a href="https://www.youtube.com/watch?v=JCALl7XEEiA">this</a> video, but again, no citations. There was a reference <a href="https://www.eurogamer.net/just-add-water-wants-asks-fans-what-oddworld-game-theyd-like-to-see-next">here</a> but again, it’s just a passing reference of the title.</p>

<p><a href="https://www.youtube.com/watch?v=iOVbAmknKUk"><img src="https://media.tenor.com/v6Awsd0YO7IAAAAd/metal-gear-rising-metal-gear-rising-revengeance.gif" alt="I made it the fuck up video" /></a></p>

<p>All we wanted to determine was <strong>1) was Munch’s Exoddus actually a title in development at some point, or just a rumor</strong>, and <strong>2) did Stranger’s Wrath have any relationship with it?</strong></p>

<p>Grinding my teeth into paste at this point, I eventually jumped onto the <a href="https://discord.gg/oddworld">Oddworld Discord</a> and asked for help, and fortunately, the community there pointed us in the right direction and introduced us to the fantastic site, <a href="https://magogonthemarch.wordpress.com/">Magog On The March</a>, and there it was…</p>

<blockquote>
  <p>Stranger’s Wrath was Munch’s Exoddus, and actually there is a build somewhere, on Xbox, which has Stranger and Munch hopping round the world. And it’s like OK, this is a bit weird now.<br />
<a href="https://magogonthemarch.wordpress.com/the-futures-odd-how-just-add-water-revived-oddworld-and-where-itll-go-next-2013/">Source</a></p>
</blockquote>

<p>Given that then, it seems reasonable to conclude that the <em>Exoddus</em> directory then that Stranger’s Wrath was seemingly located under during its development was a result of it being originally intended as the follow-up to Munch’s Oddysee before it evolved into the game we know it as today.</p>

<h2 id="taking-a-listen">Taking a Listen</h2>

<p><img src="https://media.giphy.com/media/ihSf9n1MTvGiqqFVkw/giphy.gif" alt="Stranger giving a listen gif" /></p>

<p>We thought it’d be interesting to see if any sounds here were different from what ended up shipping in the final game. Originally I was going to write our own tool for doing this, and then realised tools already existed after wasting about a day on it. Me smart. Brain big. Smack rock.</p>

<p>On Windows, <a href="https://winmerge.org/">WinMerge</a> is usually a great tool for this sort of thing, as it lets you quickly compare multiple directories for instance, so we can point it on one side to the prototype and on the other, the retail release (if you’re a Linux user, like us, <a href="http://meldmerge.org">Meld</a> seems to be alright too, but I’ve not used it quite as much).</p>

<p>Initially, it looked pretty promising.</p>

<p><a href="/assets/anoddworld_promising.png"><img src="/assets/anoddworld_promising_thumb.png" alt="Very promising screenshot" /></a></p>

<p>Unfortunately, as it turns out, the XWB files in the retail release don’t seem to retain the unique names for each of the audio tracks within, making quick comparisons tricky without spending a lot of time on it. It means some of these might actually be exactly the same, but the difference is just the lack of names.</p>

<p>To add to the pain, the number of banks stored in some of these files has changed between the final release and the prototype, so some new sounds have been added and some old sounds have been removed, and it’s difficult to quickly determine what’s what because of the lack of names in the release.</p>

<p>You could probably diff the <code class="language-plaintext highlighter-rouge">audio_master.h</code> file between the release and prototype to determine what new banks have been added (or removed) to aid in the process of plundering the XWB files, but we didn’t have time to do this ourselves.</p>

<p>That said, it at least looks like some of the music tracks in the prototype are higher quality than those in the final release, which may be the case for some sound effects too.</p>

<p><img src="/assets/anoddworld_higherqyality.png" alt="Higher quality screenshot" /></p>

<h2 id="playing-the-prototype">Playing the Prototype</h2>

<p>So we’re not sure many people are going to be able to run this right now. As it stands, we weren’t able to successfully get this running under <a href="https://cxbx-reloaded.co.uk/">Cxbx-Reloaded</a> or <a href="https://xemu.app/">Xemu</a>. In the end we resorted to good ol’ hardware instead, but obviously not everyone has access to a modded Xbox with more RAM or an Xbox development kit, so we decided to record as much footage of the prototype as possible for others to analyse.</p>

<p>If you can run this, you’ll need to move one of the Xbox executables (the ‘xbe’ files) into the same directory as the ‘data’ folder.</p>

<p>Originally the plan was to split these videos up based on the bounty, but that didn’t quite work out due to issues with video editing software, so you’re getting these videos pretty much untouched… So, forewarning; I absolutely suck at playing this game, so apologies for that.</p>

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

<p>There are so many differences here, mind that I’m not going to point out every single one but rather those that stood out to me the most.</p>

<p>For instance, as you can see upon starting a new game (<a href="https://youtu.be/gtGoGVpMkRE?t=71">1:11</a>), there is nothing prior to the moment you start playing. We drop in at the first town immediately! No introduction cutscene, either. As a matter of fact, you’ll notice that the tutorials originally featured at the start of the final game in their own unique area (see <a href="https://youtu.be/0OdovxfG-6Y?t=230">here</a>) are instead integrated with the rest of the general flow here.</p>

<p>As we move through the tunnel towards our goal (<a href="https://youtu.be/gtGoGVpMkRE?t=236">3:56</a>), we meet two characters talking over the body of another character which then introduces us to the capture mechanic. This is all completely absent from the final game.</p>

<p><img src="/assets/anoddworld/vlcsnap-2023-06-20-15h09m31s861.png" alt="Screenshot" /></p>

<p>These two characters are in the same area in the final game but are simply wondering around instead.</p>

<p>Following this, we see how the bridge gets destroyed and are subsequently introduced to <em>jumping</em> to reach the area on the other side. When the player approaches this area in the final game, the bridge is already destroyed.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-3"><a href="/assets/anoddworld/vlcsnap-2023-06-20-15h10m34s072.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-20-15h10m34s072_200x154.png" /></a><a href="/assets/anoddworld/vlcsnap-2023-06-20-15h10m48s184.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-20-15h10m48s184_200x154.png" /></a><a href="/assets/anoddworld/vlcsnap-2023-06-20-15h10m58s236.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-20-15h10m58s236_200x154.png" /></a></div></div>

<p>It’s curious why OWI decided to change this approach towards teaching the player the mechanics, as what they’ve done here certainly felt more natural. Instead, the tutorial introduced in the final game is, in my opinion, significantly more intrusive (as a matter of fact, I believe the Nintendo Switch release of the game introduced an option to <em>skip</em> the entire tutorial section).</p>

<p>Another curious change is at the Water Facility, which appears to feature a separate area for the boss fight, which is found at the top of the tower (<a href="https://youtu.be/gtGoGVpMkRE?t=2060">34:20</a>).</p>

<p><img src="/assets/anoddworld/vlcsnap-2023-06-24-17h34m30s591.png" alt="Water tower boss area screenshot" /></p>

<p>And again, much to our surprise, a cutscene plays out here introducing us to our bounty not featured in the final game.</p>

<div class="tb-img-collection-group"><div class="tb-img-collection tb-img-collection-4"><a href="/assets/anoddworld/vlcsnap-2023-06-24-17h37m26s844.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-24-17h37m26s844_200x150.png" /></a><a href="/assets/anoddworld/vlcsnap-2023-06-24-17h38m27s727.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-24-17h38m27s727_200x150.png" /></a><a href="/assets/anoddworld/vlcsnap-2023-06-24-17h38m49s562.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-24-17h38m49s562_200x150.png" /></a><a href="/assets/anoddworld/vlcsnap-2023-06-24-17h39m27s457.png"><img src="/_thumbs/assets/anoddworld/vlcsnap-2023-06-24-17h39m27s457_200x150.png" /></a></div></div>

<p>You likely noticed a few other differences in our approach to the Water Facility. Moreover, at the facility itself, such as along the top of the building, there are additional barriers, making it much harder to hit the enemies on the roof (<a href="https://youtu.be/gtGoGVpMkRE?t=1855">30:55</a>).</p>

<p><img src="/assets/anoddworld/vlcsnap-2023-06-24-17h50m44s857.png" alt="Barriers at the top of the roof screenshot" /></p>

<p>Or that the door leading into the inner room of the building is wooden instead (<a href="https://youtu.be/gtGoGVpMkRE?t=1873">31:13</a>), allowing the player to break it (which makes sense given that in the final game this was likely changed to accommodate the change in location for the boss fight).</p>

<p><img src="/assets/anoddworld/vlcsnap-2023-06-24-17h54m03s287.png" alt="Wooden door screenshot" /></p>

<p>As stated earlier, there are just so many differences throughout this build it would be probably impossible for us to easily list them all, which is all the more reason why it’s going to be great to see this build out there for others to explore.</p>

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

<p>If there’s a desire to see more footage, let us know!</p>

<h2 id="download">Download</h2>

<p>And this is probably what most of you have been after; <a href="https://archive.thedatadungeon.com/oddworld_strangers_wrath_2005/distributions/2004-05-22_microsoft_xbox_prototype/oddworldstrangerswrath.rar">grab it here</a>.</p>

<p>If you want to support what we do, we’re not accepting donations at this time, but any and all feedback is appreciated; feel free to join our <a href="https://discord.gg/EdmwgVk">Discord</a> or leave a comment letting us know what you think!</p>]]></content><author><name>Mark Sowden</name></author><category term="Game/Oddworld Stranger&apos;s Wrath" /><category term="Game" /><category term="Releases" /><category term="2023" /><category term="Oddworld Stranger&apos;s Wrath" /><category term="Dump" /><summary type="html"><![CDATA[In April, in the Hidden Palace Discord server, a user with the handle ‘Sgabbit’ asked about the Oddworld Stranger’s Wrath prototype that had been shown on the YouTube channel PtoPOnline. Unfortunately, many years later, it still wasn’t publicly available. Aiming to change that, we decided to make an offer to the individual behind the channel, and after acquiring some better capture equipment and sending our Xbox off to have more RAM soldered onto the motherboard, here we are today!]]></summary></entry></feed>