1 /*
2 	images.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 	Thursday, July 20, 1995 3:29:30 PM- rdm created.
22 
23 Feb. 4, 2000 (Loren Petrich):
24 	Changed halt() to assert(false) for better debugging
25 
26 Feb. 5, 2000 (Loren Petrich):
27 	Better handling of case of no scenario-file resource fork
28 
29 Aug 21, 2000 (Loren Petrich):
30 	Added object-oriented file handling
31 
32 	LoadedResource handles are assumed to always be locked,
33 	and HLock() and HUnlock() have been suppressed for that reason.
34 
35 Jul 6, 2001 (Loren Petrich):
36 	Added Thomas Herzog's changes for loading Win32-version image chunks
37 
38 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
39 	Added TARGET_API_MAC_CARBON for Quicktime.h
40 
41 Jul 31, 2002 (Loren Petrich)
42 	Added text-resource access in analogy with others' image- and sound-resource access;
43 	this is for supporting the M2-Win95 file format
44  */
45 
46 #include "cseries.h"
47 #include "FileHandler.h"
48 
49 #include <stdlib.h>
50 
51 #include "interface.h"
52 #include "shell.h"
53 #include "images.h"
54 #include "screen.h"
55 #include "wad.h"
56 #include "screen_drawing.h"
57 #include "Logging.h"
58 
59 #include "render.h"
60 #include "OGL_Render.h"
61 #include "OGL_Blitter.h"
62 #include "screen_definitions.h"
63 
64 
65 // Constants
66 enum {
67 	_images_file_delta16= 1000,
68 	_images_file_delta32= 2000,
69 	_scenario_file_delta16= 10000,
70 	_scenario_file_delta32= 20000
71 };
72 
73 // Structure for open image file
74 class image_file_t {
75 public:
image_file_t()76 	image_file_t() {}
~image_file_t()77 	~image_file_t() {close_file();}
78 
79 	bool open_file(FileSpecifier &file);
80 	void close_file(void);
81 	bool is_open(void);
82 
83 	int determine_pict_resource_id(int base_id, int delta16, int delta32);
84 
85 	bool has_pict(int id);
86 	bool has_clut(int id);
87 
88 	bool get_pict(int id, LoadedResource &rsrc);
89 	bool get_clut(int id, LoadedResource &rsrc);
90 	bool get_snd(int id, LoadedResource &rsrc);
91 	bool get_text(int id, LoadedResource &rsrc);
92 
93 private:
94 	bool has_rsrc(uint32 rsrc_type, uint32 wad_type, int id);
95 	bool get_rsrc(uint32 rsrc_type, uint32 wad_type, int id, LoadedResource &rsrc);
96 
97 	bool make_rsrc_from_pict(void *data, size_t length, LoadedResource &rsrc, void *clut_data, size_t clut_length);
98 	bool make_rsrc_from_clut(void *data, size_t length, LoadedResource &rsrc);
99 
100 	OpenedResourceFile rsrc_file;
101 	OpenedFile wad_file;
102 	wad_header wad_hdr;
103 };
104 
105 // Global variables
106 static image_file_t ImagesFile;
107 static image_file_t ScenarioFile;
108 static image_file_t ExternalResourcesFile;
109 static image_file_t ShapesImagesFile;
110 static image_file_t SoundsImagesFile;
111 
112 // Prototypes
113 static void shutdown_images_handler(void);
114 static void draw_picture(LoadedResource &PictRsrc);
115 
116 
117 #include <SDL_endian.h>
118 
119 #ifdef HAVE_SDL_IMAGE
120 #include <SDL_image.h>
121 #endif
122 
123 #include "byte_swapping.h"
124 #include "screen_drawing.h"
125 
126 
127 // From screen_sdl.cpp
128 extern short interface_bit_depth;
129 
130 // From screen_drawing_sdl.cpp
131 extern bool draw_clip_rect_active;
132 extern screen_rectangle draw_clip_rect;
133 
134 extern bool shapes_file_is_m1();
135 
136 /*
137  *  Uncompress picture data, returns size of compressed image data that was read
138  */
139 
140 // Uncompress (and endian-correct) scan line compressed by PackBits RLE algorithm
141 template <class T>
unpack_bits(const uint8 * src,int row_bytes,T * dst)142 static const uint8 *unpack_bits(const uint8 *src, int row_bytes, T *dst)
143 {
144 	// Read source count
145 	int src_count;
146 	if (row_bytes > 250) {
147 		src_count = (src[0] << 8) | src[1];
148 		src += 2;
149 	} else
150 		src_count = *src++;
151 
152 	while (src_count > 0) {
153 
154 		// Read flag/count byte
155 		int c = (int8)*src++;
156 		src_count--;
157 		if (c < 0) {
158 
159 			// RLE compressed run
160 			int size = -c + 1;
161 			T data;
162 			if (sizeof(T) == 1) {
163 				data = *src++;
164 				src_count--;
165 			} else {
166 				data = (src[0] << 8) | src[1];
167 				src += 2;
168 				src_count -= 2;
169 			}
170 			for (int i=0; i<size; i++)
171 				*dst++ = data;
172 
173 		} else {
174 
175 			// Uncompressed run
176 			int size = c + 1;
177 			for (int i=0; i<size; i++) {
178 				T data;
179 				if (sizeof(T) == 1) {
180 					data = *src++;
181 					src_count--;
182 				} else {
183 					data = (src[0] << 8) | src[1];
184 					src += 2;
185 					src_count -= 2;
186 				}
187 				*dst++ = data;
188 			}
189 		}
190 	}
191 	return src;
192 }
193 
194 // 8-bit picture, one scan line at a time
uncompress_rle8(const uint8 * src,int row_bytes,uint8 * dst,int dst_pitch,int height)195 static int uncompress_rle8(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int height)
196 {
197 	const uint8 *start = src;
198 	for (int y=0; y<height; y++) {
199 		src = unpack_bits(src, row_bytes, dst);
200 		dst += dst_pitch;
201 	}
202 	return static_cast<int>(src - start);
203 }
204 
205 // 16-bit picture, one scan line at a time, 16-bit chunks
uncompress_rle16(const uint8 * src,int row_bytes,uint8 * dst,int dst_pitch,int height)206 static int uncompress_rle16(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int height)
207 {
208 	const uint8 *start = src;
209 	for (int y=0; y<height; y++) {
210 		src = unpack_bits(src, row_bytes, (uint16 *)dst);
211 		dst += dst_pitch;
212 	}
213 	return static_cast<int>(src - start);
214 }
215 
copy_component_into_surface(const uint8 * src,uint8 * dst,int count,int component)216 static void copy_component_into_surface(const uint8 *src, uint8 *dst, int count, int component)
217 {
218 	if (PlatformIsLittleEndian()) {
219 		dst += 2 - component;
220 	} else {
221 		dst += component + 1;
222 	}
223 	while (count--) {
224 		*dst = *src++;
225 		dst += 4;
226 	}
227 }
228 
229 // 32-bit picture, one scan line, one component at a time
uncompress_rle32(const uint8 * src,int row_bytes,uint8 * dst,int dst_pitch,int height)230 static int uncompress_rle32(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int height)
231 {
232 	uint8 *tmp = (uint8 *)malloc(row_bytes);
233 	if (tmp == NULL)
234 		return -1;
235 	memset(tmp, 0, row_bytes);
236 
237 	const uint8 *start = src;
238 
239 	int width = row_bytes / 4;
240 	for (int y=0; y<height; y++) {
241 		src = unpack_bits(src, row_bytes, tmp);
242 
243 		// "tmp" now contains "width" bytes of red, followed by "width"
244 		// bytes of green and "width" bytes of blue, so we have to copy them
245 		// into the surface in the right order
246 		copy_component_into_surface(tmp, dst, width, 0);
247 		copy_component_into_surface(tmp + width, dst, width, 1);
248 		copy_component_into_surface(tmp + width * 2, dst, width, 2);
249 
250 		dst += dst_pitch;
251 	}
252 
253 	free(tmp);
254 
255 	return static_cast<int>(src - start);
256 }
257 
uncompress_picture(const uint8 * src,int row_bytes,uint8 * dst,int dst_pitch,int depth,int height,int pack_type)258 static int uncompress_picture(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int depth, int height, int pack_type)
259 {
260 	// Depths <8 have to be color expanded to depth 8 after uncompressing,
261 	// so we uncompress into a temporary buffer
262 	uint8 *orig_dst = dst;
263 	int orig_dst_pitch = dst_pitch;
264 	if (depth < 8) {
265 		dst = (uint8 *)malloc(row_bytes * height);
266 		dst_pitch = row_bytes;
267 		if (dst == NULL)
268 			return -1;
269 	}
270 
271 	int data_size = 0;
272 
273 	if (row_bytes < 8) {
274 
275 		// Uncompressed data
276 		const uint8 *p = src;
277 		uint8 *q = dst;
278 		for (int y=0; y<height; y++) {
279 			memcpy(q, p, MIN(row_bytes, dst_pitch));
280 			p += row_bytes;
281 			q += dst_pitch;
282 		}
283 		data_size = row_bytes * height;
284 
285 	} else {
286 
287 		// Compressed data
288 		if (depth <= 8) {
289 
290 			// Indexed color
291 			if (pack_type == 1)
292 				goto no_packing;
293 			data_size = uncompress_rle8(src, row_bytes, dst, dst_pitch, height);
294 
295 		} else {
296 
297 			// Direct color
298 			if (pack_type == 0) {
299 				if (depth == 16)
300 					pack_type = 3;
301 				else if (depth == 32)
302 					pack_type = 4;
303 
304 			}
305 			switch (pack_type) {
306 				case 1: {	// No packing
307 no_packing:			const uint8 *p = src;
308 					uint8 *q = dst;
309 					for (int y=0; y<height; y++) {
310 						memcpy(q, p, MIN(row_bytes, dst_pitch));
311 						p += row_bytes;
312 						q += dst_pitch;
313 					}
314 					data_size = row_bytes * height;
315 					if (depth == 16)
316 						byte_swap_memory(dst, _2byte, dst_pitch * height / 2);
317 					else if (depth == 32)
318 						byte_swap_memory(dst, _4byte, dst_pitch * height / 4);
319 					break;
320 				}
321 				case 3:		// Run-length encoding by 16-bit chunks
322 					data_size = uncompress_rle16(src, row_bytes, dst, dst_pitch, height);
323 					break;
324 				case 4:		// Run-length encoding one component at a time
325 					data_size = uncompress_rle32(src, row_bytes, dst, dst_pitch, height);
326 					break;
327 				default:
328 					fprintf(stderr, "Unimplemented packing type %d (depth %d) in PICT resource\n", pack_type, depth);
329 					data_size = -1;
330 					break;
331 			}
332 		}
333 	}
334 
335 	// Color expansion 1/2/4->8 bits
336 	if (depth < 8) {
337 		const uint8 *p = dst;
338 		uint8 *q = orig_dst;
339 
340 		// Source and destination may have different alignment restrictions,
341 		// don't run off the right of either
342 		int x_max = row_bytes;
343 		while (x_max * 8 / depth > orig_dst_pitch)
344 			x_max--;
345 
346 		switch (depth) {
347 			case 1:
348 				for (int y=0; y<height; y++) {
349 					for (int x=0; x<x_max; x++) {
350 						uint8 b = p[x];
351 						q[x*8+0] = (b & 0x80) ? 0x01 : 0x00;
352 						q[x*8+1] = (b & 0x40) ? 0x01 : 0x00;
353 						q[x*8+2] = (b & 0x20) ? 0x01 : 0x00;
354 						q[x*8+3] = (b & 0x10) ? 0x01 : 0x00;
355 						q[x*8+4] = (b & 0x08) ? 0x01 : 0x00;
356 						q[x*8+5] = (b & 0x04) ? 0x01 : 0x00;
357 						q[x*8+6] = (b & 0x02) ? 0x01 : 0x00;
358 						q[x*8+7] = (b & 0x01) ? 0x01 : 0x00;
359 					}
360 					p += row_bytes;
361 					q += orig_dst_pitch;
362 				}
363 				break;
364 			case 2:
365 				for (int y=0; y<height; y++) {
366 					for (int x=0; x<x_max; x++) {
367 						uint8 b = p[x];
368 						q[x*4+0] = (b >> 6) & 0x03;
369 						q[x*4+1] = (b >> 4) & 0x03;
370 						q[x*4+2] = (b >> 2) & 0x03;
371 						q[x*4+3] = b & 0x03;
372 					}
373 					p += row_bytes;
374 					q += orig_dst_pitch;
375 				}
376 				break;
377 			case 4:
378 				for (int y=0; y<height; y++) {
379 					for (int x=0; x<x_max; x++) {
380 						uint8 b = p[x];
381 						q[x*2+0] = (b >> 4) & 0x0f;
382 						q[x*2+1] = b & 0x0f;
383 					}
384 					p += row_bytes;
385 					q += orig_dst_pitch;
386 				}
387 				break;
388 		}
389 		free(dst);
390 	}
391 
392 	return data_size;
393 }
394 
get_pict_header_width(LoadedResource & rsrc)395 int get_pict_header_width(LoadedResource &rsrc)
396 {
397 	SDL_RWops *p = SDL_RWFromMem(rsrc.GetPointer(), (int) rsrc.GetLength());
398 	if (p)
399 	{
400 		SDL_RWseek(p, 8, SEEK_CUR);
401 		int width = SDL_ReadBE16(p);
402 		SDL_RWclose(p);
403 		return width;
404 	}
405 	return -1;
406 }
407 
408 /*
409  *  Convert picture resource to SDL surface
410  */
411 
picture_to_surface(LoadedResource & rsrc)412 SDL_Surface *picture_to_surface(LoadedResource &rsrc)
413 {
414 	if (!rsrc.IsLoaded())
415 		return NULL;
416 
417 	SDL_Surface *s = NULL;
418 
419 	// Open stream to picture resource
420 	SDL_RWops *p = SDL_RWFromMem(rsrc.GetPointer(), (int)rsrc.GetLength());
421 	if (p == NULL)
422 		return NULL;
423 	SDL_RWseek(p, 6, SEEK_CUR);		// picSize/top/left
424 	int pic_height = SDL_ReadBE16(p);
425 	int pic_width = SDL_ReadBE16(p);
426 	//printf("pic_width %d, pic_height %d\n", pic_width, pic_height);
427 
428 	// Read and parse picture opcodes
429 	bool done = false;
430 	while (!done) {
431 		uint16 opcode = SDL_ReadBE16(p);
432 		//printf("%04x\n", opcode);
433 		switch (opcode) {
434 
435 			case 0x0000:	// NOP
436 			case 0x0011:	// VersionOp
437 			case 0x001c:	// HiliteMode
438 			case 0x001e:	// DefHilite
439 			case 0x0038:	// FrameSameRect
440 			case 0x0039:	// PaintSameRect
441 			case 0x003a:	// EraseSameRect
442 			case 0x003b:	// InvertSameRect
443 			case 0x003c:	// FillSameRect
444 			case 0x02ff:	// Version
445 				break;
446 
447 			case 0x00ff:	// OpEndPic
448 				done = true;
449 				break;
450 
451 			case 0x0001: {	// Clipping region
452 				uint16 size = SDL_ReadBE16(p);
453 				if (size & 1)
454 					size++;
455 				SDL_RWseek(p, size - 2, SEEK_CUR);
456 				break;
457 			}
458 
459 			case 0x0003:	// TxFont
460 			case 0x0004:	// TxFace
461 			case 0x0005:	// TxMode
462 			case 0x0008:	// PnMode
463 			case 0x000d:	// TxSize
464 			case 0x0015:	// PnLocHFrac
465 			case 0x0016:	// ChExtra
466 			case 0x0023:	// ShortLineFrom
467 			case 0x00a0:	// ShortComment
468 				SDL_RWseek(p, 2, SEEK_CUR);
469 				break;
470 
471 			case 0x0006:	// SpExtra
472 			case 0x0007:	// PnSize
473 			case 0x000b:	// OvSize
474 			case 0x000c:	// Origin
475 			case 0x000e:	// FgColor
476 			case 0x000f:	// BgColor
477 			case 0x0021:	// LineFrom
478 				SDL_RWseek(p, 4, SEEK_CUR);
479 				break;
480 
481 			case 0x001a:	// RGBFgCol
482 			case 0x001b:	// RGBBkCol
483 			case 0x001d:	// HiliteColor
484 			case 0x001f:	// OpColor
485 			case 0x0022:	// ShortLine
486 				SDL_RWseek(p, 6, SEEK_CUR);
487 				break;
488 
489 			case 0x0002:	// BkPat
490 			case 0x0009:	// PnPat
491 			case 0x000a:	// FillPat
492 			case 0x0010:	// TxRatio
493 			case 0x0020:	// Line
494 			case 0x0030:	// FrameRect
495 			case 0x0031:	// PaintRect
496 			case 0x0032:	// EraseRect
497 			case 0x0033:	// InvertRect
498 			case 0x0034:	// FillRect
499 				SDL_RWseek(p, 8, SEEK_CUR);
500 				break;
501 
502 			case 0x0c00:	// HeaderOp
503 				SDL_RWseek(p, 24, SEEK_CUR);
504 				break;
505 
506 			case 0x00a1: {	// LongComment
507 				SDL_RWseek(p, 2, SEEK_CUR);
508 				int size = SDL_ReadBE16(p);
509 				if (size & 1)
510 					size++;
511 				SDL_RWseek(p, size, SEEK_CUR);
512 				break;
513 			}
514 
515 			case 0x0098:	// Packed CopyBits
516 			case 0x0099:	// Packed CopyBits with clipping region
517 			case 0x009a:	// Direct CopyBits
518 			case 0x009b: {	// Direct CopyBits with clipping region
519 				// 1. PixMap
520 				if (opcode == 0x009a || opcode == 0x009b)
521 					SDL_RWseek(p, 4, SEEK_CUR);		// pmBaseAddr
522 				uint16 row_bytes = SDL_ReadBE16(p);	// the upper 2 bits are flags
523 				//printf(" row_bytes %04x\n", row_bytes);
524 				bool is_pixmap = ((row_bytes & 0x8000) != 0);
525 				row_bytes &= 0x3fff;
526 				uint16 top = SDL_ReadBE16(p);
527 				uint16 left = SDL_ReadBE16(p);
528 				uint16 height = SDL_ReadBE16(p) - top;
529 				uint16 width = SDL_ReadBE16(p) - left;
530 				uint16 pack_type, pixel_size;
531 				if (is_pixmap) {
532 					SDL_RWseek(p, 2, SEEK_CUR);			// pmVersion
533 					pack_type = SDL_ReadBE16(p);
534 					SDL_RWseek(p, 14, SEEK_CUR);		// packSize/hRes/vRes/pixelType
535 					pixel_size = SDL_ReadBE16(p);
536 					SDL_RWseek(p, 16, SEEK_CUR);		// cmpCount/cmpSize/planeBytes/pmTable/pmReserved
537 				} else {
538 					pack_type = 0;
539 					pixel_size = 1;
540 				}
541 				//printf(" width %d, height %d, row_bytes %d, depth %d, pack_type %d\n", width, height, row_bytes, pixel_size, pack_type);
542 
543 				// Allocate surface for picture
544 				uint32 Rmask = 0, Gmask = 0, Bmask = 0;
545 				int surface_depth = 8;
546 				switch (pixel_size) {
547 					case 1:
548 					case 2:
549 					case 4:
550 					case 8:
551 						Rmask = Gmask = Bmask = 0;
552 						surface_depth = 8;	// SDL surfaces must be at least 8 bits depth, so we expand 1/2/4-bit pictures to 8-bit
553 						break;
554 					case 16:
555 						Rmask = 0x7c00;
556 						Gmask = 0x03e0;
557 						Bmask = 0x001f;
558 						surface_depth = 16;
559 						break;
560 					case 32:
561 						Rmask = 0x00ff0000;
562 						Gmask = 0x0000ff00;
563 						Bmask = 0x000000ff;
564 						surface_depth = 32;
565 						break;
566 					default:
567 						fprintf(stderr, "Unsupported PICT depth %d\n", pixel_size);
568 						done = true;
569 						break;
570 				}
571 				if (done)
572 					break;
573 				SDL_Surface *bm = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, surface_depth, Rmask, Gmask, Bmask, 0);
574 				if (bm == NULL) {
575 					done = true;
576 					break;
577 				}
578 
579 				// 2. ColorTable
580 				if (is_pixmap && (opcode == 0x0098 || opcode == 0x0099)) {
581 					SDL_Color colors[256];
582 					SDL_RWseek(p, 4, SEEK_CUR);			// ctSeed
583 					uint16 flags = SDL_ReadBE16(p);
584 					int num_colors = SDL_ReadBE16(p) + 1;
585 					for (int i=0; i<num_colors; i++) {
586 						uint8 value = SDL_ReadBE16(p) & 0xff;
587 						if (flags & 0x8000)
588 							value = i;
589 						colors[value].r = SDL_ReadBE16(p) >> 8;
590 						colors[value].g = SDL_ReadBE16(p) >> 8;
591 						colors[value].b = SDL_ReadBE16(p) >> 8;
592 						colors[value].a = 0xff;
593 					}
594 					SDL_SetPaletteColors(bm->format->palette, colors, 0, 256);
595 				}
596 
597 				// 3. source/destination Rect and transfer mode
598 				SDL_RWseek(p, 18, SEEK_CUR);
599 
600 				// 4. clipping region
601 				if (opcode == 0x0099 || opcode == 0x009b) {
602 					uint16 rgn_size = SDL_ReadBE16(p);
603 					SDL_RWseek(p, rgn_size - 2, SEEK_CUR);
604 				}
605 
606 				// 5. graphics data
607 				int data_size = uncompress_picture((uint8 *)rsrc.GetPointer() + SDL_RWtell(p), row_bytes, (uint8 *)bm->pixels, bm->pitch, pixel_size, height, pack_type);
608 				if (data_size < 0) {
609 					done = true;
610 					break;
611 				}
612 				if (data_size & 1)
613 					data_size++;
614 				SDL_RWseek(p, data_size, SEEK_CUR);
615 
616 				// If there's already a surface, throw away the decoded image
617 				// (actually, we could have skipped this entire opcode, but the
618 				// only way to do this is to decode the image data).
619 				// So we only draw the first image we encounter.
620 				if (s)
621 					SDL_FreeSurface(bm);
622 				else
623 					s = bm;
624 				break;
625 			}
626 
627 #ifdef HAVE_SDL_IMAGE
628 			case 0x8200: {	// Compressed QuickTime image (we only handle JPEG compression)
629 				// 1. Header
630 				uint32 opcode_size = SDL_ReadBE32(p);
631 				if (opcode_size & 1)
632 					opcode_size++;
633 				uint32 opcode_start = SDL_RWtell(p);
634 				SDL_RWseek(p, 26, SEEK_CUR);	// version/matrix (hom. part)
635 				int offset_x = SDL_ReadBE16(p);
636 				SDL_RWseek(p, 2, SEEK_CUR);
637 				int offset_y = SDL_ReadBE16(p);
638 				SDL_RWseek(p, 6, SEEK_CUR);	// matrix (remaining part)
639 				uint32 matte_size = SDL_ReadBE32(p);
640 				SDL_RWseek(p, 22, SEEK_CUR);	// matteRec/mode/srcRect/accuracy
641 				uint32 mask_size = SDL_ReadBE32(p);
642 
643 				// 2. Matte image description
644 				if (matte_size) {
645 					uint32 matte_id_size = SDL_ReadBE32(p);
646 					SDL_RWseek(p, matte_id_size - 4, SEEK_CUR);
647 				}
648 
649 				// 3. Matte data
650 				SDL_RWseek(p, matte_size, SEEK_CUR);
651 
652 				// 4. Mask region
653 				SDL_RWseek(p, mask_size, SEEK_CUR);
654 
655 				// 5. Image description
656 				uint32 id_start = SDL_RWtell(p);
657 				uint32 id_size = SDL_ReadBE32(p);
658 				uint32 codec_type = SDL_ReadBE32(p);
659 				if (codec_type != FOUR_CHARS_TO_INT('j','p','e','g')) {
660 					fprintf(stderr, "Unsupported codec type %c%c%c%c\n", codec_type >> 24, codec_type >> 16, codec_type >> 8, codec_type);
661 					done = true;
662 					break;
663 				}
664 				SDL_RWseek(p, 36, SEEK_CUR);	// resvd1/resvd2/dataRefIndex/version/revisionLevel/vendor/temporalQuality/spatialQuality/width/height/hRes/vRes
665 				uint32 data_size = SDL_ReadBE32(p);
666 				SDL_RWseek(p, id_start + id_size, SEEK_SET);
667 
668 				// Allocate surface for complete (but possibly banded) picture
669 				if (s == NULL) {
670 					s = SDL_CreateRGBSurface(SDL_SWSURFACE, pic_width, pic_height, 32,
671 #ifdef ALEPHONE_LITTLE_ENDIAN
672 								 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
673 #else
674 								 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
675 #endif
676 							);
677 					if (s == NULL) {
678 						done = true;
679 						break;
680 					}
681 				}
682 
683 				// 6. Compressed image data
684 				SDL_RWops *img = SDL_RWFromMem((uint8 *)rsrc.GetPointer() + SDL_RWtell(p), data_size);
685 				if (img == NULL) {
686 					done = true;
687 					break;
688 				}
689 				SDL_Surface *bm = IMG_LoadTyped_RW(img, true, const_cast<char*>("JPG"));
690 
691 				// Copy image (band) into surface
692 				if (bm) {
693 					SDL_Rect dst_rect = {offset_x, offset_y, bm->w, bm->h};
694 					SDL_BlitSurface(bm, NULL, s, &dst_rect);
695 					SDL_FreeSurface(bm);
696 				}
697 
698 				SDL_RWseek(p, opcode_start + opcode_size, SEEK_SET);
699 				break;
700 			}
701 #endif
702 
703 			default:
704 				if (opcode >= 0x0300 && opcode < 0x8000)
705 					SDL_RWseek(p, (opcode >> 8) * 2, SEEK_CUR);
706 				else if (opcode >= 0x8000 && opcode < 0x8100)
707 					break;
708 				else {
709 					fprintf(stderr, "Unimplemented opcode %04x in PICT resource\n", opcode);
710 					done = true;
711 				}
712 				break;
713 		}
714 	}
715 
716 	// Close stream, return surface
717 	SDL_RWclose(p);
718 	return s;
719 }
720 
721 
722 /*
723  *  Rescale surface to given dimensions
724  */
725 
726 template <class T>
rescale(T * src_pixels,int src_pitch,T * dst_pixels,int dst_pitch,int width,int height,uint32 dx,uint32 dy)727 static void rescale(T *src_pixels, int src_pitch, T *dst_pixels, int dst_pitch, int width, int height, uint32 dx, uint32 dy)
728 {
729 	// Brute-force rescaling, no interpolation
730 	uint32 sy = 0;
731 	for (int y=0; y<height; y++) {
732 		T *p = src_pixels + src_pitch / sizeof(T) * (sy >> 16);
733 		uint32 sx = 0;
734 		for (int x=0; x<width; x++) {
735 			dst_pixels[x] = p[sx >> 16];
736 			sx += dx;
737 		}
738 		dst_pixels += dst_pitch / sizeof(T);
739 		sy += dy;
740 	}
741 }
742 
rescale_surface(SDL_Surface * s,int width,int height)743 SDL_Surface *rescale_surface(SDL_Surface *s, int width, int height)
744 {
745 	if (s == NULL)
746 		return NULL;
747 
748 	SDL_Surface *s2 = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask, s->format->Bmask, s->format->Amask);
749 	if (s2 == NULL)
750 		return NULL;
751 
752 	uint32 dx = (s->w << 16) / width;
753 	uint32 dy = (s->h << 16) / height;
754 
755 	switch (s->format->BytesPerPixel) {
756 		case 1:
757 			rescale((pixel8 *)s->pixels, s->pitch, (pixel8 *)s2->pixels, s2->pitch, width, height, dx, dy);
758 			break;
759 		case 2:
760 			rescale((pixel16 *)s->pixels, s->pitch, (pixel16 *)s2->pixels, s2->pitch, width, height, dx, dy);
761 			break;
762 		case 4:
763 			rescale((pixel32 *)s->pixels, s->pitch, (pixel32 *)s2->pixels, s2->pitch, width, height, dx, dy);
764 			break;
765 	}
766 
767 	if (s->format->palette)
768 		SDL_SetPaletteColors(s2->format->palette, s->format->palette->colors, 0, s->format->palette->ncolors);
769 
770 	return s2;
771 }
772 
773 
774 /*
775  *  Tile surface to fill given dimensions
776  */
777 
778 template <class T>
tile(T * src_pixels,int src_pitch,T * dst_pixels,int dst_pitch,int src_width,int src_height,int dst_width,int dst_height)779 static void tile(T *src_pixels, int src_pitch, T *dst_pixels, int dst_pitch, int src_width, int src_height, int dst_width, int dst_height)
780 {
781 	T *p = src_pixels;
782 	int sy = 0;
783 	for (int y=0; y<dst_height; y++) {
784 		int sx = 0;
785 		for (int x=0; x<dst_width; x++) {
786 			dst_pixels[x] = p[sx];
787 			sx++;
788 			if (sx == src_width)
789 				sx = 0;
790 		}
791 		dst_pixels += dst_pitch / sizeof(T);
792 		sy++;
793 		if (sy == src_height) {
794 			sy = 0;
795 			p = src_pixels;
796 		} else
797 			p += src_pitch / sizeof(T);
798 	}
799 }
800 
tile_surface(SDL_Surface * s,int width,int height)801 SDL_Surface *tile_surface(SDL_Surface *s, int width, int height)
802 {
803 	if (s == NULL)
804 		return NULL;
805 
806 	SDL_Surface *s2 = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask, s->format->Bmask, s->format->Amask);
807 	if (s2 == NULL)
808 		return NULL;
809 
810 	switch (s->format->BytesPerPixel) {
811 		case 1:
812 			tile((pixel8 *)s->pixels, s->pitch, (pixel8 *)s2->pixels, s2->pitch, s->w, s->h, width, height);
813 			break;
814 		case 2:
815 			tile((pixel16 *)s->pixels, s->pitch, (pixel16 *)s2->pixels, s2->pitch, s->w, s->h, width, height);
816 			break;
817 		case 3:
818 			tile((pixel8 *)s->pixels, s->pitch, (pixel8 *)s2->pixels, s2->pitch, s->w * 3, s->h, width * 3, height);
819 			break;
820 		case 4:
821 			tile((pixel32 *)s->pixels, s->pitch, (pixel32 *)s2->pixels, s2->pitch, s->w, s->h, width, height);
822 			break;
823 	}
824 
825 	if (s->format->palette)
826 		SDL_SetPaletteColors(s2->format->palette, s->format->palette->colors, 0, s->format->palette->ncolors);
827 
828 	return s2;
829 }
830 
831 
832 /*
833  *  Draw picture resource centered on screen
834  */
835 
836 extern SDL_Surface *draw_surface;	// from screen_drawing.cpp
837 //void draw_intro_screen(void);		// from screen.cpp
838 
draw_picture_surface(SDL_Surface * s)839 static void draw_picture_surface(SDL_Surface *s)
840 {
841 	if (s == NULL)
842 		return;
843 	_set_port_to_intro();
844 	SDL_Surface *video = draw_surface;
845 
846 	// Default source rectangle
847 	SDL_Rect src_rect = {0, 0, MIN(s->w, 640), MIN(s->h, 480)};
848 
849 	// Center picture on screen
850 	SDL_Rect dst_rect = {(video->w - src_rect.w) / 2, (video->h - src_rect.h) / 2, s->w, s->h};
851 	if (dst_rect.x < 0)
852 		dst_rect.x = 0;
853 	if (dst_rect.y < 0)
854 		dst_rect.y = 0;
855 
856 	// Clip if desired (only used for menu buttons)
857 	if (draw_clip_rect_active) {
858 		src_rect.w = dst_rect.w = draw_clip_rect.right - draw_clip_rect.left;
859 		src_rect.h = dst_rect.h = draw_clip_rect.bottom - draw_clip_rect.top;
860 		src_rect.x = draw_clip_rect.left - (640 - s->w) / 2;
861 		src_rect.y = draw_clip_rect.top - (480 - s->h) / 2;
862 		dst_rect.x += draw_clip_rect.left- (640 - s->w) / 2;
863 		dst_rect.y += draw_clip_rect.top - (480 - s->h) / 2;
864 	} else {
865 			// Clear destination to black
866 			SDL_FillRect(video, NULL, SDL_MapRGB(video->format, 0, 0, 0));
867 	}
868 
869 	SDL_BlitSurface(s, &src_rect, video, &dst_rect);
870 	_restore_port();
871 	draw_intro_screen();
872 }
873 
draw_picture(LoadedResource & rsrc)874 static void draw_picture(LoadedResource &rsrc)
875 {
876     draw_picture_surface(picture_to_surface(rsrc));
877 }
878 
879 
880 /*
881  *  Get system color table
882  */
883 
build_8bit_system_color_table(void)884 struct color_table *build_8bit_system_color_table(void)
885 {
886 	// 6*6*6 RGB color cube
887 	color_table *table = new color_table;
888 	table->color_count = 6*6*6;
889 	int index = 0;
890 	for (int red=0; red<6; red++) {
891 		for (int green=0; green<6; green++) {
892 			for (int blue=0; blue<6; blue++) {
893 				uint8 r = red * 0x33;
894 				uint8 g = green * 0x33;
895 				uint8 b = blue * 0x33;
896 				table->colors[index].red = (r << 8) | r;
897 				table->colors[index].green = (g << 8) | g;
898 				table->colors[index].blue = (b << 8) | b;
899 				index++;
900 			}
901 		}
902 	}
903 	return table;
904 }
905 
906 
907 /*
908  *  Scroll image across screen
909  */
910 
911 #define SCROLLING_SPEED (MACHINE_TICKS_PER_SECOND / 20)
912 
scroll_full_screen_pict_resource_from_scenario(int pict_resource_number,bool text_block)913 void scroll_full_screen_pict_resource_from_scenario(int pict_resource_number, bool text_block)
914 {
915 	// Convert picture resource to surface, free resource
916 	LoadedResource rsrc;
917 	get_picture_resource_from_scenario(pict_resource_number, rsrc);
918 	SDL_Surface *s = picture_to_surface(rsrc);
919 	if (s == NULL)
920 		return;
921 
922 	// Find out in which direction to scroll
923 	int picture_width = s->w;
924 	int picture_height = s->h;
925 	int screen_width = 640;
926 	int screen_height = 480;
927 	bool scroll_horizontal = picture_width > screen_width;
928 	bool scroll_vertical = picture_height > screen_height;
929 
930 	if (scroll_horizontal || scroll_vertical) {
931 
932 		// Flush events
933 		SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
934 
935 		// Prepare source and destination rectangles
936 		SDL_Rect src_rect = {0, 0, scroll_horizontal ? screen_width : picture_width, scroll_vertical ? screen_height : picture_height};
937 		SDL_Rect dst_rect = {0, 0, screen_width, screen_height};
938 
939 		// Scroll loop
940 		bool done = false, aborted = false;
941 		uint32 start_tick = SDL_GetTicks();
942 		do {
943 
944 			int32 delta = (SDL_GetTicks() - start_tick) / (text_block ? (2 * SCROLLING_SPEED) : SCROLLING_SPEED);
945 			if (scroll_horizontal && delta > picture_width - screen_width) {
946 				delta = picture_width - screen_width;
947 				done = true;
948 			}
949 			if (scroll_vertical && delta > picture_height - screen_height) {
950 				delta = picture_height - screen_height;
951 				done = true;
952 			}
953 
954 			// Blit part of picture
955 			src_rect.x = scroll_horizontal ? delta : 0;
956 			src_rect.y = scroll_vertical ? delta : 0;
957 			_set_port_to_intro();
958 			SDL_BlitSurface(s, &src_rect, draw_surface, &dst_rect);
959 			_restore_port();
960 			draw_intro_screen();
961 
962 			// Give system time
963 			global_idle_proc();
964 			SDL_Delay(10);
965 
966 			// Check for events to abort
967 			SDL_Event event;
968 			if (SDL_PollEvent(&event)) {
969 				switch (event.type) {
970 					case SDL_MOUSEBUTTONDOWN:
971 					case SDL_KEYDOWN:
972 					case SDL_CONTROLLERBUTTONDOWN:
973 						aborted = true;
974 						break;
975 				}
976 			}
977 
978 		} while (!done && !aborted);
979 	}
980 
981 	// Free surface
982 	SDL_FreeSurface(s);
983 }
984 
985 
986 /*
987  *  Initialize image manager, open Images file
988  */
989 
initialize_images_manager(void)990 void initialize_images_manager(void)
991 {
992 	FileSpecifier file;
993 
994   logContext("loading Images...");
995 
996 	file.SetNameWithPath(getcstr(temporary, strFILENAMES, filenameIMAGES)); // _typecode_images
997 
998 	if (!file.Exists())
999         logContext("Images file not found");
1000 
1001 	if (!ImagesFile.open_file(file))
1002         logContext("Images file could not be opened");
1003 
1004 	atexit(shutdown_images_handler);
1005 }
1006 
1007 
1008 /*
1009  *  Shutdown image manager
1010  */
1011 
shutdown_images_handler(void)1012 static void shutdown_images_handler(void)
1013 {
1014 	SoundsImagesFile.close_file();
1015 	ExternalResourcesFile.close_file();
1016 	ShapesImagesFile.close_file();
1017 	ScenarioFile.close_file();
1018 	ImagesFile.close_file();
1019 }
1020 
1021 
1022 /*
1023  *  Set map file to load images from
1024  */
1025 
set_scenario_images_file(FileSpecifier & file)1026 void set_scenario_images_file(FileSpecifier &file)
1027 {
1028 	ScenarioFile.open_file(file);
1029 }
1030 
unset_scenario_images_file()1031 void unset_scenario_images_file()
1032 {
1033 	ScenarioFile.close_file();
1034 }
1035 
set_shapes_images_file(FileSpecifier & file)1036 void set_shapes_images_file(FileSpecifier &file)
1037 {
1038 	ShapesImagesFile.open_file(file);
1039 }
1040 
set_external_resources_images_file(FileSpecifier & file)1041 void set_external_resources_images_file(FileSpecifier &file)
1042 {
1043     // fail here, instead of above, if Images is missing
1044 	if ((!file.Exists() || !ExternalResourcesFile.open_file(file)) &&
1045         !ImagesFile.is_open())
1046         alert_bad_extra_file();
1047 
1048 }
1049 
set_sounds_images_file(FileSpecifier & file)1050 void set_sounds_images_file(FileSpecifier &file)
1051 {
1052 	SoundsImagesFile.open_file(file);
1053 }
1054 
1055 
1056 /*
1057  *  Open/close image file
1058  */
1059 
open_file(FileSpecifier & file)1060 bool image_file_t::open_file(FileSpecifier &file)
1061 {
1062 	close_file();
1063 
1064 	// Try to open as a resource file
1065 	if (!file.Open(rsrc_file)) {
1066 
1067 		// This failed, maybe it's a wad file (M2 Win95 style)
1068 		if (!open_wad_file_for_reading(file, wad_file)
1069 		 || !read_wad_header(wad_file, &wad_hdr)) {
1070 
1071 			// This also failed, bail out
1072 			wad_file.Close();
1073 			return false;
1074 		}
1075 	} // Try to open wad file, too
1076 	else if (!wad_file.IsOpen()) {
1077 		if (open_wad_file_for_reading(file, wad_file)) {
1078 			if (!read_wad_header(wad_file, &wad_hdr)) {
1079 
1080 				wad_file.Close();
1081 			}
1082 		}
1083 	}
1084 
1085 	return true;
1086 }
1087 
close_file(void)1088 void image_file_t::close_file(void)
1089 {
1090 	rsrc_file.Close();
1091 	wad_file.Close();
1092 }
1093 
is_open(void)1094 bool image_file_t::is_open(void)
1095 {
1096 	return rsrc_file.IsOpen() || wad_file.IsOpen();
1097 }
1098 
1099 
1100 /*
1101  *  Get resource from file
1102  */
1103 
has_rsrc(uint32 rsrc_type,uint32 wad_type,int id)1104 bool image_file_t::has_rsrc(uint32 rsrc_type, uint32 wad_type, int id)
1105 {
1106 	// Check for resource in resource file
1107 	if (rsrc_file.IsOpen())
1108 	{
1109 		if (rsrc_file.Check(rsrc_type, id))
1110 			return true;
1111 	}
1112 
1113 	// Check for resource in wad file
1114 	if (wad_file.IsOpen()) {
1115 		wad_data *d = read_indexed_wad_from_file(wad_file, &wad_hdr, id, true);
1116 		if (d) {
1117 			bool success = false;
1118 			size_t len;
1119 			if (extract_type_from_wad(d, wad_type, &len))
1120 				success = true;
1121 			free_wad(d);
1122 			return success;
1123 		}
1124 	}
1125 
1126 	return false;
1127 }
1128 
has_pict(int id)1129 bool image_file_t::has_pict(int id)
1130 {
1131 	return has_rsrc(FOUR_CHARS_TO_INT('P','I','C','T'), FOUR_CHARS_TO_INT('P','I','C','T'), id) || has_rsrc(FOUR_CHARS_TO_INT('P','I','C','T'), FOUR_CHARS_TO_INT('p','i','c','t'), id);
1132 }
1133 
has_clut(int id)1134 bool image_file_t::has_clut(int id)
1135 {
1136 	return has_rsrc(FOUR_CHARS_TO_INT('c','l','u','t'), FOUR_CHARS_TO_INT('c','l','u','t'), id);
1137 }
1138 
get_rsrc(uint32 rsrc_type,uint32 wad_type,int id,LoadedResource & rsrc)1139 bool image_file_t::get_rsrc(uint32 rsrc_type, uint32 wad_type, int id, LoadedResource &rsrc)
1140 {
1141 	// Get resource from resource file
1142 	if (rsrc_file.IsOpen())
1143 	{
1144 		if (rsrc_file.Get(rsrc_type, id, rsrc))
1145 			return true;
1146 	}
1147 
1148 	// Get resource from wad file
1149 	if (wad_file.IsOpen()) {
1150 		wad_data *d = read_indexed_wad_from_file(wad_file, &wad_hdr, id, true);
1151 		if (d) {
1152 			bool success = false;
1153 			size_t raw_length;
1154 			void *raw = extract_type_from_wad(d, wad_type, &raw_length);
1155 			if (raw)
1156 			{
1157 				if (rsrc_type == FOUR_CHARS_TO_INT('P','I','C','T'))
1158 				{
1159 					if (wad_type == FOUR_CHARS_TO_INT('P','I','C','T'))
1160 					{
1161 						void *pict_data = malloc(raw_length);
1162 						memcpy(pict_data, raw, raw_length);
1163 						rsrc.SetData(pict_data, raw_length);
1164 						success = true;
1165 					}
1166 					else
1167 					{
1168 						size_t clut_length;
1169 						void *clut_data = extract_type_from_wad(d, FOUR_CHARS_TO_INT('c','l','u','t'), &clut_length);
1170 						success = make_rsrc_from_pict(raw, raw_length, rsrc, clut_data, clut_length);
1171 					}
1172 				}
1173 				else if (rsrc_type == FOUR_CHARS_TO_INT('c','l','u','t'))
1174 					success = make_rsrc_from_clut(raw, raw_length, rsrc);
1175 				else if (rsrc_type == FOUR_CHARS_TO_INT('s','n','d',' '))
1176 				{
1177 					void *snd_data = malloc(raw_length);
1178 					memcpy(snd_data, raw, raw_length);
1179 					rsrc.SetData(snd_data, raw_length);
1180 					success = true;
1181 				}
1182 				else if (rsrc_type == FOUR_CHARS_TO_INT('T','E','X','T'))
1183 				{
1184 					void *text_data = malloc(raw_length);
1185 					memcpy(text_data, raw, raw_length);
1186 					rsrc.SetData(text_data, raw_length);
1187 					success = true;
1188 				}
1189 			}
1190 			free_wad(d);
1191 			return success;
1192 		}
1193 	}
1194 
1195 	return false;
1196 }
1197 
get_pict(int id,LoadedResource & rsrc)1198 bool image_file_t::get_pict(int id, LoadedResource &rsrc)
1199 {
1200 	return get_rsrc(FOUR_CHARS_TO_INT('P','I','C','T'), FOUR_CHARS_TO_INT('P','I','C','T'), id, rsrc) || get_rsrc(FOUR_CHARS_TO_INT('P','I','C','T'), FOUR_CHARS_TO_INT('p','i','c','t'), id, rsrc);
1201 }
1202 
get_clut(int id,LoadedResource & rsrc)1203 bool image_file_t::get_clut(int id, LoadedResource &rsrc)
1204 {
1205 	return get_rsrc(FOUR_CHARS_TO_INT('c','l','u','t'), FOUR_CHARS_TO_INT('c','l','u','t'), id, rsrc);
1206 }
1207 
get_snd(int id,LoadedResource & rsrc)1208 bool image_file_t::get_snd(int id, LoadedResource &rsrc)
1209 {
1210 	return get_rsrc(FOUR_CHARS_TO_INT('s','n','d',' '), FOUR_CHARS_TO_INT('s','n','d',' '), id, rsrc);
1211 }
1212 
get_text(int id,LoadedResource & rsrc)1213 bool image_file_t::get_text(int id, LoadedResource &rsrc)
1214 {
1215 	return get_rsrc(FOUR_CHARS_TO_INT('T','E','X','T'), FOUR_CHARS_TO_INT('t','e','x','t'), id, rsrc);
1216 }
1217 
1218 
1219 /*
1220  *  Get/draw image from Images file
1221  */
1222 
get_picture_resource_from_images(int base_resource,LoadedResource & PictRsrc)1223 bool get_picture_resource_from_images(int base_resource, LoadedResource &PictRsrc)
1224 {
1225     bool found = false;
1226 
1227     if (!found && ImagesFile.is_open())
1228         found = ImagesFile.get_pict(ImagesFile.determine_pict_resource_id(base_resource, _images_file_delta16, _images_file_delta32), PictRsrc);
1229     if (!found && ExternalResourcesFile.is_open())
1230         found = ExternalResourcesFile.get_pict(base_resource, PictRsrc);
1231     if (!found && ShapesImagesFile.is_open())
1232         found = ShapesImagesFile.get_pict(base_resource, PictRsrc);
1233 
1234     return found;
1235 }
1236 
get_sound_resource_from_images(int resource_number,LoadedResource & SoundRsrc)1237 bool get_sound_resource_from_images(int resource_number, LoadedResource &SoundRsrc)
1238 {
1239     bool found = false;
1240 
1241     if (!found && ImagesFile.is_open())
1242         found = ImagesFile.get_snd(resource_number, SoundRsrc);
1243     if (!found && SoundsImagesFile.is_open())
1244     {
1245         // Marathon 1 case: only one sound used for intro
1246         if (resource_number == 1111 || resource_number == 1114)
1247             found = SoundsImagesFile.get_snd(1240, SoundRsrc);
1248     }
1249 
1250     return found;
1251 }
1252 
images_picture_exists(int base_resource)1253 bool images_picture_exists(int base_resource)
1254 {
1255 	if (shapes_file_is_m1() && (base_resource == MAIN_MENU_BASE || base_resource == MAIN_MENU_BASE+1))
1256         return true;
1257 
1258     LoadedResource PictRsrc;
1259     return get_picture_resource_from_images(base_resource, PictRsrc);
1260 }
1261 
1262 
1263 // In the first Marathon, the main menu is drawn from multiple
1264 // shapes in collection 10, instead of a single image. We handle
1265 // this special case by creating the composite images in code,
1266 // and returning these surfaces when the picture is requested.
1267 
1268 static SDL_Surface *m1_menu_unpressed = NULL;
1269 static SDL_Surface *m1_menu_pressed = NULL;
1270 
create_m1_menu_surfaces(void)1271 static void create_m1_menu_surfaces(void)
1272 {
1273     if (m1_menu_unpressed || m1_menu_pressed)
1274         return;
1275 
1276     SDL_Surface *s = nullptr;
1277 	if (PlatformIsLittleEndian()) {
1278     	s = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1279 	} else {
1280     	s = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0);
1281 	}
1282     if (!s)
1283         return;
1284 
1285     SDL_FillRect(s, NULL, SDL_MapRGB(s->format, 0, 0, 0));
1286 
1287     SDL_Rect src, dst;
1288     src.x = src.y = 0;
1289 
1290     int top = 0;
1291     int bottom = s->h;
1292 
1293     SDL_Surface *logo = get_shape_surface(0, 10);
1294     if (!logo)
1295     {
1296         // did it fail because we haven't loaded the menu shapes?
1297         mark_collection_for_loading(10);
1298         load_collections(false, false);
1299         logo = get_shape_surface(0, 10);
1300     }
1301     if (logo)
1302     {
1303         src.w = dst.w = logo->w;
1304         src.h = dst.h = logo->h;
1305         dst.x = (s->w - logo->w)/2;
1306         dst.y = 0;
1307         SDL_BlitSurface(logo, &src, s, &dst);
1308         top += logo->h;
1309         SDL_FreeSurface(logo);
1310     }
1311 
1312     SDL_Surface *credits = get_shape_surface(19, 10);
1313     if (credits)
1314     {
1315         src.w = dst.w = credits->w;
1316         src.h = dst.h = credits->h;
1317         dst.x = (s->w - credits->w)/2;
1318         dst.y = s->h - credits->h;
1319         SDL_BlitSurface(credits, &src, s, &dst);
1320         bottom -= credits->h;
1321         SDL_FreeSurface(credits);
1322     }
1323 
1324     SDL_Surface *widget = get_shape_surface(1, 10);
1325     if (widget)
1326     {
1327         src.w = dst.w = widget->w;
1328         src.h = dst.h = widget->h;
1329         dst.x = (s->w - widget->w)/2;
1330         dst.y = top + (bottom - top - widget->h)/2;
1331         SDL_BlitSurface(widget, &src, s, &dst);
1332         SDL_FreeSurface(widget);
1333     }
1334 
1335     m1_menu_unpressed = s;
1336 
1337     // now, add pressed buttons to copy of this surface
1338     s = SDL_ConvertSurface(s, s->format, SDL_SWSURFACE);
1339 
1340     std::vector<std::pair<int, int> > button_shapes;
1341     button_shapes.push_back(std::pair<int, int>(_new_game_button_rect, 11));
1342     button_shapes.push_back(std::pair<int, int>(_load_game_button_rect, 12));
1343     button_shapes.push_back(std::pair<int, int>(_gather_button_rect, 3));
1344     button_shapes.push_back(std::pair<int, int>(_join_button_rect, 4));
1345     button_shapes.push_back(std::pair<int, int>(_prefs_button_rect, 5));
1346     button_shapes.push_back(std::pair<int, int>(_replay_last_button_rect, 6));
1347     button_shapes.push_back(std::pair<int, int>(_save_last_button_rect, 7));
1348     button_shapes.push_back(std::pair<int, int>(_replay_saved_button_rect, 8));
1349     button_shapes.push_back(std::pair<int, int>(_credits_button_rect, 9));
1350     button_shapes.push_back(std::pair<int, int>(_quit_button_rect, 10));
1351     button_shapes.push_back(std::pair<int, int>(_center_button_rect, 2));
1352     for (std::vector<std::pair<int, int> >::const_iterator it = button_shapes.begin(); it != button_shapes.end(); ++it)
1353     {
1354         screen_rectangle *r = get_interface_rectangle(it->first);
1355         SDL_Surface *btn = get_shape_surface(it->second, 10);
1356         if (btn)
1357         {
1358             src.w = dst.w = btn->w;
1359             src.h = dst.h = btn->h;
1360             dst.x = r->left;
1361             dst.y = r->top;
1362             SDL_BlitSurface(btn, &src, s, &dst);
1363             SDL_FreeSurface(btn);
1364         }
1365     }
1366 
1367     m1_menu_pressed = s;
1368 }
1369 
m1_draw_full_screen_pict_resource_from_images(int pict_resource_number)1370 static bool m1_draw_full_screen_pict_resource_from_images(int pict_resource_number)
1371 {
1372     if (!shapes_file_is_m1())
1373         return false;
1374     if (pict_resource_number == MAIN_MENU_BASE)
1375     {
1376         create_m1_menu_surfaces();
1377         draw_picture_surface(m1_menu_unpressed);
1378         return true;
1379     }
1380     else if (pict_resource_number == MAIN_MENU_BASE+1)
1381     {
1382         create_m1_menu_surfaces();
1383         draw_picture_surface(m1_menu_pressed);
1384         return true;
1385     }
1386     return false;
1387 }
1388 
draw_full_screen_pict_resource_from_images(int pict_resource_number)1389 void draw_full_screen_pict_resource_from_images(int pict_resource_number)
1390 {
1391 	if (m1_draw_full_screen_pict_resource_from_images(pict_resource_number))
1392 		return;
1393 
1394     LoadedResource PictRsrc;
1395     if (get_picture_resource_from_images(pict_resource_number, PictRsrc))
1396         draw_picture(PictRsrc);
1397 }
1398 
1399 
1400 /*
1401  *  Get/draw image from scenario
1402  */
1403 
get_picture_resource_from_scenario(int base_resource,LoadedResource & PictRsrc)1404 bool get_picture_resource_from_scenario(int base_resource, LoadedResource &PictRsrc)
1405 {
1406     bool found = false;
1407 
1408     if (!found && ScenarioFile.is_open())
1409         found = ScenarioFile.get_pict(ScenarioFile.determine_pict_resource_id(base_resource, _scenario_file_delta16, _scenario_file_delta32), PictRsrc);
1410     if (!found && ShapesImagesFile.is_open())
1411         found = ShapesImagesFile.get_pict(base_resource, PictRsrc);
1412 
1413     return found;
1414 }
1415 
scenario_picture_exists(int base_resource)1416 bool scenario_picture_exists(int base_resource)
1417 {
1418     LoadedResource PictRsrc;
1419     return get_picture_resource_from_scenario(base_resource, PictRsrc);
1420 }
1421 
draw_full_screen_pict_resource_from_scenario(int pict_resource_number)1422 void draw_full_screen_pict_resource_from_scenario(int pict_resource_number)
1423 {
1424 	LoadedResource PictRsrc;
1425 	if (get_picture_resource_from_scenario(pict_resource_number, PictRsrc))
1426         draw_picture(PictRsrc);
1427 }
1428 
1429 
1430 /*
1431  *  Get sound resource from scenario
1432  */
1433 
get_sound_resource_from_scenario(int resource_number,LoadedResource & SoundRsrc)1434 bool get_sound_resource_from_scenario(int resource_number, LoadedResource &SoundRsrc)
1435 {
1436     bool found = false;
1437 
1438     if (!found && ScenarioFile.is_open())
1439         found = ScenarioFile.get_snd(resource_number, SoundRsrc);
1440     if (!found && SoundsImagesFile.is_open())
1441         // Marathon 1 case: only one sound used for chapter screens
1442         found = SoundsImagesFile.get_snd(1240, SoundRsrc);
1443 
1444     return found;
1445 }
1446 
1447 
1448 // LP: do the same for text resources
1449 
get_text_resource_from_scenario(int resource_number,LoadedResource & TextRsrc)1450 bool get_text_resource_from_scenario(int resource_number, LoadedResource &TextRsrc)
1451 {
1452 	if (!ScenarioFile.is_open())
1453 		return false;
1454 
1455 	bool success = ScenarioFile.get_text(resource_number, TextRsrc);
1456 	return success;
1457 }
1458 
1459 
1460 /*
1461  *  Calculate color table for image
1462  */
1463 
calculate_picture_clut(int CLUTSource,int pict_resource_number)1464 struct color_table *calculate_picture_clut(int CLUTSource, int pict_resource_number)
1465 {
1466 	struct color_table *picture_table = NULL;
1467 
1468 #if 1
1469     // with TRUE_COLOR_ONLY turned on, specific cluts don't matter
1470     picture_table = build_8bit_system_color_table();
1471     build_direct_color_table(picture_table, interface_bit_depth);
1472 #else
1473 	// Select the source
1474 	image_file_t *OFilePtr = NULL;
1475 	switch (CLUTSource) {
1476 		case CLUTSource_Images:
1477 			OFilePtr = &ImagesFile;
1478 			break;
1479 
1480 		case CLUTSource_Scenario:
1481 			OFilePtr = &ScenarioFile;
1482 			break;
1483 
1484 		default:
1485 			vassert(false, csprintf(temporary, "Invalid resource-file selector: %d", CLUTSource));
1486 			break;
1487 	}
1488 
1489 	// Load CLUT resource
1490 	LoadedResource CLUT_Rsrc;
1491 	if (OFilePtr->get_clut(pict_resource_number, CLUT_Rsrc)) {
1492 
1493 		// Allocate color table
1494 		picture_table = new color_table;
1495 
1496 		// Convert MacOS CLUT resource to color table
1497 		if (interface_bit_depth == 8)
1498 			build_color_table(picture_table, CLUT_Rsrc);
1499 		else
1500 			build_direct_color_table(picture_table, interface_bit_depth);
1501 	}
1502 
1503 #endif
1504 	return picture_table;
1505 }
1506 
1507 
1508 /*
1509  *  Determine ID for picture resource
1510  */
1511 
determine_pict_resource_id(int base_id,int delta16,int delta32)1512 int image_file_t::determine_pict_resource_id(int base_id, int delta16, int delta32)
1513 {
1514 	int actual_id = base_id;
1515 	bool done = false;
1516 	int bit_depth = interface_bit_depth;
1517 
1518 	while (!done) {
1519 		int next_bit_depth;
1520 
1521 		actual_id = base_id;
1522 		switch(bit_depth) {
1523 			case 8:
1524 				next_bit_depth = 0;
1525 				break;
1526 
1527 			case 16:
1528 				next_bit_depth = 8;
1529 				actual_id += delta16;
1530 				break;
1531 
1532 			case 32:
1533 				next_bit_depth = 16;
1534 				actual_id += delta32;
1535 				break;
1536 
1537 			default:
1538 				assert(false);
1539 				break;
1540 		}
1541 
1542 		if (has_pict(actual_id))
1543 			done = true;
1544 
1545 		if (!done) {
1546 			if (next_bit_depth)
1547 				bit_depth = next_bit_depth;
1548 			else {
1549 				// Didn't find it. Return the 8 bit version and bail..
1550 				done = true;
1551 			}
1552 		}
1553 	}
1554 	return actual_id;
1555 }
1556 
1557 
1558 /*
1559  *  Convert picture and CLUT data from wad file to PICT resource
1560  */
1561 
make_rsrc_from_pict(void * data,size_t length,LoadedResource & rsrc,void * clut_data,size_t clut_length)1562 bool image_file_t::make_rsrc_from_pict(void *data, size_t length, LoadedResource &rsrc, void *clut_data, size_t clut_length)
1563 {
1564 	if (length < 10)
1565 		return false;
1566 
1567 	// Extract size and depth
1568 	uint8 *p = (uint8 *)data;
1569 	int height = (p[4] << 8) + p[5];
1570 	int width = (p[6] << 8) + p[7];
1571 	int depth = (p[8] << 8) + p[9];
1572 	if (depth != 8 && depth != 16)
1573 		return false;
1574 
1575 	// 8-bit depth requires CLUT
1576 	if (depth == 8) {
1577 		if (clut_data == NULL || clut_length != 6 + 256 * 6)
1578 			return false;
1579 	}
1580 
1581 	// size(2), rect(8), versionOp(2), version(2), headerOp(26)
1582 	int output_length = 2 + 8 + 2 + 2 + 26;
1583 	int row_bytes;
1584 	if (depth == 8) {
1585 		// opcode(2), pixMap(46), colorTable(8+256*8), srcRect/dstRect/mode(18), data(variable)
1586 		row_bytes = width;
1587 		output_length += 2 + 46 + 8+256*8 + 18;
1588 	} else {
1589 		// opcode(2), pixMap(50), srcRect/dstRect/mode(18), data(variable)
1590 		row_bytes = width * 2;
1591 		output_length += 2 + 50 + 18;
1592 	}
1593 	// data(variable), opEndPic(2)
1594 	output_length += row_bytes * height + 2;
1595 
1596 	// Allocate memory for Mac PICT resource
1597 	void *pict_rsrc = malloc(output_length);
1598 	if (pict_rsrc == NULL)
1599 		return false;
1600 	memset(pict_rsrc, 0, output_length);
1601 
1602 	// Convert pict tag to Mac PICT resource
1603 	uint8 *q = (uint8 *)pict_rsrc;
1604 
1605 	// 1. PICT header
1606 	q[0] = output_length >> 8;
1607 	q[1] = output_length;
1608 	memcpy(q + 2, p, 8);
1609 	q += 10;
1610 
1611 	// 2. VersionOp/Version/HeaderOp
1612 	q[0] = 0x00; q[1] = 0x11; // versionOp
1613 	q[2] = 0x02; q[3] = 0xff; // version
1614 	q[4] = 0x0c; q[5] = 0x00; // headerOp
1615 	q[6] = 0xff; q[7] = 0xfe; // header version
1616 	q[11] = 0x48; // hRes
1617 	q[15] = 0x48; // vRes
1618 	memcpy(q + 18, p, 8);
1619 	q += 30;
1620 
1621 	// 3. opcode
1622 	if (depth == 8) {
1623 		q[0] = 0x00; q[1] = 0x98;	// PackBitsRect
1624 		q += 2;
1625 	} else {
1626 		q[0] = 0x00; q[1] = 0x9a;	// DirectBitsRect
1627 		q += 6; // skip pmBaseAddr
1628 	}
1629 
1630 	// 4. PixMap
1631 	q[0] = (row_bytes >> 8) | 0x80;
1632 	q[1] = row_bytes;
1633 	memcpy(q + 2, p, 8);
1634 	q[13] = 0x01; // packType = unpacked
1635 	q[19] = 0x48; // hRes
1636 	q[23] = 0x48; // vRes
1637 	q[27] = (depth == 8 ? 0 : 0x10); // pixelType
1638 	q[29] = depth; // pixelSize
1639 	q[31] = (depth == 8 ? 1 : 3); // cmpCount
1640 	q[33] = (depth == 8 ? 8 : 5); // cmpSize
1641 	q += 46;
1642 
1643 	// 5. ColorTable
1644 	if (depth == 8) {
1645 		q[7] = 0xff; // ctSize
1646 		q += 8;
1647 		uint8 *p = (uint8 *)clut_data + 6;
1648 		for (int i=0; i<256; i++) {
1649 			q++;
1650 			*q++ = i;	// value
1651 			*q++ = *p++;	// red
1652 			*q++ = *p++;
1653 			*q++ = *p++;	// green
1654 			*q++ = *p++;
1655 			*q++ = *p++;	// blue
1656 			*q++ = *p++;
1657 		}
1658 	}
1659 
1660 	// 6. source/destination Rect and transfer mode
1661 	memcpy(q, p, 8);
1662 	memcpy(q + 8, p, 8);
1663 	q += 18;
1664 
1665 	// 7. graphics data
1666 	memcpy(q, p + 10, row_bytes * height);
1667 	q += row_bytes * height;
1668 
1669 	// 8. OpEndPic
1670 	q[0] = 0x00;
1671 	q[1] = 0xff;
1672 
1673 	rsrc.SetData(pict_rsrc, output_length);
1674 	return true;
1675 }
1676 
make_rsrc_from_clut(void * data,size_t length,LoadedResource & rsrc)1677 bool image_file_t::make_rsrc_from_clut(void *data, size_t length, LoadedResource &rsrc)
1678 {
1679 	const size_t input_length = 6 + 256 * 6;	// 6 bytes header, 256 entries with 6 bytes each
1680 	const size_t output_length = 8 + 256 * 8;	// 8 bytes header, 256 entries with 8 bytes each
1681 
1682 	if (length != input_length)
1683 		return false;
1684 
1685 	// Allocate memory for Mac CLUT resource
1686 	void *clut_rsrc = malloc(output_length);
1687 	if (clut_rsrc == NULL)
1688 		return false;
1689 	memset(clut_rsrc, 0, output_length);
1690 
1691 	// Convert clut tag to Mac CLUT resource
1692 	uint8 *p = (uint8 *)data;
1693 	uint8 *q = (uint8 *)clut_rsrc;
1694 
1695 	// 1. Header
1696 	q[6] = p[0]; // color count
1697 	q[7] = p[1];
1698 	p += 6;
1699 	q += 8;
1700 
1701 	// 2. Color table
1702 	for (int i=0; i<256; i++) {
1703 		q++;
1704 		*q++ = i;		// value
1705 		*q++ = *p++;	// red
1706 		*q++ = *p++;
1707 		*q++ = *p++;	// green
1708 		*q++ = *p++;
1709 		*q++ = *p++;	// blue
1710 		*q++ = *p++;
1711 	}
1712 
1713 	rsrc.SetData(clut_rsrc, output_length);
1714 	return true;
1715 }
1716