Jump to content

Home

AkosView Alpha 1 - Now Available


checkmate

Recommended Posts

The newest, windowed version of AkosView is now here, although it's an alpha version. It doesn't have all the features of the final which I will hopefully release in the near future, but it is functional, so go here to get it. Have fun browsing, and I hope you find something truly interesting in all that data.

Link to comment
Share on other sites

Getting AkosView to extract costumes from resource files by itself is my top priority right now, followed by getting the frame's position on the screen correct, fixing palette issues in several codec 5 images, and implementing codec 16.

 

I believe it's also possible to get AkosView to play back animations, but I know almost nothing about the AKSQ blocks, among other things.

 

I once had some code written that opened SCUMM resource files and found AKOS costumes. Unfortunately, it was flakey, had trouble differentiating SCUMM V8 and V7, and crashed for no reason whatsoever...but the main problem with it is that I LOST IT!:o I'll have to write it again!

 

It would help if the Index File specification was cleaned up. bgbennyboy's has several innacuracies, I believe (I think it has SCUMM V7 and V8 mixed up somewhat :confused: ). But another source for information is the ScummVM source code, so I'll look into that.

 

Previously, I detected SCUMM V8 (which uses 32-bit data in some places which V7 used 16-bit) by reading the name of the file opened to see if it said "COMI.LA0".:o But it turns out that the size of the MAXS block is different for the two versions (V8 is 176 bytes, V7 is 138). I could use that:cool:

Link to comment
Share on other sites

I think my index file specs might have been wrong for DOBJ blocks but I think it was right for V7/V8. I cant check right now, but i'll have a look tomorrow if I get chance. I think I detected the difference between V7/8 in the RNAM block but I cant remember offhand :)

Link to comment
Share on other sites

My main concern with your Index File Specs is the definition of the Directory Blocks. It says that SCUMM V8 Directory blocks have this structure:

Block Name (4 bytes)
Block Size (4 bytes BE)
No. of Items (4 bytes)
*Room Number (1 byte)
*Offset (2 bytes)

While the SCUMM V7 Directory blocks are equivalent to SCUMM V5, which looks like this:

Block Name (4 bytes)
Block Size (4 bytes BE)
No. of Items (2 bytes)
*Room Number (1 byte)
*Offset (4 bytes)

Are you sure that the SCUMM V7 Directory blocks use 4 bytes for the Offset field, while SCUMM V8 (which is newer) uses only 2 bytes? Seems to me rather odd...:confused:

Link to comment
Share on other sites

You have V7 and V8 the wrong way round there :)

 

Most blocks in V7 are the same as their V5 equivalents, except for the RNAM, MAXS and the new ANAM block. So most blocks have 2 bytes for the no of items. With V8 its 4 bytes for the no of items.

 

I've just had a quick look at it and as far as I know the only thing definitely wrong with the index file specs is the DOBJ stuff. The blocksize for it is correct but I dont know what data the blocks contain. I remember having a look at the scummvm source but being ignorant of c++ I got scared.

 

There's a newer version of the index file specs included with the source to my index file reader by the way, its on 'ere.

 

The easy way to differentiate between V7/V8 is with the RNAM block at the beginning. V8 has room name data while with V7 theres just a blank byte. So:

 

If blockheader=RNAM

read 4 bytes Big Endian

if those 4 bytes=9 then its v7, if not its v8

Link to comment
Share on other sites

I have V7 and V8 the wrong way around? I was copying what IndexFileSpecs.txt said, which I'm pretty sure is backward...:confused: So if I got it mixed up in my previous message, then the Index File Specs does too. You should probably double-check.

Link to comment
Share on other sites

Wait, i see what you mean now. I was very tired last night, I thought you were talking about NoOfItems not offsets, thats what I thought I read anyway, sorry about that :~

 

The best way to check, would be to check those offsets with the resource files. Either both the specs are right or the 4 bytes is actually 2 sets of 2 bytes, ie. 2 bytes=offset 2 bytes=something else.

 

I'll have a look later when I get chance.

 

You are definitely right though, you'd expect V8 to use 4 bytes and V5-7 to use 2. Hmmm......

Link to comment
Share on other sites

