The pic (background picture) resource format used in SCI0 is rather complex in comparison to the other graphical resource formats. It is best described as a sequence of drawing operations on a set of four 320x200 canvases, three of which are later used in the game (visual, priority, and control), and one of which is used during the drawing process for auxiliary purposes[1]
In order to describe the process, we will first need to define a set of operations we base them on:
FUNCTION peek_input(): Byte; /* returns the byte pointed to by the input pointer */
FUNCTION get_input(): Byte; /* works like peek_input(), but also increminates the
** input pointer */
FUNCTION skip_input(x): Byte; /* skips x input bytes */
Using these pre-defined functions, we will now define additional helper functions used for reading specifically encoded data tuples:
FUNCTION GetAbsCoordinates(): (Integer, Integer)
VAR
x, y, coordinate_prefix : Integer;
BEGIN
coordinate_prefix := get_input();
x := get_input();
y := get_input();
x |= (coordinate_prefix & 0xf0) << 4;
y |= (coordinate_prefix & 0x0f) << 8;
RETURN (x,y)
END
FUNCTION GetRelCoordinates(x : Integer, y: Integer): (Integer, Integer)
VAR
input : Integer;
BEGIN
input := get_input();
IF (input & 0x80) THEN
x -= (input >> 4);
ELSE
x += (input >> 4);
FI
IF (input & 0x08) THEN
y -= (input & 0x7);
ELSE
y += (input & 0x7);
FI
RETURN (x,y)
END
We also need some data types based on EGACOLOR and PRIORITY, which can be thought of as integers:
TYPE Palette = ARRAY[0..39] of EGACOLOR[0..1]
TYPE Priority_Table = ARRAY[0..39] of PRIORITY
Palette default_palette =
<(0,0), (1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7),
(8,8), (9,9), (a,a), (b,b), (c,c), (d,d), (e,e), (8,8),
(8,8), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (8,8),
(8,8), (f,9), (f,a), (f,b), (f,c), (f,d), (f,e), (f,f),
(0,8), (9,1), (2,a), (3,b), (4,c), (5,d), (6,e), (8,8)>;
#define DRAW_ENABLE_VISUAL 1
#define DRAW_ENABLE_PRIORITY 2
#define DRAW_ENABLE_CONTROL 4
#define PATTERN_FLAG_RECTANGLE 0x10
#define PATTERN_FLAG_USE_PATTERN 0x20
And now for the actual algorithm:
FUNCTION DrawPic (cumulative, fill_in_black : Boolean; default_palette: Integer; visual_map, priority_map, control_map, aux_map : Map): Map^4
VAR
palette : Array [0..3] of Palette;
drawenable, priority, col1, col2, pattern_nr, pattern_code : Integer;
BEGIN
palette := (default_palette × 4);
drawenable := DRAW_ENABLE_VISUAL | DRAW_ENABLE_PRIORITY
priority := 0;
col1 := col2 := 0;
pattern_nr := 0;
pattern_code := 0;
IF (!cumulative) THEN BEGIN
visual_map := (0xf × 320 × 200);
map control := map priority := map aux := (0 × 320 × 200);
END
FOREVER DO BEGIN
opcode := get_input();
COND opcode:
0xf0 → /* PIC_OP_SET_COLOR */
code := get_input();
(col1, col2) := palette[default_palette + (code / 40)][code % 40];
drawenable |= DRAW_ENABLE_VISUAL;
0xf1 → /* PIC_OP_DISABLE_VISUAL */
drawenable &= ~DRAW_ENABLE_VISUAL;
0xf2 → /* PIC_OP_SET_PRIORITY */
code := get_input();
priority := code & 0xf;
drawenable |= DRAW_ENABLE_PRIORITY;
0xf3 → /* PIC_OP_DISABLE_PRIORITY */
drawenable &= ~DRAW_ENABLE_PRIORITY;
0xf4 → /* PIC_OP_RELATIVE_PATTERNS */
IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
pattern_nr := (get_input() >> 1) & 0x7f
FI
(x,y) := GetAbsCoordinates();
DrawPattern(x, y, col1, col2, priority, control, drawenable,
pattern_code & PATTERN_FLAG_USE_PATTERN,
pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
WHILE (peek_input() < 0xf0) DO BEGIN
IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
pattern_nr := (get_input() >> 1) & 0x7f
FI
(x,y) = GetRelCoordinates(x,y);
DrawPattern(x, y, col1, col2, priority, control, drawenable,
pattern_code & PATTERN_FLAG_USE_PATTERN,
pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
END
0xf5 → /* PIC_OP_RELATIVE_MEDIUM_LINES */
(oldx, oldy) := GetAbsCoordinates();
WHILE (peek_input() < 0xf0) DO BEGIN
temp := get_input();
IF (temp & 0x80) THEN
y := oldy - (temp & 0x7f)
ELSE
y := oldy + temp
FI
x = oldx + get_input();
DitherLine(oldx, oldy, x, y, col1, col2, priority, special, drawenable);
(oldx, oldy) := (x, y);
END
0xf6 → /* PIC_OP_RELATIVE_LONG_LINES */
(oldx, oldy) := GetAbsCoordinates()
WHILE (peek_input() < 0xf0) DO BEGIN
(x, y) := GetAbsCoordinates();
DitherLine(oldx, oldy, x, y, col1, col2, priority, special, drawenable);
(oldx, oldy) := (x, y);
END
0xf7 → /* PIC_OP_RELATIVE_SHORT_LINES */
(oldx, oldy) = GetAbsCoordinates()
WHILE (peek_input() < 0xf0) DO BEGIN
(x, y) := GetRelCoordinates(oldx, oldy);
DitherLine(oldx, oldy, x, y, col1, col2, priority, special, drawenable);
(oldx, oldy) := (x, y);
END
0xf8 → /* PIC_OP_FILL */
IF (fill_in_black) THEN
(oldc1, oldc2) := (c1, c2);
FI
WHILE (peek_unput() < 0xf0) DO BEGIN
(x, y) := GetAbsCoordinates();
DitherFill(x, y, col1, col2, priority, special, drawenable);
END
IF (fill_in_black) THEN
(c1, c2) := (oldc1, oldc2);
FI
0xf9 → /* PIC_OP_SET_PATTERN */
pattern_code := get_input() & 0x37;
pattern_size := pattern_code & 0x7;
0xfa → /* PIC_OP_ABSOLUTE_PATTERNS */
WHILE (peek_input() < 0xf0) DO
IF (pattern_code & PATTERN_FLAG_USE_PATTERN)
pattern_nr := (get_input() >> 1) & 0x7f
FI
(x, y) := GetAbsCoordinates();
DrawPattern(x, y, col1, col2, priority, control, drawenable,
pattern_code & PATTERN_FLAG_USE_PATTERN,
pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
END
0xfb → /* PIC_OP_SET_CONTROL */
control := get_input() & 0x0f;
drawenable |= DRAW_ENABLE_CONTROL;
0xfc → /* PIC_OP_DISABLE_CONTROL */
drawenable &= ~DRAW_ENABLE_CONTROL;
0xfd → /* PIC_OP_RELATIVE_MEDIUM_PATTERNS */
IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
pattern_nr := (get_input() >> 1) & 0x7f;
FI
(oldx, oldy) := GetAbsCoordinates();
DrawPattern(x, y, col1, col2, priority, control, drawenable,
pattern_code & PATTERN_FLAG_USE_PATTERN,
pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
WHILE (peek_input() < 0xf0) DO BEGIN
IF (pattern_code & PATTERN_FLAG_USE_PATTERN) THEN
pattern_nr := (get_input() >> 1) & 0x7f;
FI
temp := get_input();
IF (temp & 0x80)
y := oldy - (temp & 0x7f)
ELSE
y := oldy + temp
FI
x := oldx + get_input();
DrawPattern(x, y, col1, col2, priority, control, drawenable,
pattern_code & PATTERN_FLAG_USE_PATTERN,
pattern_size, pattern_nr, pattern_code & PATTERN_FLAG_RECTANGLE);
END
0xfd → /* PIC_OP_OPX */
COND get_input():
0x00 → /* PIC_OPX_SET_PALETTE_ENTRY */
WHILE peek_input() < 0xf0 DO BEGIN
index := get_input();
color := get_input();
palette[index / 40][color % 40] := color;
END
0x01 → /* PIC_OPX_SET_PALETTE */
palette_number := get_input();
FOR i := 0 TO 39 DO
palette[palette_number][i] := get_input();
OD
0x02 → /* PIC_OPX_MONO0 */
skip_input(41);
0x03 → /* PIC_OPX_MONO1 */
skip_input(1);
0x04 → /* PIC_OPX_MONO2 */
0x05 → /* PIC_OPX_MONO3 */
skip_input(1);
0x06 → /* PIC_OPX_MONO4 */
0x07 → /* PIC_OPX_EMBEDDED_VIEW */ /* SCI01 operation */
0x08 → /* PIC_OPX_SET_PRIORITY_TABLE */ /* SCI01 operation */
0xff → return (visual, control, priority, aux);
END OF COND
END
END
This algorithm uses three auxiliary algorithms, DrawPattern, DitherLine, and DitherFill, which are sketched below. All of these functions are supposed
to take the four maps as implicit parameters.
PROCEDURE DrawPattern(x, y, col1, col2, priority, control, drawenable : Integer; solid : Boolean ; pattern_size, pattern_nr : Integer; rectangle : Boolean)
Alters (x,y) so that 0 <= (x - pattern_size), 319 >= (x + pattern_size), 189 >= (y + pattern_size) and
0 <= (y - pattern_size), then draws a rectangle or a circle filled with col1, col2, priority, control,
as determined by drawenable.
If rectangle is not set, it will draw a rectangle, otherwise a circle of size pattern_size.
pattern_nr is used to specify the start index in the random bit table (256 random bits)
PROCEDURE DitherLine(x, y, xend, yend, color1, color2, priority, control, drawenable : Integer)
Draws a dithered line between (x, y+10) and (xend, yend+10). If the appropriate drawenable flags
are set, it draws 'priority' to the priority map, 'control' to the control map, and 'color1' and 'color2'
(alternating) to the visual map. The auxiliary map is bitwise-or'd with the drawenable flag while this is
done.
PROCEDURE DitherFill(x, y, col0, col1, priority, control, drawenable : Integer)
Fills all layers for which drawenable is set with the appropriate content.
Diagonal filling is not allowed.
Boundaries are determined as follows:
x<0, x>319, y<10, y>199 are hard boundaries. We now determine the
'boundary map' bound_map and the allowed color legal_color.
If bound_map[coordinates] = legal_color, then the pixel may be filled.
IF (drawenable & DRAW_ENABLE_VISUAL)
bound_map = visual;
legal_color = 0xf;
ELSIF (drawenable & DRAW_ENABLE_PRIORITY)
bound_map = priority;
legal_color = 0;
ELSIF (drawenable & DRAW_ENABLE_CONTROL)
bound_map = control;
legal_color = 0;
ELSE
return;
FI
[1] Due to the vector graphics nature of these drawing operations, they are inherently more scaleable than pixmaps.
Top
You can help keep The Sierra Help Pages and its affiliates alive by helping to defray some of the costs of hosting this site. If it has been of help to you, please consider contributing to help keep it online.Thank you.
The Sierra Help Pages | Sierra Game Help | Walkthroughs | Hints, Tips & Spoilers | Utilities | Links | SHP Forums | Search
© 2013 to present The Sierra Help Pages. All rights reserved. All Sierra games, artwork and music © Sierra.