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