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) ENDWe 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 0x20And 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 ENDThis 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.