/***********************************************************************\
*
* Level 9 interpreter
* Version 5.1
* Copyright (c) 1996-2011 Glen Summers and contributors.
* Contributions from David Kinder, Alan Staniforth, Simon Baldwin,
* Dieter Baron and Andreas Scherrer.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
\***********************************************************************/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "level9.h"
extern Bitmap* bitmap;
L9UINT32 filelength(FILE *f);
void L9Allocate(L9BYTE **ptr,L9UINT32 Size);
L9BOOL bitmap_exists(char* file)
{
FILE* f = fopen(file,"rb");
if (f != NULL)
{
fclose(f);
return TRUE;
}
return FALSE;
}
L9BYTE* bitmap_load(char* file, L9UINT32* size)
{
L9BYTE* data = NULL;
FILE* f = fopen(file,"rb");
if (f)
{
*size = filelength(f);
L9Allocate(&data,*size);
if (fread(data,1,*size,f) != *size)
{
free(data);
data = NULL;
}
fclose(f);
}
return data;
}
Bitmap* bitmap_alloc(int x, int y)
{
Bitmap* bitmap = NULL;
L9Allocate((L9BYTE**)&bitmap,sizeof(Bitmap)+(x*y));
bitmap->width = x;
bitmap->height = y;
bitmap->bitmap = ((L9BYTE*)bitmap)+sizeof(Bitmap);
bitmap->npalette = 0;
return bitmap;
}
/*
A PC or ST palette colour is a sixteen bit value in which the low three nybbles
hold the rgb colour values. The lowest nybble holds the blue value, the
second nybble the blue value and the third nybble the red value. (The high
nybble is ignored). Within each nybble, only the low three bits are used
IE the value can only be 0-7 not the full possible 0-15 and so the MSbit in
each nybble is always 0.
*/
Colour bitmap_pcst_colour(int big, int small)
{
Colour col;
L9UINT32 r = big & 0xF;
L9UINT32 g = (small >> 4) & 0xF;
L9UINT32 b = small & 0xF;
r *= 0x49; r >>= 1;
g *= 0x49; g >>= 1;
b *= 0x49; b >>= 1;
col.red = (L9BYTE)(r&0xFF);
col.green = (L9BYTE)(g&0xFF);
col.blue = (L9BYTE)(b&0xFF);
return col;
}
/*
ST Bitmaps
On the ST different graphics file formats were used for the early V4
games (Knight Orc, Gnome Ranger) and the later V4 games (Lancelot,
Ingrid's Back, Time & Magik and Scapeghost).
*/
/*
Extracts the number of pixels requested from an eight-byte data block (4 bit-
planes) passed to it.
Note: On entry each one of four pointers is set to point to the start of each
bit-plane in the block. The function then indexes through each byte in
each bit plane. and uses shift and mask operations to extract each four
bit pixel into an L9PIXEL.
The bit belonging to the pixel in the current byte of the current bit-
plane is moved to its position in an eight-bit pixel. The byte is then
masked by a value to select only that bit and added to the final pixel
value.
*/
L9UINT32 bitmap_st1_decode_pixels(L9BYTE* bitmap, L9BYTE* data, L9UINT32 count, L9UINT32 pixels)
{
L9UINT32 bitplane_length = count / 4; /* length of each bitplane */
L9BYTE *bitplane0 = data; /* address of bit0 bitplane */
L9BYTE *bitplane1 = data + (bitplane_length); /* address of bit1 bitplane */
L9BYTE *bitplane2 = data + (bitplane_length * 2); /* address of bit2 bitplane */
L9BYTE *bitplane3 = data + (bitplane_length * 3); /* address of bit3 bitplane */
L9UINT32 bitplane_index, pixel_index = 0; /* index variables */
for (bitplane_index = 0; bitplane_index < bitplane_length; bitplane_index++)
{
/* build the eight pixels from the current bitplane bytes, high bit to low */
/* bit7 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 4) & 0x08)
+ ((bitplane2[bitplane_index] >> 5) & 0x04)
+ ((bitplane1[bitplane_index] >> 6) & 0x02)
+ ((bitplane0[bitplane_index] >> 7) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit6 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 3) & 0x08)
+ ((bitplane2[bitplane_index] >> 4) & 0x04)
+ ((bitplane1[bitplane_index] >> 5) & 0x02)
+ ((bitplane0[bitplane_index] >> 6) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit5 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 2) & 0x08)
+ ((bitplane2[bitplane_index] >> 3) & 0x04)
+ ((bitplane1[bitplane_index] >> 4) & 0x02)
+ ((bitplane0[bitplane_index] >> 5) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit4 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 1) & 0x08)
+ ((bitplane2[bitplane_index] >> 2) & 0x04)
+ ((bitplane1[bitplane_index] >> 3) & 0x02)
+ ((bitplane0[bitplane_index] >> 4) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit3 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index]) & 0x08)
+ ((bitplane2[bitplane_index] >> 1) & 0x04)
+ ((bitplane1[bitplane_index] >> 2) & 0x02)
+ ((bitplane0[bitplane_index] >> 3) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit2 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] << 1) & 0x08)
+ ((bitplane2[bitplane_index]) & 0x04)
+ ((bitplane1[bitplane_index] >> 1) & 0x02)
+ ((bitplane0[bitplane_index] >> 2) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit1 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] << 2) & 0x08)
+ ((bitplane2[bitplane_index] << 1) & 0x04)
+ ((bitplane1[bitplane_index]) & 0x02)
+ ((bitplane0[bitplane_index] >> 1) & 0x01);
if (pixels == ++pixel_index)
break;
/* bit0 byte */
bitmap[pixel_index] = ((bitplane3[bitplane_index] << 3) & 0x08)
+ ((bitplane2[bitplane_index] << 2) & 0x04)
+ ((bitplane1[bitplane_index] << 1) & 0x02)
+ ((bitplane0[bitplane_index]) & 0x01);
if (pixels == ++pixel_index)
break;
}
return pixel_index;
}
/*
The ST image file has the following format. It consists of a 44 byte header
followed by the image data.
The header has the following format:
Bytes 0-31: sixteen entry ST palette
Bytes 32-33: padding
Bytes 34-35: big-endian word holding number of bitplanes needed to make
up a row of pixels*
Bytes 36-37: padding
Bytes 38-39: big-endian word holding number of rows in the image*
Bytes 40-41: padding**
Bytes 42-43: mask for pixels to show in last 16 pixel block. Again, this
is big endian
[*] these are probably big-endian unsigned longs but I have designated
the upper two bytes as padding because (a) Level 9 does not need
them as longs and (b) using unsigned shorts reduces byte sex induced
byte order juggling.
[**] not certain what this is for but I suspect that, like bytes 42-43
it is a mask to indicate which pixels to show, in this case in the
first 16 pixel block
The image data is essentially a memory dump of the video RAM representing
the image in lo-res mode. In lo-res mode each row is 320 pixels wide
and each pixel can be any one of sixteen colours - needs 4 bits to store.
In the ST video memory (in lo-res mode which we are dealing with here)
is organised as follows. The lowest point in memory in the frame buffer
represents the top-left of the screen, the highest the bottom-right.
Each row of pixels is stored in sequence.
Within each pixel row the pixels are stored as follows. Each row is
divided into groups of 16 pixels. Each sixteen pixel group is stored
in 8 bytes, logically four groups of two. Each two byte pair
is a bit-plane for that sixteen pixel group - that is it stores the
same bit of each pixel in that group. The first two bytes store the
lowest bit, the second pair the second bit &c.
The word at bytes 34-35 of the header stores the number of bitplanes
that make up each pixel row in the image. Multplying this number by
four gives the number of pixels in the row***. For title and frame
images that will be 320, for sub-images it will be less.
[***] Not always exactly. For GnomeRanger sub-images this value is 60
- implying there are 240 pixels per row. In fact there are only
225 pixels in each row. To identify this situation look at the
big-endian word in bytes 42-43 of the header. This is a mask
telling you the pixels to use. Each bit represents one pixel in
the block, with the MSBit representing the first pixel and the
LSbit the last.
In this situation, the file does contain the entire sixteen
pixel block (it has to with the bitplane arrangement) but
the pixels which are not part of the image are just noise. When
decoding the image, the L9BITMAP produced has the actual pixel
dimensions - the surplus pixels are discarded.
I suspect, though I have not found an instance, that in theory
the same situation could apply at the start of a pixel row and that
in this case the big-endian word at bytes 40-41 is the mask.
Having obtained the pixel dimensions of the image the function uses
them to allocate memory for the bitmap and then extracts the pixel
information from the bitmap row by row. For each row eight byte blocks
are read from the image data and passed to UnpackSTv1Pixels along with
the number of pixels to extract (usually 16, possibly less for the last
block in a row.)
*/
L9BOOL bitmap_st1_decode(char* file, int x, int y)
{
L9BYTE* data = NULL;
int i, xi, yi, max_x, max_y, last_block;
int bitplanes_row, bitmaps_row, pixel_count, get_pixels;
L9UINT32 size;
data = bitmap_load(file,&size);
if (data == NULL)
return FALSE;
bitplanes_row = data[35]+data[34]*256;
bitmaps_row = bitplanes_row/4;
max_x = bitplanes_row*4;
max_y = data[39]+data[38]*256;
last_block = data[43]+data[42]*256;
/* Check if sub-image with rows shorter than max_x */
if (last_block != 0xFFFF)
{
/* use last_block to adjust max_x */
int i = 0;
while ((0x0001 & last_block) == 0) /* test for ls bit set */
{
last_block >>= 1; /* if not, shift right one bit */
i++;
}
max_x = max_x - i;
}
if (max_x > MAX_BITMAP_WIDTH || max_y > MAX_BITMAP_HEIGHT)
{
free(data);
return FALSE;
}
if ((x == 0) && (y == 0))
{
if (bitmap)
free(bitmap);
bitmap = bitmap_alloc(max_x,max_y);
}
if (bitmap == NULL)
{
free(data);
return FALSE;
}
if (x+max_x > bitmap->width)
max_x = bitmap->width-x;
if (y+max_y > bitmap->height)
max_y = bitmap->height-y;
for (yi = 0; yi < max_y; yi++)
{
pixel_count = 0;
for (xi = 0; xi < bitmaps_row; xi++)
{
if ((max_x - pixel_count) < 16)
get_pixels = max_x - pixel_count;
else
get_pixels = 16;
pixel_count += bitmap_st1_decode_pixels(
bitmap->bitmap+((y+yi)*bitmap->width)+x+(xi*16),
data+44+(yi*bitplanes_row*2)+(xi*8),8,get_pixels);
}
}
bitmap->npalette = 16;
for (i = 0; i < 16; i++)
bitmap->palette[i] = bitmap_pcst_colour(data[(i*2)],data[1+(i*2)]);
free(data);
return TRUE;
}
void bitmap_st2_name(int num, char* dir, char* out)
{
/* title picture is #30 */
if (num == 0)
num = 30;
sprintf(out,"%s%d.squ",dir,num);
}
/*
PC Bitmaps
On the PC different graphics file formats were used for the early V4
games (Knight Orc, Gnome Ranger) and the later V4 games (Lancelot,
Ingrid's Back, Time & Magik and Scapeghost).
The ST and the PC both use the same image file format for the later
V4 games (Lancelot, Ingrid's Back, Time & Magik and Scapeghost.)
*/
void bitmap_pc_name(int num, char* dir, char* out)
{
/* title picture is #30 */
if (num == 0)
num = 30;
sprintf(out,"%s%d.pic",dir,num);
}
/*
The EGA standard for the IBM PCs and compatibles defines 64 colors, any
16 of which can be mapped to the usable palette at any given time. If
you display these 64 colors in numerical order, 16 at a time, you get a
hodgepodge of colors in no logical order. The 64 EGA color numbers are
assigned in a way that the numbers can easily be converted to a relative
intensity of each of the three phosphor colors R,G,B. If the number is
converted to six bit binary, the most significant three bits represent
the 25% level of R,G,B in that order and the least significant three
bits represent the 75% level of R,G,B in that order. Take EGA color 53
for example. In binary, 53 is 110101. Since both R bits are on, R = 1.0.
Of the G bits only the 25% bit is on so G = 0.25. Of the B bits only the
75% bit is on so B = 0.75.
*/
Colour bitmap_pc1_colour(int i)
{
Colour col;
col.red = (((i&4)>>1) | ((i&0x20)>>5)) * 0x55;
col.green = ((i&2) | ((i&0x10)>>4)) * 0x55;
col.blue = (((i&1)<<1) | ((i&8)>>3)) * 0x55;
return col;
}
/*
The PC (v1) image file has the following format. It consists of a 22
byte header organised like this:
Byte 0: probably a file type flag
Byte 1: the MSB of the file's length as a word
Bytes 2-3: little-endian word with picture width in pixels
Bytes 4-5: little-endian word with picture height in pixel rows
Bytes 6-21: the image colour table. One EGA colour in each byte
The image data is extremely simple. The entire block is packed array
of 4-bit pixels - IE each byte holds two pixels - the first in the high
nybble, the second in the low. The pixel value is an index into the
image colour table. The pixels are organised with the top left first and
bottom left last, each row in turn.
*/
L9BOOL bitmap_pc1_decode(char* file, int x, int y)
{
L9BYTE* data = NULL;
int i, xi, yi, max_x, max_y;
L9UINT32 size;
data = bitmap_load(file,&size);
if (data == NULL)
return FALSE;
max_x = data[2]+data[3]*256;
max_y = data[4]+data[5]*256;
if (max_x > MAX_BITMAP_WIDTH || max_y > MAX_BITMAP_HEIGHT)
{
free(data);
return FALSE;
}
if ((x == 0) && (y == 0))
{
if (bitmap)
free(bitmap);
bitmap = bitmap_alloc(max_x,max_y);
}
if (bitmap == NULL)
{
free(data);
return FALSE;
}
if (x+max_x > bitmap->width)
max_x = bitmap->width-x;
if (y+max_y > bitmap->height)
max_y = bitmap->height-y;
for (yi = 0; yi < max_y; yi++)
{
for (xi = 0; xi < max_x; xi++)
{
bitmap->bitmap[(bitmap->width*(y+yi))+(x+xi)] =
(data[23+((yi*max_x)/2)+(xi/2)]>>((1-(xi&1))*4)) & 0x0f;
}
}
bitmap->npalette = 16;
for (i = 0; i < 16; i++)
bitmap->palette[i] = bitmap_pc1_colour(data[6+i]);
free(data);
return TRUE;
}
/*
The PC (v2) image file has the following format. It consists of a 44
byte header followed by the image data.
The header has the following format:
Bytes 0-1: "datalen": length of file -1 as a big-endian word*
Bytes 2-3: "flagbyte1 & flagbyte2": unknown, possibly type identifiers.
Usually 0xFF or 0xFE followed by 0x84, 0x72, 0xFF, 0xFE or
some other (of a fairly small range of possibles) byte.
Bytes 4-35: "colour_index[]": sixteen entry palette. Basically an ST
palette (even if in a PC image file. Each entry is a sixteen
bit value in which the low three nybbles hold the rgb colour
values. The lowest nybble holds the blue value, the second
nybble the blue value and the third nybble the red value. (The
high nybble is ignored). Within each nybble, only the low
three bits are used IE the value can only be 0-7 not the full
possible 0-15 and so the MSbit in each nybble is always 0.**,
Bytes 36-37: "width": image width in pixels as a big-endian word
Bytes 38-39: "numrows": image height in pixel rows as a big-endian word
Byte 40: "seedByte": seed byte to start picture decoding.
Byte 41: "padByte": unknown. Possibly padding to word align the next
element?
Bytes 42-297: "pixelTable": an array of 0x100 bytes used as a lookup table
for pixel values
Bytes 298-313: "bitStripTable": an array of 0x10 bytes used as a lookup table
for the number of bytes to strip from the bit stream for the pixel being
decoded
Bytes 314-569: "indexByteTable": an array of 0x100 bytes used as a lookup
table to index into bitStripTable and pixelTable****
The encoded image data then follows ending in a 0x00 at the file length stored
in the first two bytes of the file. there is then one extra byte holding a
Lines 1–500 of 1578. Load more
/***********************************************************************\ * * Level 9 interpreter * Version 5.1 * Copyright (c) 1996-2011 Glen Summers and contributors. * Contributions from David Kinder, Alan Staniforth, Simon Baldwin, * Dieter Baron and Andreas Scherrer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * \***********************************************************************/ #include <math.h> #include <stdio.h> #include <stdlib.h> #include "level9.h" extern Bitmap* bitmap; L9UINT32 filelength(FILE *f); void L9Allocate(L9BYTE **ptr,L9UINT32 Size); L9BOOL bitmap_exists(char* file) { FILE* f = fopen(file,"rb"); if (f != NULL) { fclose(f); return TRUE; } return FALSE; } L9BYTE* bitmap_load(char* file, L9UINT32* size) { L9BYTE* data = NULL; FILE* f = fopen(file,"rb"); if (f) { *size = filelength(f); L9Allocate(&data,*size); if (fread(data,1,*size,f) != *size) { free(data); data = NULL; } fclose(f); } return data; } Bitmap* bitmap_alloc(int x, int y) { Bitmap* bitmap = NULL; L9Allocate((L9BYTE**)&bitmap,sizeof(Bitmap)+(x*y)); bitmap->width = x; bitmap->height = y; bitmap->bitmap = ((L9BYTE*)bitmap)+sizeof(Bitmap); bitmap->npalette = 0; return bitmap; } /* A PC or ST palette colour is a sixteen bit value in which the low three nybbles hold the rgb colour values. The lowest nybble holds the blue value, the second nybble the blue value and the third nybble the red value. (The high nybble is ignored). Within each nybble, only the low three bits are used IE the value can only be 0-7 not the full possible 0-15 and so the MSbit in each nybble is always 0. */ Colour bitmap_pcst_colour(int big, int small) { Colour col; L9UINT32 r = big & 0xF; L9UINT32 g = (small >> 4) & 0xF; L9UINT32 b = small & 0xF; r *= 0x49; r >>= 1; g *= 0x49; g >>= 1; b *= 0x49; b >>= 1; col.red = (L9BYTE)(r&0xFF); col.green = (L9BYTE)(g&0xFF); col.blue = (L9BYTE)(b&0xFF); return col; } /* ST Bitmaps On the ST different graphics file formats were used for the early V4 games (Knight Orc, Gnome Ranger) and the later V4 games (Lancelot, Ingrid's Back, Time & Magik and Scapeghost). */ /* Extracts the number of pixels requested from an eight-byte data block (4 bit- planes) passed to it. Note: On entry each one of four pointers is set to point to the start of each bit-plane in the block. The function then indexes through each byte in each bit plane. and uses shift and mask operations to extract each four bit pixel into an L9PIXEL. The bit belonging to the pixel in the current byte of the current bit- plane is moved to its position in an eight-bit pixel. The byte is then masked by a value to select only that bit and added to the final pixel value. */ L9UINT32 bitmap_st1_decode_pixels(L9BYTE* bitmap, L9BYTE* data, L9UINT32 count, L9UINT32 pixels) { L9UINT32 bitplane_length = count / 4; /* length of each bitplane */ L9BYTE *bitplane0 = data; /* address of bit0 bitplane */ L9BYTE *bitplane1 = data + (bitplane_length); /* address of bit1 bitplane */ L9BYTE *bitplane2 = data + (bitplane_length * 2); /* address of bit2 bitplane */ L9BYTE *bitplane3 = data + (bitplane_length * 3); /* address of bit3 bitplane */ L9UINT32 bitplane_index, pixel_index = 0; /* index variables */ for (bitplane_index = 0; bitplane_index < bitplane_length; bitplane_index++) { /* build the eight pixels from the current bitplane bytes, high bit to low */ /* bit7 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 4) & 0x08) + ((bitplane2[bitplane_index] >> 5) & 0x04) + ((bitplane1[bitplane_index] >> 6) & 0x02) + ((bitplane0[bitplane_index] >> 7) & 0x01); if (pixels == ++pixel_index) break; /* bit6 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 3) & 0x08) + ((bitplane2[bitplane_index] >> 4) & 0x04) + ((bitplane1[bitplane_index] >> 5) & 0x02) + ((bitplane0[bitplane_index] >> 6) & 0x01); if (pixels == ++pixel_index) break; /* bit5 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 2) & 0x08) + ((bitplane2[bitplane_index] >> 3) & 0x04) + ((bitplane1[bitplane_index] >> 4) & 0x02) + ((bitplane0[bitplane_index] >> 5) & 0x01); if (pixels == ++pixel_index) break; /* bit4 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] >> 1) & 0x08) + ((bitplane2[bitplane_index] >> 2) & 0x04) + ((bitplane1[bitplane_index] >> 3) & 0x02) + ((bitplane0[bitplane_index] >> 4) & 0x01); if (pixels == ++pixel_index) break; /* bit3 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index]) & 0x08) + ((bitplane2[bitplane_index] >> 1) & 0x04) + ((bitplane1[bitplane_index] >> 2) & 0x02) + ((bitplane0[bitplane_index] >> 3) & 0x01); if (pixels == ++pixel_index) break; /* bit2 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] << 1) & 0x08) + ((bitplane2[bitplane_index]) & 0x04) + ((bitplane1[bitplane_index] >> 1) & 0x02) + ((bitplane0[bitplane_index] >> 2) & 0x01); if (pixels == ++pixel_index) break; /* bit1 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] << 2) & 0x08) + ((bitplane2[bitplane_index] << 1) & 0x04) + ((bitplane1[bitplane_index]) & 0x02) + ((bitplane0[bitplane_index] >> 1) & 0x01); if (pixels == ++pixel_index) break; /* bit0 byte */ bitmap[pixel_index] = ((bitplane3[bitplane_index] << 3) & 0x08) + ((bitplane2[bitplane_index] << 2) & 0x04) + ((bitplane1[bitplane_index] << 1) & 0x02) + ((bitplane0[bitplane_index]) & 0x01); if (pixels == ++pixel_index) break; } return pixel_index; } /* The ST image file has the following format. It consists of a 44 byte header followed by the image data. The header has the following format: Bytes 0-31: sixteen entry ST palette Bytes 32-33: padding Bytes 34-35: big-endian word holding number of bitplanes needed to make up a row of pixels* Bytes 36-37: padding Bytes 38-39: big-endian word holding number of rows in the image* Bytes 40-41: padding** Bytes 42-43: mask for pixels to show in last 16 pixel block. Again, this is big endian [*] these are probably big-endian unsigned longs but I have designated the upper two bytes as padding because (a) Level 9 does not need them as longs and (b) using unsigned shorts reduces byte sex induced byte order juggling. [**] not certain what this is for but I suspect that, like bytes 42-43 it is a mask to indicate which pixels to show, in this case in the first 16 pixel block The image data is essentially a memory dump of the video RAM representing the image in lo-res mode. In lo-res mode each row is 320 pixels wide and each pixel can be any one of sixteen colours - needs 4 bits to store. In the ST video memory (in lo-res mode which we are dealing with here) is organised as follows. The lowest point in memory in the frame buffer represents the top-left of the screen, the highest the bottom-right. Each row of pixels is stored in sequence. Within each pixel row the pixels are stored as follows. Each row is divided into groups of 16 pixels. Each sixteen pixel group is stored in 8 bytes, logically four groups of two. Each two byte pair is a bit-plane for that sixteen pixel group - that is it stores the same bit of each pixel in that group. The first two bytes store the lowest bit, the second pair the second bit &c. The word at bytes 34-35 of the header stores the number of bitplanes that make up each pixel row in the image. Multplying this number by four gives the number of pixels in the row***. For title and frame images that will be 320, for sub-images it will be less. [***] Not always exactly. For GnomeRanger sub-images this value is 60 - implying there are 240 pixels per row. In fact there are only 225 pixels in each row. To identify this situation look at the big-endian word in bytes 42-43 of the header. This is a mask telling you the pixels to use. Each bit represents one pixel in the block, with the MSBit representing the first pixel and the LSbit the last. In this situation, the file does contain the entire sixteen pixel block (it has to with the bitplane arrangement) but the pixels which are not part of the image are just noise. When decoding the image, the L9BITMAP produced has the actual pixel dimensions - the surplus pixels are discarded. I suspect, though I have not found an instance, that in theory the same situation could apply at the start of a pixel row and that in this case the big-endian word at bytes 40-41 is the mask. Having obtained the pixel dimensions of the image the function uses them to allocate memory for the bitmap and then extracts the pixel information from the bitmap row by row. For each row eight byte blocks are read from the image data and passed to UnpackSTv1Pixels along with the number of pixels to extract (usually 16, possibly less for the last block in a row.) */ L9BOOL bitmap_st1_decode(char* file, int x, int y) { L9BYTE* data = NULL; int i, xi, yi, max_x, max_y, last_block; int bitplanes_row, bitmaps_row, pixel_count, get_pixels; L9UINT32 size; data = bitmap_load(file,&size); if (data == NULL) return FALSE; bitplanes_row = data[35]+data[34]*256; bitmaps_row = bitplanes_row/4; max_x = bitplanes_row*4; max_y = data[39]+data[38]*256; last_block = data[43]+data[42]*256; /* Check if sub-image with rows shorter than max_x */ if (last_block != 0xFFFF) { /* use last_block to adjust max_x */ int i = 0; while ((0x0001 & last_block) == 0) /* test for ls bit set */ { last_block >>= 1; /* if not, shift right one bit */ i++; } max_x = max_x - i; } if (max_x > MAX_BITMAP_WIDTH || max_y > MAX_BITMAP_HEIGHT) { free(data); return FALSE; } if ((x == 0) && (y == 0)) { if (bitmap) free(bitmap); bitmap = bitmap_alloc(max_x,max_y); } if (bitmap == NULL) { free(data); return FALSE; } if (x+max_x > bitmap->width) max_x = bitmap->width-x; if (y+max_y > bitmap->height) max_y = bitmap->height-y; for (yi = 0; yi < max_y; yi++) { pixel_count = 0; for (xi = 0; xi < bitmaps_row; xi++) { if ((max_x - pixel_count) < 16) get_pixels = max_x - pixel_count; else get_pixels = 16; pixel_count += bitmap_st1_decode_pixels( bitmap->bitmap+((y+yi)*bitmap->width)+x+(xi*16), data+44+(yi*bitplanes_row*2)+(xi*8),8,get_pixels); } } bitmap->npalette = 16; for (i = 0; i < 16; i++) bitmap->palette[i] = bitmap_pcst_colour(data[(i*2)],data[1+(i*2)]); free(data); return TRUE; } void bitmap_st2_name(int num, char* dir, char* out) { /* title picture is #30 */ if (num == 0) num = 30; sprintf(out,"%s%d.squ",dir,num); } /* PC Bitmaps On the PC different graphics file formats were used for the early V4 games (Knight Orc, Gnome Ranger) and the later V4 games (Lancelot, Ingrid's Back, Time & Magik and Scapeghost). The ST and the PC both use the same image file format for the later V4 games (Lancelot, Ingrid's Back, Time & Magik and Scapeghost.) */ void bitmap_pc_name(int num, char* dir, char* out) { /* title picture is #30 */ if (num == 0) num = 30; sprintf(out,"%s%d.pic",dir,num); } /* The EGA standard for the IBM PCs and compatibles defines 64 colors, any 16 of which can be mapped to the usable palette at any given time. If you display these 64 colors in numerical order, 16 at a time, you get a hodgepodge of colors in no logical order. The 64 EGA color numbers are assigned in a way that the numbers can easily be converted to a relative intensity of each of the three phosphor colors R,G,B. If the number is converted to six bit binary, the most significant three bits represent the 25% level of R,G,B in that order and the least significant three bits represent the 75% level of R,G,B in that order. Take EGA color 53 for example. In binary, 53 is 110101. Since both R bits are on, R = 1.0. Of the G bits only the 25% bit is on so G = 0.25. Of the B bits only the 75% bit is on so B = 0.75. */ Colour bitmap_pc1_colour(int i) { Colour col; col.red = (((i&4)>>1) | ((i&0x20)>>5)) * 0x55; col.green = ((i&2) | ((i&0x10)>>4)) * 0x55; col.blue = (((i&1)<<1) | ((i&8)>>3)) * 0x55; return col; } /* The PC (v1) image file has the following format. It consists of a 22 byte header organised like this: Byte 0: probably a file type flag Byte 1: the MSB of the file's length as a word Bytes 2-3: little-endian word with picture width in pixels Bytes 4-5: little-endian word with picture height in pixel rows Bytes 6-21: the image colour table. One EGA colour in each byte The image data is extremely simple. The entire block is packed array of 4-bit pixels - IE each byte holds two pixels - the first in the high nybble, the second in the low. The pixel value is an index into the image colour table. The pixels are organised with the top left first and bottom left last, each row in turn. */ L9BOOL bitmap_pc1_decode(char* file, int x, int y) { L9BYTE* data = NULL; int i, xi, yi, max_x, max_y; L9UINT32 size; data = bitmap_load(file,&size); if (data == NULL) return FALSE; max_x = data[2]+data[3]*256; max_y = data[4]+data[5]*256; if (max_x > MAX_BITMAP_WIDTH || max_y > MAX_BITMAP_HEIGHT) { free(data); return FALSE; } if ((x == 0) && (y == 0)) { if (bitmap) free(bitmap); bitmap = bitmap_alloc(max_x,max_y); } if (bitmap == NULL) { free(data); return FALSE; } if (x+max_x > bitmap->width) max_x = bitmap->width-x; if (y+max_y > bitmap->height) max_y = bitmap->height-y; for (yi = 0; yi < max_y; yi++) { for (xi = 0; xi < max_x; xi++) { bitmap->bitmap[(bitmap->width*(y+yi))+(x+xi)] = (data[23+((yi*max_x)/2)+(xi/2)]>>((1-(xi&1))*4)) & 0x0f; } } bitmap->npalette = 16; for (i = 0; i < 16; i++) bitmap->palette[i] = bitmap_pc1_colour(data[6+i]); free(data); return TRUE; } /* The PC (v2) image file has the following format. It consists of a 44 byte header followed by the image data. The header has the following format: Bytes 0-1: "datalen": length of file -1 as a big-endian word* Bytes 2-3: "flagbyte1 & flagbyte2": unknown, possibly type identifiers. Usually 0xFF or 0xFE followed by 0x84, 0x72, 0xFF, 0xFE or some other (of a fairly small range of possibles) byte. Bytes 4-35: "colour_index[]": sixteen entry palette. Basically an ST palette (even if in a PC image file. Each entry is a sixteen bit value in which the low three nybbles hold the rgb colour values. The lowest nybble holds the blue value, the second nybble the blue value and the third nybble the red value. (The high nybble is ignored). Within each nybble, only the low three bits are used IE the value can only be 0-7 not the full possible 0-15 and so the MSbit in each nybble is always 0.**, Bytes 36-37: "width": image width in pixels as a big-endian word Bytes 38-39: "numrows": image height in pixel rows as a big-endian word Byte 40: "seedByte": seed byte to start picture decoding. Byte 41: "padByte": unknown. Possibly padding to word align the next element? Bytes 42-297: "pixelTable": an array of 0x100 bytes used as a lookup table for pixel values Bytes 298-313: "bitStripTable": an array of 0x10 bytes used as a lookup table for the number of bytes to strip from the bit stream for the pixel being decoded Bytes 314-569: "indexByteTable": an array of 0x100 bytes used as a lookup table to index into bitStripTable and pixelTable**** The encoded image data then follows ending in a 0x00 at the file length stored in the first two bytes of the file. there is then one extra byte holding a