Files
scummvm-cursorfix/engines/ultima/nuvie/files/u6_shape.cpp
2026-02-02 04:50:13 +01:00

409 lines
9.7 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#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