/* images.c Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html Thursday, July 20, 1995 3:29:30 PM- rdm created. Feb. 4, 2000 (Loren Petrich): Changed halt() to assert(false) for better debugging Feb. 5, 2000 (Loren Petrich): Better handling of case of no scenario-file resource fork Aug 21, 2000 (Loren Petrich): Added object-oriented file handling LoadedResource handles are assumed to always be locked, and HLock() and HUnlock() have been suppressed for that reason. Jul 6, 2001 (Loren Petrich): Added Thomas Herzog's changes for loading Win32-version image chunks Jan 25, 2002 (Br'fin (Jeremy Parsons)): Added TARGET_API_MAC_CARBON for Quicktime.h Jul 31, 2002 (Loren Petrich) Added text-resource access in analogy with others' image- and sound-resource access; this is for supporting the M2-Win95 file format */ #include "cseries.h" #include "FileHandler.h" #include #if defined(mac) #if defined(EXPLICIT_CARBON_HEADER) # include #else # include #endif #endif #include "interface.h" #include "shell.h" #include "images.h" #include "screen.h" #include "wad.h" #include "screen_drawing.h" #include "Logging.h" // Constants enum { _images_file_delta16= 1000, _images_file_delta32= 2000, _scenario_file_delta16= 10000, _scenario_file_delta32= 20000 }; // Structure for open image file class image_file_t { public: image_file_t() {} ~image_file_t() {close_file();} bool open_file(FileSpecifier &file); void close_file(void); bool is_open(void); int determine_pict_resource_id(int base_id, int delta16, int delta32); bool has_pict(int id); bool has_clut(int id); bool get_pict(int id, LoadedResource &rsrc); bool get_clut(int id, LoadedResource &rsrc); bool get_snd(int id, LoadedResource &rsrc); bool get_text(int id, LoadedResource &rsrc); private: bool has_rsrc(uint32 rsrc_type, uint32 wad_type, int id); bool get_rsrc(uint32 rsrc_type, uint32 wad_type, int id, LoadedResource &rsrc); bool make_rsrc_from_pict(void *data, size_t length, LoadedResource &rsrc, void *clut_data, size_t clut_length); bool make_rsrc_from_clut(void *data, size_t length, LoadedResource &rsrc); OpenedResourceFile rsrc_file; OpenedFile wad_file; wad_header wad_hdr; }; #ifdef mac // M2/Win picture structures (do not use in portable code) typedef struct pict_head { screen_rectangle bounds; /* screen_rectangle from screen_drawing.h */ short depth; /* 8 or 16 */ /* pixel data follows here */ } pict_head; typedef struct clut_record { short count; short unknown; short id; rgb_color colors[256]; /* rgb_color from cscluts.h */ } clut_record; #endif // Global variables static image_file_t ImagesFile; static image_file_t ScenarioFile; // Prototypes static void shutdown_images_handler(void); static void draw_picture(LoadedResource &PictRsrc); #include #ifdef HAVE_SDL_IMAGE_H #include #endif #include "byte_swapping.h" #include "screen_drawing.h" // From screen_sdl.cpp extern short interface_bit_depth; // From screen_drawing_sdl.cpp extern bool draw_clip_rect_active; extern screen_rectangle draw_clip_rect; /* * Uncompress picture data, returns size of compressed image data that was read */ // Uncompress (and endian-correct) scan line compressed by PackBits RLE algorithm template static const uint8 *unpack_bits(const uint8 *src, int row_bytes, T *dst) { // Read source count int src_count; if (row_bytes > 250) { src_count = (src[0] << 8) | src[1]; src += 2; } else src_count = *src++; while (src_count > 0) { // Read flag/count byte int c = (int8)*src++; src_count--; if (c < 0) { // RLE compressed run int size = -c + 1; T data; if (sizeof(T) == 1) { data = *src++; src_count--; } else { data = (src[0] << 8) | src[1]; src += 2; src_count -= 2; } for (int i=0; i(src - start); } // 16-bit picture, one scan line at a time, 16-bit chunks static int uncompress_rle16(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int height) { const uint8 *start = src; for (int y=0; y(src - start); } static void copy_component_into_surface(const uint8 *src, uint8 *dst, int count, int component) { #ifdef ALEPHONE_LITTLE_ENDIAN dst += 2 - component; #else dst += component + 1; #endif while (count--) { *dst = *src++; dst += 4; } } // 32-bit picture, one scan line, one component at a time static int uncompress_rle32(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int height) { uint8 *tmp = (uint8 *)malloc(row_bytes); if (tmp == NULL) return -1; const uint8 *start = src; int width = row_bytes / 4; for (int y=0; y(src - start); } static int uncompress_picture(const uint8 *src, int row_bytes, uint8 *dst, int dst_pitch, int depth, int height, int pack_type) { // Depths <8 have to be color expanded to depth 8 after uncompressing, // so we uncompress into a temporary buffer uint8 *orig_dst = dst; int orig_dst_pitch = dst_pitch; if (depth < 8) { dst = (uint8 *)malloc(row_bytes * height); dst_pitch = row_bytes; if (dst == NULL) return -1; } int data_size = 0; if (row_bytes < 8) { // Uncompressed data const uint8 *p = src; uint8 *q = dst; for (int y=0; y8 bits if (depth < 8) { const uint8 *p = dst; uint8 *q = orig_dst; // Source and destination may have different alignment restrictions, // don't run off the right of either int x_max = row_bytes; while (x_max * 8 / depth > orig_dst_pitch) x_max--; switch (depth) { case 1: for (int y=0; y> 6) & 0x03; q[x*4+1] = (b >> 4) & 0x03; q[x*4+2] = (b >> 2) & 0x03; q[x*4+3] = b & 0x03; } p += row_bytes; q += orig_dst_pitch; } break; case 4: for (int y=0; y> 4) & 0x0f; q[x*2+1] = b & 0x0f; } p += row_bytes; q += orig_dst_pitch; } break; } free(dst); } return data_size; } int get_pict_header_width(LoadedResource &rsrc) { SDL_RWops *p = SDL_RWFromMem(rsrc.GetPointer(), (int) rsrc.GetLength()); if (p) { SDL_RWseek(p, 8, SEEK_CUR); int width = SDL_ReadBE16(p); SDL_RWclose(p); return width; } return -1; } /* * Convert picture resource to SDL surface */ SDL_Surface *picture_to_surface(LoadedResource &rsrc) { if (!rsrc.IsLoaded()) return NULL; SDL_Surface *s = NULL; // Open stream to picture resource SDL_RWops *p = SDL_RWFromMem(rsrc.GetPointer(), (int)rsrc.GetLength()); if (p == NULL) return NULL; SDL_RWseek(p, 6, SEEK_CUR); // picSize/top/left int pic_height = SDL_ReadBE16(p); int pic_width = SDL_ReadBE16(p); //printf("pic_width %d, pic_height %d\n", pic_width, pic_height); // Read and parse picture opcodes bool done = false; while (!done) { uint16 opcode = SDL_ReadBE16(p); //printf("%04x\n", opcode); switch (opcode) { case 0x0000: // NOP case 0x0011: // VersionOp case 0x001c: // HiliteMode case 0x001e: // DefHilite case 0x0038: // FrameSameRect case 0x0039: // PaintSameRect case 0x003a: // EraseSameRect case 0x003b: // InvertSameRect case 0x003c: // FillSameRect case 0x02ff: // Version break; case 0x00ff: // OpEndPic done = true; break; case 0x0001: { // Clipping region uint16 size = SDL_ReadBE16(p); if (size & 1) size++; SDL_RWseek(p, size - 2, SEEK_CUR); break; } case 0x0003: // TxFont case 0x0004: // TxFace case 0x0005: // TxMode case 0x0008: // PnMode case 0x000d: // TxSize case 0x0015: // PnLocHFrac case 0x0016: // ChExtra case 0x0023: // ShortLineFrom case 0x00a0: // ShortComment SDL_RWseek(p, 2, SEEK_CUR); break; case 0x0006: // SpExtra case 0x0007: // PnSize case 0x000b: // OvSize case 0x000c: // Origin case 0x000e: // FgColor case 0x000f: // BgColor case 0x0021: // LineFrom SDL_RWseek(p, 4, SEEK_CUR); break; case 0x001a: // RGBFgCol case 0x001b: // RGBBkCol case 0x001d: // HiliteColor case 0x001f: // OpColor case 0x0022: // ShortLine SDL_RWseek(p, 6, SEEK_CUR); break; case 0x0002: // BkPat case 0x0009: // PnPat case 0x000a: // FillPat case 0x0010: // TxRatio case 0x0020: // Line case 0x0030: // FrameRect case 0x0031: // PaintRect case 0x0032: // EraseRect case 0x0033: // InvertRect case 0x0034: // FillRect SDL_RWseek(p, 8, SEEK_CUR); break; case 0x0c00: // HeaderOp SDL_RWseek(p, 24, SEEK_CUR); break; case 0x00a1: { // LongComment SDL_RWseek(p, 2, SEEK_CUR); int size = SDL_ReadBE16(p); if (size & 1) size++; SDL_RWseek(p, size, SEEK_CUR); break; } case 0x0098: // Packed CopyBits case 0x0099: // Packed CopyBits with clipping region case 0x009a: // Direct CopyBits case 0x009b: { // Direct CopyBits with clipping region // 1. PixMap if (opcode == 0x009a || opcode == 0x009b) SDL_RWseek(p, 4, SEEK_CUR); // pmBaseAddr uint16 row_bytes = SDL_ReadBE16(p); // the upper 2 bits are flags //printf(" row_bytes %04x\n", row_bytes); bool is_pixmap = ((row_bytes & 0x8000) != 0); row_bytes &= 0x3fff; uint16 top = SDL_ReadBE16(p); uint16 left = SDL_ReadBE16(p); uint16 height = SDL_ReadBE16(p) - top; uint16 width = SDL_ReadBE16(p) - left; uint16 pack_type, pixel_size; if (is_pixmap) { SDL_RWseek(p, 2, SEEK_CUR); // pmVersion pack_type = SDL_ReadBE16(p); SDL_RWseek(p, 14, SEEK_CUR); // packSize/hRes/vRes/pixelType pixel_size = SDL_ReadBE16(p); SDL_RWseek(p, 16, SEEK_CUR); // cmpCount/cmpSize/planeBytes/pmTable/pmReserved } else { pack_type = 0; pixel_size = 1; } //printf(" width %d, height %d, row_bytes %d, depth %d, pack_type %d\n", width, height, row_bytes, pixel_size, pack_type); // Allocate surface for picture uint32 Rmask = 0, Gmask = 0, Bmask = 0; int surface_depth = 8; switch (pixel_size) { case 1: case 2: case 4: case 8: Rmask = Gmask = Bmask = 0xff; surface_depth = 8; // SDL surfaces must be at least 8 bits depth, so we expand 1/2/4-bit pictures to 8-bit break; case 16: Rmask = 0x7c00; Gmask = 0x03e0; Bmask = 0x001f; surface_depth = 16; break; case 32: Rmask = 0x00ff0000; Gmask = 0x0000ff00; Bmask = 0x000000ff; surface_depth = 32; break; default: fprintf(stderr, "Unsupported PICT depth %d\n", pixel_size); done = true; break; } if (done) break; SDL_Surface *bm = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, surface_depth, Rmask, Gmask, Bmask, 0); if (bm == NULL) { done = true; break; } // 2. ColorTable if (is_pixmap && (opcode == 0x0098 || opcode == 0x0099)) { SDL_Color colors[256]; SDL_RWseek(p, 4, SEEK_CUR); // ctSeed uint16 flags = SDL_ReadBE16(p); int num_colors = SDL_ReadBE16(p) + 1; for (int i=0; i> 8; colors[value].g = SDL_ReadBE16(p) >> 8; colors[value].b = SDL_ReadBE16(p) >> 8; } SDL_SetColors(bm, colors, 0, 256); } // 3. source/destination Rect and transfer mode SDL_RWseek(p, 18, SEEK_CUR); // 4. clipping region if (opcode == 0x0099 || opcode == 0x009b) { uint16 rgn_size = SDL_ReadBE16(p); SDL_RWseek(p, rgn_size - 2, SEEK_CUR); } // 5. graphics data int data_size = uncompress_picture((uint8 *)rsrc.GetPointer() + SDL_RWtell(p), row_bytes, (uint8 *)bm->pixels, bm->pitch, pixel_size, height, pack_type); if (data_size < 0) { done = true; break; } if (data_size & 1) data_size++; SDL_RWseek(p, data_size, SEEK_CUR); // If there's already a surface, throw away the decoded image // (actually, we could have skipped this entire opcode, but the // only way to do this is to decode the image data). // So we only draw the first image we encounter. if (s) SDL_FreeSurface(bm); else s = bm; break; } #ifdef HAVE_SDL_IMAGE case 0x8200: { // Compressed QuickTime image (we only handle JPEG compression) // 1. Header uint32 opcode_size = SDL_ReadBE32(p); if (opcode_size & 1) opcode_size++; uint32 opcode_start = SDL_RWtell(p); SDL_RWseek(p, 26, SEEK_CUR); // version/matrix (hom. part) int offset_x = SDL_ReadBE16(p); SDL_RWseek(p, 2, SEEK_CUR); int offset_y = SDL_ReadBE16(p); SDL_RWseek(p, 6, SEEK_CUR); // matrix (remaining part) uint32 matte_size = SDL_ReadBE32(p); SDL_RWseek(p, 22, SEEK_CUR); // matteRec/mode/srcRect/accuracy uint32 mask_size = SDL_ReadBE32(p); // 2. Matte image description if (matte_size) { uint32 matte_id_size = SDL_ReadBE32(p); SDL_RWseek(p, matte_id_size - 4, SEEK_CUR); } // 3. Matte data SDL_RWseek(p, matte_size, SEEK_CUR); // 4. Mask region SDL_RWseek(p, mask_size, SEEK_CUR); // 5. Image description uint32 id_start = SDL_RWtell(p); uint32 id_size = SDL_ReadBE32(p); uint32 codec_type = SDL_ReadBE32(p); if (codec_type != FOUR_CHARS_TO_INT('j','p','e','g')) { fprintf(stderr, "Unsupported codec type %c%c%c%c\n", codec_type >> 24, codec_type >> 16, codec_type >> 8, codec_type); done = true; break; } SDL_RWseek(p, 36, SEEK_CUR); // resvd1/resvd2/dataRefIndex/version/revisionLevel/vendor/temporalQuality/spatialQuality/width/height/hRes/vRes uint32 data_size = SDL_ReadBE32(p); SDL_RWseek(p, id_start + id_size, SEEK_SET); // Allocate surface for complete (but possibly banded) picture if (s == NULL) { s = SDL_CreateRGBSurface(SDL_SWSURFACE, pic_width, pic_height, 32, #ifdef ALEPHONE_LITTLE_ENDIAN 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 #else 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff #endif ); if (s == NULL) { done = true; break; } } // 6. Compressed image data SDL_RWops *img = SDL_RWFromMem((uint8 *)rsrc.GetPointer() + SDL_RWtell(p), data_size); if (img == NULL) { done = true; break; } SDL_Surface *bm = IMG_LoadTyped_RW(img, true, "JPG"); // Copy image (band) into surface if (bm) { SDL_Rect dst_rect = {offset_x, offset_y, bm->w, bm->h}; SDL_BlitSurface(bm, NULL, s, &dst_rect); SDL_FreeSurface(bm); } SDL_RWseek(p, opcode_start + opcode_size, SEEK_SET); break; } #endif default: if (opcode >= 0x0300 && opcode < 0x8000) SDL_RWseek(p, (opcode >> 8) * 2, SEEK_CUR); else if (opcode >= 0x8000 && opcode < 0x8100) break; else { fprintf(stderr, "Unimplemented opcode %04x in PICT resource\n", opcode); done = true; } break; } } // Close stream, return surface SDL_RWclose(p); return s; } /* * Rescale surface to given dimensions */ template static void rescale(T *src_pixels, int src_pitch, T *dst_pixels, int dst_pitch, int width, int height, uint32 dx, uint32 dy) { // Brute-force rescaling, no interpolation uint32 sy = 0; for (int y=0; y> 16); uint32 sx = 0; for (int x=0; x> 16]; sx += dx; } dst_pixels += dst_pitch / sizeof(T); sy += dy; } } SDL_Surface *rescale_surface(SDL_Surface *s, int width, int height) { if (s == NULL) return NULL; SDL_Surface *s2 = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask, s->format->Bmask, s->format->Amask); if (s2 == NULL) return NULL; uint32 dx = (s->w << 16) / width; uint32 dy = (s->h << 16) / height; switch (s->format->BytesPerPixel) { case 1: rescale((pixel8 *)s->pixels, s->pitch, (pixel8 *)s2->pixels, s2->pitch, width, height, dx, dy); break; case 2: rescale((pixel16 *)s->pixels, s->pitch, (pixel16 *)s2->pixels, s2->pitch, width, height, dx, dy); break; case 4: rescale((pixel32 *)s->pixels, s->pitch, (pixel32 *)s2->pixels, s2->pitch, width, height, dx, dy); break; } if (s->format->palette) SDL_SetColors(s2, s->format->palette->colors, 0, s->format->palette->ncolors); return s2; } /* * Tile surface to fill given dimensions */ template 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) { T *p = src_pixels; int sy = 0; for (int y=0; yformat->BitsPerPixel, s->format->Rmask, s->format->Gmask, s->format->Bmask, s->format->Amask); if (s2 == NULL) return NULL; switch (s->format->BytesPerPixel) { case 1: tile((pixel8 *)s->pixels, s->pitch, (pixel8 *)s2->pixels, s2->pitch, s->w, s->h, width, height); break; case 2: tile((pixel16 *)s->pixels, s->pitch, (pixel16 *)s2->pixels, s2->pitch, s->w, s->h, width, height); break; case 3: tile((pixel8 *)s->pixels, s->pitch, (pixel8 *)s2->pixels, s2->pitch, s->w * 3, s->h, width * 3, height); break; case 4: tile((pixel32 *)s->pixels, s->pitch, (pixel32 *)s2->pixels, s2->pitch, s->w, s->h, width, height); break; } if (s->format->palette) SDL_SetColors(s2, s->format->palette->colors, 0, s->format->palette->ncolors); return s2; } /* * Draw picture resource centered on screen */ static void draw_picture(LoadedResource &rsrc) { // Convert picture resource to surface, free resource SDL_Surface *s = picture_to_surface(rsrc); if (s == NULL) return; SDL_Surface *video = SDL_GetVideoSurface(); // Default source rectangle SDL_Rect src_rect = {0, 0, MIN(s->w, 640), MIN(s->h, 480)}; // Center picture on screen SDL_Rect dst_rect = {(video->w - src_rect.w) / 2, (video->h - src_rect.h) / 2, s->w, s->h}; if (dst_rect.x < 0) dst_rect.x = 0; if (dst_rect.y < 0) dst_rect.y = 0; // Clip if desired (only used for menu buttons) if (draw_clip_rect_active) { src_rect.w = dst_rect.w = draw_clip_rect.right - draw_clip_rect.left; src_rect.h = dst_rect.h = draw_clip_rect.bottom - draw_clip_rect.top; src_rect.x = draw_clip_rect.left - (640 - s->w) / 2; src_rect.y = draw_clip_rect.top - (480 - s->h) / 2; dst_rect.x += draw_clip_rect.left- (640 - s->w) / 2; dst_rect.y += draw_clip_rect.top - (480 - s->h) / 2; } else { // Clear destination to black SDL_FillRect(video, NULL, SDL_MapRGB(video->format, 0, 0, 0)); } // Blit picture to screen SDL_BlitSurface(s, &src_rect, video, &dst_rect); SDL_FreeSurface(s); // Update display and free picture surface SDL_UpdateRects(video, 1, &dst_rect); #ifdef HAVE_OPENGL if (video->flags & SDL_OPENGL) SDL_GL_SwapBuffers(); #endif } /* * Get system color table */ struct color_table *build_8bit_system_color_table(void) { // 6*6*6 RGB color cube color_table *table = new color_table; table->color_count = 6*6*6; int index = 0; for (int red=0; red<6; red++) { for (int green=0; green<6; green++) { for (int blue=0; blue<6; blue++) { uint8 r = red * 0x33; uint8 g = green * 0x33; uint8 b = blue * 0x33; table->colors[index].red = (r << 8) | r; table->colors[index].green = (g << 8) | g; table->colors[index].blue = (b << 8) | b; index++; } } } return table; } /* * Scroll image across screen */ #define SCROLLING_SPEED (MACHINE_TICKS_PER_SECOND / 20) void scroll_full_screen_pict_resource_from_scenario(int pict_resource_number, bool text_block) { // Convert picture resource to surface, free resource LoadedResource rsrc; get_picture_resource_from_scenario(pict_resource_number, rsrc); SDL_Surface *s = picture_to_surface(rsrc); if (s == NULL) return; // Find out in which direction to scroll int picture_width = s->w; int picture_height = s->h; int screen_width = 640; int screen_height = 480; bool scroll_horizontal = picture_width > screen_width; bool scroll_vertical = picture_height > screen_height; if (scroll_horizontal || scroll_vertical) { // Flush events SDL_Event event; while (SDL_PollEvent(&event)) ; // Prepare source and destination rectangles SDL_Rect src_rect = {0, 0, scroll_horizontal ? screen_width : picture_width, scroll_vertical ? screen_height : picture_height}; SDL_Rect dst_rect = {(SDL_GetVideoSurface()->w - screen_width) / 2, (SDL_GetVideoSurface()->h - screen_height) / 2, screen_width, screen_height}; // Scroll loop bool done = false, aborted = false; uint32 start_tick = SDL_GetTicks(); do { int32 delta = (SDL_GetTicks() - start_tick) / (text_block ? (2 * SCROLLING_SPEED) : SCROLLING_SPEED); if (scroll_horizontal && delta > picture_width - screen_width) { delta = picture_width - screen_width; done = true; } if (scroll_vertical && delta > picture_height - screen_height) { delta = picture_height - screen_height; done = true; } // Blit part of picture src_rect.x = scroll_horizontal ? delta : 0; src_rect.y = scroll_vertical ? delta : 0; SDL_BlitSurface(s, &src_rect, SDL_GetVideoSurface(), &dst_rect); SDL_UpdateRects(SDL_GetVideoSurface(), 1, &dst_rect); #ifdef HAVE_OPENGL if (SDL_GetVideoSurface()->flags & SDL_OPENGL) SDL_GL_SwapBuffers(); #endif // Give system time global_idle_proc(); SDL_Delay(10); // Check for events to abort event.type = SDL_NOEVENT; SDL_PollEvent(&event); switch (event.type) { case SDL_MOUSEBUTTONDOWN: case SDL_KEYDOWN: aborted = true; break; } } while (!done && !aborted); } // Free surface SDL_FreeSurface(s); } /* * Initialize image manager, open Images file */ void initialize_images_manager(void) { FileSpecifier file; logContext("loading Images..."); file.SetNameWithPath(getcstr(temporary, strFILENAMES, filenameIMAGES)); // _typecode_images if (!file.Exists()) alert_user(fatalError, strERRORS, badExtraFileLocations, fnfErr); if (!ImagesFile.open_file(file)) alert_user(fatalError, strERRORS, badExtraFileLocations, -1); atexit(shutdown_images_handler); } /* * Shutdown image manager */ static void shutdown_images_handler(void) { ScenarioFile.close_file(); ImagesFile.close_file(); } /* * Set map file to load images from */ void set_scenario_images_file(FileSpecifier &file) { ScenarioFile.open_file(file); } /* * Open/close image file */ bool image_file_t::open_file(FileSpecifier &file) { close_file(); // Try to open as a resource file if (!file.Open(rsrc_file)) { // This failed, maybe it's a wad file (M2 Win95 style) if (!open_wad_file_for_reading(file, wad_file) || !read_wad_header(wad_file, &wad_hdr)) { // This also failed, bail out wad_file.Close(); return false; } } // Try to open wad file, too else if (!wad_file.IsOpen()) { if (open_wad_file_for_reading(file, wad_file)) { if (!read_wad_header(wad_file, &wad_hdr)) { wad_file.Close(); } } } return true; } void image_file_t::close_file(void) { rsrc_file.Close(); wad_file.Close(); } bool image_file_t::is_open(void) { return rsrc_file.IsOpen() || wad_file.IsOpen(); } /* * Get resource from file */ bool image_file_t::has_rsrc(uint32 rsrc_type, uint32 wad_type, int id) { // Check for resource in resource file if (rsrc_file.IsOpen()) { if (rsrc_file.Check(rsrc_type, id)) return true; } // Check for resource in wad file if (wad_file.IsOpen()) { wad_data *d = read_indexed_wad_from_file(wad_file, &wad_hdr, id, true); if (d) { bool success = false; size_t len; if (extract_type_from_wad(d, wad_type, &len)) success = true; free_wad(d); return success; } } return false; } bool image_file_t::has_pict(int id) { return has_rsrc(FOUR_CHARS_TO_INT('P','I','C','T'), FOUR_CHARS_TO_INT('p','i','c','t'), id); } bool image_file_t::has_clut(int id) { return has_rsrc(FOUR_CHARS_TO_INT('c','l','u','t'), FOUR_CHARS_TO_INT('c','l','u','t'), id); } bool image_file_t::get_rsrc(uint32 rsrc_type, uint32 wad_type, int id, LoadedResource &rsrc) { // Get resource from resource file if (rsrc_file.IsOpen()) { if (rsrc_file.Get(rsrc_type, id, rsrc)) return true; } // Get resource from wad file if (wad_file.IsOpen()) { wad_data *d = read_indexed_wad_from_file(wad_file, &wad_hdr, id, true); if (d) { bool success = false; size_t raw_length; void *raw = extract_type_from_wad(d, wad_type, &raw_length); if (raw) { if (rsrc_type == FOUR_CHARS_TO_INT('P','I','C','T')) { size_t clut_length; void *clut_data = extract_type_from_wad(d, FOUR_CHARS_TO_INT('c','l','u','t'), &clut_length); success = make_rsrc_from_pict(raw, raw_length, rsrc, clut_data, clut_length); } else if (rsrc_type == FOUR_CHARS_TO_INT('c','l','u','t')) success = make_rsrc_from_clut(raw, raw_length, rsrc); else if (rsrc_type == FOUR_CHARS_TO_INT('s','n','d',' ')) { void *snd_data = malloc(raw_length); memcpy(snd_data, raw, raw_length); rsrc.SetData(snd_data, raw_length); success = true; } else if (rsrc_type == FOUR_CHARS_TO_INT('T','E','X','T')) { void *text_data = malloc(raw_length); memcpy(text_data, raw, raw_length); rsrc.SetData(text_data, raw_length); success = true; } } free_wad(d); return success; } } return false; } bool image_file_t::get_pict(int id, LoadedResource &rsrc) { return get_rsrc(FOUR_CHARS_TO_INT('P','I','C','T'), FOUR_CHARS_TO_INT('p','i','c','t'), id, rsrc); } bool image_file_t::get_clut(int id, LoadedResource &rsrc) { return get_rsrc(FOUR_CHARS_TO_INT('c','l','u','t'), FOUR_CHARS_TO_INT('c','l','u','t'), id, rsrc); } bool image_file_t::get_snd(int id, LoadedResource &rsrc) { return get_rsrc(FOUR_CHARS_TO_INT('s','n','d',' '), FOUR_CHARS_TO_INT('s','n','d',' '), id, rsrc); } bool image_file_t::get_text(int id, LoadedResource &rsrc) { return get_rsrc(FOUR_CHARS_TO_INT('T','E','X','T'), FOUR_CHARS_TO_INT('t','e','x','t'), id, rsrc); } /* * Get/draw image from Images file */ bool get_picture_resource_from_images(int base_resource, LoadedResource &PictRsrc) { assert(ImagesFile.is_open()); int RsrcID = ImagesFile.determine_pict_resource_id( base_resource, _images_file_delta16, _images_file_delta32); return ImagesFile.get_pict(RsrcID, PictRsrc); } bool images_picture_exists(int base_resource) { assert(ImagesFile.is_open()); int RsrcID = ImagesFile.determine_pict_resource_id( base_resource, _images_file_delta16, _images_file_delta32); return ImagesFile.has_pict(RsrcID); } void draw_full_screen_pict_resource_from_images(int pict_resource_number) { LoadedResource PictRsrc; get_picture_resource_from_images(pict_resource_number, PictRsrc); draw_picture(PictRsrc); } /* * Get/draw image from scenario */ bool get_picture_resource_from_scenario(int base_resource, LoadedResource &PictRsrc) { if (!ScenarioFile.is_open()) return false; int RsrcID = ScenarioFile.determine_pict_resource_id( base_resource, _scenario_file_delta16, _scenario_file_delta32); bool success = ScenarioFile.get_pict(RsrcID, PictRsrc); #ifdef mac if (success) { Handle PictHdl = PictRsrc.GetHandle(); if (PictHdl) HNoPurge(PictHdl); } #endif return success; } bool scenario_picture_exists(int base_resource) { if (!ScenarioFile.is_open()) return false; int RsrcID = ScenarioFile.determine_pict_resource_id( base_resource, _scenario_file_delta16, _scenario_file_delta32); return ScenarioFile.has_pict(RsrcID); } void draw_full_screen_pict_resource_from_scenario(int pict_resource_number) { LoadedResource PictRsrc; get_picture_resource_from_scenario(pict_resource_number, PictRsrc); draw_picture(PictRsrc); } /* * Get sound resource from scenario */ bool get_sound_resource_from_scenario(int resource_number, LoadedResource &SoundRsrc) { if (!ScenarioFile.is_open()) return false; bool success = ScenarioFile.get_snd(resource_number, SoundRsrc); #ifdef mac if (success) { Handle SndHdl = SoundRsrc.GetHandle(); if (SndHdl) HNoPurge(SndHdl); } #endif return success; } // LP: do the same for text resources bool get_text_resource_from_scenario(int resource_number, LoadedResource &TextRsrc) { if (!ScenarioFile.is_open()) return false; bool success = ScenarioFile.get_text(resource_number, TextRsrc); #ifdef mac if (success) { Handle TxtHdl = TextRsrc.GetHandle(); if (TxtHdl) HNoPurge(TxtHdl); } #endif return success; } /* * Calculate color table for image */ struct color_table *calculate_picture_clut(int CLUTSource, int pict_resource_number) { struct color_table *picture_table = NULL; // Select the source image_file_t *OFilePtr = NULL; switch (CLUTSource) { case CLUTSource_Images: OFilePtr = &ImagesFile; break; case CLUTSource_Scenario: OFilePtr = &ScenarioFile; break; default: vassert(false, csprintf(temporary, "Invalid resource-file selector: %d", CLUTSource)); break; } // Load CLUT resource LoadedResource CLUT_Rsrc; if (OFilePtr->get_clut(pict_resource_number, CLUT_Rsrc)) { #ifdef mac Handle resource = CLUT_Rsrc.GetHandle(); HNoPurge(resource); #endif // Allocate color table picture_table = new color_table; // Convert MacOS CLUT resource to color table if (interface_bit_depth == 8) build_color_table(picture_table, CLUT_Rsrc); else build_direct_color_table(picture_table, interface_bit_depth); } return picture_table; } /* * Determine ID for picture resource */ int image_file_t::determine_pict_resource_id(int base_id, int delta16, int delta32) { int actual_id = base_id; bool done = false; int bit_depth = interface_bit_depth; while (!done) { int next_bit_depth; actual_id = base_id; switch(bit_depth) { case 8: next_bit_depth = 0; break; case 16: next_bit_depth = 8; actual_id += delta16; break; case 32: next_bit_depth = 16; actual_id += delta32; break; default: assert(false); break; } if (has_pict(actual_id)) done = true; if (!done) { if (next_bit_depth) bit_depth = next_bit_depth; else { // Didn't find it. Return the 8 bit version and bail.. done = true; } } } return actual_id; } /* * Convert picture and CLUT data from wad file to PICT resource */ #ifdef mac // MacOS version bool image_file_t::make_rsrc_from_pict(void *data, size_t length, LoadedResource &rsrc, void *clut_data, size_t clut_length) { pict_head *head; PicHandle picture; Rect bounds; short i; GWorldPtr gworld; PixMapHandle pixmap; void *pict_data; void *clut; uint8 *p, *q; CTabHandle clut_handle; CGrafPtr saveport; if (length < 10) return false; head = (pict_head *)data; bounds.left = head->bounds.left; bounds.top = head->bounds.top; bounds.right = head->bounds.right; bounds.bottom = head->bounds.bottom; if (head->depth == 8) { clut = malloc(8 + 256 * 8); memset(clut, 0, 8 + 256 * 8); p = (uint8 *)clut_data; q = (uint8 *)clut; // 1. Header q[6] = p[0]; // color count q[7] = p[1]; p += 6; q += 8; // 2. Color table for (i = 0; i < 256; i++) { q++; *q++ = i; // value *q++ = *p++; // red *q++ = *p++; *q++ = *p++; // green *q++ = *p++; *q++ = *p++; // blue *q++ = *p++; } clut_handle = (CTabHandle)NewHandleClear(8 + 256 * 8); PtrToHand(clut, (Handle *)&clut_handle, 8 + 256 * 8); QTNewGWorldFromPtr(&gworld, k8IndexedPixelFormat, &bounds, clut_handle, NULL, 0, (uint8 *)data + 10, head->bounds.right - head->bounds.left); free(clut); DisposeHandle((Handle)clut_handle); } else if (head->depth == 16) { QTNewGWorldFromPtr(&gworld, k16BE555PixelFormat, &bounds, NULL, NULL, 0, (uint8 *)data + 10, (head->bounds.right - head->bounds.left) * 2); } else if (head->depth == 24) { QTNewGWorldFromPtr(&gworld, k24RGBPixelFormat, &bounds, NULL, NULL, 0, (uint8 *)data + 10, (head->bounds.right - head->bounds.left) * 3); } else if (head->depth == 32) { QTNewGWorldFromPtr(&gworld, k32ARGBPixelFormat, &bounds, NULL, NULL, 0, (uint8 *)data + 10, (head->bounds.right - head->bounds.left) * 4); } else { return false; } PenNormal(); GetGWorld(&saveport, NULL); SetGWorld(gworld, NULL); ForeColor(blackColor); BackColor(whiteColor); pixmap = GetGWorldPixMap(gworld); LockPixels(pixmap); picture = OpenPicture(&bounds); if (picture != NULL) { CopyBits((BitMap *)*pixmap, (BitMap *)*pixmap, &bounds, &bounds, srcCopy, NULL); ClosePicture(); } if (picture != NULL) { UnlockPixels(pixmap); SetGWorld(saveport, NULL); DisposeGWorld(gworld); HLock((Handle)picture); pict_data = malloc(GetHandleSize((Handle)picture)); memcpy(pict_data, *picture, GetHandleSize((Handle)picture)); rsrc.SetData(pict_data, GetHandleSize((Handle)picture)); KillPicture(picture); } return true; } #else // SDL version bool image_file_t::make_rsrc_from_pict(void *data, size_t length, LoadedResource &rsrc, void *clut_data, size_t clut_length) { if (length < 10) return false; // Extract size and depth uint8 *p = (uint8 *)data; int height = (p[4] << 8) + p[5]; int width = (p[6] << 8) + p[7]; int depth = (p[8] << 8) + p[9]; if (depth != 8 && depth != 16) return false; // 8-bit depth requires CLUT if (depth == 8) { if (clut_data == NULL || clut_length != 6 + 256 * 6) return false; } // size(2), rect(8), versionOp(2), version(2), headerOp(26) int output_length = 2 + 8 + 2 + 2 + 26; int row_bytes; if (depth == 8) { // opcode(2), pixMap(46), colorTable(8+256*8), srcRect/dstRect/mode(18), data(variable) row_bytes = width; output_length += 2 + 46 + 8+256*8 + 18; } else { // opcode(2), pixMap(50), srcRect/dstRect/mode(18), data(variable) row_bytes = width * 2; output_length += 2 + 50 + 18; } // data(variable), opEndPic(2) output_length += row_bytes * height + 2; // Allocate memory for Mac PICT resource void *pict_rsrc = malloc(output_length); if (pict_rsrc == NULL) return false; memset(pict_rsrc, 0, output_length); // Convert pict tag to Mac PICT resource uint8 *q = (uint8 *)pict_rsrc; // 1. PICT header q[0] = output_length >> 8; q[1] = output_length; memcpy(q + 2, p, 8); q += 10; // 2. VersionOp/Version/HeaderOp q[0] = 0x00; q[1] = 0x11; // versionOp q[2] = 0x02; q[3] = 0xff; // version q[4] = 0x0c; q[5] = 0x00; // headerOp q[6] = 0xff; q[7] = 0xfe; // header version q[11] = 0x48; // hRes q[15] = 0x48; // vRes memcpy(q + 18, p, 8); q += 30; // 3. opcode if (depth == 8) { q[0] = 0x00; q[1] = 0x98; // PackBitsRect q += 2; } else { q[0] = 0x00; q[1] = 0x9a; // DirectBitsRect q += 6; // skip pmBaseAddr } // 4. PixMap q[0] = (row_bytes >> 8) | 0x80; q[1] = row_bytes; memcpy(q + 2, p, 8); q[13] = 0x01; // packType = unpacked q[19] = 0x48; // hRes q[23] = 0x48; // vRes q[27] = (depth == 8 ? 0 : 0x10); // pixelType q[29] = depth; // pixelSize q[31] = (depth == 8 ? 1 : 3); // cmpCount q[33] = (depth == 8 ? 8 : 5); // cmpSize q += 46; // 5. ColorTable if (depth == 8) { q[7] = 0xff; // ctSize q += 8; uint8 *p = (uint8 *)clut_data + 6; for (int i=0; i<256; i++) { q++; *q++ = i; // value *q++ = *p++; // red *q++ = *p++; *q++ = *p++; // green *q++ = *p++; *q++ = *p++; // blue *q++ = *p++; } } // 6. source/destination Rect and transfer mode memcpy(q, p, 8); memcpy(q + 8, p, 8); q += 18; // 7. graphics data memcpy(q, p + 10, row_bytes * height); q += row_bytes * height; // 8. OpEndPic q[0] = 0x00; q[1] = 0xff; rsrc.SetData(pict_rsrc, output_length); return true; } #endif bool image_file_t::make_rsrc_from_clut(void *data, size_t length, LoadedResource &rsrc) { const size_t input_length = 6 + 256 * 6; // 6 bytes header, 256 entries with 6 bytes each const size_t output_length = 8 + 256 * 8; // 8 bytes header, 256 entries with 8 bytes each if (length != input_length) return false; // Allocate memory for Mac CLUT resource void *clut_rsrc = malloc(output_length); if (clut_rsrc == NULL) return false; memset(clut_rsrc, 0, output_length); // Convert clut tag to Mac CLUT resource uint8 *p = (uint8 *)data; uint8 *q = (uint8 *)clut_rsrc; // 1. Header q[6] = p[0]; // color count q[7] = p[1]; p += 6; q += 8; // 2. Color table for (int i=0; i<256; i++) { q++; *q++ = i; // value *q++ = *p++; // red *q++ = *p++; *q++ = *p++; // green *q++ = *p++; *q++ = *p++; // blue *q++ = *p++; } rsrc.SetData(clut_rsrc, output_length); return true; }