No Remorse AVI file playing

Candy

Member
I intend to make this a thread for discussing the parts of the AVI files that are understood & what is still to research / code / test.

I've been going over the internals of the AVI file wrt the video channel. I've taken 05D as example video to analyse the video codec.

The video has a palette at around 0xD0 to about 0x4D0. The colors don't match in my test bitmap so I'm not exactly sure. Each color has 4 bytes; the 4th of each is a 0-byte.

At 0x27D8 there's the first movie chunk, called 00db. It's length field (the next 4 bytes, little endian) indicate how long the video chunk will be, in this case 0xA910 bytes total. Then follow 5 4-byte indices that say where the chunk starts for a given 1/5th of the video frame. At the address indicated here is a small struct that contains the following. This is identical for all 5 of these blocks.

DWORD 4
DWORD 0xC02105
< 8652 bytes of image data, raw >
DWORD 0

Each block contains 36 lines of 240 pixels. You can copy/paste these to a paletted BMP file, apply the right bitmap and you'll see the plain image. I've tried this, it works.

The next frame is of type 00dc. This one is "compressed", which means at the very least it isn't just another raw image. The header is similar, it also contains 5 pointers to data. At the location indicated there's another thing though; instead of the 4... 0 struct outlined above there's a struct that contains modification data:

DWORD N
<N rounded up to the nearest multiple of 4 bytes>

These may also contain raw data afterward; haven't checked yet. In the example movie, the last block always seems to be very short; this might be because those 36 lines don't change or barely change.

<addendum>

You can also extract audio; it's 22050Hz mono 8-bit PCM data. Create a blank audio sample in the sound recorder and save it as such. Put each bit of audio chunk (01bw chunks) after the header and then add the bytecount you added to the values at addresses 0x4 and 0x36.

Anybody else want to venture a potshot at parts?

Kind regards,
Candy
 
As for a bit of grounding; you can check that this works by creating a bitmap in MS Paint of 240*180 pixels and saving it as a 256-color bitmap. Then, open it in Winhex (or so) and remove all bytes after 0x36, IE, only leave a header intact. The rest will come from the video.

Open the video, copy from D4 to 4D3 (400 hex bytes, that's 1024 bytes regular). Paste these at the end of the header of the bitmap. This is the
Copy 8640 bytes from 0x2730 (until 0x48F0) to the end of the bitmap.
Copy 8640 bytes from 0x48FC (until 0x6ABC) to the end of the bitmap.
Copy 8640 bytes from 0x6AC8 (until 0x8C88) to the end of the bitmap.
Copy 8640 bytes from 0x8C94 (until 0xAE54) to the end of the bitmap.
Copy 8640 bytes from 0xAE60 (until 0xD020) to the end of the bitmap.

Save the bitmap, open in any image viewer and behold. The image is upside-down because AVI files are internally the right way up (the topmost line is the first one) and bitmaps are internally upside down (the bottommost line is the first one), so we flipped it upside down. That should be all.

The second image will take more trouble :)

Good will hacking,
Candy
[edit] minor calculation oops, copy is 12 bytes shorter than that. [/edit]
 
Thanks!

Small update on the video codec:

All video subchunks have a byte-opcode structure. They start with a length field of 4 bytes indicating how many bytes are command bytes, the bytes that are left after these are the data section. These command bytes are then interpreted byte-by-byte:

command 00: end of commands
command 05: followed by 2 bytes in big-endian indicating the length. Copy the first N bytes of the data section to the target image.

This allows the 00db chunks to be read problem-free (and without assumptions!). The 00dc chunks seem to follow the same structure but start with a different opcode. I'm investigating what these may be and what format they have. I now have a program that can take any of the AVI's with this format and dump the first frame to a bitmap. I'll upload it after a bit more work (to get the command parsing bit in properly).
 
So, theres two important things to be noted.

