Initial commit
This commit is contained in:
408
engines/ultima/nuvie/files/u6_shape.cpp
Normal file
408
engines/ultima/nuvie/files/u6_shape.cpp
Normal file
@@ -0,0 +1,408 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user