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.

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.

If you’re not familiar with Wild Water Adrenaline, it’s a uh, not fantastically well-received PlayStation 2 game produced by Fresh3D; 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).

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.

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

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…)

You can read more about Wild Water Adrenaline’s development here, as written up by one of the developers involved with the game.

If you’d like to learn more about the ill-fated Outcast 2, there’s some great information available here written up by the same developer. That said, I’ll throw a spoiler warning 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 Outcast: A New Beginning title.

Anyway, I digress, let’s get on with it.

When viewing the contents of the disc for the game, we see the following.

.
├── 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

Can you guess where the actual data for the game is stored? If you guessed DATA.BIN then you guessed correctly!

DATA.BIN

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.

Overview of DATA.BIN in Rehex

The header of the package is as follows.

struct Header
{
	char ident[ 8 ];
	int32_t unk0[ 2 ]; // <- these are 0, so I've no idea
	uint32_t numFiles;
	Index indices[numFiles];
};

And each Index is the following.

struct Index
{
	char name[ 12 ];
	uint32_t blockNum;
	uint32_t size;
};

The blockNum 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.

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

If this isn’t sufficient for you to understand the format, for whatever reason, I’d thrown together an implementation here.

We’re finally left with this.

With binary template loaded into Rehex

Right, so we’ve got that out the way and extracted the DATA.BIN file - what are we left with?

A big ol’ blob o’ more files… (click here if you want to skip)

.
├── 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

sigh

NAMES.BIN

So one of the files in here actually has a duplicate stored outside under the PS2 directory, and that’s NAMES.BIN. 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.

IDirect3D8
IDirect3DDevice8
IDirect3DResource8
IDirect3DBaseTexture8
IDirect3DTexture8
IDirect3DVolumeTexture8
IDirect3DCubeTexture8
IDirect3DVertexBuffer8
IDirect3DIndexBuffer8
IDirect3DSurface8
IDirect3DVolume8
IDirect3DSwapChain8

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).

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

My reaction “What? Rowing boats with guns!?”

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.

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 here. 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.

Also, before you get too 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.

Loadsalua and Modding

Oh, Lua! We know Lua. Yeah, not too surprising when you read up the specs of the engine here (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.

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

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.

mkisofs -udf -o WILDMOD.ISO ./Wild\ Water/

Aaaaand, nothing.

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.

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.

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

Visible confusion

Long story short, it seems that if the files are any larger, 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…

[...] changing 'English' to 'Butt' worked fine...
But changing it to 'ButtButtButt' did not work, file size increased marginally.

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 here (just be aware it depends on this).

They did have documentation for Lua available on their website at some stage (seen here), but much of that has unfortunately been lost.

A’ight, what else?

PC Version

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.

PAKs

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.

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 merge function that’s executed through the Lua scripts.

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.

merge (EFFECTS_DIR.."splashM.pak")
ShaderSpriteParticlePS2.find("lambert2__X"):name("splash2")
ShaderSpriteParticlePS2.find("lambert3__X"):name("splash3")
ShaderHeatParticlePS2.find("lambert4__X"):name("splash4")

[...]

merge(LEVEL_DIR.."CA_Snow.pak")
local snow =Transform.find("SnowEffect")
self.rain = Transform.new("Snow")
snow:parent(self.rain)
snow:position(0,10,0)

[...]

local mesh = SkinMesh.find("Raft")
mesh:visible(false)
World.getActive():remove(mesh)

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!

img.png

Should add, if it’s not obvious, but packageClass is a sized string, so the first byte determines it’s size.

Alternate Title

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.

R:/XTeam_Rafting/Gfx/Textures/Tex_balise_bonus.tga
P:/XTremeRafting/Gfx/Textures/Tex_Raft_H.tga

I’d figure ‘XTeam’ was a typo there.



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.