1. Not all videos use the JYV1 codec (which stands for, I think, Jason elY Version 1). Some have RRV1 and RRV2 (my guess: eRic willmaR Version 1 and 2). It appears they *do* have the exact same internal format though, with one minor adjustment for RRV2 which has a different screen size. Both JYV1 and RRV1 have 160*120 pixel images divided into 8640 byte chunks, giving 5 chunks per frame. RRV2 seems to work with 320*120 pixel images (presumably anamorph with actually 4:3 ratio) divided into 15 chunks of 2560 bytes each. Other than that, they're identical. Woo!
[ minor addition ] apparently, somebody figured "we are going to show them this enter-base-movie ten times, better make it high quality" and put it in 320*160 in a RRV1 format... so the chunks are an amazing 0x2800 large. Total is 51200 bytes - my temp buffer was 50000 so I crashed a bit... :) RRV1 then only indicates the subchunk count, not the actual size. Hmmm.... I should be able to hack up a minor tool to check these assumptions - you can statically convert a RRV2 file into an RRV1 file by merging three subchunk streams... That's a todo for tomorrow though.

2. The codec isn't byte oriented but nibble-oriented. That means that the amount of possible simple commands has just shrunk from 256 to 16. Best yet, I've figured out about 4-5 of them:

0: Extension; see separate list for 0-started chunks. Check that the next nibble isn't just outside the command buffer, that happens a lot.
5: Read the next 4 nibbles as a big-endian int (read nibbles in the order they're in the hex editor) and skip that many bytes in the video buffer. This one is seen a lot with "521c0" in JYV1 files and with "50A00" in RRV2 files meaning skip the entire chunk, don't change a byte.
9: Read the next 2 nibbles, same behaviour as 5.
a: Read the next nibble (one), same behaviour as 5.
8: Output one byte from the data pointer to the video buffer.

0-started commands:
0: end of stream. Not sure why but the 00db chunks have this.
5: large copy-block command as described in previous post.

Each subchunk seems to be formatted as follows:

[chunk command length / data offset: 4bytes] [command bytes, indicated before] [data bytes, until end of subchunk].

You start the command serie by initialising the video buffer pointer to the start of this chunk's worth of video buffer, the data pointer to the data bytes and the command pointer to the command bytes. You then read 1 nibble at a time in the command stream and use the data stream meanwhile. Not all data bytes are used all the time; the last 0-3 bytes may be padding for alignment. Also, if the command stream ends on half a nibble, it's padded with a 0 nibble.





Long story short, I can read all Crusader no Remorse movies (no regret has, again, different codecs and these are actually different). I can also play back all chunks that only skip bytes and change single pixels. There are at least 6 commands left to explore (that actually occur): 1, 2, 4, 6, c and e. 3, 7, b, d and f don't occur at all (greatly helping reverse engineering).
 
If you want, I'll point Jason to this thread and he could probably help you.
 
If you could, that would be an enourmous help. I found that with the 5, 9, a and 8 commands decoded there are a few movies that don't sync up - the command stream is fully decodable but doesn't add up to the amount of bytes that should have been found. Also, having a hint for the other commands would be incredibly useful as I currently (still) have no idea.
 
A bit stuck on AVI playback so I switched back to the engine (which is a lot more complete now and may even start working), a few models (about two dozen) to test with, adding physics models & behaviour to manage the stuff and doing that ran against not having any sound effects. After a single attempt at recording anything somewhat usable from my own voice (and recovering from laughter) I decided to see if the sounds are hard to reverse engineer...

As it appears, it's dead easy. They're in a FLX archive, the 0th file is an index into the rest with 0-terminated strings prefixed with a 4-byte something (that probably indicates something about the sound but is kind of irrelevant). The rest are SFX format files stored plain in the archive. Extract them from the archive, rename according to index and you're done.

That is, if you can play SFX files. SFX is pretty much like WAV, except that the header is smaller & offers less options (which nobody uses anyway, but in WAV they exist and in SFX they don't). Also, SFX is 8-bit signed samples where WAV (the one the Windows sound recorder defaults to) is 8-bit unsigned. Toggle the top bit to convert from one to another.

After doing that, you end up with a directory full of WAV files with recognisable names that do what they say they do.

Some of the entries are very interesting - the spoken sound fragments appear to have been scripted and then recorded by a number of people and then cherry-picked for the game. Some strings are kept more than once, a number of them are completely absent. The names are still the original ones, prefixed with the name of the soldier type saying it and postfixed with the initials of the person doing the speaking. So, you can find out who said what! :)

A small reference for the sentences so you know what's what (Eric Willmar's only line seems to have been misnumbered):