Right, i'm typing this as I do it so bear with me if it looks like gibberish.

 

First V7 - Full Throttle

In the DSCR the last script according to my program is:

 

Room Number 174: Offset 38181

 

In room 174 (The plane hanging over the gorge) the room offset is 81109804, the offset of the last SCRP script is 81147985.

 

The offsets are relative to the Room's (So it's Script offset - Room offset) 81147985 - 81109804 = 38181, so the index file is right there and the offset is read correctly.

 

To check I did the same thing with a random costume in DCOS:

Room Number 047: Offset 366182

 

Room offset is 20708347. So there should be an AKOS at 20708347 + 366182

 

Sure enough, the 2nd of 3 AKOS' is at offset 21074529, just as the index file says :) So V7 is definately right.

 

 

Now V8 with CMI:

 

Again in DCOS:

Room Number 82: Offset 4207639

 

That puts it in Comi.LA2 according to the LOFF block in the main resource files. Room offset=70455475. 70455475 + 4207639 = 74663114 - which is WRONG. It should be the 7th costume in the block, but the 7th costume=74663106 - thats out by 8 bytes. I think thats because in V8 the LOFF references LFLF blocks, while in others it references ROOM blocks. That accounts for the 8 bytes difference anyway. Relative to the LFLF block, its correct, relative to the ROOM block its wrong by 8 bytes (the 4 bytes for the LFLF block name + the 4 bytes for the LFLF block size).

 

I cant check that because I dont really have proper access to my stuff right now i'm just using scummrev2, I seem to remember there being something slightly wonky with the LOFF anyway. So V8 is right too :)

 

If you cant be bothered to read all that then here's the synopsis: The specs are right, they dont look right, but they are. Hope that helps :)

 

[Edit] I hope you're going to release the source to this when its done, oh or update your AKOS specs to include codec 16 :)

Link to comment
Share on other sites

Thanks!:cool: I think I'll release the source code with the full, non-Alpha version comes out.

 

As for codec 16, I pretty much copied some bits out of ScummVM.:o But after looking at it carefully, I've drawn up a flow-chart that should help...so with that, I wrote up an implementation by hand. Trouble is, that implementation outputs a pile of pixel gibberish.

 

I've written a small file that should really help with implementing Bit Streams:

 

// BitStream.h
// By Nolan Check

#ifndef BITSTREAM_H
#define BITSTREAM_H

#include "AkosView.h"

class BitStream
{
public:
BitStream(BYTE* theBits)
{
	bits = theBits;
	numBits = 8;
	buffer = *bits;
};

BYTE readBits(int count)
{
	BYTE result = 0;
	for (int i = 0; i < count; i++)
	{
		if (!--numBits)
		{
			bits++;
			numBits = 8;
			buffer = *bits;
		}
		if (buffer & 1)
		{
			result |= (1 << count);
		}
		buffer >>= 1;
	}
	return result;
};
private:
BYTE* bits;
int numBits;
BYTE buffer;
};

#endif

 

And my hand-written implementation of codec 16 looks like this:

void AKOS::codec16(BYTE* result)
{
BYTE colorBits = *currentAKCD;
BYTE color = *(currentAKCD + 1);

BitStream stream(currentAKCD + 2);

WORD height = currentAKCI->height;
while (height--)
{
	*result++ = color;

	if (stream.readBits(1))
	{
		if (stream.readBits(1))
		{
			BYTE code = stream.readBits(3);
			if (code == 4)
			{
				BYTE rep = stream.readBits(8);
				while (--rep)
				{
					*result++ = color;
				}
			}
			else
			{
				color += code - 4;
			}
		}
		else
		{
			color = stream.readBits(colorBits);
		}
	}
}
}

Remember: this implementation outputs gibberish, so it's got a bug somewhere. Would anybody like to help me find it??:)

Link to comment
Share on other sites

Well, I've just found one bug in the above code. I'm going down the rows too fast. Here's another version, but it's STILL NOT QUITE RIGHT! It still outputs gibberish, though I have to admit, this gibberish is more readable than the previous version.:)

