1 /*
2 	computer_interface.c
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 	Monday, May 8, 1995 7:18:55 PM- rdm created.
22 
23 Feb 4, 2000 (Loren Petrich):
24 	Changed halt() to assert(false) for better debugging
25 
26 Feb 16, 2000 (Loren Petrich):
27 	Set up for more graceful degradation when
28 	there is no terminal data in a map;
29 	also had get_indexed_terminal_data() return NULL
30 	if terminal data could not be found.
31 
32 Feb 18, 2000 (Loren Petrich):
33 	Suppressed all the dprintf warnings, such as one for a picture being
34 	too large for a terminal.
35 
36 Feb 19, 2000 (Loren Petrich):
37 	Had a frustrating time getting get_date_string() to work properly;
38 	SecondsToDate() seems to be broken.
39 
40 Feb 21, 2000 (Loren Petrich):
41 	Corrected a troublesome assert in find_checkpoint_location()
42 	Moved drawing of borders to after drawing of other stuff,
43 	to fix checkpoint-overdraw bug.
44 
45 Feb 26, 2000 (Loren Petrich):
46 	Fixed level-0 teleportation bug; the hack is to move the destination
47 	down by 1.
48 
49 	Set font in PICT-error-message code to "systemFont".
50 
51 Mar 5, 2000 (Loren Petrich):
52 	Improved handling of clipping in _render_computer_interface();
53 	it is trimmed to the terminal window when rendering there, and restored
54 	to the screen's full size when leaving.
55 
56 May 11, 2000 (Loren Petrich):
57 	Suppressed vhalts here; these were for unrecognized terminal states.
58 	Changed them into vwarns.
59 
60 May 20, 2000 (Loren Petrich):
61 	Put in more graceful degradation for
62 	get_indexed_grouping()
63 	get_indexed_font_changes()
64 	These will return NULL for an invalid index
65 
66 July 5, 2000 (Loren Petrich):
67 	Using world_pixels instead of screen_window
68 
69 Aug 10, 2000 (Loren Petrich):
70 	Added Chris Pruett's Pfhortran changes
71 
72 Aug 22, 2000 (Loren Petrich):
73 	Added object-oriented resource handling
74 
75 Sep 24, 2000 (Loren Petrich):
76 	Banished OverallBounds as unnecessary; world_pixels->portRect does fine here
77 
78 Jun 23, 2001 (Loren Petrich):
79 	Suppressed some of the asserts in the code; tried to make degradation more graceful
80 
81 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
82 	Added accessors for datafields now opaque in Carbon
83 */
84 
85 // add logon/logoff keywords. (& make terminal display them)
86 // cameras
87 // static
88 // delayed teleport
89 
90 // activate tags
91 // picture with scrolling text.
92 // checkpoint with scrolling text
93 // don't use charwidth
94 
95 #include <stdlib.h>
96 #include <stdio.h>
97 #include <string.h>
98 #include <time.h>
99 #include <limits.h>
100 
101 #include <vector>
102 
103 #include "cseries.h"
104 #include "FileHandler.h"
105 
106 #include "world.h"
107 #include "map.h"
108 #include "player.h"
109 #include "computer_interface.h"
110 #include "screen_drawing.h"
111 #include "overhead_map.h"
112 #include "SoundManager.h"
113 #include "interface.h" // for the error strings.
114 #include "shell.h"
115 #include "platforms.h" // for tagged platforms
116 #include "lightsource.h" // for tagged lightsources
117 #include "screen.h"
118 
119 #include "images.h"
120 #include "Packing.h"
121 
122 // MH: Lua scripting
123 #include "lua_script.h"
124 
125 #include "Logging.h"
126 
127 #include <boost/algorithm/string/predicate.hpp>
128 #include <boost/iostreams/device/array.hpp>
129 #include <boost/iostreams/stream_buffer.hpp>
130 
131 #include <sstream>
132 
133 namespace algo = boost::algorithm;
134 namespace io = boost::iostreams;
135 
136 #define LABEL_INSET 3
137 #define LOG_DURATION_BEFORE_TIMEOUT (2*TICKS_PER_SECOND)
138 #define BORDER_HEIGHT 18
139 #define BORDER_INSET 9
140 #define FUDGE_FACTOR 1
141 
142 #define MAC_LINE_END 13
143 
144 enum {
145 	_reading_terminal,
146 	_no_terminal_state,
147 	NUMBER_OF_TERMINAL_STATES
148 };
149 
150 enum {
151 	_terminal_is_dirty= 0x01
152 };
153 
154 enum {
155 	_any_abort_key_mask= _action_trigger_state,
156 	_terminal_up_arrow= _moving_forward,
157 	_terminal_down_arrow= _moving_backward,
158 	_terminal_page_down= _turning_right,
159 	_terminal_page_up= _turning_left,
160 	_terminal_next_state= _left_trigger_state
161 };
162 
163 #define strCOMPUTER_LABELS 135
164 enum
165 {
166 	_marathon_name,
167 	_computer_starting_up,
168 	_computer_manufacturer,
169 	_computer_address,
170 	_computer_terminal,
171 	_scrolling_message,
172 	_acknowledgement_message,
173 	_disconnecting_message,
174 	_connection_terminated_message,
175 	_date_format
176 };
177 
178 #define TERMINAL_IS_DIRTY(term) ((term)->flags & _terminal_is_dirty)
179 #define SET_TERMINAL_IS_DIRTY(term, v) ((void)((v)? ((term)->flags |= _terminal_is_dirty) : ((term)->flags &= ~_terminal_is_dirty)))
180 
181 /* Maximum face changes per text grouping.. */
182 #define MAXIMUM_FACE_CHANGES_PER_TEXT_GROUPING (128)
183 
184 enum {
185 	_text_is_encoded_flag= 0x0001
186 };
187 
188 enum {
189 	_logon_group,
190 	_unfinished_group,
191 	_success_group,
192 	_failure_group,
193 	_information_group,
194 	_end_group,
195 	_interlevel_teleport_group, // permutation is level to go to
196 	_intralevel_teleport_group, // permutation is polygon to go to.
197 	_checkpoint_group, // permutation is the goal to show
198 	_sound_group, // permutation is the sound id to play
199 	_movie_group, // permutation is the movie id to play
200 	_track_group, // permutation is the track to play
201 	_pict_group, // permutation is the pict to display
202 	_logoff_group,
203 	_camera_group, //  permutation is the object index
204 	_static_group, // permutation is the duration of static.
205 	_tag_group, // permutation is the tag to activate
206 
207 	NUMBER_OF_GROUP_TYPES
208 };
209 
210 enum // flags to indicate text styles for paragraphs
211 {
212 	_plain_text      = 0x00,
213 	_bold_text       = 0x01,
214 	_italic_text     = 0x02,
215 	_underline_text  = 0x04
216 };
217 
218 enum { /* terminal grouping flags */
219 	_draw_object_on_right= 0x01,  // for drawing checkpoints, picts, movies.
220 	_center_object= 0x02,
221 	_group_is_marathon_1 = 0x100
222 };
223 
224 struct terminal_groupings {
225 	int16 flags; /* varies.. */
226 	int16 type; /* _information_text, _checkpoint_text, _briefing_text, _movie, _sound_bite, _soundtrack */
227 	int16 permutation; /* checkpoint id for chkpt, level id for _briefing, movie id for movie, sound id for sound, soundtrack id for soundtrack */
228 	int16 start_index;
229 	int16 length;
230 	int16 maximum_line_count;
231 };
232 const int SIZEOF_terminal_groupings = 12;
233 
234 struct text_face_data {
235 	int16 index;
236 	int16 face;
237 	int16 color;
238 };
239 const int SIZEOF_text_face_data = 6;
240 
241 // This is externally visible, so its external size is defined in the header file
242 struct player_terminal_data
243 {
244 	int16 flags;
245 	int16 phase;
246 	int16 state;
247 	int16 current_group;
248 	int16 level_completion_state;
249 	int16 current_line;
250 	int16 maximum_line;
251 	int16 terminal_id;
252 	int32 last_action_flag;
253 };
254 
255 struct terminal_key {
256 	int16 keycode;
257 	int16 offset;
258 	int16 mask;
259 	int32 action_flag;
260 };
261 
262 struct font_dimensions {
263 	int16 lines_per_screen;
264 	int16 character_width;
265 };
266 
267 /* Terminal data loaded from map (maintained by computer_interface.cpp) */
268 struct terminal_text_t {	// Object describing one terminal
terminal_text_tterminal_text_t269 	terminal_text_t() {}
270 	uint16 flags = 0;
271 	int16 lines_per_page = 0;
272 	vector<terminal_groupings> groupings;
273 	vector<text_face_data> font_changes;
274 	vector<uint8> text;
275 };
276 
277 static vector<terminal_text_t> map_terminal_text;
278 
279 // ghs: for Lua
number_of_terminal_texts()280 short number_of_terminal_texts() { return map_terminal_text.size(); }
281 
282 /* internal global structure */
283 static struct player_terminal_data *player_terminals;
284 
285 #define NUMBER_OF_TERMINAL_KEYS (sizeof(terminal_keys)/sizeof(struct terminal_key))
286 
287 // Get the interface font to use from screen_drawing_<platform>.cpp
288 extern TextSpec *_get_font_spec(short font_index);
289 extern font_info *GetInterfaceFont(short font_index);
290 extern uint16 GetInterfaceStyle(short font_index);
291 
292 /* ------------ private prototypes */
293 static player_terminal_data *get_player_terminal_data(
294 	short player_index);
295 
296 static void draw_logon_text(terminal_text_t *terminal_text,
297 	short current_group_index, short logon_shape_id);
298 static void draw_computer_text(Rect *bounds,
299 	terminal_text_t *terminal_text, short current_group_index, short current_line);
300 static void _draw_computer_text(char *base_text, short start_index, Rect *bounds,
301 	terminal_text_t *terminal_text, short current_line);
302 static short find_group_type(terminal_text_t *data,
303 	short group_type);
304 static void teleport_to_level(short level_number);
305 static void teleport_to_polygon(short player_index, short polygon_index);
306 static struct terminal_groupings *get_indexed_grouping(
307 	terminal_text_t *data, short index);
308 static struct text_face_data *get_indexed_font_changes(
309 	terminal_text_t *data, short index);
310 static char *get_text_base(terminal_text_t *data);
311 // LP change: added a flag to indicate whether stuff after the other
312 // terminal stuff is to be drawn; if not, then draw the stuff before the
313 // other terminal stuff.
314 static void draw_terminal_borders(struct player_terminal_data *terminal_data,
315 	bool after_other_terminal_stuff);
316 static void next_terminal_state(short player_index);
317 static void next_terminal_group(short player_index, terminal_text_t *terminal_text);
318 static void get_date_string(char *date_string, short flags);
319 static void present_checkpoint_text(terminal_text_t *terminal_text, short current_group_index,
320 	short current_line);
321 static bool find_checkpoint_location(short checkpoint_index, world_point2d *location,
322 	short *polygon_index);
323 static void	set_text_face(struct text_face_data *text_face);
324 static void draw_line(char *base_text, short start_index, short end_index, Rect *bounds,
325 	terminal_text_t *terminal_text, short *text_face_start_index,
326 	short line_number);
327 static bool calculate_line(char *base_text, short width, short start_index,
328 	short text_end_index, short *end_index);
329 static void handle_reading_terminal_keys(short player_index, int32 action_flags);
330 static void calculate_bounds_for_object(short flags, Rect *bounds, Rect *source);
331 static void display_picture(short picture_id, Rect *frame, short flags);
332 static void display_shape(short shape, Rect* frame);
333 static void display_picture_with_text(struct player_terminal_data *terminal_data,
334 	Rect *bounds, terminal_text_t *terminal_text, short current_lien);
335 static short count_total_lines(char *base_text, short width, short start_index, short end_index);
336 static void calculate_bounds_for_text_box(short flags, Rect *bounds);
337 static void goto_terminal_group(short player_index, terminal_text_t *terminal_text,
338 	short new_group_index);
339 static bool previous_terminal_group(short player_index, terminal_text_t *terminal_text);
340 static void fill_terminal_with_static(Rect *bounds);
341 static short calculate_lines_per_page(void);
342 static Rect get_term_rectangle(short index);
343 
344 static terminal_text_t *get_indexed_terminal_data(short id);
345 static void encode_text(terminal_text_t *terminal_text);
346 static void decode_text(terminal_text_t *terminal_text);
347 
348 #include "sdl_fonts.h"
349 #include "joystick.h" // for AO_SCANCODE_BASE_JOYSTICK_BUTTON
350 
351 
352 // Global variables
353 // static const sdl_font_info *terminal_font = NULL;
354 static uint32 current_pixel;				// Current color pixel value
355 static uint16 current_style = styleNormal;	// Current style flags
356 
357 // From screen_sdl.cpp
358 extern SDL_Surface *world_pixels;
359 
360 
361 // Terminal key definitions
362 static struct terminal_key terminal_keys[]= {
363 	{SDL_SCANCODE_UP, 0, 0, _terminal_page_up},				// arrow up
364 	{SDL_SCANCODE_DOWN, 0, 0, _terminal_page_down},			// arrow down
365 	{SDL_SCANCODE_PAGEUP, 0, 0, _terminal_page_up},			// page up
366 	{SDL_SCANCODE_PAGEDOWN, 0, 0, _terminal_page_down},		// page down
367 	{SDL_SCANCODE_TAB, 0, 0, _terminal_next_state},			// tab
368 	{SDL_SCANCODE_KP_ENTER, 0, 0, _terminal_next_state},	// enter
369 	{SDL_SCANCODE_RETURN, 0, 0, _terminal_next_state},		// return
370 	{SDL_SCANCODE_SPACE, 0, 0, _terminal_next_state},		// space
371 	{SDL_SCANCODE_ESCAPE, 0, 0, _any_abort_key_mask},		// escape
372 	{AO_SCANCODE_JOYSTICK_ESCAPE, 0, 0, _any_abort_key_mask},
373 	{AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_DPAD_UP, 0, 0, _terminal_page_up},
374 	{AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_DPAD_DOWN, 0, 0, _terminal_page_down},
375 	{AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_A, 0, 0, _terminal_next_state},
376 	{AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_X, 0, 0, _terminal_next_state},
377 	{AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_Y, 0, 0, _terminal_next_state},
378 	{AO_SCANCODE_BASE_JOYSTICK_BUTTON + SDL_CONTROLLER_BUTTON_B, 0, 0, _any_abort_key_mask}
379 };
380 
381 
382 // Emulation of MacOS functions
InsetRect(Rect * r,int dx,int dy)383 static void InsetRect(Rect *r, int dx, int dy)
384 {
385 	r->top += dy;
386 	r->left += dx;
387 	r->bottom -= dy;
388 	r->right -= dx;
389 }
390 
OffsetRect(Rect * r,int dx,int dy)391 static void OffsetRect(Rect *r, int dx, int dy)
392 {
393 	r->top += dy;
394 	r->left += dx;
395 	r->bottom += dy;
396 	r->right += dx;
397 }
398 
399 extern SDL_Surface *draw_surface;
400 
set_text_face(struct text_face_data * text_face)401 static void	set_text_face(struct text_face_data *text_face)
402 {
403 	current_style = styleNormal;
404 
405 	// Set style
406 	if (text_face->face & _bold_text)
407 		current_style |= styleBold;
408 	if (text_face->face & _italic_text)
409 		current_style |= styleItalic;
410 	if (text_face->face & _underline_text)
411 		current_style |= styleUnderline;
412 
413 	// Set color
414 	SDL_Color color;
415 	_get_interface_color(text_face->color + _computer_interface_text_color, &color);
416 	current_pixel = SDL_MapRGB(/*world_pixels*/draw_surface->format, color.r, color.g, color.b);
417 }
418 
419 
calculate_line(char * base_text,short width,short start_index,short text_end_index,short * end_index)420 static bool calculate_line(char *base_text, short width, short start_index, short text_end_index, short *end_index)
421 {
422 	bool done = false;
423 
424 	if (base_text[start_index]) {
425 		int index = start_index, running_width = 0;
426 
427 		// terminal_font no longer a global, since it may change
428 		font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
429 
430 		while (running_width < width && base_text[index] && base_text[index] != MAC_LINE_END) {
431 			running_width += char_width(base_text[index], terminal_font, current_style);
432 			index++;
433 		}
434 
435 		// Now go backwards, looking for whitespace to split on
436 		if (base_text[index] == MAC_LINE_END)
437 			index++;
438 		else if (base_text[index]) {
439 			int break_point = index;
440 
441 			while (break_point>start_index) {
442 				if (base_text[break_point] == ' ')
443 					break; 	// Non printing
444 				break_point--;	// this needs to be in front of the test
445 			}
446 
447 			if (break_point != start_index)
448 				index = break_point+1;	// Space at the end of the line
449 		}
450 
451 		*end_index= index;
452 	} else
453 		done = true;
454 
455 	return done;
456 }
457 
458 /* ------------ code begins */
459 
get_player_terminal_data(short player_index)460 player_terminal_data *get_player_terminal_data(
461 	short player_index)
462 {
463 	struct player_terminal_data *player_terminal = GetMemberWithBounds(player_terminals,player_index,MAXIMUM_NUMBER_OF_PLAYERS);
464 
465 	vassert(player_terminal, csprintf(temporary, "player index #%d is out of range", player_index));
466 
467 	return player_terminal;
468 }
469 
initialize_terminal_manager(void)470 void initialize_terminal_manager(
471 	void)
472 {
473 	player_terminals= new player_terminal_data[MAXIMUM_NUMBER_OF_PLAYERS];
474 	assert(player_terminals);
475 	objlist_clear(player_terminals, MAXIMUM_NUMBER_OF_PLAYERS);
476 
477 /*
478 	terminal_font = load_font(*_get_font_spec(_computer_interface_font));
479 */
480 }
481 
initialize_player_terminal_info(short player_index)482 void initialize_player_terminal_info(
483 	short player_index)
484 {
485 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
486 
487 	//CP Addition: trap for logout!
488 	if (terminal->state != _no_terminal_state)
489         {
490                 L_Call_Terminal_Exit(terminal->terminal_id, player_index);
491         }
492 
493 	terminal->flags= 0;
494 	terminal->phase = NONE; // not using a control panel.
495 	terminal->state= _no_terminal_state; // And there is no line..
496 	terminal->current_group= NONE;
497 	terminal->level_completion_state= 0;
498 	terminal->current_line= 0;
499 	terminal->maximum_line= 0;
500 	terminal->terminal_id= 0;
501 	terminal->last_action_flag= -1l; /* Eat the first key */
502 }
503 
enter_computer_interface(short player_index,short text_number,short completion_flag)504 void enter_computer_interface(
505 	short player_index,
506 	short text_number,
507 	short completion_flag)
508 {
509 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
510 	struct player_data *player= get_player_data(player_index);
511 
512 	// LP addition: if there is no terminal-data chunk, then just make the logon sound and quit
513 /*	if (map_terminal_text.size() == 0)
514 	{
515 		play_object_sound(player->object_index, Sound_TerminalLogon());
516 		return;
517 	}
518 */
519 	struct terminal_text_t *terminal_text = get_indexed_terminal_data(text_number);
520 	if (terminal_text == NULL)
521 	{
522 		play_object_sound(player->object_index, Sound_TerminalLogon());
523 		return;
524 	}
525 
526 	if(dynamic_world->player_count==1)
527 	{
528 		short lines_per_page;
529 
530 		/* Reset the lines per page to the actual value for whatever fucked up font that they have */
531 		lines_per_page= calculate_lines_per_page();
532 		if(lines_per_page != terminal_text->lines_per_page)
533 		{
534 // dprintf("You have one confused font.");
535 			terminal_text->lines_per_page= lines_per_page;
536 		}
537 	}
538 
539 	/* Tell everyone that this player is in the computer interface.. */
540 	terminal->state= _reading_terminal;
541 	terminal->phase= NONE;
542 	terminal->current_group= NONE;
543 	terminal->level_completion_state= completion_flag;
544 	terminal->current_line= 0;
545 	terminal->maximum_line= 1; // any click or keypress will get us out.
546 	terminal->terminal_id= text_number;
547 	terminal->last_action_flag= -1l; /* Eat the first key */
548 
549 	/* And select the first one. */
550 	next_terminal_group(player_index, terminal_text);
551 }
552 
553 /*  Assumes �t==1 tick */
update_player_for_terminal_mode(short player_index)554 void update_player_for_terminal_mode(
555 	short player_index)
556 {
557 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
558 	if (!terminal) return;
559 
560 	if(terminal->state != _no_terminal_state)
561 	{
562 		/* Phase is a timer for logging in and out only.. */
563 		switch(terminal->state)
564 		{
565 			case _reading_terminal:
566 				if(terminal->phase != NONE)
567 				{
568 					if(--terminal->phase<=0)
569 					{
570 						terminal_text_t *terminal_text = get_indexed_terminal_data(terminal->terminal_id);
571 						if (terminal_text == NULL)
572 							break;
573 						next_terminal_group(player_index, terminal_text);
574 					}
575 				}
576 				break;
577 
578 			default:
579 				break;
580 		}
581 	}
582 }
583 
update_player_keys_for_terminal(short player_index,uint32 action_flags)584 void update_player_keys_for_terminal(
585 	short player_index,
586 	uint32 action_flags)
587 {
588 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
589 	if (!terminal) return;
590 
591 	switch(terminal->state)
592 	{
593 		case _reading_terminal:
594 			handle_reading_terminal_keys(player_index, action_flags);
595 			break;
596 
597 		case _no_terminal_state:
598 		default:
599 			break;
600 	}
601 }
602 
player_in_terminal_mode(short player_index)603 bool player_in_terminal_mode(
604 	short player_index)
605 {
606 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
607 	bool in_terminal_mode;
608 
609 	if(terminal->state==_no_terminal_state)
610 	{
611 		in_terminal_mode= false;
612 	} else {
613 		in_terminal_mode= true;
614 	}
615 
616 	return in_terminal_mode;
617 }
618 
_render_computer_interface(void)619 void _render_computer_interface(void)
620 {
621 	struct player_terminal_data *terminal_data= get_player_terminal_data(current_player_index);
622 
623 	if (terminal_data->state == _no_terminal_state) return;
624 	// assert(terminal_data->state != _no_terminal_state);
625 	if(TERMINAL_IS_DIRTY(terminal_data))
626 	{
627 		SET_TERMINAL_IS_DIRTY(terminal_data, false);
628 
629 		terminal_text_t *terminal_text;
630 		struct terminal_groupings *current_group;
631 		Rect bounds;
632 
633 		/* Get the terminal text.. */
634 		terminal_text = get_indexed_terminal_data(terminal_data->terminal_id);
635 		// LP addition: quit if none
636 		if (terminal_text == NULL) return;
637 
638 		switch(terminal_data->state)
639 		{
640 			case _reading_terminal:
641 				/* Initialize it if it hasn't been done.. */
642 				current_group= get_indexed_grouping(terminal_text, terminal_data->current_group);
643 				// LP change: just in case...
644 				if (!current_group) return;
645 
646 				/* Draw the borders! */
647 				// LP change: draw these after, so as to avoid overdraw bug
648 				draw_terminal_borders(terminal_data, false);
649 				switch(current_group->type)
650 				{
651 					case _logon_group:
652 					case _logoff_group:
653 						draw_logon_text(terminal_text, terminal_data->current_group,
654 							current_group->permutation);
655 						break;
656 
657 					case _unfinished_group:
658 					case _success_group:
659 					case _failure_group:
660 						// dprintf("You shouldn't try to render this view.;g");
661 						break;
662 
663 					case _information_group:
664 						/* Draw as normal... */
665                         bounds= get_term_rectangle(_terminal_full_text_rect);
666 						draw_computer_text(&bounds, terminal_text, terminal_data->current_group,
667 							terminal_data->current_line);
668 						break;
669 
670 					case _checkpoint_group: // permutation is the goal to show
671 						/* note that checkpoints can only be equal to one screenful.. */
672 						present_checkpoint_text(terminal_text, terminal_data->current_group,
673 							terminal_data->current_line);
674 						break;
675 
676 					case _end_group:
677 					case _interlevel_teleport_group: // permutation is level to go to
678 					case _intralevel_teleport_group: // permutation is polygon to go to.
679 					case _sound_group: // permutation is the sound id to play
680 					case _tag_group:
681 						// These are all handled elsewhere
682 						break;
683 
684 					case _movie_group:
685 					case _track_group:
686 						if(!game_is_networked)
687 						{
688 							// dprintf("Movies/Music Tracks not supported on playback (yet);g");
689 						} else {
690 							// dprintf("On networked games, should we display a PICT here?;g");
691 						}
692 						break;
693 
694 					case _pict_group:
695 						display_picture_with_text(terminal_data, &bounds, terminal_text,
696 							terminal_data->current_line);
697 						break;
698 
699 					case _static_group:
700 						bounds = get_term_rectangle(_terminal_screen_rect);
701 						fill_terminal_with_static(&bounds);
702 						SET_TERMINAL_IS_DIRTY(terminal_data, true);
703 						break;
704 
705 					case _camera_group:
706 						break;
707 
708 					default:
709 						break;
710 				}
711 				// Moved down here, so they'd overdraw the other stuff
712 				draw_terminal_borders(terminal_data, true);
713 				break;
714 
715 			default:
716 				break;
717 		}
718 
719 		RequestDrawingTerm();
720 	}
721 }
722 
723 /* Only care about local_player_index */
build_terminal_action_flags(char * keymap)724 uint32 build_terminal_action_flags(
725 	char *keymap)
726 {
727 	uint32 flags, raw_flags;
728 	struct terminal_key *key= terminal_keys;
729 	struct player_terminal_data *terminal= get_player_terminal_data(local_player_index);
730 
731 	raw_flags= 0;
732 	for(unsigned index= 0; index<NUMBER_OF_TERMINAL_KEYS; ++index)
733 	{
734 		if (keymap[key->keycode]) raw_flags |= key->action_flag;
735 		key++;
736 	}
737 
738 	/* Only catch the key the first time. */
739 	flags= raw_flags^terminal->last_action_flag;
740 	flags&= raw_flags;
741 	terminal->last_action_flag= raw_flags;
742 
743 //dprintf("Flags: %x (Raw: %x Mask: %x);g", flags, raw_flags, terminal->last_action_flag);
744 	return flags;
745 }
746 
747 /* called from walk_player_list.. */
dirty_terminal_view(short player_index)748 void dirty_terminal_view(
749 	short player_index)
750 {
751 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
752 
753 	if(terminal->state != _no_terminal_state)
754 	{
755 		SET_TERMINAL_IS_DIRTY(terminal, true);
756 	}
757 }
758 
759 /* Take damage, abort.. */
abort_terminal_mode(short player_index)760 void abort_terminal_mode(
761 	short player_index)
762 {
763 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
764 
765 	if(terminal->state != _no_terminal_state)
766 	{
767 		terminal->state= _no_terminal_state;
768 		L_Call_Terminal_Exit(terminal->terminal_id, player_index);
769 	}
770 }
771 
772 static uint16_t MARATHON_LOGON_SHAPE = 44;
773 
774 /* --------- local code */
draw_logon_text(terminal_text_t * terminal_text,short current_group_index,short logon_shape_id)775 static void draw_logon_text(
776 	terminal_text_t *terminal_text,
777 	short current_group_index,
778 	short logon_shape_id)
779 {
780 	Rect picture_bounds= get_term_rectangle(_terminal_logon_graphic_rect);
781 	char *base_text= get_text_base(terminal_text);
782 	short width;
783 	struct terminal_groupings *current_group= get_indexed_grouping(terminal_text,
784 		current_group_index);
785 	// LP change: just in case...
786 	if (!current_group) return;
787 
788 	if (current_group->flags & _group_is_marathon_1)
789 	{
790 		display_shape(MARATHON_LOGON_SHAPE, &picture_bounds);
791 
792         // draw static title below logo
793         picture_bounds= get_term_rectangle(_terminal_logon_title_rect);
794         getcstr(temporary, strCOMPUTER_LABELS, _marathon_name);
795 		_draw_screen_text(temporary, (screen_rectangle *) &picture_bounds, _center_vertical | _center_horizontal, _computer_interface_title_font, _computer_interface_text_color);
796 
797         picture_bounds= get_term_rectangle(_terminal_logon_location_rect);
798 	}
799 	else
800 	{
801         Rect bounds= picture_bounds;
802 		display_picture(logon_shape_id, &picture_bounds,  _center_object);
803 
804         /* Use the picture bounds to create the logon text crap . */
805         picture_bounds.top= picture_bounds.bottom;
806         picture_bounds.bottom= bounds.bottom;
807         picture_bounds.left= bounds.left;
808         picture_bounds.right= bounds.right;
809     }
810 
811 	/* This is always just a line, so we can do this here.. */
812 	font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
813 	uint16 terminal_style = GetInterfaceStyle(_computer_interface_font);
814 	width = text_width(base_text + current_group->start_index, current_group->length, terminal_font, terminal_style);
815 	// width = text_width(base_text + current_group->start_index, current_group->length, terminal_font, _get_font_spec(_computer_interface_font)->style);
816 	picture_bounds.left += (RECTANGLE_WIDTH(&picture_bounds) - width) / 2;
817 
818 	_draw_computer_text(base_text, current_group_index, &picture_bounds, terminal_text, 0);
819 }
820 
821 /* returns true for phase chagne */
draw_computer_text(Rect * bounds,terminal_text_t * terminal_text,short current_group_index,short current_line)822 static void draw_computer_text(
823 	Rect *bounds,
824 	terminal_text_t *terminal_text,
825 	short current_group_index,
826 	short current_line)
827 {
828 	char *base_text= get_text_base(terminal_text);
829 
830 	_draw_computer_text(base_text, current_group_index, bounds,
831 		terminal_text, current_line);
832 }
833 
834 /* Returns true if current_line> end of text.. (for phase change) */
_draw_computer_text(char * base_text,short group_index,Rect * bounds,terminal_text_t * terminal_text,short current_line)835 static void _draw_computer_text(
836 	char *base_text,
837 	short group_index,
838 	Rect *bounds,
839 	terminal_text_t *terminal_text,
840 	short current_line)
841 {
842 	bool done= false;
843 	short start_index;
844 	struct terminal_groupings *current_group= get_indexed_grouping(terminal_text, group_index);
845 	// LP change: just in case...
846 	if (!current_group) return;
847 	struct text_face_data text_face;
848 	short index, last_index, last_text_index, end_index;
849 	unsigned text_index;
850 
851 	uint16 old_style = current_style;
852 	current_style = GetInterfaceStyle(_computer_interface_font);
853 	// current_style = _get_font_spec(_computer_interface_font)->style;
854 
855 	start_index= current_group->start_index;
856 	end_index= current_group->length+current_group->start_index;
857 
858 	/* eat the previous lines */
859 	for(index= 0; index<current_line; ++index)
860 	{
861 		/* Calculate one line.. */
862 		if(!calculate_line(base_text, RECTANGLE_WIDTH(bounds), start_index, current_group->start_index+current_group->length,
863 			&end_index))
864 		{
865 			if(end_index>current_group->start_index+current_group->length)
866 			{
867 				// dprintf("Start: %d Length: %d End: %d;g", current_group->start_index, current_group->length, end_index);
868 				// dprintf("Width: %d", RECTANGLE_WIDTH(bounds));
869 				end_index= current_group->start_index+current_group->length;
870 			}
871 			//dprintf("calculate line: %d start: %d end: %d", index, start_index, end_index);
872 			assert(end_index<=current_group->start_index+current_group->length);
873 
874 			start_index= end_index;
875 		} else {
876 			/* End of text.. */
877 			done= true;
878 		}
879 	}
880 
881 	if(!done)
882 	{
883 		/* Go backwards, and see if there were any other face changes... */
884 		last_index= current_group->start_index;
885 		last_text_index= NONE;
886 		for(text_index= 0; text_index<terminal_text->font_changes.size(); ++text_index)
887 		{
888 			struct text_face_data *font_face= get_indexed_font_changes(terminal_text, text_index);
889 			// LP change: just in case...
890 			if (!font_face) return;
891 
892 			/* Go backwards from the scrolled starting location.. */
893 			if(font_face->index>last_index && font_face->index<start_index)
894 			{
895 				// dprintf("ff index: %d last: %d end: %d", font_face->index, last_index, start_index);
896 				last_index= font_face->index;
897 				last_text_index= text_index;
898 			}
899 		}
900 
901 		// dprintf("last index: %d", last_text_index);
902 		/* Figure out the font.. */
903 		if(last_text_index==NONE)
904 		{
905 			/* Default-> plain, etc.. */
906 			text_face.color= 0;
907 			text_face.face= 0;
908 		} else {
909 			struct text_face_data *font_face= get_indexed_font_changes(terminal_text,
910 				last_text_index);
911 			// LP change: just in case...
912 			if (!font_face) return;
913 			text_face= *font_face;
914 		}
915 
916 		/* Set the font.. */
917 		set_text_face(&text_face);
918 
919 		/* Draw what is one the screen */
920 		for(index= 0; !done && index<terminal_text->lines_per_page; ++index)
921 		{
922 			//dprintf("calculating the line");
923 			/* Calculate one line.. */
924 			if(!calculate_line(base_text, RECTANGLE_WIDTH(bounds), start_index, current_group->start_index+current_group->length, &end_index))
925 			{
926 				//dprintf("draw calculate line: %d start: %d end: %d text: %x length: %d lti: %d", index, start_index, end_index, base_text, current_group->length, last_text_index);
927 				if(end_index>current_group->start_index+current_group->length)
928 				{
929 					// dprintf("Start: %d Length: %d End: %d;g", current_group->start_index, current_group->length, end_index);
930 					// dprintf("Width: %d", RECTANGLE_WIDTH(bounds));
931 					end_index= current_group->start_index+current_group->length;
932 				}
933 				assert(end_index<=current_group->start_index+current_group->length);
934 				draw_line(base_text, start_index, end_index, bounds, terminal_text, &last_text_index,
935 					index);
936 				start_index= end_index;
937 			} else {
938 				/* End of text. */
939 //				done= true;
940 			}
941 		}
942 	}
943 
944 	current_style = old_style;
945 }
946 
count_total_lines(char * base_text,short width,short start_index,short end_index)947 static short count_total_lines(
948 	char *base_text,
949 	short width,
950 	short start_index,
951 	short end_index)
952 {
953 	short total_line_count= 0;
954 	short text_end_index= end_index;
955 
956 	uint16 old_style = current_style;
957 	current_style = GetInterfaceStyle(_computer_interface_font);
958 	// current_style = _get_font_spec(_computer_interface_font)->style;
959 
960 	while(!calculate_line(base_text, width, start_index, text_end_index, &end_index))
961 	{
962 		total_line_count++;
963 		start_index= end_index;
964 	}
965 
966 	current_style = old_style;
967 
968 	return total_line_count;
969 }
970 
draw_line(char * base_text,short start_index,short end_index,Rect * bounds,terminal_text_t * terminal_text,short * text_face_start_index,short line_number)971 static void draw_line(
972 	char *base_text,
973 	short start_index,
974 	short end_index,
975 	Rect *bounds,
976 	terminal_text_t *terminal_text,
977 	short *text_face_start_index,
978 	short line_number)
979 {
980 	short line_height= _get_font_line_height(_computer_interface_font);
981 	bool done= false;
982 	short current_start, current_end= end_index;
983 	struct text_face_data *face_data= NULL;
984 	unsigned text_index;
985 
986 	if((*text_face_start_index)==NONE)
987 	{
988 		text_index= 0;
989 	} else {
990 		text_index= (*text_face_start_index);
991 	}
992 
993 	/* Get to the first one that concerns us.. */
994 	if(text_index<terminal_text->font_changes.size())
995 	{
996 		do {
997 			face_data= get_indexed_font_changes(terminal_text, text_index);
998 			// LP change: just in case...
999 			if (!face_data) return;
1000 			if(face_data->index<start_index) text_index++;
1001 		} while(face_data->index<start_index && text_index<terminal_text->font_changes.size());
1002 	}
1003 
1004 	current_start= start_index;
1005 	font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
1006 	int xpos = bounds->left;
1007 
1008 	while(!done)
1009 	{
1010 		if(text_index<terminal_text->font_changes.size())
1011 		{
1012 			face_data= get_indexed_font_changes(terminal_text, text_index);
1013 			// LP change: just in case...
1014 			if (!face_data) return;
1015 
1016 			if(face_data->index>=current_start && face_data->index<current_end)
1017 			{
1018 				current_end= face_data->index;
1019 				text_index++;
1020 				(*text_face_start_index)= text_index;
1021 			}
1022 		}
1023 
1024 		xpos += draw_text(/*world_pixels*/ draw_surface, base_text + current_start, current_end - current_start,
1025 		                  xpos, bounds->top + line_height * (line_number + FUDGE_FACTOR),
1026 		                  current_pixel, terminal_font, current_style);
1027 		if(current_end!=end_index)
1028 		{
1029 			current_start= current_end;
1030 			current_end= end_index;
1031 			assert(face_data);
1032 			set_text_face(face_data);
1033 		} else {
1034 			done= true;
1035 		}
1036 	}
1037 }
1038 
find_group_type(terminal_text_t * data,short group_type)1039 static short find_group_type(
1040 	terminal_text_t *data,
1041 	short group_type)
1042 {
1043 	unsigned index;
1044 
1045 	for(index= 0; index<data->groupings.size(); index++)
1046 	{
1047 		struct terminal_groupings *group= get_indexed_grouping(data, index);
1048 		// LP change: just in case...
1049 		if (!group) return NONE;
1050 		if(group->type==group_type) break;
1051 	}
1052 
1053 	return index;
1054 }
1055 
teleport_to_level(short level_number)1056 static void teleport_to_level(
1057 	short level_number)
1058 {
1059 	/* It doesn't matter which player we get. */
1060 	struct player_data *player= get_player_data(0);
1061 
1062 	// LP change: moved down by 1 so that level 0 will be valid
1063 	player->teleporting_destination= -level_number - 1;
1064 	player->delay_before_teleport= TICKS_PER_SECOND/2; // delay before we teleport.
1065 }
1066 
teleport_to_polygon(short player_index,short polygon_index)1067 static void teleport_to_polygon(
1068 	short player_index,
1069 	short polygon_index)
1070 {
1071 	struct player_data *player= get_player_data(player_index);
1072 
1073 	player->teleporting_destination= polygon_index;
1074 	assert(!player->delay_before_teleport);
1075 }
1076 
calculate_bounds_for_text_box(short flags,Rect * bounds)1077 static void calculate_bounds_for_text_box(
1078 	short flags,
1079 	Rect *bounds)
1080 {
1081 	if(flags & _center_object)
1082 	{
1083 		// dprintf("splitting text not supported!");
1084 		calculate_bounds_for_object(_draw_object_on_right, bounds, NULL);
1085 	}
1086 	else if(flags & _draw_object_on_right)
1087 	{
1088 		calculate_bounds_for_object(0, bounds, NULL);
1089 	}
1090 	else
1091 	{
1092 		calculate_bounds_for_object(_draw_object_on_right, bounds, NULL);
1093 	}
1094 	if (flags & _group_is_marathon_1)
1095 		bounds->top += _get_font_line_height(_computer_interface_font);
1096 }
1097 
display_picture_with_text(struct player_terminal_data * terminal_data,Rect * bounds,terminal_text_t * terminal_text,short current_line)1098 static void display_picture_with_text(
1099 	struct player_terminal_data *terminal_data,
1100 	Rect *bounds,
1101 	terminal_text_t *terminal_text,
1102 	short current_line)
1103 {
1104 	struct terminal_groupings *current_group= get_indexed_grouping(terminal_text, terminal_data->current_group);
1105 	// LP change: just in case...
1106 	if (!current_group) return;
1107 	Rect text_bounds;
1108 	Rect picture_bounds;
1109 
1110 	assert(current_group->type==_pict_group);
1111     calculate_bounds_for_object(current_group->flags, &picture_bounds, NULL);
1112 	display_picture(current_group->permutation, &picture_bounds, current_group->flags);
1113 
1114 	/* Display the text */
1115 	calculate_bounds_for_text_box(current_group->flags, &text_bounds);
1116 	draw_computer_text(&text_bounds, terminal_text, terminal_data->current_group, current_line);
1117 }
1118 
display_shape(short shape,Rect * frame)1119 static void display_shape(short shape, Rect* frame)
1120 {
1121 	SDL_Surface* s = get_shape_surface(shape);
1122 	if (s)
1123 	{
1124 		Rect bounds;
1125 		Rect screen_bounds;
1126 
1127 		bounds.left = bounds.top = 0;
1128 		bounds.right = s->w;
1129 		bounds.bottom = s->h;
1130 
1131 		OffsetRect(&bounds, -bounds.left, -bounds.top);
1132 		calculate_bounds_for_object(_center_object, &screen_bounds, &bounds);
1133 
1134 		OffsetRect(&bounds, screen_bounds.left + (RECTANGLE_WIDTH(&screen_bounds)-RECTANGLE_WIDTH(&bounds))/2, screen_bounds.top + (RECTANGLE_HEIGHT(&screen_bounds)-RECTANGLE_HEIGHT(&bounds))/2);
1135 
1136 		SDL_Rect r = { bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top };
1137 		SDL_SetSurfaceAlphaMod(s, 255);
1138 		SDL_BlitSurface(s, NULL, draw_surface, &r);
1139 
1140 		SDL_FreeSurface(s);
1141 		*frame = bounds;
1142 	}
1143 }
1144 
1145 extern int get_pict_header_width(LoadedResource &);
1146 
display_picture(short picture_id,Rect * frame,short flags)1147 static void display_picture(
1148 	short picture_id,
1149 	Rect *frame,
1150 	short flags)
1151 {
1152 	LoadedResource PictRsrc;
1153 	SDL_Surface *s = NULL;
1154 
1155 	if (get_picture_resource_from_scenario(picture_id, PictRsrc))
1156 	{
1157 		s = picture_to_surface(PictRsrc);
1158 	}
1159 	if (s)
1160 	{
1161 		Rect bounds;
1162 		Rect screen_bounds;
1163 
1164 		bounds.left = bounds.top = 0;
1165 		bounds.right = s->w;
1166 		bounds.bottom = s->h;
1167 
1168 		int pict_header_width = get_pict_header_width(PictRsrc);
1169 		bool cinemascopeHack = false;
1170 		if (bounds.right != pict_header_width)
1171 		{
1172 			cinemascopeHack = true;
1173 			bounds.right = pict_header_width;
1174 		}
1175 		OffsetRect(&bounds, -bounds.left, -bounds.top);
1176 		calculate_bounds_for_object(flags, &screen_bounds, &bounds);
1177 
1178 		if(RECTANGLE_WIDTH(&bounds)<=RECTANGLE_WIDTH(&screen_bounds) &&
1179 			RECTANGLE_HEIGHT(&bounds)<=RECTANGLE_HEIGHT(&screen_bounds))
1180 		{
1181 			/* It fits-> center it. */
1182 			OffsetRect(&bounds, screen_bounds.left+(RECTANGLE_WIDTH(&screen_bounds)-RECTANGLE_WIDTH(&bounds))/2,
1183 				screen_bounds.top+(RECTANGLE_HEIGHT(&screen_bounds)-RECTANGLE_HEIGHT(&bounds))/2);
1184 		} else {
1185 			/* Doesn't fit.  Make it, but preserve the aspect ratio like a good little boy */
1186 			if(RECTANGLE_HEIGHT(&bounds)-RECTANGLE_HEIGHT(&screen_bounds)>=
1187 				RECTANGLE_WIDTH(&bounds)-RECTANGLE_WIDTH(&screen_bounds))
1188 			{
1189 				short adjusted_width;
1190 
1191 				adjusted_width= RECTANGLE_HEIGHT(&screen_bounds)*RECTANGLE_WIDTH(&bounds)/RECTANGLE_HEIGHT(&bounds);
1192 				bounds= screen_bounds;
1193 				InsetRect(&bounds, (RECTANGLE_WIDTH(&screen_bounds)-adjusted_width)/2, 0);
1194 				// dprintf("Warning: Not large enough for pict: %d (height);g", picture_id);
1195 			} else {
1196 				/* Width is the predominant factor */
1197 				short adjusted_height;
1198 
1199 				adjusted_height= RECTANGLE_WIDTH(&screen_bounds)*RECTANGLE_HEIGHT(&bounds)/RECTANGLE_WIDTH(&bounds);
1200 				bounds= screen_bounds;
1201 				InsetRect(&bounds, 0, (RECTANGLE_HEIGHT(&screen_bounds)-adjusted_height)/2);
1202 				// dprintf("Warning: Not large enough for pict: %d (width);g", picture_id);
1203 			}
1204 		}
1205 
1206 //		warn(HGetState((Handle) picture) & 0x40); // assert it is purgable.
1207 
1208 		SDL_Rect r = {bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top};
1209 		if ((s->w == r.w && s->h == r.h) || cinemascopeHack)
1210 			SDL_BlitSurface(s, NULL, /*world_pixels*/draw_surface, &r);
1211 		else {
1212 			// Rescale picture
1213 			SDL_Surface *s2 = rescale_surface(s, r.w, r.h);
1214 			if (s2) {
1215 				SDL_BlitSurface(s2, NULL, /*world_pixels*/draw_surface, &r);
1216 				SDL_FreeSurface(s2);
1217 			}
1218 		}
1219 		SDL_FreeSurface(s);
1220 		/* And let the caller know where we drew the picture */
1221 		*frame= bounds;
1222 	} else {
1223 		Rect bounds;
1224 		char format_string[128];
1225 
1226 		calculate_bounds_for_object(flags, &bounds, NULL);
1227 
1228 		SDL_Rect r = {bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top};
1229 		SDL_FillRect(/*world_pixels*/draw_surface, &r, SDL_MapRGB(/*world_pixels*/draw_surface->format, 0, 0, 0));
1230 
1231 		getcstr(format_string, strERRORS, pictureNotFound);
1232 		sprintf(temporary, format_string, picture_id);
1233 
1234 		const font_info *font = GetInterfaceFont(_computer_interface_title_font);
1235 		int width = text_width(temporary, font, styleNormal);
1236 		draw_text(/*world_pixels*/draw_surface, temporary,
1237 		          bounds.left + (RECTANGLE_WIDTH(&bounds) - width) / 2,
1238 		          bounds.top + RECTANGLE_HEIGHT(&bounds) / 2,
1239 		          SDL_MapRGB(/*world_pixels*/draw_surface->format, 0xff, 0xff, 0xff),
1240 		          font, styleNormal);
1241 	}
1242 }
1243 
1244 template <typename T>
randomize_pixel(uint16 pixel)1245 static inline T randomize_pixel(uint16 pixel)
1246 {
1247 	return static_cast<T>(pixel);
1248 }
1249 
1250 extern SDL_PixelFormat pixel_format_32;
1251 
1252 template <>
randomize_pixel(uint16 pixel)1253 inline uint32 randomize_pixel(uint16 pixel)
1254 {
1255 	return ((uint32)pixel^(((uint32)pixel)<<8)) | pixel_format_32.Amask;
1256 }
1257 
1258 template <typename T>
randomize_line(T * start,uint32 count)1259 static inline void randomize_line(T* start, uint32 count)
1260 {
1261 	static uint16 random_seed = 6906;
1262 	for (int i = 0; i < count; ++i)
1263 	{
1264 		*start++ = randomize_pixel<T>(random_seed);
1265 		if (random_seed&1) random_seed = (random_seed>>1)^0xb400;
1266 		else random_seed = random_seed >> 1;
1267 	}
1268 }
1269 
fill_terminal_with_static(Rect * bounds)1270 static void fill_terminal_with_static(
1271 	Rect *bounds)
1272 {
1273 	for (int y = bounds->top; y < bounds->bottom; ++y)
1274 	{
1275 		int bpp = draw_surface->format->BytesPerPixel;
1276 		int width = bounds->right - bounds->left;
1277 
1278 		uint8* p = (uint8*)draw_surface->pixels + y * draw_surface->pitch + bounds->left * bpp;
1279 		if (bpp == 1)
1280 		{
1281 			randomize_line<uint8>(p, width);
1282 		}
1283 		else if (bpp == 2)
1284 		{
1285 			randomize_line<uint16>(reinterpret_cast<uint16*>(p), width);
1286 		}
1287 		else if (bpp == 4)
1288 		{
1289 			randomize_line<uint32>(reinterpret_cast<uint32*>(p), width);
1290 		}
1291 	}
1292 }
1293 
1294 // LP addition: will return NULL if no terminal data was found for this terminal number
1295 
1296 static boost::scoped_ptr<terminal_text_t> resource_terminal;
1297 static int resource_terminal_id = NONE;
1298 
clear_compiled_terminal_cache()1299 void clear_compiled_terminal_cache()
1300 {
1301 	resource_terminal.reset();
1302 	resource_terminal_id = NONE;
1303 }
1304 
1305 static terminal_text_t* compile_marathon_terminal(char*, short);
1306 
get_indexed_terminal_data(short id)1307 static terminal_text_t *get_indexed_terminal_data(
1308 	short id)
1309 {
1310 	if (id < 0 || id >= int(map_terminal_text.size()))
1311 	{
1312 		id = 1000 + dynamic_world->current_level_number * 10 + id;
1313 		if (id == resource_terminal_id)
1314 		{
1315 			return resource_terminal.get();
1316 		}
1317 		else if (ExternalResources.IsOpen())
1318 		{
1319 			LoadedResource rsrc;
1320 			if (ExternalResources.Get('t', 'e', 'r', 'm', id, rsrc))
1321 			{
1322 				resource_terminal.reset(compile_marathon_terminal(reinterpret_cast<char*>(rsrc.GetPointer()), rsrc.GetLength()));
1323 
1324 				resource_terminal_id = id;
1325 				return resource_terminal.get();
1326 			}
1327 		}
1328 
1329 		return NULL;
1330 	}
1331 
1332 	terminal_text_t *t = &map_terminal_text[id];
1333 
1334 	// Note that this will only decode the text once
1335 	decode_text(t);
1336 	return t;
1337 }
1338 
decode_text(terminal_text_t * terminal_text)1339 static void decode_text(
1340 	terminal_text_t *terminal_text)
1341 {
1342 	if(terminal_text->flags & _text_is_encoded_flag)
1343 	{
1344 		encode_text(terminal_text);
1345 		terminal_text->flags &= ~_text_is_encoded_flag;
1346 	}
1347 }
1348 
encode_text(terminal_text_t * terminal_text)1349 static void encode_text(
1350 	terminal_text_t *terminal_text)
1351 {
1352 	int length = terminal_text->text.size();
1353 	uint8 *p = terminal_text->text.data();
1354 
1355 	for (int i=0; i<length/4; i++) {
1356 		p += 2;
1357 		*p++ ^= 0xfe;
1358 		*p++ ^= 0xed;
1359 	}
1360 	for (int i=0; i<length%4; i++)
1361 		*p++ ^= 0xfe;
1362 
1363 	terminal_text->flags |= _text_is_encoded_flag;
1364 }
1365 
1366 // LP change: added a flag to indicate whether stuff after the other
1367 // terminal stuff is to be drawn; if not, then draw the stuff before the
1368 // other terminal stuff.
draw_terminal_borders(struct player_terminal_data * terminal_data,bool after_other_terminal_stuff)1369 static void draw_terminal_borders(
1370 	struct player_terminal_data *terminal_data,
1371 	bool after_other_terminal_stuff)
1372 {
1373 	Rect frame, border;
1374 	short top_message, bottom_left_message, bottom_right_message;
1375 	terminal_text_t *terminal_text= get_indexed_terminal_data(terminal_data->terminal_id);
1376 
1377 	// LP addition: quit if none
1378 	if (terminal_text == NULL) return;
1379 
1380 	// LP change: just in case...
1381 	struct terminal_groupings *current_group= get_indexed_grouping(terminal_text, terminal_data->current_group);
1382 	if (current_group == NULL) return;
1383 
1384 	switch(current_group->type)
1385 	{
1386 		case _logon_group:
1387 			top_message= _computer_starting_up;
1388 			bottom_left_message= _computer_manufacturer;
1389 			bottom_right_message= _computer_address;
1390 			break;
1391 
1392 		case _logoff_group:
1393 			top_message= _disconnecting_message;
1394 			bottom_left_message= _computer_manufacturer;
1395 			bottom_right_message= _computer_address;
1396 			break;
1397 
1398 		default:
1399 			top_message= _computer_terminal;
1400 			bottom_left_message= _scrolling_message;
1401 			bottom_right_message= _acknowledgement_message;
1402 			break;
1403 	}
1404 
1405 	/* First things first: draw the border.. */
1406 	/* Get the destination frame.. */
1407     frame= get_term_rectangle(_terminal_screen_rect);
1408 
1409 	if (!after_other_terminal_stuff)
1410 	{
1411 		/* Erase the rectangle.. */
1412 		_fill_screen_rectangle((screen_rectangle *) &frame, _black_color);
1413 	} else {
1414 
1415 		/* Draw the top rectangle */
1416 		border= get_term_rectangle(_terminal_header_rect);
1417 		_fill_screen_rectangle((screen_rectangle *) &border, _computer_border_background_text_color);
1418 
1419 		/* Draw the top login header text... */
1420 		border.left += LABEL_INSET; border.right -= LABEL_INSET;
1421 		getcstr(temporary, strCOMPUTER_LABELS, top_message);
1422 		_draw_screen_text(temporary, (screen_rectangle *) &border, _center_vertical, _computer_interface_font, _computer_border_text_color);
1423 		get_date_string(temporary, current_group->flags);
1424 		_draw_screen_text(temporary, (screen_rectangle *) &border, _right_justified | _center_vertical,
1425 			_computer_interface_font, _computer_border_text_color);
1426 
1427 		/* Draw the the bottom rectangle & text */
1428 		border= get_term_rectangle(_terminal_footer_rect);
1429 		_fill_screen_rectangle((screen_rectangle *) &border, _computer_border_background_text_color);
1430 		border.left += LABEL_INSET; border.right -= LABEL_INSET;
1431 		getcstr(temporary, strCOMPUTER_LABELS, bottom_left_message);
1432 		_draw_screen_text(temporary, (screen_rectangle *) &border, _center_vertical, _computer_interface_font,
1433 			_computer_border_text_color);
1434 		getcstr(temporary, strCOMPUTER_LABELS, bottom_right_message);
1435 		_draw_screen_text(temporary, (screen_rectangle *) &border, _right_justified | _center_vertical,
1436 			_computer_interface_font, _computer_border_text_color);
1437 
1438 		// LP change: done with stuff for after the other rendering
1439 	}
1440 }
1441 
next_terminal_state(short player_index)1442 static void next_terminal_state(
1443 	short player_index)
1444 {
1445 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
1446 
1447 	switch(terminal->state)
1448 	{
1449 		case _reading_terminal:
1450 			initialize_player_terminal_info(player_index);
1451 			break;
1452 
1453 		case _no_terminal_state:
1454 		default:
1455 			break;
1456 	}
1457 }
1458 
previous_terminal_group(short player_index,terminal_text_t * terminal_text)1459 static bool previous_terminal_group(
1460 	short player_index,
1461 	terminal_text_t *terminal_text)
1462 {
1463 	struct player_terminal_data *terminal_data= get_player_terminal_data(player_index);
1464 	bool success= false;
1465 
1466 	if(terminal_data->state==_reading_terminal)
1467 	{
1468 		short new_group_index= terminal_data->current_group-1;
1469 		bool use_new_group= true;
1470 		bool done= true;
1471 
1472 		do
1473 		{
1474 			if(new_group_index>=0)
1475 			{
1476 				struct terminal_groupings *new_group= get_indexed_grouping(terminal_text, new_group_index);
1477 				// LP change: just in case...
1478 				if (!new_group) return success;
1479 				switch(new_group->type)
1480 				{
1481 					case _logon_group:
1482 					case _end_group:
1483 						use_new_group= false;
1484 						break;
1485 
1486 					case _interlevel_teleport_group:
1487 					case _intralevel_teleport_group:
1488 						// dprintf("This shouldn't happen!");
1489 						break;
1490 
1491  					case _sound_group:
1492 					case _tag_group:
1493 						new_group_index--;
1494 						done= false;
1495 						break;
1496 
1497 					case _movie_group:
1498 					case _track_group:
1499 					case _checkpoint_group:
1500 					case _pict_group:
1501 					case _information_group:
1502 					case _camera_group:
1503 						done = true;
1504 						break;
1505 
1506 					case _unfinished_group:
1507 					case _success_group:
1508 					case _failure_group:
1509 					case _static_group:
1510 						use_new_group= false;
1511 						break;
1512 
1513 					default:
1514 						break;
1515 				}
1516 			} else {
1517 				done= true;
1518 				use_new_group= false;
1519 			}
1520 			if (!use_new_group) done = true; // this fixes perma-loop freeze
1521 		} while(!done);
1522 
1523 		if(use_new_group)
1524 		{
1525 			/* Go there.. */
1526 			goto_terminal_group(player_index, terminal_text, new_group_index);
1527 			success= true;
1528 		}
1529 	}
1530 
1531 	return success;
1532 }
1533 
next_terminal_group(short player_index,terminal_text_t * terminal_text)1534 static void next_terminal_group(
1535 	short player_index,
1536 	terminal_text_t *terminal_text)
1537 {
1538 	struct player_terminal_data *terminal_data= get_player_terminal_data(player_index);
1539 	bool update_line_count= false;
1540 
1541 	if(terminal_data->current_group==NONE)
1542 	{
1543 		update_line_count= true;
1544 
1545 		switch(terminal_data->level_completion_state)
1546 		{
1547 			case _level_unfinished:
1548 				terminal_data->current_group= find_group_type(terminal_text, _unfinished_group);
1549 				break;
1550 
1551 			case _level_finished:
1552 				terminal_data->current_group= find_group_type(terminal_text, _success_group);
1553 				if(terminal_data->current_group==static_cast<int16>(terminal_text->groupings.size()))
1554 				{
1555 					/* Fallback. */
1556 					terminal_data->current_group= find_group_type(terminal_text, _unfinished_group);
1557 					// assert(terminal_data->current_group != terminal_text->groupings.size());
1558 				}
1559 				break;
1560 
1561 			case _level_failed:
1562 				terminal_data->current_group= find_group_type(terminal_text, _failure_group);
1563 				if(terminal_data->current_group==static_cast<int16>(terminal_text->groupings.size()))
1564 				{
1565 					/* Fallback. */
1566 					terminal_data->current_group= find_group_type(terminal_text, _unfinished_group);
1567 					// assert(terminal_data->current_group != terminal_text->groupings.size());
1568 				}
1569 				break;
1570 
1571 			default:
1572 				break;
1573 		}
1574 
1575 		if (terminal_data->current_group==static_cast<int16>(terminal_text->groupings.size()) && terminal_text->groupings.size() && (terminal_text->groupings[0].flags & _group_is_marathon_1))
1576 		{
1577 			// Marathon 1 fallback
1578 			terminal_data->current_group = 0;
1579 		} else {
1580 
1581 			/* Note that the information groups are now keywords, and can have no data associated with them */
1582 			next_terminal_group(player_index, terminal_text);
1583 		}
1584 	} else {
1585 		terminal_data->current_group++;
1586 		assert(terminal_data->current_group >= 0);
1587 		if((size_t)terminal_data->current_group>=terminal_text->groupings.size())
1588 		{
1589 			next_terminal_state(player_index);
1590 		} else {
1591 			update_line_count= true;
1592 		}
1593 	}
1594 
1595 	if(update_line_count)
1596 	{
1597 		goto_terminal_group(player_index, terminal_text, terminal_data->current_group);
1598 	}
1599 
1600 	SET_TERMINAL_IS_DIRTY(terminal_data, true);
1601 }
1602 
goto_terminal_group(short player_index,terminal_text_t * terminal_text,short new_group_index)1603 static void goto_terminal_group(
1604 	short player_index,
1605 	terminal_text_t *terminal_text,
1606 	short new_group_index)
1607 {
1608 	struct player_terminal_data *terminal_data= get_player_terminal_data(player_index);
1609 	struct player_data *player= get_player_data(player_index);
1610 	struct terminal_groupings *current_group;
1611 
1612 	terminal_data->current_group= new_group_index;
1613 
1614 	current_group= get_indexed_grouping(terminal_text, terminal_data->current_group);
1615 	// LP change: just in case...
1616 	if (!current_group)
1617 	{
1618 		// Copied from _end_group case
1619 		next_terminal_state(player_index);
1620 		terminal_data->maximum_line= 1; // any click or keypress will get us out...
1621 		return;
1622 	}
1623 
1624 	terminal_data->current_line= 0;
1625 
1626 	switch(current_group->type)
1627 	{
1628 		case _logon_group:
1629 			play_object_sound(player->object_index, Sound_TerminalLogon());
1630 			terminal_data->phase= LOG_DURATION_BEFORE_TIMEOUT;
1631 			terminal_data->maximum_line= current_group->maximum_line_count;
1632 			break;
1633 
1634 		case _logoff_group:
1635 			play_object_sound(player->object_index, Sound_TerminalLogoff());
1636 			terminal_data->phase= LOG_DURATION_BEFORE_TIMEOUT;
1637 			terminal_data->maximum_line= current_group->maximum_line_count;
1638 			break;
1639 
1640 		case _interlevel_teleport_group:
1641 		case _intralevel_teleport_group:
1642 		case _sound_group:
1643 		case _tag_group:
1644 		case _movie_group:
1645 		case _track_group:
1646 		case _camera_group:
1647 			terminal_data->phase= NONE;
1648 			terminal_data->maximum_line= current_group->maximum_line_count;
1649 			break;
1650 
1651 		case _checkpoint_group:
1652 		case _pict_group:
1653 			terminal_data->phase= NONE;
1654 			if(dynamic_world->player_count>1)
1655 			{
1656 				/* Use what the server told us */
1657 				terminal_data->maximum_line= current_group->maximum_line_count;
1658 			} else {
1659 				/* Calculate this for ourselves. */
1660 				Rect text_bounds;
1661 
1662 				/* The only thing we care about is the width. */
1663 				calculate_bounds_for_text_box(current_group->flags, &text_bounds);
1664 				terminal_data->maximum_line= count_total_lines(get_text_base(terminal_text),
1665 					RECTANGLE_WIDTH(&text_bounds), current_group->start_index,
1666 					current_group->start_index+current_group->length);
1667 			}
1668 			break;
1669 
1670 		case _information_group:
1671 			terminal_data->phase= NONE;
1672 			if(dynamic_world->player_count>1)
1673 			{
1674 				/* Use what the server told us */
1675 				terminal_data->maximum_line= current_group->maximum_line_count;
1676 			} else {
1677 				/* Calculate this for ourselves. */
1678                 Rect bounds= get_term_rectangle(_terminal_full_text_rect);
1679 				terminal_data->maximum_line= count_total_lines(get_text_base(terminal_text),
1680 					RECTANGLE_WIDTH(&bounds), current_group->start_index,
1681 					current_group->start_index+current_group->length);
1682 			}
1683 			break;
1684 
1685 		case _static_group:
1686 			terminal_data->phase= current_group->permutation;
1687 			terminal_data->maximum_line= current_group->maximum_line_count;
1688 			break;
1689 
1690 		case _end_group:
1691 			/* Get Out! */
1692 			next_terminal_state(player_index);
1693 			terminal_data->maximum_line= 1; // any click or keypress will get us out...
1694 			break;
1695 
1696 		case _unfinished_group:
1697 		case _success_group:
1698 		case _failure_group:
1699 			vwarn(0, "You shouldn't be coming to this group");
1700 			break;
1701 
1702 		default:
1703 			break;
1704 	}
1705 }
1706 
1707 /* I'll use this function, almost untouched.. */
get_date_string(char * date_string,short flags)1708 static void get_date_string(
1709 	char *date_string,
1710 	short flags)
1711 {
1712 	char temp_string[101];
1713 	int32 game_time_passed;
1714 	time_t seconds;
1715 	struct tm game_time;
1716 
1717 	/* Treat the date as if it were recent. */
1718 	game_time_passed= INT32_MAX - dynamic_world->game_information.game_time_remaining;
1719 
1720 	/* convert the game seconds to machine seconds */
1721 	if (flags & _group_is_marathon_1)
1722 	{
1723 		seconds = 809304137;
1724 		seconds += 7*60*(game_time_passed / TICKS_PER_SECOND);
1725 	}
1726 	else
1727 	{
1728 		seconds = 800070137;
1729 		seconds += game_time_passed / TICKS_PER_SECOND;
1730 	}
1731 	game_time = *localtime(&seconds);
1732 	game_time.tm_year= 437;
1733 	game_time.tm_yday= 0;
1734 	game_time.tm_isdst= 0;
1735 
1736 	getcstr(temp_string, strCOMPUTER_LABELS, _date_format);
1737 	strftime(date_string, 100, temp_string, &game_time);
1738 }
1739 
present_checkpoint_text(terminal_text_t * terminal_text,short current_group_index,short current_line)1740 static void present_checkpoint_text(
1741 	terminal_text_t *terminal_text,
1742 	short current_group_index,
1743 	short current_line)
1744 {
1745 	Rect bounds;
1746 	struct overhead_map_data overhead_data;
1747 	struct terminal_groupings *current_group;
1748 
1749 	current_group= get_indexed_grouping(terminal_text, current_group_index);
1750 	// LP change: just in case...
1751 	if (!current_group) return;
1752 
1753 	// draw the overhead map.
1754 	calculate_bounds_for_object(current_group->flags, &bounds, NULL);
1755 	overhead_data.scale =  1;
1756 
1757 	if(find_checkpoint_location(current_group->permutation, &overhead_data.origin,
1758 		&overhead_data.origin_polygon_index))
1759 	{
1760 		overhead_data.top= bounds.top;
1761 		overhead_data.left= bounds.left;
1762 		overhead_data.half_width= RECTANGLE_WIDTH(&bounds)/2;
1763 		overhead_data.half_height= RECTANGLE_HEIGHT(&bounds)/2;
1764 		overhead_data.width= RECTANGLE_WIDTH(&bounds);
1765 		overhead_data.height= RECTANGLE_HEIGHT(&bounds);
1766 		overhead_data.mode= _rendering_checkpoint_map;
1767         set_drawing_clip_rectangle(bounds.top, bounds.left, bounds.bottom, bounds.right);
1768 		_render_overhead_map(&overhead_data);
1769         set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1770 	} else {
1771 		char format_string[128];
1772 
1773 		SDL_Rect r = {bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top};
1774 		SDL_FillRect(/*world_pixels*/draw_surface, &r, SDL_MapRGB(/*world_pixels*/draw_surface->format, 0, 0, 0));
1775 
1776 		getcstr(format_string, strERRORS, checkpointNotFound);
1777 		sprintf(temporary, format_string, current_group->permutation);
1778 
1779 		const font_info *font = GetInterfaceFont(_computer_interface_title_font);
1780 		// const sdl_font_info *font = load_font(*_get_font_spec(_computer_interface_title_font));
1781 		int width = text_width(temporary, font, styleNormal);
1782 		draw_text(/*world_pixels*/draw_surface, temporary,
1783 		          bounds.left + (RECTANGLE_WIDTH(&bounds) - width) / 2,
1784 		          bounds.top + RECTANGLE_HEIGHT(&bounds) / 2,
1785 		          SDL_MapRGB(/*world_pixels*/draw_surface->format, 0xff, 0xff, 0xff),
1786 		          font, styleNormal);
1787 	}
1788 
1789 	// draw the text
1790 	calculate_bounds_for_text_box(current_group->flags, &bounds);
1791 	draw_computer_text(&bounds, terminal_text, current_group_index, current_line);
1792 }
1793 
find_checkpoint_location(short checkpoint_index,world_point2d * location,short * polygon_index)1794 static bool find_checkpoint_location(
1795 	short checkpoint_index,
1796 	world_point2d *location,
1797 	short *polygon_index)
1798 {
1799 	bool success= false;
1800 	short ii;
1801 	struct map_object *saved_object;
1802 	short match_count;
1803 
1804 	location->x= location->y= match_count= 0;
1805 	for (ii = 0, saved_object= saved_objects; ii < dynamic_world->initial_objects_count; ii++, saved_object++)
1806 	{
1807 		if (saved_object->type==_saved_goal && saved_object->index==checkpoint_index)
1808 		{
1809 			location->x+= saved_object->location.x;
1810 			location->y+= saved_object->location.y;
1811 //			*polygon_index= saved_object->polygon_index;
1812 			match_count++;
1813 		}
1814 	}
1815 
1816 	if(match_count)
1817 	{
1818 		/* Now average.. */
1819 		location->x /= match_count;
1820 		location->y /= match_count;
1821 		*polygon_index= world_point_to_polygon_index(location);
1822 		success = (*polygon_index != NONE);
1823 	}
1824 
1825 	return success;
1826 }
1827 
handle_reading_terminal_keys(short player_index,int32 action_flags)1828 static void handle_reading_terminal_keys(
1829 	short player_index,
1830 	int32 action_flags)
1831 {
1832 	struct player_terminal_data *terminal= get_player_terminal_data(player_index);
1833 	terminal_text_t *terminal_text= get_indexed_terminal_data(terminal->terminal_id);
1834 	// LP addition: quit if none
1835 	if (!terminal_text) return;
1836 	struct terminal_groupings *current_group;
1837 	short initial_group= terminal->current_group;
1838 	short initial_line= terminal->current_line;
1839 	bool aborted= false;
1840 	struct player_data *player= get_player_data(player_index);
1841 	short line_delta= 0;
1842 	bool change_state= false;
1843 
1844 	current_group= get_indexed_grouping(terminal_text, terminal->current_group);
1845 	// LP change: just in case...
1846 	if (!current_group)
1847 	{
1848 		// Copied from _end_group case
1849 		next_terminal_state(player_index);
1850 		aborted= true;
1851 	}
1852 
1853 	switch(current_group->type)
1854 	{
1855 		case _logon_group:
1856 		case _logoff_group:
1857 		case _unfinished_group:
1858 		case _success_group:
1859 		case _failure_group:
1860 		case _information_group:
1861 		case _checkpoint_group:
1862 		case _pict_group:
1863 		case _camera_group:
1864 		case _static_group:
1865 			if(action_flags & _terminal_up_arrow)
1866 			{
1867 				line_delta= -1;
1868 			}
1869 
1870 			if(action_flags & _terminal_down_arrow)
1871 			{
1872 				line_delta= 1;
1873 			}
1874 
1875 			if(action_flags & _terminal_page_down)
1876 			{
1877 				play_object_sound(player->object_index, Sound_TerminalPage());
1878 				line_delta= terminal_text->lines_per_page;
1879 			}
1880 
1881 			if(action_flags & _terminal_page_up)
1882 			{
1883 				play_object_sound(player->object_index, Sound_TerminalPage());
1884 				line_delta= -terminal_text->lines_per_page;
1885 			}
1886 
1887 			/* this one should change state, if necessary */
1888 			if(action_flags & _terminal_next_state)
1889 			{
1890 				play_object_sound(player->object_index, Sound_TerminalPage());
1891 				/* Force a state change. */
1892 				line_delta= terminal_text->lines_per_page;
1893 				change_state= true;
1894 			}
1895 
1896 			if(action_flags & _any_abort_key_mask)
1897 			{
1898 				/* Abort! */
1899 				initialize_player_terminal_info(player_index);
1900 				aborted= true;
1901 			}
1902 			break;
1903 
1904 		case _movie_group:
1905 		case _track_group:
1906 			break;
1907 
1908 		case _end_group:
1909 			next_terminal_state(player_index);
1910 			aborted= true;
1911 			break;
1912 
1913 		case _interlevel_teleport_group: // permutation is level to go to
1914 			teleport_to_level(current_group->permutation);
1915 			initialize_player_terminal_info(player_index);
1916 			aborted= true;
1917 			break;
1918 
1919 		case _intralevel_teleport_group: // permutation is polygon to go to.
1920 			teleport_to_polygon(player_index, current_group->permutation);
1921 			initialize_player_terminal_info(player_index);
1922 			aborted= true;
1923 			break;
1924 
1925 		case _sound_group: // permutation is the sound id to play
1926 			/* Play the sound immediately, and then go to the next level.. */
1927 			{
1928 				struct player_data *player= get_player_data(player_index);
1929 				play_object_sound(player->object_index, current_group->permutation);
1930 				next_terminal_group(player_index, terminal_text);
1931 				aborted= true;
1932 			}
1933 			break;
1934 
1935 		case _tag_group:
1936 			set_tagged_light_statuses(current_group->permutation, true);
1937 			try_and_change_tagged_platform_states(current_group->permutation, true);
1938 			next_terminal_group(player_index, terminal_text);
1939 			aborted= true;
1940 			break;
1941 
1942 		default:
1943 			break;
1944 	}
1945 
1946 	/* If we are dirty.. */
1947 	terminal->current_line+= line_delta;
1948 	if(!aborted && (initial_group != terminal->current_group ||
1949 			initial_line != terminal->current_line))
1950 	{
1951 		if(terminal->current_line<0)
1952 		{
1953 			if(!previous_terminal_group(player_index, terminal_text))
1954 			{
1955 				terminal->current_line= 0;
1956 			}
1957 		}
1958 
1959 		if(terminal->current_line>=terminal->maximum_line)
1960 		{
1961 			assert(terminal->current_group >= 0);
1962 			if(static_cast<size_t>(terminal->current_group)+1>=terminal_text->groupings.size())
1963 			{
1964 				if(change_state)
1965 				{
1966 					/* Go ahead an let the terminal group deal with it.. */
1967 					next_terminal_group(player_index, terminal_text);
1968 				}
1969 				else
1970 				{
1971 					/* renumber the lines */
1972 					terminal->current_line-= line_delta;
1973 				}
1974 			}
1975 			else
1976 			{
1977 				next_terminal_group(player_index, terminal_text);
1978 			}
1979 		}
1980 
1981 		SET_TERMINAL_IS_DIRTY(terminal, true);
1982 	}
1983 }
1984 
calculate_bounds_for_object(short flags,Rect * bounds,Rect * source)1985 static void calculate_bounds_for_object(
1986 	short flags,
1987 	Rect *bounds,
1988 	Rect *source)
1989 {
1990 	if(source && flags & _center_object)
1991 	{
1992         *bounds = get_term_rectangle(_terminal_logon_graphic_rect);
1993 		if(RECTANGLE_WIDTH(source)>RECTANGLE_WIDTH(bounds)
1994 			|| RECTANGLE_HEIGHT(source)>RECTANGLE_HEIGHT(bounds))
1995 		{
1996 			/* Just return the normal frame.  Aspect ratio will take care of us.. */
1997 		} else {
1998 			InsetRect(bounds, (RECTANGLE_WIDTH(bounds)-RECTANGLE_WIDTH(source))/2,
1999 				(RECTANGLE_HEIGHT(bounds)-RECTANGLE_HEIGHT(source))/2);
2000 		}
2001 	}
2002 	else if(flags & _draw_object_on_right)
2003 	{
2004         *bounds = get_term_rectangle(_terminal_right_rect);
2005 	}
2006 	else
2007 	{
2008 		*bounds = get_term_rectangle(_terminal_left_rect);
2009 	}
2010 }
2011 
2012 #define MAXIMUM_GROUPS_PER_TERMINAL 15
2013 
2014 static void calculate_maximum_lines_for_groups(struct terminal_groupings *groups,
2015 	short group_count, char *text_base);
2016 
2017 class MarathonTerminalCompiler
2018 {
2019 public:
MarathonTerminalCompiler(char * text,short length)2020 	MarathonTerminalCompiler(char* text, short length) : terminal(new terminal_text_t), in_buffer(text, length), in(&in_buffer) {
2021 		briefing_group.type = 0;
2022 		success_group.type = 0;
2023 		failure_group.type = 0;
2024 		unfinished_group.type = 0;
2025 		group.type = NONE;
2026 	}
2027 	terminal_text_t* Compile();
2028 
2029 private:
2030 	void FinishGroup();
2031 	void CompileLine(const std::string& line);
2032 
2033 	void BuildUnfinishedGroup();
2034 	void BuildSuccessGroup();
2035 	void BuildFailureGroup();
2036 
2037 	std::unique_ptr<terminal_text_t> terminal;
2038 
2039 	io::stream_buffer<io::array_source> in_buffer;
2040 	std::istream in;
2041 
2042 	std::vector<uint8_t> out;
2043 
2044 	int16 current_group_type;
2045 	terminal_groupings group;
2046 
2047 	terminal_groupings logon_group;
2048 	terminal_groupings briefing_group;
2049 	terminal_groupings unfinished_group;
2050 	terminal_groupings failure_group;
2051 	terminal_groupings success_group;
2052 	std::vector<terminal_groupings> other_groups;
2053 };
2054 
Compile()2055 terminal_text_t* MarathonTerminalCompiler::Compile()
2056 {
2057 	std::string line;
2058 	while (std::getline(in, line, static_cast<char>(MAC_LINE_END)))
2059 	{
2060 		if (line[0] == '#')
2061 		{
2062 			FinishGroup();
2063 
2064 			group.flags = _group_is_marathon_1;
2065 			group.permutation = 0;
2066 
2067 			if (algo::istarts_with(line, "#logon"))
2068 			{
2069 				group.type = _logon_group;
2070 			}
2071 			else if (algo::istarts_with(line, "#information"))
2072 			{
2073 				group.type = _information_group;
2074 			}
2075 			else if (algo::istarts_with(line, "#checkpoint"))
2076 			{
2077 				group.type = _checkpoint_group;
2078 				std::istringstream permutation(line.substr(1 + strlen("#checkpoint")));
2079 				permutation >> group.permutation;
2080 			}
2081 			else if (algo::istarts_with(line, "#briefing"))
2082 			{
2083 				group.type = _interlevel_teleport_group;
2084 				std::istringstream permutation(line.substr(1 + strlen("#briefing")));
2085 				permutation >> group.permutation;
2086 			}
2087 			else if (algo::istarts_with(line, "#unfinished"))
2088 			{
2089 				group.type = _unfinished_group;
2090 			}
2091 			else if (algo::istarts_with(line, "#success"))
2092 			{
2093 				group.type = _success_group;
2094 			}
2095 			else if (algo::istarts_with(line, "#failure"))
2096 			{
2097 				group.type = _failure_group;
2098 			}
2099 			else
2100 			{
2101 				logWarning("Unrecognized group");
2102 				terminal.reset(0);
2103 				return 0;
2104 			}
2105 
2106 			group.start_index = out.size();
2107 		}
2108 		else if (line[0] != ';')
2109 		{
2110 			CompileLine(line);
2111 		}
2112 	}
2113 
2114 	FinishGroup();
2115 
2116 	BuildUnfinishedGroup();
2117 	BuildSuccessGroup();
2118 	BuildFailureGroup();
2119 
2120 	terminal->text = out;
2121 
2122 	terminal->lines_per_page = calculate_lines_per_page();
2123 	calculate_maximum_lines_for_groups(&terminal->groupings[0], terminal->groupings.size(), reinterpret_cast<char*>(terminal->text.data()));
2124 
2125 	return terminal.release();
2126 }
2127 
compile_marathon_terminal(char * text,short length)2128 static terminal_text_t* compile_marathon_terminal(char* text, short length)
2129 {
2130 	MarathonTerminalCompiler compiler(text, length);
2131 	return compiler.Compile();
2132 }
2133 
FinishGroup()2134 void MarathonTerminalCompiler::FinishGroup()
2135 {
2136 	group.length = out.size() - group.start_index;
2137 
2138 	out.push_back('\0');
2139 
2140 	switch (group.type)
2141 	{
2142 	case _logon_group:
2143 		logon_group = group;
2144 		break;
2145 	case _information_group:
2146 	case _checkpoint_group:
2147 		other_groups.push_back(group);
2148 		break;
2149 	case _interlevel_teleport_group:
2150 		briefing_group = group;
2151 		break;
2152 	case _success_group:
2153 		success_group = group;
2154 		break;
2155 	case _failure_group:
2156 		failure_group = group;
2157 		break;
2158 	case _unfinished_group:
2159 		unfinished_group = group;
2160 		break;
2161 	}
2162 }
2163 
CompileLine(const std::string & line)2164 void MarathonTerminalCompiler::CompileLine(const std::string& line)
2165 {
2166 	// Marathon formatting resets at the beginning of the line
2167 	text_face_data font_change;
2168 	font_change.index = out.size();
2169 	font_change.face = 0;
2170 	font_change.color = 0;
2171 
2172 	terminal->font_changes.push_back(font_change);
2173 
2174 	for (std::string::const_iterator it = line.begin(); it != line.end(); ++it)
2175 	{
2176 		if (*it == '$' && (it + 1 != line.end()))
2177 		{
2178 			font_change.index = out.size();
2179 			char c = *(it + 1);
2180 			switch (c)
2181 			{
2182 			case 'B':
2183 				font_change.face |= _bold_text;
2184 				terminal->font_changes.push_back(font_change);
2185 				++it;
2186 				break;
2187 			case 'b':
2188 				font_change.face &= ~_bold_text;
2189 				terminal->font_changes.push_back(font_change);
2190 				++it;
2191 				break;
2192 			case 'I':
2193 				font_change.face |= _italic_text;
2194 				terminal->font_changes.push_back(font_change);
2195 				++it;
2196 				break;
2197 			case 'i':
2198 				font_change.face &= ~_italic_text;
2199 				terminal->font_changes.push_back(font_change);
2200 				++it;
2201 				break;
2202 			case 'U':
2203 				font_change.face |= _underline_text;
2204 				terminal->font_changes.push_back(font_change);
2205 				++it;
2206 				break;
2207 			case 'u':
2208 				font_change.face &= ~_underline_text;
2209 				terminal->font_changes.push_back(font_change);
2210 				++it;
2211 				break;
2212 			case 'C':
2213 				if (it + 2 != line.end())
2214 				{
2215 					char cc = *(it + 2);
2216 					if (cc >= '0' && cc <= '7')
2217 					{
2218 						font_change.color = cc - '0';
2219 						terminal->font_changes.push_back(font_change);
2220 						it += 2;
2221 					}
2222 					else
2223 					{
2224 						out.push_back(*it);
2225 					}
2226 				}
2227 				else
2228 				{
2229 					out.push_back(*it);
2230 				}
2231 				break;
2232 			default:
2233 				++it;
2234 			}
2235 		}
2236 		else if (*it == '%' && (it + 1 != line.end()))
2237 		{
2238 			static char replacement[] = "The colony has been wiped out. Phhht! Just like that.";
2239 
2240 			char c = *(it + 1);
2241 			switch (c)
2242 			{
2243 			case 'r':
2244 				out.insert(out.end(), replacement, replacement + strlen(replacement));
2245 				++it;
2246 				break;
2247 			case '%':
2248 				out.push_back(*it);
2249 				++it;
2250 				break;
2251 			default:
2252 				out.push_back(*it);
2253 			}
2254 		}
2255 		else
2256 		{
2257 			out.push_back(*it);
2258 		}
2259 	}
2260 
2261 	out.push_back(MAC_LINE_END);
2262 }
2263 
BuildUnfinishedGroup()2264 void MarathonTerminalCompiler::BuildUnfinishedGroup()
2265 {
2266 	terminal_groupings group;
2267 	group.flags = 0;
2268 	group.type = _unfinished_group;
2269 	group.permutation = 0;
2270 	group.start_index = 0;
2271 	group.length = 0;
2272 	terminal->groupings.push_back(group);
2273 
2274 	terminal->groupings.push_back(logon_group);
2275 
2276 	// if there's an unfinished group, push it as unfinished
2277 	// otherwise, this must not be an end-of-level term, use all the other groups
2278 	if (unfinished_group.type)
2279 	{
2280 		group = unfinished_group;
2281 		group.type = _information_group;
2282 		terminal->groupings.push_back(group);
2283 
2284 		group = logon_group;
2285 		group.type = _logoff_group;
2286 		terminal->groupings.push_back(group);
2287 	}
2288 	else
2289 	{
2290 		terminal->groupings.insert(terminal->groupings.end(), other_groups.begin(), other_groups.end());
2291 
2292 		group = logon_group;
2293 		group.type = _logoff_group;
2294 		terminal->groupings.push_back(group);
2295 	}
2296 
2297 	group.type = _end_group;
2298 	group.flags = 0;
2299 	group.permutation = 0;
2300 	group.start_index = 0;
2301 	group.length = 0;
2302 	terminal->groupings.push_back(group);
2303 }
2304 
BuildSuccessGroup()2305 void MarathonTerminalCompiler::BuildSuccessGroup()
2306 {
2307 	if (success_group.type || briefing_group.type)
2308 	{
2309 		terminal_groupings group;
2310 		group.flags = 0;
2311 		group.type = _success_group;
2312 		group.permutation = 0;
2313 		group.start_index = 0;
2314 		group.length = 0;
2315 		terminal->groupings.push_back(group);
2316 
2317 		terminal->groupings.push_back(logon_group);
2318 
2319 		if (success_group.type == _success_group)
2320 		{
2321 			group = success_group;
2322 			group.type = _information_group;
2323 			terminal->groupings.push_back(group);
2324 		}
2325 
2326 		if (briefing_group.type)
2327 		{
2328 			group = briefing_group;
2329 			group.type = _information_group;
2330 			group.permutation = 0;
2331 			terminal->groupings.push_back(group);
2332 		}
2333 
2334 		group = logon_group;
2335 		group.type = _logoff_group;
2336 		terminal->groupings.push_back(group);
2337 
2338 		if (briefing_group.type)
2339 		{
2340 			group.type = _interlevel_teleport_group;
2341 			group.permutation = briefing_group.permutation;
2342 			group.start_index = 0;
2343 			group.length = 0;
2344 			terminal->groupings.push_back(group);
2345 		}
2346 
2347 		group.type = _end_group;
2348 		group.flags = 0;
2349 		group.permutation = 0;
2350 		group.start_index = 0;
2351 		group.length = 0;
2352 		terminal->groupings.push_back(group);
2353 	}
2354 }
2355 
BuildFailureGroup()2356 void MarathonTerminalCompiler::BuildFailureGroup()
2357 {
2358 	if (failure_group.type)
2359 	{
2360 		terminal_groupings group;
2361 		group.flags = 0;
2362 		group.type = _failure_group;
2363 		group.permutation = 0;
2364 		group.start_index = 0;
2365 		group.length = 0;
2366 		terminal->groupings.push_back(group);
2367 
2368 		terminal->groupings.push_back(logon_group);
2369 
2370 		group = failure_group;
2371 		group.type = _information_group;
2372 		terminal->groupings.push_back(group);
2373 
2374 		if (briefing_group.type)
2375 		{
2376 			group = briefing_group;
2377 			group.type = _information_group;
2378 			group.permutation = 0;
2379 			terminal->groupings.push_back(group);
2380 		}
2381 
2382 		group = logon_group;
2383 		group.type = _logoff_group;
2384 		terminal->groupings.push_back(group);
2385 
2386 		if (briefing_group.type)
2387 		{
2388 			group.type = _interlevel_teleport_group;
2389 			group.permutation = briefing_group.permutation;
2390 			group.start_index = 0;
2391 			group.length = 0;
2392 			terminal->groupings.push_back(group);
2393 		}
2394 
2395 		group.type = _end_group;
2396 		group.flags = 0;
2397 		group.permutation = 0;
2398 		group.start_index = 0;
2399 		group.length = 0;
2400 		terminal->groupings.push_back(group);
2401 	}
2402 }
2403 
calculate_maximum_lines_for_groups(struct terminal_groupings * groups,short group_count,char * text_base)2404 static void calculate_maximum_lines_for_groups(
2405 	struct terminal_groupings *groups,
2406 	short group_count,
2407 	char *text_base)
2408 {
2409 	short index;
2410 
2411 	for(index= 0; index<group_count; ++index)
2412 	{
2413 		switch(groups[index].type)
2414 		{
2415 			case _logon_group:
2416 			case _logoff_group:
2417 			case _interlevel_teleport_group:
2418 			case _intralevel_teleport_group:
2419 			case _sound_group:
2420 			case _tag_group:
2421 			case _movie_group:
2422 			case _track_group:
2423 			case _camera_group:
2424 			case _static_group:
2425 			case _end_group:
2426 				groups[index].maximum_line_count= 1; // any click or keypress gets us out.
2427 				break;
2428 
2429 			case _unfinished_group:
2430 			case _success_group:
2431 			case _failure_group:
2432 				groups[index].maximum_line_count= 0; // should never get to one of these groups.
2433 				break;
2434 
2435 			case _checkpoint_group:
2436 			case _pict_group:
2437 				{
2438 					Rect text_bounds;
2439 
2440 					/* The only thing we care about is the width. */
2441 					calculate_bounds_for_text_box(groups[index].flags, &text_bounds);
2442 					groups[index].maximum_line_count= count_total_lines(text_base,
2443 						RECTANGLE_WIDTH(&text_bounds), groups[index].start_index,
2444 						groups[index].start_index+groups[index].length);
2445 				}
2446 				break;
2447 
2448 			case _information_group:
2449 				{
2450 					Rect text_bounds= get_term_rectangle(_terminal_full_text_rect);
2451 
2452 					groups[index].maximum_line_count= count_total_lines(text_base,
2453 						RECTANGLE_WIDTH(&text_bounds), groups[index].start_index,
2454 						groups[index].start_index+groups[index].length);
2455 				}
2456 				break;
2457 
2458 			default:
2459 				break;
2460 		}
2461 	}
2462 }
2463 
get_indexed_grouping(terminal_text_t * data,short index)2464 static struct terminal_groupings *get_indexed_grouping(
2465 	terminal_text_t *data,
2466 	short index)
2467 {
2468 	if (index < 0 || index >= int(data->groupings.size()))
2469 		return NULL;
2470 
2471 	return &data->groupings[index];
2472 }
2473 
get_indexed_font_changes(terminal_text_t * data,short index)2474 static struct text_face_data *get_indexed_font_changes(
2475 	terminal_text_t *data,
2476 	short index)
2477 {
2478 	if (index < 0 || index >= int(data->font_changes.size()))
2479 		return NULL;
2480 
2481 	return &data->font_changes[index];
2482 }
2483 
get_text_base(terminal_text_t * data)2484 static char *get_text_base(
2485 	terminal_text_t *data)
2486 {
2487 	return (char *)data->text.data();
2488 }
2489 
calculate_lines_per_page(void)2490 static short calculate_lines_per_page(
2491 	void)
2492 {
2493 	Rect bounds;
2494 	short lines_per_page;
2495 
2496     if (!film_profile.calculate_terminal_lines_correctly)
2497     {
2498         bounds= get_term_rectangle(_terminal_screen_rect);
2499         lines_per_page= (RECTANGLE_HEIGHT(&bounds)-2*BORDER_HEIGHT)/_get_font_line_height(_computer_interface_font);
2500         lines_per_page-= FUDGE_FACTOR;
2501     }
2502     else
2503     {
2504         bounds= get_term_rectangle(_terminal_full_text_rect);
2505         lines_per_page= RECTANGLE_HEIGHT(&bounds)/_get_font_line_height(_computer_interface_font);
2506     }
2507 
2508 	return lines_per_page;
2509 }
2510 
get_term_rectangle(short index)2511 static Rect get_term_rectangle(short index)
2512 {
2513     Rect bounds;
2514     screen_rectangle *term_rect = get_interface_rectangle(_terminal_screen_rect);
2515     screen_rectangle *target_rect = get_interface_rectangle(index);
2516     bounds.left = target_rect->left - term_rect->left;
2517     bounds.top = target_rect->top - term_rect->top;
2518     bounds.right = target_rect->right - term_rect->left;
2519     bounds.bottom = target_rect->bottom - term_rect->top;
2520     return bounds;
2521 }
2522 
2523 
2524 /*
2525  *  Calculate the length the loaded terminal data would take up on disk
2526  *  (for saving)
2527  */
2528 
packed_terminal_length(const terminal_text_t & t)2529 static size_t packed_terminal_length(const terminal_text_t &t)
2530 {
2531 	return SIZEOF_static_preprocessed_terminal_data
2532 	     + t.groupings.size() * SIZEOF_terminal_groupings
2533 	     + t.font_changes.size() * SIZEOF_text_face_data
2534 	     + t.text.size();
2535 }
2536 
calculate_packed_terminal_data_length(void)2537 size_t calculate_packed_terminal_data_length(void)
2538 {
2539 	size_t total = 0;
2540 
2541 	// Loop for all terminals
2542 	vector<terminal_text_t>::const_iterator t = map_terminal_text.begin(), tend = map_terminal_text.end();
2543 	while (t != tend) {
2544 		total += packed_terminal_length(*t);
2545 		t++;
2546 	}
2547 
2548 	return total;
2549 }
2550 
2551 
2552 /*
2553  *  Unpack terminal data from stream
2554  */
2555 
unpack_map_terminal_data(uint8 * p,size_t count)2556 void unpack_map_terminal_data(uint8 *p, size_t count)
2557 {
2558 	// Clear existing terminals
2559 	map_terminal_text.clear();
2560 
2561 	// Unpack all terminals
2562 	while (count > 0) {
2563 
2564 		// Create new terminal_text_t
2565 		terminal_text_t t;
2566 		map_terminal_text.push_back(t);
2567 		terminal_text_t &data = map_terminal_text.back();
2568 
2569 		// Read header
2570 		uint8 *p_start = p, *p_header = p;
2571 		uint16 total_length, grouping_count, font_changes_count;
2572 		StreamToValue(p, total_length);
2573 		StreamToValue(p, data.flags);
2574 		StreamToValue(p, data.lines_per_page);
2575 		StreamToValue(p, grouping_count);
2576 		StreamToValue(p, font_changes_count);
2577 		assert((p - p_start) == static_cast<ptrdiff_t>(SIZEOF_static_preprocessed_terminal_data));
2578 
2579 		// Reserve memory for groupings and font changes
2580 		data.groupings.reserve(grouping_count);
2581 		data.font_changes.reserve(font_changes_count);
2582 
2583 		// Read groupings
2584 		p_start = p;
2585 		for (int i=0; i<grouping_count; i++) {
2586 			terminal_groupings g;
2587 			StreamToValue(p, g.flags);
2588 			StreamToValue(p, g.type);
2589 			StreamToValue(p, g.permutation);
2590 			StreamToValue(p, g.start_index);
2591 			StreamToValue(p, g.length);
2592 			StreamToValue(p, g.maximum_line_count);
2593 			data.groupings.push_back(g);
2594 		}
2595 		assert((p - p_start) == static_cast<ptrdiff_t>(SIZEOF_terminal_groupings) * grouping_count);
2596 
2597 		// Read font changes
2598 		p_start = p;
2599 		for (int i=0; i<font_changes_count; i++) {
2600 			text_face_data f;
2601 			StreamToValue(p, f.index);
2602 			StreamToValue(p, f.face);
2603 			StreamToValue(p, f.color);
2604 			data.font_changes.push_back(f);
2605 		}
2606 		assert((p - p_start) == static_cast<ptrdiff_t>(SIZEOF_text_face_data) * font_changes_count);
2607 
2608 		// Read text (no conversion)
2609 		const int text_length = total_length - static_cast<int>(p - p_header);
2610 		assert(text_length >= 0);
2611 		data.text.resize(text_length);
2612 		StreamToBytes(p, data.text.data(), data.text.size());
2613 
2614 		// Continue with next terminal
2615 		count -= total_length;
2616 	}
2617 }
2618 
2619 
2620 /*
2621  *  Pack terminal data to stream
2622  */
2623 
pack_map_terminal_data(uint8 * p,size_t count)2624 void pack_map_terminal_data(uint8 *p, size_t count)
2625 {
2626 	// Pack all terminals
2627 	vector<terminal_text_t>::const_iterator t = map_terminal_text.begin(), tend = map_terminal_text.end();
2628 	while (t != tend) {
2629 
2630 		// Write header
2631 		uint8 *p_start = p;
2632 		uint16 total_length = static_cast<uint16>(packed_terminal_length(*t));
2633 		uint16 grouping_count = static_cast<uint16>(t->groupings.size());
2634 		uint16 font_changes_count = static_cast<uint16>(t->font_changes.size());
2635 		ValueToStream(p, total_length);
2636 		ValueToStream(p, t->flags);
2637 		ValueToStream(p, t->lines_per_page);
2638 		ValueToStream(p, grouping_count);
2639 		ValueToStream(p, font_changes_count);
2640 		assert((p - p_start) == static_cast<ptrdiff_t>(SIZEOF_static_preprocessed_terminal_data));
2641 
2642 		// Write groupings
2643 		p_start = p;
2644 		vector<terminal_groupings>::const_iterator g = t->groupings.begin(), gend = t->groupings.end();
2645 		while (g != gend) {
2646 			ValueToStream(p, g->flags);
2647 			ValueToStream(p, g->type);
2648 			ValueToStream(p, g->permutation);
2649 			ValueToStream(p, g->start_index);
2650 			ValueToStream(p, g->length);
2651 			ValueToStream(p, g->maximum_line_count);
2652 			g++;
2653 		}
2654 		assert((p - p_start) == static_cast<ptrdiff_t>(SIZEOF_terminal_groupings) * grouping_count);
2655 
2656 		// Write font changes
2657 		p_start = p;
2658 		vector<text_face_data>::const_iterator f = t->font_changes.begin(), fend = t->font_changes.end();
2659 		while (f != fend) {
2660 			ValueToStream(p, f->index);
2661 			ValueToStream(p, f->face);
2662 			ValueToStream(p, f->color);
2663 			f++;
2664 		}
2665 		assert((p - p_start) == static_cast<ptrdiff_t>(SIZEOF_text_face_data) * font_changes_count);
2666 
2667 		// Write text (no conversion)
2668 		BytesToStream(p, t->text.data(), t->text.size());
2669 
2670 		// Continue with next terminal
2671 		t++;
2672 	}
2673 }
2674 
2675 
unpack_player_terminal_data(uint8 * Stream,size_t Count)2676 uint8 *unpack_player_terminal_data(uint8 *Stream, size_t Count)
2677 {
2678 	uint8* S = Stream;
2679 	player_terminal_data* ObjPtr = player_terminals;
2680 
2681 	for (size_t k = 0; k < Count; k++, ObjPtr++)
2682 	{
2683 		StreamToValue(S,ObjPtr->flags);
2684 		StreamToValue(S,ObjPtr->phase);
2685 		StreamToValue(S,ObjPtr->state);
2686 		StreamToValue(S,ObjPtr->current_group);
2687 		StreamToValue(S,ObjPtr->level_completion_state);
2688 		StreamToValue(S,ObjPtr->current_line);
2689 		StreamToValue(S,ObjPtr->maximum_line);
2690 		StreamToValue(S,ObjPtr->terminal_id);
2691 		StreamToValue(S,ObjPtr->last_action_flag);
2692 	}
2693 
2694 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_player_terminal_data));
2695 	return S;
2696 }
2697 
pack_player_terminal_data(uint8 * Stream,size_t Count)2698 uint8 *pack_player_terminal_data(uint8 *Stream, size_t Count)
2699 {
2700 	uint8* S = Stream;
2701 	player_terminal_data* ObjPtr = player_terminals;
2702 
2703 	for (size_t k = 0; k < Count; k++, ObjPtr++)
2704 	{
2705 		ValueToStream(S,ObjPtr->flags);
2706 		ValueToStream(S,ObjPtr->phase);
2707 		ValueToStream(S,ObjPtr->state);
2708 		ValueToStream(S,ObjPtr->current_group);
2709 		ValueToStream(S,ObjPtr->level_completion_state);
2710 		ValueToStream(S,ObjPtr->current_line);
2711 		ValueToStream(S,ObjPtr->maximum_line);
2712 		ValueToStream(S,ObjPtr->terminal_id);
2713 		ValueToStream(S,ObjPtr->last_action_flag);
2714 	}
2715 
2716 	assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_player_terminal_data));
2717 	return S;
2718 }
2719