/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 3 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ultima/nuvie/core/nuvie_defs.h" #include "ultima/nuvie/misc/u6_misc.h" #include "ultima/nuvie/files/u6_lzw.h" #include "ultima/nuvie/files/u6_lib_n.h" #include "ultima/nuvie/files/nuvie_io.h" #include "ultima/nuvie/files/u6_shape.h" #include "common/endian.h" namespace Ultima { namespace Nuvie { /* * Structure of shape file: * ======================== * * -> means input from file * <- means storing pixel data * * .shp files are lzw compressed. After decompressing the file represents * following structure: -> file size (dword) * -> set of offsets (each word) * -> set of shapes * * File size should be quite clear. * * Offsets are stored as unsigned words. The first offset in file is the * offset of the first object (simple, huh?). The number of offsets (objects) * in the file can be calculated as follows: * num_objects = (1st offset - 4) / 4. * * Frame structure: -> num of pixels right from hot spot, X1 (word) * -> num of pixels left from hot spot, X2 (word) * -> num of pixels above hot spot, Y1 (word) * -> num of pixels below hot spot, Y2 (word) * -> set of pixel blocks * * The width of the shape can be calculated by adding X1 and X2 together and * height by adding Y1 and Y2 together. Coordinates for hot spot are X2 and Y1. * * Now the data it self is stored in pixel blocks which are quite complex: * -> number of pixels or repeats, num1 (word) * if (num1 and 1) * repeat num1 >> 1 times * -> temp value (unsigned byte) * if (temp value and 1) * -> pixel * <- store pixel temp value >> 1 times * else * <- read temp value >> 1 bytes * end * else * <- read num >> 1 bytes * * Color number 255 seems to be transperent. * * I hope this clears things up a bit. */ /* * ===================== * U6Shape::U6Shape(); * ===================== * * Just intializes all structures to 0. */ U6Shape::U6Shape() : raw(nullptr), hotx(0), hoty(0), width(0), height(0) { } /* * ====================== * U6Shape::~U6Shape(); * ====================== * * Frees all memory allocated by this instance of U6Shape class. */ U6Shape::~U6Shape(void) { if (raw) free(raw); } bool U6Shape::init(uint16 w, uint16 h, uint16 hx, uint16 hy) { width = w; height = h; hotx = hx; hoty = hy; raw = (uint8 *)malloc(width * height); if (raw == nullptr) { DEBUG(0, LEVEL_ERROR, "malloc failed to allocate space for shape\n"); return false; } memset(raw, 0xff, width * height); return true; } bool U6Shape::load(const Common::Path &filename) { return false; } bool U6Shape::load(U6Lib_n *file, uint32 index) { unsigned char *buf = file->get_item(index); if (buf != nullptr) { if (load(buf)) { free(buf); return true; } else free(buf); } return false; } bool U6Shape::load_from_lzc(const Common::Path &filename, uint32 idx, uint32 sub_idx) { U6Lib_n lib_n; if (!lib_n.open(filename, 4, NUVIE_GAME_MD)) { return false; } if (idx >= lib_n.get_num_items()) { return false; } unsigned char *buf = lib_n.get_item(idx, nullptr); NuvieIOBuffer io; io.open(buf, lib_n.get_item_size(idx), false); U6Lib_n lib1; lib1.open(&io, 4, NUVIE_GAME_MD); if (sub_idx >= lib1.get_num_items()) { return false; } if (load(&lib1, (uint32)sub_idx)) { free(buf); return true; } free(buf); return false; } /* * ========================================= * bool U6Shape::load(unsigned char *buf); * ========================================= * * Loads shape from buf * Returns true if successful, else returns false. */ bool U6Shape::load(unsigned char *buf) { /* A file already loaded. */ if (raw != nullptr) return false; /* NOT REACHED */ unsigned char *data = buf; /* Size and hot point. */ width = READ_LE_UINT16(data); data += 2; width += hotx = READ_LE_UINT16(data); data += 2; height = hoty = READ_LE_UINT16(data); data += 2; height += READ_LE_UINT16(data); data += 2; width++; height++; /* Allocate memory for shape and make it all transperent. */ raw = (unsigned char *)malloc(width * height); if (raw == nullptr) { DEBUG(0, LEVEL_ERROR, "malloc failed to allocate space for shape\n"); return false; } memset(raw, 255, width * height); /* Get the pixel data. */ uint16 num_pixels; while ((num_pixels = READ_LE_UINT16(data)) != 0) { data += 2; /* Coordinates relative to hot spot. */ sint16 xpos = READ_LE_UINT16(data); data += 2; sint16 ypos = READ_LE_UINT16(data); data += 2; if (((hotx + xpos) >= width) || ((hoty + ypos) >= height)) { break; } /* * Test if this block of pixels is encoded * (bit0 is set). */ int encoded = num_pixels & 1; /* Divide it by 2. */ num_pixels >>= 1; /* Normal pixel: * ============= * * Just fetch as many pixels as num_pixels suggests. */ if (!encoded) { memcpy(raw + (hotx + xpos) + (hoty + ypos) * width, data, num_pixels); data += num_pixels; continue; /* NOT REACHED */ } /* Encoded pixel: * ============== * * Do as many repeats as num_pixels suggests. */ for (int j = 0; j < num_pixels;) { unsigned char num_pixels2 = *data++; int repeat = num_pixels2 & 1; num_pixels2 >>= 1; /* * Repeat pixel value (data + 1) num_pixels2 * times. */ if (repeat) { memset(raw + (hotx + xpos) + (hoty + ypos) * width + j, *data++, num_pixels2); } /* * Just fetch as many pixels as num_pixels2 * suggests. */ else { memcpy(raw + (hotx + xpos) + (hoty + ypos) * width + j, data, num_pixels2); data += num_pixels2; } j += num_pixels2; } } return true; } // TODO - allow for failure bool U6Shape::load_WoU_background(const Configuration *config, nuvie_game_t game_type) { U6Lib_n file; Common::Path filename; if (game_type == NUVIE_GAME_MD) config_get_path(config, "mdscreen.lzc", filename); else // SE config_get_path(config, "screen.lzc", filename); file.open(filename, 4, game_type); unsigned char *temp_buf = file.get_item(0); load(temp_buf + 8); free(temp_buf); return true; } /* * ===================================== * unsigned char *U6Shape::get_data(); * ===================================== * * Returns raw data representing the shape or nullptr on failure. */ const unsigned char *U6Shape::get_data() const { return raw; } unsigned char *U6Shape::get_data() { return raw; } /* * ============================================ * Graphics::ManagedSurface *U6Shape::get_shape_surface(); * ============================================ * * Returns a Graphics::ManagedSurface representing the shape * or nullptr on failure. NOTE! user must free this * data. */ Graphics::ManagedSurface *U6Shape::get_shape_surface() { if (raw == nullptr) return nullptr; // Create the surface Graphics::ManagedSurface *surface = new Graphics::ManagedSurface(width, height, Graphics::PixelFormat::createFormatCLUT8()); // Copy the raw pixels into it byte *dest = (byte *)surface->getPixels(); Common::copy(raw, raw + width * height, dest); return surface; } /* * ==================================================== * bool U6Shape::get_hot_point(uint16 *x, uint16 *y); * ==================================================== * * Puts the coordinates of the shape in x and y and * returns true or on failure just returns false. */ bool U6Shape::get_hot_point(uint16 *x, uint16 *y) { if (raw == nullptr) return false; /* NOT REACHED */ *x = hotx; *y = hoty; return true; } /* * =============================================== * bool U6Shape::get_size(uint16 *w, uint16 *h); * =============================================== * * Puts the size of the shape in w and h and * returns true or on failure just returns false. */ bool U6Shape::get_size(uint16 *w, uint16 *h) { if (raw == nullptr) return false; /* NOT REACHED */ *w = width; *h = height; return true; } void U6Shape::draw_line(uint16 sx, uint16 sy, uint16 ex, uint16 ey, uint8 color) { if (raw == nullptr) return; draw_line_8bit(sx, sy, ex, ey, color, raw, width, height); } bool U6Shape::blit(U6Shape *shp, uint16 x, uint16 y) { if (shp == nullptr) return false; const unsigned char *src_data = shp->get_data(); uint16 src_w = 0, src_h = 0; shp->get_size(&src_w, &src_h); if (x + src_w > width || y + src_h > height) return false; for (int i = 0; i < src_h; i++) { memcpy(&raw[x + y * width + i * width], &src_data[i * src_w], src_w); } return true; } void U6Shape::fill(uint8 color) { memset(raw, color, width * height); } } // End of namespace Nuvie } // End of namespace Ultima