void AKOS::codec16(BYTE* result)
{
BYTE colorBits = *currentAKCD;
BYTE color = *(currentAKCD + 1);

BitStream stream(currentAKCD + 2);

WORD height = currentAKCI->height;
BYTE rep = 0;
while (height--)
{
	WORD width = currentAKCI->width;
	while (width--)
	{
		*result++ = color;

		if (rep)
		{
			rep--;
			continue;
		}

		if (stream.readBits(1))
		{
			if (stream.readBits(1))
			{
				BYTE code = stream.readBits(3);
				if (code == 4)
				{
					rep = stream.readBits(8);
				}
				else
				{
					color += code - 4;
				}
			}
			else
			{
				color = stream.readBits(colorBits);
			}
		}
	}
}
}

There may also be a problem with my BitStream reader, so that deserves some scrutiny too.

Link to comment
Share on other sites

I'm not at all sure about this, since I haven't actually added (or looked into) codec layer 16 to the probably-never-to-be-released SCUMMRev 5, but I do know this, from looking at your code:

 

Layer 16 is similar to MMucus layer 0x40-0x44. Looking quickly over the code (haven't tested it to actually see the output) and comparing to the MMucus code for SR5, you might need to subtract 1 from rep, i.e. rep = stream.readBits(8) - 1;

 

If the image is skewed in some weird way, that would be it. Since "rep" indicates the number of pixels to repeat the colour for, 1 rep means 1 pixel, which you draw no matter what (*result++ = color;). At least, that's actually how MMucus 0x40 works. I may look a bit more into it.

 

- Serge

Link to comment
Share on other sites

Well, I'm convinced there's a problem with the bitstream reader...that was confusing in the ScummVM source code. This is an implementation that was copied and slightly modified from ScummVM. The Alpha 3 version uses this implementation; it actually works:

#define AKOS16_FILL_BITS() \
if (numBits <= 8) \
{ \
	bits |= (*dataPtr++) << numBits; \
	numBits += 8; \
}

#define AKOS16_EAT_BITS(n)	\
numBits -= n; \
bits >>= n;

void AKOS::codec16(BYTE* result)
{
BYTE* src = currentAKCD;

BYTE unk5 = 0;
int unk6 = 0;
BYTE mask = (1 << *src) - 1;
BYTE color = *(src + 1);
BYTE shift = *src;
WORD bits = (*(src + 2) | *(src + 3) << 8);
BYTE numBits = 16;
BYTE* dataPtr = src + 4;

WORD height = currentAKCI->height;
while (height--)
{
	BYTE* curDst = result;

	WORD width = currentAKCI->width;
	while (width--)
	{
		WORD lineBits;

		*curDst++ = color;

		if (!unk5)
		{
			AKOS16_FILL_BITS()
			lineBits = bits & 3;
			if (lineBits & 1)
			{
				AKOS16_EAT_BITS(2)
				if (lineBits & 2)
				{
					WORD tmpBits = bits & 7;
					AKOS16_EAT_BITS(3)
					if (tmpBits != 4)
					{
						color += tmpBits - 4;
					}
					else
					{
						unk5 = 1;
						AKOS16_FILL_BITS()
						unk6 = (bits & 0xFF) - 1;
						AKOS16_EAT_BITS(8)
						AKOS16_FILL_BITS()
					}
				}
				else
				{
					AKOS16_FILL_BITS()
					color = ((BYTE)bits) & mask;
					AKOS16_EAT_BITS(shift)
					AKOS16_FILL_BITS()
				}
			}
			else
			{
				AKOS16_EAT_BITS(1)
			}
		}
		else
		{
			if (!--unk6)
			{
				unk5 = 0;
			}
		}
	}

	result += currentAKCI->width;
}
}

The AKOS16_FILL_BITS and AKOS16_EAT_BITS are rather odd...I'm not sure how the bits are structured in the data.

 

The implementation I wrote (which doesn't work) reads bits in this order ("1" is the least-significant bit):

Byte 1: 8  7  6  5  4  3  2  1
Byte 2: 16 15 14 13 12 11 10 9
...etc...

Link to comment
Share on other sites

Originally posted by checkmate

<snip>

The implementation I wrote (which doesn't work) reads bits in this order ("1" is the least-significant bit):

Byte 1:  8  7  6  5  4  3  2 1
Byte 2: 16 15 14 13 12 11 10 9
...etc...

 

Yes, that would be wrong. Least significant bit should be read first.

 

AKOS16_FILL_BITS does this:

If there are less than 9 bits left in the bitbuffer, it reads 8 new bits, and places them after (i.e., above in significance) the remaining bits in the buffer, example:

 

Before FILL_BITS:

Buffer: 0000 0000 0101 0111
BitCount: 8

=>

After FILL_BITS:

        ---new--- ---old---
Buffer: 1010 1010 0101 0111 
BitCount: 16

 

AKOS16_EAT_BITS quite simply discards n bits from the buffer, as if they'd been read (i.e., the n least significant bits).

 

Also notice that, yes, you subtract one from the rep variable:

unk6 = (bits & 0xFF) [b]- 1[/b];

 

- Serge

Link to comment
Share on other sites

In more simple terms, you just read the bitstream byte by byte, and fetch the bits with the least significant bit first, so in the above example you should get the bits in the following order:

 

1110 1010 0101 0101

 

(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...)

Link to comment
Share on other sites

Originally posted by checkmate

Well, I'm convinced there's a problem with the bitstream reader...that was confusing in the ScummVM source code. This is an implementation that was copied and slightly modified from ScummVM. The Alpha 3 version uses this implementation; it actually works:

[/code]

 

And remember, if you borrow any code from ScummVM, you must GPL your own program :)

 

I need to add AKOS viewing functionality to ScummEx.

Link to comment
Share on other sites

Ai-yai-yai! I understand your post, now I'm just trying to make the BitStream reader go "in reverse"! Unfortunately, it's more difficult than I thought...

 

This obviously isn't correct (it still outputs trash), but it's what I've got so far:

BYTE readBits(int count)
{
BYTE result = 0;
for (int i = 0; i < count; i++)
{
	if (!--numBits)
	{
		bits++;
		numBits = 8;
		buffer = *bits;
	}
	if (buffer & 0x80)
	{
		result |= (1 << (count - (i + 1)));
	}
	buffer <<= 1;
}
return result;
};

Link to comment
Share on other sites

Yeah, that's still the wrong order - don't know if I misinterpreted you in the previous post or not...

 

You need to read the bits right to left (which is also simpler), so (I'm not C-literate, there may be errors inhere):

 

BYTE readBits(int count)
{
BYTE result = 0;
for (int i = 0; i < count; i++)
{
	if (!--numBits)
	{
		bits++;
		numBits = 8;
		buffer = *bits;
	}
	if (buffer & [u]1[/u])
	{
		[u]result |= (1 << i);[/u]
	}
	buffer [u]>>=[/u] 1;
}
return result;
};

 

Quickly edited, this, so I may be thinking wrong, but try it... Also, it's not optimized. It might be faster to ensure enough bits are in the buffer, and then drop the for-loop, and simply do something like:

byte mask = (1 << count) - 1;	// (1 << [i]x[/i]) - 1 yields a result of (in binary) [i]x[/i] 1's, 
			// so for a [i]count[/i] of 4: 1 << 4 = 10000b. 10000b - 1 = 1111b
result = buffer & mask;		// so now we have a mask for the number of bits we need.
buffer >>= count;		// ... and we just shift right by [i]count[/i] to get rid of the read bits.

Link to comment
Share on other sites

Hooray!:D Thanks for your help. I misinterpreted your post and thought the bits were supposed to be read in reverse order...wrong.

 

And I also discovered a bug in the code I had to start with...I was doing (1 << count) instead of (1 << i). Why didn't that just jump out at me??:o Could've saved us a lot of trouble.

 

Codec 16, it turns out, is actually one of the Background Image codecs.

Link to comment
Share on other sites

Yes, it's a background image codec, as I mentioned:

 

Originally posted by Serge

I'm not at all sure about this, since I haven't actually added (or looked into) codec layer 16 to the probably-never-to-be-released SCUMMRev 5, but I do know this, from looking at your code:

 

Layer 16 is similar to MMucus layer 0x40-0x44.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...