Code:
SERVO80 -> camera focusing
CAMTURN -> camera beweging
JETSON1A -> Spider bomb from camera 

AN01 RB Resistance is futile
AN02 RB Halt
AN03 RB Human intruder sighted
AN04 RB Scanners indicate: an intruder
AN05 RB Alert! Alert!

CM01 BJ Die rebel!
CM02 BJ You won't succeed!
CM03 BJ Guards! Guards!

CS01 DP You're not supposed to be here
CS02 BJ Please! I'm unarmed!
CS03 DP Don't shoot!
CS05 TM Hey, what' ya doing here?

EN01 MS Enemy sighted
EN02 BJ Die rebel bastard!
EN03 BJ Taste plasma traitor!
EN04 BJ Right on time, just like she'd said he'd be!

ES02 DP Die traitor!
ES02 SS Die traitor!
ES03 SS Rebel scum!
ES04 SS Die silencer!

# this is a female voice, no initials
FW01 No, don't shoot!
FW02 Help guards!
FW03 Please, don't!
FW04 Aaah!

GS01 PW Halt!
GS02 PW Stop right there!
GS04 TM You're not authorised!
GS06 PW Intruder sighted!
GU03 DP Security!
GU04 DP Surrender!

MW02 SS What are you doing here?
MW03 SS Oh no!
MW04 TM Where do you think you're going?
MW07 TM Authorized personnel only!

# the female announcer voice
NC01 Code red terminated
NC02 Area secure
NC03 Stand down from code red
NC04 The alarm has been deactivated
NC05 The alarm has been activated
NC06 Security has been breached
NC07 Access denied
NC08 Security lockdown during code red
NC09 Alarm must be deactivated
NC10 Blast-PAC is now active
NC12 Have a nice day
NC13 Access denied
NC14 Invalid keycard
NC15 Crusader: No Remorse
NC16 Are you sure?
NC17 Saving game
NC18 Loading game
NC19 Quicksave
NC20 Quickload
NC21 Exit to DOS?
NC22 Silencer terminated
NC23 Thank you. Have a nice day!

OW02 MS Somebody call the guards!
OW02 PE Please don't shoot me!
OW03 EW Somebody call the guards!
OW04 SS Help guards!
OW06 TM I didn't do it!
OW03 SS What are you doing here? 

SC02 DP I surrender
SC03 SS Okay, I give up
SC04 SS How'd you get in?
SC05 DP Guards???

SO02 BJ Freeze!
SO03 BJ Drop your weapon!
SO04 BJ Eat lead, traitor!
SO05 BJ Sound the alarm!
SO06 BJ Die sucker!

SR03 BM Drop your weapon!

ST01 MS There he is!
ST02 MS Chew on this rebel
ST03 SS You there! Halt!
ST05 MS Take this!

The initials found above are:
BJ ->
BM ->
DP ->
EW -> Eric Willmar
MS -> Matt Sheffield
PE ->
PW -> Phil Wattenbarger (assistant lead design)
RB -> Randy Buck (Audio post supervisor)
SS ->
TM -> Terry Manderfeld

Filled in a few from the credits, anybody want a potshot who the other initials are?
 
Back
Top