Initial commit
This commit is contained in:
310
engines/ultima/nuvie/files/nuvie_bmp_file.cpp
Normal file
310
engines/ultima/nuvie/files/nuvie_bmp_file.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/* 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/files/nuvie_bmp_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
#define NUVIEBMPFILE_MAGIC 0x4d42 // 'BM'
|
||||
|
||||
NuvieBmpFile::NuvieBmpFile() : data(nullptr), prev_width(0), prev_height(0),
|
||||
prev_bits(0), bmp_line_width(0) {
|
||||
memset(&header, 0, sizeof(header));
|
||||
memset(&infoHeader, 0, sizeof(infoHeader));
|
||||
ARRAYCLEAR(palette);
|
||||
}
|
||||
|
||||
NuvieBmpFile::~NuvieBmpFile() {
|
||||
if (data != nullptr)
|
||||
free(data);
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::initNewBlankImage(sint32 width, sint32 height, const unsigned char *pal) {
|
||||
infoHeader.size = 40;
|
||||
infoHeader.width = width;
|
||||
infoHeader.height = height;
|
||||
infoHeader.planes = 1;
|
||||
infoHeader.bits = 8;
|
||||
infoHeader.compression = 0;
|
||||
infoHeader.imagesize = 0;
|
||||
infoHeader.xresolution = 0; //FIXME
|
||||
infoHeader.yresolution = 0; //FIXME
|
||||
infoHeader.ncolours = 256;
|
||||
infoHeader.importantcolours = 256;
|
||||
|
||||
bmp_line_width = infoHeader.width;
|
||||
if (bmp_line_width % 4 != 0) {
|
||||
bmp_line_width += (4 - (bmp_line_width % 4));
|
||||
}
|
||||
|
||||
header.type = NUVIEBMPFILE_MAGIC;
|
||||
header.reserved1 = 0;
|
||||
header.reserved2 = 0;
|
||||
header.offset = NUVIEBMP_HEADER_SIZE + NUVIEBMP_INFOHEADER_SIZE + 256 * 4;
|
||||
header.size = header.offset + bmp_line_width * infoHeader.height;
|
||||
|
||||
memcpy(&palette, pal, sizeof(palette));
|
||||
|
||||
data = (unsigned char *)malloc(infoHeader.width * infoHeader.height);
|
||||
if (!data) {
|
||||
return handleError("Allocating pixel data");
|
||||
}
|
||||
|
||||
memset(data, 0, infoHeader.width * infoHeader.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::load(const Common::Path &filename) {
|
||||
NuvieIOFileRead file;
|
||||
|
||||
if (filename.empty())
|
||||
return handleError("zero byte file");
|
||||
|
||||
if (!file.open(filename)) {
|
||||
return handleError("opening file");
|
||||
}
|
||||
|
||||
if (file.get_size() < 0x36) { //invalid header.
|
||||
return handleError("filesize < 0x36");
|
||||
}
|
||||
|
||||
header.type = file.read2();
|
||||
header.size = file.read4();
|
||||
header.reserved1 = file.read2();
|
||||
header.reserved2 = file.read2();
|
||||
header.offset = file.read4();
|
||||
|
||||
infoHeader.size = file.read4();
|
||||
infoHeader.width = file.read4();
|
||||
infoHeader.height = file.read4();
|
||||
infoHeader.planes = file.read2();
|
||||
infoHeader.bits = file.read2();
|
||||
infoHeader.compression = file.read4();
|
||||
infoHeader.imagesize = file.read4();
|
||||
infoHeader.xresolution = file.read4();
|
||||
infoHeader.yresolution = file.read4();
|
||||
infoHeader.ncolours = file.read4();
|
||||
infoHeader.importantcolours = file.read4();
|
||||
|
||||
if (header.type != NUVIEBMPFILE_MAGIC) { //invalid magic.
|
||||
return handleError("invalid BMP magic.");
|
||||
}
|
||||
|
||||
if (infoHeader.bits != 8 && infoHeader.bits != 24) {
|
||||
return handleError("only 256 colour bitmaps supported.");
|
||||
}
|
||||
|
||||
if (infoHeader.compression != 0) { // && infoHeader.compression != 2)
|
||||
return handleError("only uncompressed BMP files are supported");
|
||||
//return handleError("only raw and bi_rle8 compression formats are supported.");
|
||||
|
||||
//FIXME need to handle rle compression.
|
||||
}
|
||||
|
||||
if (infoHeader.bits == 8) {
|
||||
for (uint32 i = 0; i < infoHeader.ncolours; i++) {
|
||||
uint8 b = file.read1();
|
||||
uint8 g = file.read1();
|
||||
uint8 r = file.read1();
|
||||
file.read1(); // 0
|
||||
palette[i] = (uint32)r | (uint32)(g << 8) | (uint32)(b << 16);
|
||||
}
|
||||
}
|
||||
|
||||
file.seekStart();
|
||||
file.seek(header.offset);
|
||||
|
||||
uint16 bytes_per_pixel = infoHeader.bits / 8;
|
||||
bmp_line_width = infoHeader.width * bytes_per_pixel;
|
||||
if (bmp_line_width % 4 != 0) {
|
||||
bmp_line_width += (4 - (bmp_line_width % 4));
|
||||
}
|
||||
|
||||
if (data == nullptr || infoHeader.width != prev_width || infoHeader.height != prev_height || prev_bits != infoHeader.bits) {
|
||||
if (data) {
|
||||
free(data);
|
||||
}
|
||||
data = (unsigned char *)malloc(infoHeader.width * infoHeader.height * bytes_per_pixel);
|
||||
prev_width = infoHeader.width;
|
||||
prev_height = infoHeader.height;
|
||||
prev_bits = infoHeader.bits;
|
||||
if (data == nullptr) {
|
||||
return handleError("allocating memory for image");
|
||||
}
|
||||
}
|
||||
|
||||
uint32 end = header.offset + bmp_line_width * infoHeader.height;
|
||||
|
||||
uint32 data_width = infoHeader.width * bytes_per_pixel;
|
||||
|
||||
for (sint32 i = 0; i < infoHeader.height; i++) {
|
||||
file.seek(end - bmp_line_width - (bmp_line_width * i));
|
||||
file.readToBuf(&data[i * data_width], data_width);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::save(const Common::Path &filename) {
|
||||
NuvieIOFileWrite file;
|
||||
|
||||
if (!file.open(filename)) {
|
||||
return handleError("Opening " + filename.toString() + ".");
|
||||
}
|
||||
|
||||
file.write2(header.type);
|
||||
file.write4(header.size);
|
||||
file.write2(header.reserved1);
|
||||
file.write2(header.reserved2);
|
||||
file.write4(header.offset);
|
||||
|
||||
file.write4(infoHeader.size);
|
||||
file.write4(infoHeader.width);
|
||||
file.write4(infoHeader.height);
|
||||
file.write2(infoHeader.planes);
|
||||
file.write2(infoHeader.bits);
|
||||
file.write4(infoHeader.compression);
|
||||
file.write4(infoHeader.imagesize);
|
||||
file.write4(infoHeader.xresolution);
|
||||
file.write4(infoHeader.yresolution);
|
||||
file.write4(infoHeader.ncolours);
|
||||
file.write4(infoHeader.importantcolours);
|
||||
|
||||
if (infoHeader.bits == 8) {
|
||||
for (uint32 i = 0; i < infoHeader.ncolours; i++) {
|
||||
file.write1((palette[i] >> 16) & 0xff); //B
|
||||
file.write1((palette[i] >> 8) & 0xff); //G
|
||||
file.write1(palette[i] & 0xff); //R
|
||||
file.write1((palette[i] >> 24) & 0xff); //A
|
||||
}
|
||||
write8BitData(&file);
|
||||
} else {
|
||||
//FIXME write out 24bit data here.
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieBmpFile::write8BitData(NuvieIOFileWrite *file) {
|
||||
uint32 i;
|
||||
for (i = infoHeader.height; i > 0; i--) {
|
||||
file->writeBuf(&data[(i - 1)*infoHeader.width], infoHeader.width);
|
||||
if ((sint32)bmp_line_width > infoHeader.width) {
|
||||
//write out padding bytes.
|
||||
for (uint8 j = 0; j < bmp_line_width - infoHeader.width; j++) {
|
||||
file->write1(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NuvieBmpFile::handleError(Std::string error) {
|
||||
if (data) {
|
||||
free(data);
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
DEBUG(0, LEVEL_ERROR, error.c_str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Tile *NuvieBmpFile::getTile() {
|
||||
if (infoHeader.width != 16 || infoHeader.height != 16 || infoHeader.bits != 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Tile *t = (Tile *)malloc(sizeof(Tile));
|
||||
if (t == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
memset(t, 0, sizeof(Tile));
|
||||
memcpy(t->data, data, 256);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
unsigned char *NuvieBmpFile::getRawIndexedData() {
|
||||
if (infoHeader.bits != 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
unsigned char *NuvieBmpFile::getRawIndexedDataCopy() {
|
||||
if (data == nullptr || infoHeader.bits != 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned char *copy = (unsigned char *)malloc(infoHeader.width * infoHeader.height);
|
||||
if (copy == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
memcpy(copy, data, infoHeader.width * infoHeader.height);
|
||||
return copy;
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *NuvieBmpFile::getSdlSurface32(const Common::Path &filename) {
|
||||
load(filename);
|
||||
return getSdlSurface32();
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *NuvieBmpFile::getSdlSurface32() {
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface(
|
||||
infoHeader.width, infoHeader.height,
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 0, 0, 8, 16, 24)
|
||||
);
|
||||
|
||||
unsigned char *src_buf = data;
|
||||
Graphics::Surface s = surface->getSubArea(Common::Rect(0, 0, surface->w, surface->h));
|
||||
uint32 *pixels = (uint32 *)s.getPixels();
|
||||
|
||||
if (infoHeader.bits == 8) {
|
||||
for (sint32 i = 0; i < infoHeader.height; i++) {
|
||||
for (sint32 j = 0; j < infoHeader.width; j++) {
|
||||
pixels[j] = palette[src_buf[j]];
|
||||
}
|
||||
src_buf += infoHeader.width;
|
||||
pixels += infoHeader.width;
|
||||
}
|
||||
} else { //bits == 24
|
||||
for (sint32 i = 0; i < infoHeader.height; i++) {
|
||||
for (sint32 j = 0; j < infoHeader.width; j++) {
|
||||
pixels[j] = (uint32)src_buf[j * 3 + 2] | (uint32)(src_buf[j * 3 + 1] << 8) | (uint32)(src_buf[j * 3 + 0] << 16);
|
||||
}
|
||||
src_buf += infoHeader.width * 3;
|
||||
pixels += infoHeader.width;
|
||||
}
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
97
engines/ultima/nuvie/files/nuvie_bmp_file.h
Normal file
97
engines/ultima/nuvie/files/nuvie_bmp_file.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_BMP_FILE_H
|
||||
#define NUVIE_FILES_NUVIE_BMP_FILE_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/core/tile_manager.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class NuvieBmpFile {
|
||||
private:
|
||||
unsigned char *data;
|
||||
uint32 palette[256];
|
||||
sint32 prev_width;
|
||||
sint32 prev_height;
|
||||
uint16 prev_bits;
|
||||
uint32 bmp_line_width;
|
||||
|
||||
struct {
|
||||
uint16 type; /* Magic identifier */
|
||||
uint32 size; /* File size in bytes */
|
||||
uint16 reserved1, reserved2;
|
||||
uint32 offset; /* Offset to image data, bytes */
|
||||
} header;
|
||||
|
||||
#define NUVIEBMP_HEADER_SIZE 14
|
||||
|
||||
struct {
|
||||
uint32 size; /* Header size in bytes */
|
||||
sint32 width, height; /* Width and height of image */
|
||||
uint16 planes; /* Number of colour planes */
|
||||
uint16 bits; /* Bits per pixel */
|
||||
uint32 compression; /* Compression type */
|
||||
uint32 imagesize; /* Image size in bytes */
|
||||
sint32 xresolution, yresolution; /* Pixels per meter */
|
||||
uint32 ncolours; /* Number of colours */
|
||||
uint32 importantcolours; /* Important colours */
|
||||
} infoHeader;
|
||||
|
||||
#define NUVIEBMP_INFOHEADER_SIZE 40
|
||||
|
||||
public:
|
||||
|
||||
NuvieBmpFile();
|
||||
~NuvieBmpFile();
|
||||
|
||||
bool initNewBlankImage(sint32 width, sint32 height, const unsigned char *palette);
|
||||
|
||||
bool load(const Common::Path &filename);
|
||||
bool save(const Common::Path &filename);
|
||||
|
||||
uint16 getWidth() const {
|
||||
return (uint16)infoHeader.width;
|
||||
}
|
||||
uint16 getHeight() const {
|
||||
return (uint16)infoHeader.height;
|
||||
}
|
||||
|
||||
|
||||
Tile *getTile();
|
||||
unsigned char *getRawIndexedData();
|
||||
unsigned char *getRawIndexedDataCopy();
|
||||
Graphics::ManagedSurface *getSdlSurface32();
|
||||
Graphics::ManagedSurface *getSdlSurface32(const Common::Path &filename);
|
||||
|
||||
private:
|
||||
bool handleError(Std::string error);
|
||||
void write8BitData(NuvieIOFileWrite *file);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
102
engines/ultima/nuvie/files/nuvie_file_list.cpp
Normal file
102
engines/ultima/nuvie/files/nuvie_file_list.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/* 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/shared/std/string.h"
|
||||
#include "ultima/nuvie/gui/widgets/console.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/nuvie_file_list.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
NuvieFileList::NuvieFileList() : sort_mode(NUVIE_SORT_NAME_ASC) {
|
||||
}
|
||||
|
||||
NuvieFileList::~NuvieFileList() {
|
||||
}
|
||||
|
||||
bool NuvieFileList::open(const Common::Path &directory, const char *search, uint8 s_mode) {
|
||||
Common::ArchiveMemberPtr arcMember = SearchMan.getMember(directory);
|
||||
|
||||
sort_mode = s_mode;
|
||||
|
||||
if (!arcMember || !arcMember->isDirectory()) {
|
||||
ConsoleAddWarning(Std::string("Failed to open ") + directory.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::ArchiveMemberList children;
|
||||
|
||||
arcMember->listChildren(children, search);
|
||||
if (children.empty()) {
|
||||
ConsoleAddWarning(Std::string("Failed to get children of ") + directory.toString());
|
||||
return false;
|
||||
};
|
||||
|
||||
for (const auto &child : children) {
|
||||
if (!child->isDirectory())
|
||||
add_filename(child->getFileName());
|
||||
}
|
||||
|
||||
//sort list by time last modified in decending order.
|
||||
Common::sort(file_list.begin(), file_list.end(), NuvieFileDesc());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieFileList::add_filename(const Common::String &fileName) {
|
||||
NuvieFileDesc filedesc;
|
||||
filedesc.m_time = 0;
|
||||
filedesc.filename = fileName;
|
||||
|
||||
file_list.push_front(filedesc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Std::string *NuvieFileList::get_latest() const {
|
||||
Std::list<NuvieFileDesc>::const_iterator iter;
|
||||
|
||||
iter = file_list.begin();
|
||||
|
||||
if (iter != file_list.end()) {
|
||||
return &((*iter).filename);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 NuvieFileList::get_num_files() const {
|
||||
return (uint32)file_list.size();
|
||||
}
|
||||
|
||||
void NuvieFileList::close() {
|
||||
return;
|
||||
}
|
||||
|
||||
const Std::list<NuvieFileDesc> &NuvieFileList::get_filelist() const {
|
||||
return file_list;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
82
engines/ultima/nuvie/files/nuvie_file_list.h
Normal file
82
engines/ultima/nuvie/files/nuvie_file_list.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_FILE_LIST_H
|
||||
#define NUVIE_FILES_NUVIE_FILE_LIST_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::list;
|
||||
using Std::string;
|
||||
|
||||
|
||||
#define NUVIE_SORT_TIME_DESC 0x1
|
||||
#define NUVIE_SORT_TIME_ASC 0x2
|
||||
#define NUVIE_SORT_NAME_DESC 0x3
|
||||
#define NUVIE_SORT_NAME_ASC 0x5
|
||||
|
||||
class Configuration;
|
||||
|
||||
class NuvieFileDesc {
|
||||
public:
|
||||
Std::string filename;
|
||||
uint32 m_time;
|
||||
|
||||
bool operator<(const NuvieFileDesc &rhs) const {
|
||||
return (rhs.m_time < this->m_time);
|
||||
};
|
||||
bool operator()(const NuvieFileDesc &lhs, const NuvieFileDesc &rhs) {
|
||||
return (lhs.m_time > rhs.m_time);
|
||||
};
|
||||
};
|
||||
|
||||
class NuvieFileList {
|
||||
protected:
|
||||
Std::list<NuvieFileDesc> file_list;
|
||||
|
||||
uint8 sort_mode;
|
||||
protected:
|
||||
bool add_filename(const Common::String &fileName);
|
||||
public:
|
||||
|
||||
NuvieFileList();
|
||||
~NuvieFileList();
|
||||
|
||||
bool open(const Common::Path &directory, const char *restrict, uint8 sort_mode);
|
||||
|
||||
Std::string *next();
|
||||
const Std::string *get_latest() const;
|
||||
uint32 get_num_files() const;
|
||||
|
||||
const Std::list<NuvieFileDesc> &get_filelist() const;
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
224
engines/ultima/nuvie/files/nuvie_io.cpp
Normal file
224
engines/ultima/nuvie/files/nuvie_io.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* 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/files/nuvie_io.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
NuvieIO::NuvieIO() : size(0), pos(0) {
|
||||
}
|
||||
|
||||
NuvieIO::~NuvieIO() {
|
||||
}
|
||||
|
||||
unsigned char *NuvieIO::readAll() {
|
||||
uint32 bytes_read;
|
||||
return readBuf(size, &bytes_read);
|
||||
}
|
||||
|
||||
unsigned char *NuvieIO::readBuf(uint32 read_size, uint32 *bytes_read) {
|
||||
unsigned char *buf;
|
||||
|
||||
*bytes_read = 0;
|
||||
|
||||
if (pos + read_size > size)
|
||||
return nullptr;
|
||||
|
||||
buf = (unsigned char *)malloc(read_size);
|
||||
if (buf == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (readToBuf(buf, read_size) == false) {
|
||||
free(buf);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*bytes_read = read_size;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
// NuvieIOBuffer
|
||||
|
||||
NuvieIOBuffer::NuvieIOBuffer() : NuvieIO() {
|
||||
data = nullptr;
|
||||
copied_data = false;
|
||||
}
|
||||
|
||||
NuvieIOBuffer::~NuvieIOBuffer() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::open(unsigned char *buf, uint32 buf_size, bool copy_buf) {
|
||||
if (data != nullptr)
|
||||
return false;
|
||||
|
||||
if (copy_buf == NUVIE_BUF_COPY) {
|
||||
copied_data = true;
|
||||
data = (unsigned char *)malloc(buf_size);
|
||||
if (data == nullptr) {
|
||||
DEBUG(0, LEVEL_ERROR, "NuvieIOBuffer::open() allocating %d bytes.\n", buf_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, buf, buf_size);
|
||||
} else
|
||||
data = buf;
|
||||
|
||||
size = buf_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieIOBuffer::close() {
|
||||
size = 0;
|
||||
pos = 0;
|
||||
|
||||
if (copied_data && data != nullptr)
|
||||
free(data);
|
||||
|
||||
data = nullptr;
|
||||
|
||||
}
|
||||
|
||||
uint8 NuvieIOBuffer::read1() {
|
||||
if (pos >= size)
|
||||
return 0;
|
||||
|
||||
return data[pos++];
|
||||
}
|
||||
|
||||
uint16 NuvieIOBuffer::read2() {
|
||||
uint16 val;
|
||||
|
||||
if (pos > size - 2)
|
||||
return 0;
|
||||
|
||||
val = data[pos] + (data[pos + 1] << 8);
|
||||
pos += 2;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32 NuvieIOBuffer::read4() {
|
||||
uint32 val;
|
||||
|
||||
if (pos > size - 4)
|
||||
return 0;
|
||||
|
||||
val = (data[pos] + (data[pos + 1] << 8) + (data[pos + 2] << 16) + (data[pos + 3] << 24));
|
||||
pos += 4;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::readToBuf(unsigned char *buf, uint32 buf_size) {
|
||||
if (pos + buf_size > size || buf == nullptr)
|
||||
return false;
|
||||
|
||||
memcpy(buf, &data[pos], buf_size);
|
||||
|
||||
pos += buf_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::write1(uint8 src) {
|
||||
if (pos >= size)
|
||||
return false;
|
||||
|
||||
data[pos] = src;
|
||||
pos++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool NuvieIOBuffer::write2(uint16 src) {
|
||||
if (pos > size - 2)
|
||||
return false;
|
||||
|
||||
data[pos] = src & 0xff;
|
||||
data[pos + 1] = (src >> 8) & 0xff;
|
||||
pos += 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOBuffer::write4(uint32 src) {
|
||||
unsigned char *ptr;
|
||||
if (pos > size - 4)
|
||||
return false;
|
||||
|
||||
ptr = &data[pos];
|
||||
|
||||
*ptr++ = src & 0xff;
|
||||
*ptr++ = (src >> 8) & 0xff;
|
||||
*ptr++ = (src >> 16) & 0xff;
|
||||
*ptr++ = (src >> 24) & 0xff;
|
||||
|
||||
pos += 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 NuvieIOBuffer::writeBuf(const unsigned char *src, uint32 src_size) {
|
||||
if (pos + src_size > size || src == nullptr)
|
||||
return 0;
|
||||
|
||||
memcpy(&data[pos], src, src_size);
|
||||
|
||||
pos += src_size;
|
||||
|
||||
return src_size;
|
||||
}
|
||||
|
||||
uint32 NuvieIOBuffer::write(NuvieIO *src) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void NuvieIOBuffer::seek(uint32 new_pos) {
|
||||
if (data && new_pos < size)
|
||||
pos = new_pos;
|
||||
}
|
||||
|
||||
char *strgets(char *str, int n, Common::ReadStream *stream) {
|
||||
int count = 0;
|
||||
char c;
|
||||
|
||||
while (!stream->eos() && (count < (n - 1)) && (c = stream->readByte()) != '\n') {
|
||||
if (c == '\r')
|
||||
continue;
|
||||
|
||||
str[count] = c;
|
||||
++count;
|
||||
}
|
||||
|
||||
str[count] = '\0';
|
||||
return count ? str : nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
143
engines/ultima/nuvie/files/nuvie_io.h
Normal file
143
engines/ultima/nuvie/files/nuvie_io.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_IO_H
|
||||
#define NUVIE_FILES_NUVIE_IO_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Configuration;
|
||||
|
||||
class NuvieIO {
|
||||
protected:
|
||||
uint32 size;
|
||||
uint32 pos;
|
||||
public:
|
||||
NuvieIO();
|
||||
virtual ~NuvieIO();
|
||||
|
||||
virtual void close() {
|
||||
size = 0;
|
||||
pos = 0;
|
||||
};
|
||||
|
||||
virtual uint8 read1() {
|
||||
return 0;
|
||||
};
|
||||
virtual uint16 read2() {
|
||||
return 0;
|
||||
};
|
||||
virtual uint32 read4() {
|
||||
return 0;
|
||||
};
|
||||
|
||||
unsigned char *readAll();
|
||||
unsigned char *readBuf(uint32 read_size, uint32 *bytes_read);
|
||||
virtual bool readToBuf(unsigned char *buf, uint32 buf_size) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
virtual bool write1(uint8 src) {
|
||||
return false;
|
||||
};
|
||||
virtual bool write2(uint16 src) {
|
||||
return false;
|
||||
};
|
||||
virtual bool write4(uint32 src) {
|
||||
return false;
|
||||
};
|
||||
virtual uint32 writeBuf(const unsigned char *src, uint32 src_size) {
|
||||
return 0;
|
||||
};
|
||||
virtual uint32 write(NuvieIO *src) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
uint32 get_size() const {
|
||||
return size;
|
||||
};
|
||||
|
||||
|
||||
inline void seekStart() {
|
||||
seek(0);
|
||||
};
|
||||
inline void seekEnd() {
|
||||
seek(size);
|
||||
};
|
||||
virtual void seek(uint32 new_pos) = 0;
|
||||
|
||||
inline bool is_end() const {
|
||||
return (pos == size - 1);
|
||||
};
|
||||
inline bool is_eof() const {
|
||||
return (size == 0 || pos >= size);
|
||||
};
|
||||
uint32 position() const {
|
||||
return pos;
|
||||
};
|
||||
};
|
||||
|
||||
#define NUVIE_BUF_COPY true
|
||||
#define NUVIE_BUF_NOCOPY false
|
||||
|
||||
|
||||
class NuvieIOBuffer: public NuvieIO {
|
||||
protected:
|
||||
|
||||
unsigned char *data;
|
||||
bool copied_data;
|
||||
|
||||
public:
|
||||
NuvieIOBuffer();
|
||||
~NuvieIOBuffer() override;
|
||||
|
||||
bool open(unsigned char *buf, uint32 buf_size, bool copy_buf = NUVIE_BUF_COPY);
|
||||
|
||||
void close() override;
|
||||
|
||||
unsigned char *get_raw_data() {
|
||||
return data;
|
||||
}; //hehe evil
|
||||
|
||||
uint8 read1() override;
|
||||
uint16 read2() override;
|
||||
uint32 read4() override;
|
||||
bool readToBuf(unsigned char *buf, uint32 buf_size) override;
|
||||
|
||||
bool write1(uint8 src) override;
|
||||
bool write2(uint16 src) override;
|
||||
bool write4(uint32 src) override;
|
||||
uint32 writeBuf(const unsigned char *src, uint32 src_size) override;
|
||||
uint32 write(NuvieIO *src) override;
|
||||
|
||||
void seek(uint32 new_pos) override;
|
||||
};
|
||||
|
||||
extern char *strgets(char *str, int n, Common::ReadStream *stream);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
264
engines/ultima/nuvie/files/nuvie_io_file.cpp
Normal file
264
engines/ultima/nuvie/files/nuvie_io_file.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/* 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/files/nuvie_io_file.h"
|
||||
#include "engines/metaengine.h"
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "engines/engine.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
NuvieIOFileRead::~NuvieIOFileRead() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool NuvieIOFileRead::open(const Common::Path &filename) {
|
||||
if (isOpen())
|
||||
// We already have a file open, lets bail.
|
||||
return false;
|
||||
|
||||
// Handle any relative files under the game path, such as for FM-Towns sound. Though path
|
||||
// delimiters can also be used for resources in ultima.dat
|
||||
|
||||
Common::StringArray components = filename.splitComponents();
|
||||
if (components.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (components.size() >= 2) {
|
||||
Common::FSNode node(ConfMan.getPath("path"));
|
||||
for(const auto &c : components) {
|
||||
node = node.getChild(c);
|
||||
if (!node.exists())
|
||||
break;
|
||||
}
|
||||
if (node.exists())
|
||||
_srcFile.open(node);
|
||||
}
|
||||
|
||||
if (!_srcFile.isOpen())
|
||||
_srcFile.open(filename);
|
||||
|
||||
if (!_srcFile.isOpen()) {
|
||||
DEBUG(0, LEVEL_ERROR, "Failed opening '%s'\n", filename.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_file = &_srcFile;
|
||||
size = _srcFile.size();
|
||||
pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileRead::open(Common::InSaveFile *saveFile) {
|
||||
assert(saveFile);
|
||||
_file = saveFile;
|
||||
|
||||
size = _file->size();
|
||||
pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieIOFileRead::close() {
|
||||
if (_srcFile.isOpen())
|
||||
_srcFile.close();
|
||||
_file = nullptr;
|
||||
|
||||
NuvieIO::close();
|
||||
}
|
||||
|
||||
void NuvieIOFileRead::seek(uint32 new_pos) {
|
||||
if (isOpen() && new_pos <= size) {
|
||||
_file->seek(new_pos);
|
||||
pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 NuvieIOFileRead::read1() {
|
||||
if (pos > size - 1)
|
||||
return 0;
|
||||
|
||||
pos++;
|
||||
|
||||
return _file->readByte();
|
||||
}
|
||||
|
||||
uint16 NuvieIOFileRead::read2() {
|
||||
if (pos > size - 2)
|
||||
return 0;
|
||||
|
||||
pos += 2;
|
||||
return _file->readUint16LE();
|
||||
}
|
||||
|
||||
uint32 NuvieIOFileRead::read4() {
|
||||
if (pos > size - 4)
|
||||
return 0;
|
||||
|
||||
pos += 4;
|
||||
return _file->readUint32LE();
|
||||
}
|
||||
|
||||
bool NuvieIOFileRead::readToBuf(unsigned char *buf, uint32 buf_size) {
|
||||
if (pos + buf_size > size)
|
||||
return false;
|
||||
|
||||
_file->read(buf, buf_size);
|
||||
pos += buf_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// NuvieIOFileWrite
|
||||
//
|
||||
|
||||
NuvieIOFileWrite::NuvieIOFileWrite() : _saveFileData(DisposeAfterUse::YES),
|
||||
_file(nullptr), _saveFile(nullptr), _isAutosave(false) {
|
||||
}
|
||||
|
||||
NuvieIOFileWrite::~NuvieIOFileWrite() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::open(const Common::Path &filename) {
|
||||
if (isOpen())
|
||||
// We already have an open file
|
||||
return false;
|
||||
|
||||
// Ensure it's a relative path, that we can open for writing using a DumpFile
|
||||
assert(!filename.getParent().empty());
|
||||
|
||||
if (!_dumpFile.open(filename, true)) {
|
||||
DEBUG(0, LEVEL_ERROR, "Failed opening '%s'\n", filename.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_file = &_dumpFile;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::open(const Common::String &filename, bool isAutosave) {
|
||||
if (isOpen())
|
||||
// We already have an open file
|
||||
return false;
|
||||
|
||||
// Singular file, so open it as a save file
|
||||
_saveFile = g_system->getSavefileManager()->openForSaving(filename, false);
|
||||
assert(_saveFile);
|
||||
|
||||
// Point _file to the _saveFileData member for initial writing,
|
||||
// since save files don't allow seeking
|
||||
_file = &_saveFileData;
|
||||
_isAutosave = isAutosave;
|
||||
|
||||
size = 0;
|
||||
pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NuvieIOFileWrite::close() {
|
||||
if (!isOpen()) {
|
||||
// Nothing needed
|
||||
} else if (_saveFile) {
|
||||
// Writing using savefile interface, so flush out data
|
||||
_saveFile->write(_saveFileData.getData(), _saveFileData.size());
|
||||
g_engine->getMetaEngine()->appendExtendedSave(_saveFile, g_engine->getTotalPlayTime(), _description, _isAutosave);
|
||||
|
||||
_saveFile->finalize();
|
||||
delete _saveFile;
|
||||
_saveFile = nullptr;
|
||||
} else {
|
||||
// Writing to a dump file, so simply close it
|
||||
_dumpFile.close();
|
||||
}
|
||||
|
||||
_file = nullptr;
|
||||
}
|
||||
|
||||
void NuvieIOFileWrite::seek(uint32 new_pos) {
|
||||
if (isOpen() && new_pos <= size) {
|
||||
_file->seek(new_pos);
|
||||
pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::write1(uint8 src) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
_file->writeByte(src);
|
||||
++pos;
|
||||
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::write2(uint16 src) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
_file->writeUint16LE(src);
|
||||
pos += 2;
|
||||
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NuvieIOFileWrite::write4(uint32 src) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
_file->writeUint32LE(src);
|
||||
pos += 4;
|
||||
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 NuvieIOFileWrite::writeBuf(const unsigned char *src, uint32 src_size) {
|
||||
if (!isOpen())
|
||||
return false;
|
||||
|
||||
pos += src_size;
|
||||
if (pos > size)
|
||||
size = pos;
|
||||
|
||||
return (_file->write(src, src_size));
|
||||
}
|
||||
|
||||
uint32 NuvieIOFileWrite::write(NuvieIO *src) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
109
engines/ultima/nuvie/files/nuvie_io_file.h
Normal file
109
engines/ultima/nuvie/files/nuvie_io_file.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_NUVIE_IO_FILE_H
|
||||
#define NUVIE_FILES_NUVIE_IO_FILE_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/files/nuvie_io.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class NuvieIOFile : public NuvieIO {
|
||||
public:
|
||||
NuvieIOFile() {}
|
||||
~NuvieIOFile() override {}
|
||||
|
||||
virtual bool open(const Common::Path &filename) {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
class NuvieIOFileRead : public NuvieIOFile {
|
||||
private:
|
||||
Common::SeekableReadStream *_file;
|
||||
Common::File _srcFile;
|
||||
public:
|
||||
NuvieIOFileRead() : NuvieIOFile(), _file(nullptr) {}
|
||||
~NuvieIOFileRead() override;
|
||||
|
||||
bool open(const Common::Path &filename) override;
|
||||
virtual bool open(Common::InSaveFile *saveFile);
|
||||
void close() override;
|
||||
void seek(uint32 new_pos) override;
|
||||
|
||||
uint8 read1() override;
|
||||
uint16 read2() override;
|
||||
uint32 read4() override;
|
||||
|
||||
bool readToBuf(unsigned char *buf, uint32 buf_size) override;
|
||||
|
||||
bool isOpen() const {
|
||||
return _file != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* File writing class. This can be done in one of two ways.
|
||||
* If it's a simple filename, then it uses the savefile interface to
|
||||
* write it as uncompresed to the save folder. However, if it has
|
||||
* relative paths, such as used by the code to dump out all tiles or maps,
|
||||
* it uses a Common::DumpFile instead
|
||||
*/
|
||||
class NuvieIOFileWrite : public NuvieIOFile {
|
||||
private:
|
||||
Common::SeekableWriteStream *_file;
|
||||
Common::DumpFile _dumpFile;
|
||||
Common::OutSaveFile *_saveFile;
|
||||
Common::MemoryWriteStreamDynamic _saveFileData;
|
||||
Common::String _description;
|
||||
bool _isAutosave;
|
||||
protected:
|
||||
bool isOpen() const {
|
||||
return _file != nullptr;
|
||||
}
|
||||
public:
|
||||
NuvieIOFileWrite();
|
||||
~NuvieIOFileWrite() override;
|
||||
bool open(const Common::Path &filename) override;
|
||||
bool open(const Common::String &filename, bool isAutosave);
|
||||
void close() override;
|
||||
void seek(uint32 new_pos) override;
|
||||
|
||||
bool write1(uint8 src) override;
|
||||
bool write2(uint16 src) override;
|
||||
bool write4(uint32 src) override;
|
||||
void writeDesc(const Common::String &desc) {
|
||||
_description = desc;
|
||||
}
|
||||
|
||||
uint32 writeBuf(const unsigned char *src, uint32 src_size) override;
|
||||
uint32 write(NuvieIO *src) override;
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
249
engines/ultima/nuvie/files/tmx_map.cpp
Normal file
249
engines/ultima/nuvie/files/tmx_map.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/* 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/tile_manager.h"
|
||||
#include "ultima/nuvie/core/map.h"
|
||||
#include "ultima/nuvie/core/obj_manager.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
#include "ultima/nuvie/files/tmx_map.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
TMXMap::TMXMap(TileManager *tm, Map *m, ObjManager *om) : tile_manager(tm),
|
||||
map(m), obj_manager(om), mapdata(nullptr) {
|
||||
}
|
||||
|
||||
TMXMap::~TMXMap() {
|
||||
|
||||
}
|
||||
|
||||
bool TMXMap::exportTmxMapFiles(const Common::Path &dir, nuvie_game_t type) {
|
||||
savedir = dir;
|
||||
savename = get_game_tag(type);
|
||||
Common::Path filename;
|
||||
build_path(savedir, savename + "_tileset.bmp", filename);
|
||||
|
||||
|
||||
|
||||
tile_manager->exportTilesetToBmpFile(filename);
|
||||
|
||||
for (uint8 i = 0; i < 6; i++) {
|
||||
writeRoofTileset(i);
|
||||
exportMapLevel(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TMXMap::writeRoofTileset(uint8 level) {
|
||||
if (map->get_roof_data(level) == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Path filename = map->getRoofTilesetFilename();
|
||||
Common::Path destFilename;
|
||||
build_path(savedir, savename + "_roof_tileset.bmp", destFilename);
|
||||
NuvieIOFileRead read;
|
||||
NuvieIOFileWrite write;
|
||||
read.open(filename);
|
||||
write.open(destFilename);
|
||||
unsigned char *buf = read.readAll();
|
||||
write.writeBuf(buf, read.get_size());
|
||||
write.close();
|
||||
read.close();
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void TMXMap::writeLayer(NuvieIOFileWrite *tmx, uint16 sideLength, Std::string layerName,
|
||||
uint16 gidOffset, uint16 bitsPerTile, const unsigned char *data) {
|
||||
Std::string slen = sint32ToString((sint32)sideLength);
|
||||
Std::string header = " <layer name=\"" + layerName + "\" width=\"" + slen + "\" height=\""
|
||||
+ slen + "\">\n";
|
||||
header += " <data encoding=\"csv\">\n";
|
||||
|
||||
tmx->writeBuf((const unsigned char *)header.c_str(), header.size());
|
||||
|
||||
uint16 mx, my;
|
||||
for (my = 0; my < sideLength; my++) {
|
||||
for (mx = 0; mx < sideLength; mx++) {
|
||||
uint16 gid = 0;
|
||||
if (bitsPerTile == 8) { //base map is uint8
|
||||
gid = (uint16)data[my * sideLength + mx] + 1 + gidOffset;
|
||||
} else { //everything else is uint16
|
||||
gid = ((const uint16 *)data)[my * sideLength + mx] + 1 + gidOffset;
|
||||
}
|
||||
// 'nnnn\0'
|
||||
Common::String temp = Common::String::format("%d", gid);
|
||||
tmx->writeBuf((const unsigned char *)temp.c_str(), temp.size());
|
||||
if (mx < sideLength - 1 || my < sideLength - 1) { //don't write comma after last element in the array.
|
||||
tmx->write1(',');
|
||||
}
|
||||
}
|
||||
tmx->write1('\n');
|
||||
}
|
||||
|
||||
Std::string footer = " </data>\n";
|
||||
footer += " </layer>\n";
|
||||
|
||||
tmx->writeBuf((const unsigned char *)footer.c_str(), footer.size());
|
||||
}
|
||||
|
||||
void TMXMap::writeObjectLayer(NuvieIOFileWrite *tmx, uint8 level) {
|
||||
Std::string xml = "<objectgroup name=\"Object Layer\">\n";
|
||||
tmx->writeBuf((const unsigned char *)xml.c_str(), xml.size());
|
||||
|
||||
writeObjects(tmx, level, true, false);
|
||||
writeObjects(tmx, level, false, false);
|
||||
writeObjects(tmx, level, false, true);
|
||||
|
||||
xml = "</objectgroup>\n";
|
||||
tmx->writeBuf((const unsigned char *)xml.c_str(), xml.size());
|
||||
}
|
||||
|
||||
bool TMXMap::canDrawTile(Tile *t, bool forceLower, bool toptile) {
|
||||
if (forceLower == false && (t->flags3 & 0x4) && toptile == false) //don't display force lower tiles.
|
||||
return false;
|
||||
|
||||
if (forceLower == true && !(t->flags3 & 0x4))
|
||||
return false;
|
||||
|
||||
if ((toptile && !t->toptile) || (!toptile && t->toptile))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Std::string TMXMap::writeObjectTile(Obj *obj, Std::string nameSuffix, uint16 tile_num, uint16 x, uint16 y, bool forceLower, bool toptile) {
|
||||
Tile *t = tile_manager->get_tile(tile_num);
|
||||
|
||||
if (canDrawTile(t, forceLower, toptile)) {
|
||||
return " <object name=\"" + encode_xml_entity(Std::string(obj_manager->get_obj_name(obj))) + nameSuffix + "\" gid=\"" + sint32ToString(tile_num + 1) + "\" x=\"" + sint32ToString(x * 16) + "\" y=\"" + sint32ToString((y + 1) * 16) + "\" width=\"16\" height=\"16\"/>\n";
|
||||
}
|
||||
|
||||
return Std::string();
|
||||
}
|
||||
|
||||
void TMXMap::writeObjects(NuvieIOFileWrite *tmx, uint8 level, bool forceLower, bool toptiles) {
|
||||
uint16 width = map->get_width(level);
|
||||
|
||||
for (uint16 y = 0; y < width; y++) {
|
||||
for (uint16 x = 0; x < width; x++) {
|
||||
U6LList *list = obj_manager->get_obj_list(x, y, level);
|
||||
if (list) {
|
||||
for (U6Link *link = list->start(); link != nullptr; link = link->next) {
|
||||
Obj *obj = (Obj *)link->data;
|
||||
Tile *t = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
|
||||
Std::string s;
|
||||
if (canDrawTile(t, forceLower, toptiles)) {
|
||||
s = " <object name=\"" + encode_xml_entity(Std::string(obj_manager->get_obj_name(obj))) + "\" gid=\"" + sint32ToString(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n + 1) + "\" x=\"" + sint32ToString((x) * 16) + "\" y=\"" + sint32ToString((y + 1) * 16) + "\" width=\"16\" height=\"16\">\n";
|
||||
s += " <properties>\n";
|
||||
s += " <property name=\"obj_n\" value=\"" + sint32ToString(obj->obj_n) + "\"/>\n";
|
||||
s += " <property name=\"frame_n\" value=\"" + sint32ToString(obj->frame_n) + "\"/>\n";
|
||||
s += " <property name=\"qty\" value=\"" + sint32ToString(obj->qty) + "\"/>\n";
|
||||
s += " <property name=\"quality\" value=\"" + sint32ToString(obj->quality) + "\"/>\n";
|
||||
s += " <property name=\"status\" value=\"" + sint32ToString(obj->status) + "\"/>\n";
|
||||
s += " <property name=\"toptile\" value=\"" + boolToString(t->toptile) + "\"/>\n";
|
||||
s += " </properties>\n";
|
||||
s += " </object>\n";
|
||||
}
|
||||
if (t->dbl_width) {
|
||||
s += writeObjectTile(obj, " -x", t->tile_num - 1, x - 1, y, forceLower, toptiles);
|
||||
}
|
||||
if (t->dbl_height) {
|
||||
uint16 tile_num = t->tile_num - 1;
|
||||
if (t->dbl_width) {
|
||||
tile_num--;
|
||||
}
|
||||
s += writeObjectTile(obj, " -y", tile_num, x, y - 1, forceLower, toptiles);
|
||||
}
|
||||
if (t->dbl_width && t->dbl_height) {
|
||||
s += writeObjectTile(obj, " -x,-y", t->tile_num - 3, x - 1, y - 1, forceLower, toptiles);
|
||||
}
|
||||
tmx->writeBuf((const unsigned char *)s.c_str(), s.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TMXMap::exportMapLevel(uint8 level) {
|
||||
NuvieIOFileWrite tmx;
|
||||
uint16 width = map->get_width(level);
|
||||
mapdata = map->get_map_data(level);
|
||||
Common::String level_string = Common::String::format("%d", level); // 'nn\0'
|
||||
Common::Path filename;
|
||||
build_path(savedir, savename + "_" + Std::string(level_string.c_str()) + ".tmx", filename);
|
||||
|
||||
tmx.open(filename);
|
||||
Std::string swidth = sint32ToString((sint32)width);
|
||||
Std::string header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
header +=
|
||||
"<map version=\"1.0\" orientation=\"orthogonal\" renderorder=\"right-down\" width=\""
|
||||
+ swidth + "\" height=\"" + swidth
|
||||
+ "\" tilewidth=\"16\" tileheight=\"16\">\n";
|
||||
header +=
|
||||
" <tileset firstgid=\"1\" name=\"tileset\" tilewidth=\"16\" tileheight=\"16\">\n";
|
||||
header += " <image source=\"" + savename
|
||||
+ "_tileset.bmp\" trans=\"00dffc\" width=\"512\" height=\"1024\"/>\n";
|
||||
header += " </tileset>\n";
|
||||
|
||||
if (map->get_roof_data(level) != nullptr) {
|
||||
header +=
|
||||
" <tileset firstgid=\"2048\" name=\"roof_tileset\" tilewidth=\"16\" tileheight=\"16\">\n";
|
||||
header += " <image source=\"" + savename + "_roof_tileset.bmp\" trans=\"0070fc\" width=\"80\" height=\"3264\"/>\n";
|
||||
header += " </tileset>\n";
|
||||
}
|
||||
|
||||
tmx.writeBuf((const unsigned char *)header.c_str(), header.size());
|
||||
|
||||
writeLayer(&tmx, width, "BaseLayer", 0, 8, mapdata);
|
||||
|
||||
writeObjectLayer(&tmx, level);
|
||||
|
||||
if (map->get_roof_data(level) != nullptr) {
|
||||
writeLayer(&tmx, width, "RoofLayer", 2047, 16, (const unsigned char *)map->get_roof_data(level));
|
||||
}
|
||||
|
||||
Std::string footer = "</map>\n";
|
||||
|
||||
|
||||
tmx.writeBuf((const unsigned char *)footer.c_str(), footer.size());
|
||||
|
||||
|
||||
|
||||
tmx.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Std::string TMXMap::sint32ToString(sint32 value) {
|
||||
char buf[12];
|
||||
snprintf(buf, sizeof(buf), "%d", value);
|
||||
return Std::string(buf);
|
||||
}
|
||||
|
||||
Std::string TMXMap::boolToString(bool value) {
|
||||
return value ? Std::string("true") : Std::string("false");
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
67
engines/ultima/nuvie/files/tmx_map.h
Normal file
67
engines/ultima/nuvie/files/tmx_map.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_TMX_MAP_H
|
||||
#define NUVIE_FILES_TMX_MAP_H
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class Map;
|
||||
class ObjManager;
|
||||
class TileManager;
|
||||
class NuvieIOFileWrite;
|
||||
|
||||
class TMXMap {
|
||||
private:
|
||||
unsigned char *mapdata;
|
||||
NuvieIOFileWrite file;
|
||||
TileManager *tile_manager;
|
||||
Map *map;
|
||||
ObjManager *obj_manager;
|
||||
Common::Path savedir;
|
||||
Std::string savename;
|
||||
//nuvie_game_t game_type;
|
||||
|
||||
public:
|
||||
TMXMap(TileManager *tm, Map *m, ObjManager *om);
|
||||
virtual ~TMXMap();
|
||||
bool exportTmxMapFiles(const Common::Path &dir, nuvie_game_t type);
|
||||
private:
|
||||
bool exportMapLevel(uint8 level);
|
||||
void writeRoofTileset(uint8 level);
|
||||
void writeLayer(NuvieIOFileWrite *tmx, uint16 width, Std::string layerName,
|
||||
uint16 gidOffset, uint16 bitsPerTile, const unsigned char *data);
|
||||
void writeObjectLayer(NuvieIOFileWrite *tmx, uint8 level);
|
||||
void writeObjects(NuvieIOFileWrite *tmx, uint8 level, bool forceLower, bool toptiles);
|
||||
Std::string writeObjectTile(Obj *obj, Std::string nameSuffix, uint16 tile_num, uint16 x, uint16 y, bool forceLower, bool toptile);
|
||||
Std::string sint32ToString(sint32 value);
|
||||
Std::string boolToString(bool value);
|
||||
bool canDrawTile(Tile *t, bool forceLower, bool toptile);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
65
engines/ultima/nuvie/files/u6_bmp.cpp
Normal file
65
engines/ultima/nuvie/files/u6_bmp.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/* 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/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_bmp.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Bmp::U6Bmp(): U6Shape(), data(nullptr) {
|
||||
}
|
||||
|
||||
U6Bmp::~U6Bmp() {
|
||||
if (data != nullptr)
|
||||
free(data);
|
||||
|
||||
raw = nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool U6Bmp::load(const Common::Path &filename) {
|
||||
U6Lzw lzw;
|
||||
uint32 data_size;
|
||||
|
||||
if (data != nullptr)
|
||||
return false;
|
||||
|
||||
if (filename.empty())
|
||||
return false;
|
||||
|
||||
data = lzw.decompress_file(filename, data_size);
|
||||
|
||||
if (data == nullptr)
|
||||
return false;
|
||||
|
||||
width = (data[0] + (data[1] << 8));
|
||||
height = (data[2] + (data[3] << 8));
|
||||
|
||||
raw = data + 0x4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
48
engines/ultima/nuvie/files/u6_bmp.h
Normal file
48
engines/ultima/nuvie/files/u6_bmp.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6BMP_H
|
||||
#define NUVIE_FILES_U6BMP_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
#include "ultima/nuvie/files/u6_shape.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class U6Bmp: public U6Shape {
|
||||
private:
|
||||
unsigned char *data;
|
||||
|
||||
public:
|
||||
|
||||
U6Bmp();
|
||||
~U6Bmp() override;
|
||||
|
||||
bool load(const Common::Path &filename) override;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
475
engines/ultima/nuvie/files/u6_lib_n.cpp
Normal file
475
engines/ultima/nuvie/files/u6_lib_n.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
/* 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/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/files/u6_lib_n.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Lib_n::U6Lib_n() : num_offsets(0), items(nullptr), data(nullptr),
|
||||
del_data(false), filesize(0), game_type(NUVIE_GAME_U6), lib_size(0) {
|
||||
}
|
||||
|
||||
|
||||
U6Lib_n::~U6Lib_n(void) {
|
||||
close();
|
||||
}
|
||||
|
||||
// load u6lib from `filename'
|
||||
bool U6Lib_n::open(const Common::Path &filename, uint8 size, uint8 type) {
|
||||
NuvieIOFileRead *file;
|
||||
|
||||
file = new NuvieIOFileRead();
|
||||
|
||||
if (file->open(filename) == false) {
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
|
||||
del_data = true;
|
||||
|
||||
return open((NuvieIO *)file, size, type);
|
||||
}
|
||||
|
||||
|
||||
// load u6lib from opened stream
|
||||
bool U6Lib_n::open(NuvieIO *new_data, uint8 size, uint8 type) {
|
||||
game_type = type;
|
||||
data = new_data;
|
||||
|
||||
lib_size = size;
|
||||
this->parse_lib();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void U6Lib_n::close() {
|
||||
if (items) {
|
||||
for (uint32 i = 0; i < num_offsets; i++)
|
||||
delete items[i].name;
|
||||
free(items);
|
||||
}
|
||||
items = nullptr;
|
||||
|
||||
if (del_data) {
|
||||
if (data != nullptr)
|
||||
data->close();
|
||||
delete data;
|
||||
}
|
||||
|
||||
data = nullptr;
|
||||
del_data = false;
|
||||
|
||||
num_offsets = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Open a ^new^ file for writing, with lib_size and type.
|
||||
*/
|
||||
bool U6Lib_n::create(const Common::Path &filename, uint8 size, uint8 type) {
|
||||
NuvieIOFileWrite *file = new NuvieIOFileWrite();
|
||||
if (!file->open(filename)) {
|
||||
DEBUG(0, LEVEL_ERROR, "U6Lib: Error creating %s\n", filename.toString().c_str());
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
game_type = type;
|
||||
lib_size = size;
|
||||
data = (NuvieIO *)file;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint32 U6Lib_n::get_num_items(void) {
|
||||
return num_offsets;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the location of `item_number' in the library file.
|
||||
*/
|
||||
uint32 U6Lib_n::get_item_offset(uint32 item_number) {
|
||||
if (item_number >= num_offsets)
|
||||
return 0;
|
||||
return (items[item_number].offset);
|
||||
}
|
||||
|
||||
uint32 U6Lib_n::get_item_size(uint32 item_number) {
|
||||
if (item_number >= num_offsets)
|
||||
return 0;
|
||||
|
||||
return (items[item_number].uncomp_size);
|
||||
}
|
||||
|
||||
|
||||
// read and return item data
|
||||
unsigned char *U6Lib_n::get_item(uint32 item_number, unsigned char *ret_buf) {
|
||||
U6LibItem *item;
|
||||
unsigned char *buf, *lzw_buf;
|
||||
|
||||
if (item_number >= num_offsets)
|
||||
return nullptr;
|
||||
|
||||
item = &items[item_number];
|
||||
|
||||
if (item->size == 0 || item->offset == 0)
|
||||
return nullptr;
|
||||
|
||||
if (ret_buf == nullptr)
|
||||
buf = (unsigned char *)malloc(item->uncomp_size);
|
||||
else
|
||||
buf = ret_buf;
|
||||
|
||||
data->seek(item->offset);
|
||||
|
||||
if (is_compressed(item_number)) {
|
||||
U6Lzw lzw;
|
||||
lzw_buf = (unsigned char *)malloc(item->size);
|
||||
data->readToBuf(lzw_buf, item->size);
|
||||
lzw.decompress_buffer(lzw_buf, item->size, buf, item->uncomp_size);
|
||||
} else {
|
||||
data->readToBuf(buf, item->size);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool U6Lib_n::is_compressed(uint32 item_number) {
|
||||
uint32 i;
|
||||
|
||||
switch (items[item_number].flag) {
|
||||
case 0x1 :
|
||||
case 0x20 :
|
||||
return true;
|
||||
case 0xff :
|
||||
for (i = item_number; i < num_offsets; i++) {
|
||||
if (items[i].flag != 0xff)
|
||||
break;
|
||||
}
|
||||
if (i < num_offsets)
|
||||
return is_compressed(i);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void U6Lib_n::parse_lib() {
|
||||
uint32 i;
|
||||
bool skip4 = false;
|
||||
|
||||
if (lib_size != 2 && lib_size != 4)
|
||||
return;
|
||||
|
||||
data->seekStart();
|
||||
|
||||
if (game_type != NUVIE_GAME_U6) { //U6 doesn't have a 4 byte filesize header.
|
||||
skip4 = true;
|
||||
filesize = data->read4();
|
||||
} else
|
||||
filesize = data->get_size();
|
||||
|
||||
num_offsets = calculate_num_offsets(skip4);
|
||||
|
||||
items = (U6LibItem *)malloc(sizeof(U6LibItem) * (num_offsets + 1));
|
||||
memset(items, 0, sizeof(U6LibItem) * (num_offsets + 1));
|
||||
|
||||
data->seekStart();
|
||||
if (skip4)
|
||||
data->seek(0x4);
|
||||
for (i = 0; i < num_offsets && !data->is_end(); i++) {
|
||||
if (lib_size == 2)
|
||||
items[i].offset = data->read2();
|
||||
else {
|
||||
items[i].offset = data->read4();
|
||||
// U6 converse files dont have flag?
|
||||
items[i].flag = (items[i].offset & 0xff000000) >> 24; //extract flag byte
|
||||
items[i].offset &= 0xffffff;
|
||||
}
|
||||
}
|
||||
|
||||
items[num_offsets].offset = filesize; //this is used to calculate the size of the last item in the lib.
|
||||
|
||||
calculate_item_sizes();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// for reading, calculate item sizes based on offsets
|
||||
void U6Lib_n::calculate_item_sizes() {
|
||||
uint32 i, next_offset = 0;
|
||||
|
||||
for (i = 0; i < num_offsets; i++) {
|
||||
items[i].size = 0;
|
||||
// get next non-zero offset, including the filesize at items[num_offsets]
|
||||
for (uint32 o = (i + 1); o <= num_offsets; o++)
|
||||
if (items[o].offset) {
|
||||
next_offset = items[o].offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (items[i].offset && (next_offset > items[i].offset))
|
||||
items[i].size = next_offset - items[i].offset;
|
||||
|
||||
items[i].uncomp_size = calculate_item_uncomp_size(&items[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// for reading, calculate uncompressed item size based on item flag
|
||||
uint32 U6Lib_n::calculate_item_uncomp_size(U6LibItem *item) {
|
||||
uint32 uncomp_size = 0;
|
||||
|
||||
switch (item->flag) {
|
||||
case 0x01 : //compressed
|
||||
case 0x20 : //MD fonts.lzc, MDD_MUS.LZC use this tag among others
|
||||
data->seek(item->offset);
|
||||
uncomp_size = data->read4();
|
||||
break;
|
||||
|
||||
//FIX check this. uncompressed 4 byte item size header
|
||||
case 0xc1 :
|
||||
uncomp_size = item->size; // - 4;
|
||||
break;
|
||||
|
||||
// uncompressed
|
||||
case 0x0 :
|
||||
case 0x2 :
|
||||
case 0xe0 :
|
||||
default :
|
||||
uncomp_size = item->size;
|
||||
break;
|
||||
}
|
||||
|
||||
return uncomp_size;
|
||||
}
|
||||
|
||||
// we need to handle nullptr offsets at the start of the offset table in the converse.a file
|
||||
uint32 U6Lib_n::calculate_num_offsets(bool skip4) { //skip4 bytes of header.
|
||||
uint32 i;
|
||||
uint32 offset = 0;
|
||||
|
||||
if (skip4)
|
||||
data->seek(0x4);
|
||||
|
||||
|
||||
// We assume the first data in the file is directly behind the offset table,
|
||||
// so we continue scanning until we hit a data block.
|
||||
uint32 max_count = 0xffffffff;
|
||||
for (i = 0; !data->is_end(); i++) {
|
||||
if (i == max_count)
|
||||
return i;
|
||||
|
||||
if (lib_size == 2)
|
||||
offset = data->read2();
|
||||
else {
|
||||
offset = data->read4();
|
||||
offset &= 0xffffff; // clear flag byte.
|
||||
}
|
||||
if (offset != 0) {
|
||||
if (skip4)
|
||||
offset -= 4;
|
||||
|
||||
if (offset / lib_size < max_count)
|
||||
max_count = offset / lib_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* For writing multiple files to a lib, read in source filenames and offsets
|
||||
* from an opened index file. Offsets may be ignored when writing.
|
||||
*/
|
||||
void U6Lib_n::load_index(Common::ReadStream *index_f) {
|
||||
char input[256] = "", // input line
|
||||
offset_str[9] = "", // listed offset
|
||||
name[256] = ""; // source file name
|
||||
int in_len = 0, oc = 0; // length of input line, character in copy string
|
||||
int c = 0, entry_count = 0; // character in input line, number of entries
|
||||
|
||||
if (!index_f)
|
||||
return;
|
||||
while (strgets(input, 256, index_f)) {
|
||||
in_len = strlen(input);
|
||||
// skip spaces, read offset, break on #
|
||||
for (c = 0; c < in_len && Common::isSpace(input[c]) && input[c] != '#'; c++);
|
||||
for (oc = 0; c < in_len && !Common::isSpace(input[c]) && input[c] != '#'; c++)
|
||||
offset_str[oc++] = input[c];
|
||||
offset_str[oc] = '\0';
|
||||
// skip spaces, read name, break on # or \n or \r
|
||||
for (; c < in_len && Common::isSpace(input[c]) && input[c] != '#'; c++);
|
||||
for (oc = 0; c < in_len && input[c] != '\n' && input[c] != '\r' && input[c] != '#'; c++)
|
||||
name[oc++] = input[c];
|
||||
name[oc] = '\0';
|
||||
if (strlen(offset_str)) { // if line is not empty (!= zero entry)
|
||||
uint32 offset32 = strtol(offset_str, nullptr, 16);
|
||||
add_item(offset32, name);
|
||||
++entry_count;
|
||||
}
|
||||
offset_str[0] = '\0';
|
||||
oc = 0;
|
||||
}
|
||||
(void)entry_count; // Fix "unused varable" warning
|
||||
}
|
||||
|
||||
|
||||
/* Append an offset and a name to the library. The other fields are initialized.
|
||||
*/
|
||||
void U6Lib_n::add_item(uint32 offset32, const char *name) {
|
||||
if (!num_offsets)
|
||||
items = (U6LibItem *)malloc(sizeof(U6LibItem));
|
||||
else
|
||||
items = (U6LibItem *)nuvie_realloc(items, sizeof(U6LibItem) * (num_offsets + 1));
|
||||
U6LibItem *item = &items[num_offsets];
|
||||
item->offset = offset32;
|
||||
item->name = new string(name);
|
||||
item->size = 0;
|
||||
item->uncomp_size = 0;
|
||||
item->flag = 0; // uncompressed
|
||||
item->data = nullptr;
|
||||
++num_offsets;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the name of (filename associated with) `item_number'.
|
||||
*/
|
||||
const char *U6Lib_n::get_item_name(uint32 item_number) {
|
||||
if (item_number >= num_offsets)
|
||||
return nullptr;
|
||||
return (items[item_number].name ? items[item_number].name->c_str() : nullptr);
|
||||
}
|
||||
|
||||
|
||||
/* Set data for an item, in preparation of writing or to cache the library.
|
||||
* Size & uncompressed size is set to source length.
|
||||
*/
|
||||
void U6Lib_n::set_item_data(uint32 item_number, unsigned char *src, uint32 src_len) {
|
||||
if (item_number >= num_offsets)
|
||||
return;
|
||||
// FIXME: need a way to set an item as compressed or uncompressed so we know
|
||||
// which size to set
|
||||
items[item_number].size = src_len;
|
||||
items[item_number].uncomp_size = src_len;
|
||||
if (src_len) {
|
||||
unsigned char *dcopy = (unsigned char *)malloc(src_len);
|
||||
memcpy(dcopy, src, src_len);
|
||||
items[item_number].data = dcopy;
|
||||
} else
|
||||
items[item_number].data = 0;
|
||||
}
|
||||
|
||||
|
||||
/* For writing, (re)calculate item offsets from item sizes.
|
||||
*/
|
||||
void U6Lib_n::calc_item_offsets() {
|
||||
if (num_offsets == 0)
|
||||
return;
|
||||
if (items[0].size) // first offset is past library index
|
||||
items[0].offset = (num_offsets * lib_size);
|
||||
else
|
||||
items[0].offset = 0; // 0 = no data, no affect on other items
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: sizes[0] == %d\n", sizes[0]);
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: offsets[0] == %d\n", offsets[0]);
|
||||
for (uint32 i = 1; i < num_offsets; i++) {
|
||||
if (items[i].size) {
|
||||
// find previous item with non-zero offset
|
||||
uint32 prev_i = 0;
|
||||
for (uint32 i_sub = 1; i_sub <= i; i_sub++) {
|
||||
prev_i = i - i_sub;
|
||||
if (items[prev_i].offset != 0)
|
||||
break;
|
||||
}
|
||||
items[i].offset = (items[prev_i].offset + items[prev_i].size);
|
||||
if (items[i].offset == 0) // last item had no data; skip index here
|
||||
items[i].offset = (num_offsets * lib_size);
|
||||
} else
|
||||
items[i].offset = 0; // 0 = no data, no affect on other items
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: sizes[%d] == %d\n", i, sizes[i]);
|
||||
// DEBUG(0,LEVEL_DEBUGGING,"calc_item_offsets: offsets[%d] == %d\n", i, offsets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void U6Lib_n::write_header() {
|
||||
data->seekStart();
|
||||
if (game_type == NUVIE_GAME_U6)
|
||||
return;
|
||||
|
||||
uint32 totalSize = 4 + num_offsets * lib_size;
|
||||
|
||||
for (uint i = 0; i < num_offsets; i++) {
|
||||
totalSize += items[i].size;
|
||||
}
|
||||
|
||||
data->write4(totalSize);
|
||||
}
|
||||
|
||||
/* Write the library index. (the 2 or 4 byte offsets before the data)
|
||||
*/
|
||||
void U6Lib_n::write_index() {
|
||||
data->seekStart();
|
||||
if (game_type != NUVIE_GAME_U6) {
|
||||
data->seek(4);
|
||||
}
|
||||
|
||||
for (uint32 o = 0; o < num_offsets; o++) {
|
||||
uint32 offset = items[o].offset;
|
||||
if (game_type != NUVIE_GAME_U6 && offset != 0) {
|
||||
offset += 4;
|
||||
}
|
||||
if (lib_size == 2)
|
||||
data->write2((uint16)offset);
|
||||
else if (lib_size == 4)
|
||||
data->write4(offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Write all item data to the library file at their respective offsets.
|
||||
*/
|
||||
void U6Lib_n::write_items() {
|
||||
for (uint32 i = 0; i < num_offsets; i++)
|
||||
write_item(i);
|
||||
}
|
||||
|
||||
|
||||
/* Write item data to the library file at the indicated offset, unless the
|
||||
* offset is 0 (then the data is considered empty).
|
||||
*/
|
||||
void U6Lib_n::write_item(uint32 item_number) {
|
||||
if (item_number >= num_offsets
|
||||
|| items[item_number].offset == 0 || items[item_number].size == 0)
|
||||
return;
|
||||
if (game_type == NUVIE_GAME_U6)
|
||||
data->seek(items[item_number].offset);
|
||||
else
|
||||
data->seek(items[item_number].offset + 4);
|
||||
((NuvieIOFileWrite *)data)->writeBuf(items[item_number].data, items[item_number].size);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
104
engines/ultima/nuvie/files/u6_lib_n.h
Normal file
104
engines/ultima/nuvie/files/u6_lib_n.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6LIB_N_H
|
||||
#define NUVIE_FILES_U6LIB_N_H
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
using Std::string;
|
||||
//using Std::vector;
|
||||
|
||||
class NuvieIO;
|
||||
|
||||
struct U6LibItem {
|
||||
uint32 offset;
|
||||
uint8 flag;
|
||||
uint32 uncomp_size;
|
||||
uint32 size;
|
||||
string *name;
|
||||
unsigned char *data; // for writing or cache
|
||||
};
|
||||
|
||||
class U6Lib_n {
|
||||
uint32 filesize; // total size of file
|
||||
uint8 game_type; // there are three types of lib files.
|
||||
uint8 lib_size; // measured in bytes either 2 or 4
|
||||
uint32 num_offsets; // number of items, size of lists
|
||||
U6LibItem *items;
|
||||
NuvieIO *data;
|
||||
bool del_data;
|
||||
|
||||
public:
|
||||
U6Lib_n();
|
||||
~U6Lib_n();
|
||||
|
||||
bool open(const Common::Path &filename, uint8 size, uint8 type = NUVIE_GAME_U6);
|
||||
bool open(NuvieIO *new_data, uint8 size, uint8 type = NUVIE_GAME_U6);
|
||||
void close();
|
||||
bool create(const Common::Path &filename, uint8 size, uint8 type = NUVIE_GAME_U6);
|
||||
uint8 get_game_type() {
|
||||
return game_type;
|
||||
}
|
||||
|
||||
unsigned char *get_item(uint32 item_number, unsigned char *buf = nullptr); // read
|
||||
void set_item_data(uint32 item_number, unsigned char *src, uint32 src_len);
|
||||
|
||||
uint32 get_num_items();
|
||||
uint32 get_item_size(uint32 item_number);
|
||||
uint32 get_item_offset(uint32 item_number);
|
||||
const char *get_item_name(uint32 item_number);
|
||||
bool is_compressed(uint32 item_number);
|
||||
|
||||
void add_item(uint32 offset32, const char *name = nullptr);
|
||||
void write_item(uint32 item_number);
|
||||
void write_items();
|
||||
|
||||
void load_index(Common::ReadStream *index_f);
|
||||
void write_index();
|
||||
void write_header();
|
||||
|
||||
void calc_item_offsets();
|
||||
|
||||
protected:
|
||||
void parse_lib();
|
||||
void calculate_item_sizes();
|
||||
uint32 calculate_item_uncomp_size(U6LibItem *item);
|
||||
uint32 calculate_num_offsets(bool skip4);
|
||||
};
|
||||
|
||||
#if 0
|
||||
class U6ConverseLib: U6Lib_n {
|
||||
private:
|
||||
uint32 zero_offset_count;
|
||||
string *conversefile;
|
||||
};
|
||||
#endif
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
|
||||
#endif
|
||||
454
engines/ultima/nuvie/files/u6_lzw.cpp
Normal file
454
engines/ultima/nuvie/files/u6_lzw.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// This code is a modified version of the code from nodling's Ultima 6 website.
|
||||
// https://web.archive.org/web/20091019144234/http://www.geocities.com/nodling/
|
||||
//
|
||||
|
||||
// =============================================================
|
||||
// This program decompresses Ultima_6-style LZW-compressed files
|
||||
// =============================================================
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/files/nuvie_io_file.h"
|
||||
#include "ultima/nuvie/files/u6_lzw.h"
|
||||
#include "ultima/nuvie/misc/u6_misc.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
U6Lzw::U6Lzw() : dict(new U6LzwDict), stack(new U6LzwStack), errstr("unknown error") {
|
||||
}
|
||||
|
||||
U6Lzw::~U6Lzw() {
|
||||
delete dict;
|
||||
delete stack;
|
||||
}
|
||||
|
||||
|
||||
/* Copy and return the contents of `src' buffer, in LZW form. It is not really
|
||||
* compressed, it just makes it suitable to be read by an LZW decoder.
|
||||
*/
|
||||
unsigned char *U6Lzw::compress_buffer(unsigned char *src, uint32 src_len,
|
||||
uint32 &dest_len) {
|
||||
// FIXME - didn't bother fixing this since its output will be larger than
|
||||
// the uncompressed data
|
||||
uint32 blocks = 0; //, block = 0, b = 0, d = 0, rshift = 0;
|
||||
//uint16 val = 0;
|
||||
//unsigned char *dest_pt = nullptr;
|
||||
unsigned char *dest_buf = (unsigned char *)malloc(4);
|
||||
// add 4 byte uncompressed length value
|
||||
dest_len = 4;
|
||||
memcpy(dest_buf, &src_len, dest_len);
|
||||
blocks = (src_len / 64);
|
||||
if ((blocks * 64) < src_len)
|
||||
blocks += 1;
|
||||
dest_buf = (unsigned char *)nuvie_realloc(dest_buf, src_len + 4);
|
||||
dest_len = src_len + 4;
|
||||
memset(&dest_buf[4], 0, src_len);
|
||||
#if 0
|
||||
for (block = 0, d = 4; block < blocks && b < src_len; block++) {
|
||||
dest_len += 128;
|
||||
dest_buf = (unsigned char *)realloc(dest_buf, dest_len);
|
||||
// add 9 bit value 0x100
|
||||
// rshift += (rshift < 7) ? 1 : -rshift;
|
||||
for (; b < src_len; b++) {
|
||||
// for each byte in block, add 9bit value, upper bit = 0
|
||||
}
|
||||
}
|
||||
// add 9 bit value 0x101
|
||||
#endif
|
||||
return (dest_buf);
|
||||
}
|
||||
|
||||
|
||||
// this function only checks a few *necessary* conditions
|
||||
// returns "FALSE" if the file doesn't satisfy these conditions
|
||||
// return "TRUE" otherwise
|
||||
|
||||
bool U6Lzw::is_valid_lzw_file(NuvieIOFileRead *input_file) {
|
||||
// file must contain 4-byte size header and space for the 9-bit value 0x100
|
||||
if (input_file->get_size() < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the last byte of the size header must be 0 (U6's files aren't *that* big)
|
||||
input_file->seek(3);
|
||||
unsigned char byte3 = input_file->read1();
|
||||
if (byte3 != 0) {
|
||||
return false;
|
||||
}
|
||||
// the 9 bits after the size header must be 0x100
|
||||
input_file->seek(4);
|
||||
unsigned char b0 = input_file->read1();
|
||||
unsigned char b1 = input_file->read1();
|
||||
input_file->seekStart();
|
||||
if ((b0 != 0) || ((b1 & 1) != 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool U6Lzw::is_valid_lzw_buffer(unsigned char *buf, uint32 length) {
|
||||
if (length < 6) {
|
||||
errstr = "is_valid_lzw_buffer: buffer length < 6";
|
||||
return false;
|
||||
}
|
||||
if (buf[3] != 0) {
|
||||
errstr = "is_valid_lzw_buffer: buffer size > 16MB";
|
||||
return false;
|
||||
}
|
||||
if ((buf[4] != 0) || ((buf[5] & 1) != 1)) {
|
||||
errstr = "is_valid_lzw_buffer: first 9 bits of data != 0x100";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
long U6Lzw::get_uncompressed_file_size(NuvieIOFileRead *input_file) {
|
||||
long uncompressed_file_length;
|
||||
|
||||
if (is_valid_lzw_file(input_file)) {
|
||||
input_file->seekStart();
|
||||
uncompressed_file_length = input_file->read4();
|
||||
input_file->seekStart();
|
||||
return (uncompressed_file_length);
|
||||
} else {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
long U6Lzw::get_uncompressed_buffer_size(unsigned char *buf, uint32 length) {
|
||||
if (is_valid_lzw_buffer(buf, length)) {
|
||||
return (buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LZW-decompress from buffer to buffer.
|
||||
// The parameters "source_length" and "destination_length" are currently unused.
|
||||
// They might be used to prevent reading/writing outside the buffers.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned char *U6Lzw::decompress_buffer(unsigned char *source, uint32 source_length, uint32 &destination_length) {
|
||||
unsigned char *destination;
|
||||
sint32 uncomp_size;
|
||||
|
||||
uncomp_size = this->get_uncompressed_buffer_size(source, source_length);
|
||||
if (uncomp_size == -1)
|
||||
return nullptr;
|
||||
else
|
||||
destination_length = uncomp_size;
|
||||
|
||||
destination = (unsigned char *)malloc(destination_length);
|
||||
|
||||
if (decompress_buffer(source, source_length, destination, destination_length) == false) {
|
||||
free(destination);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
bool U6Lzw::decompress_buffer(unsigned char *source, uint32 source_length, unsigned char *destination, uint32 destination_length) {
|
||||
const int max_codeword_length = 12;
|
||||
bool end_marker_reached = false;
|
||||
int codeword_size = 9;
|
||||
long bits_read = 0;
|
||||
int next_free_codeword = 0x102;
|
||||
int dictionary_size = 0x200;
|
||||
|
||||
long bytes_written = 0;
|
||||
|
||||
int cW;
|
||||
int pW = 0; // get rid of uninitialized warning.
|
||||
unsigned char C;
|
||||
|
||||
source += 4; //skip the filesize dword.
|
||||
|
||||
while (! end_marker_reached) {
|
||||
cW = get_next_codeword(&bits_read, source, codeword_size);
|
||||
switch (cW) {
|
||||
// re-init the dictionary
|
||||
case 0x100:
|
||||
codeword_size = 9;
|
||||
next_free_codeword = 0x102;
|
||||
dictionary_size = 0x200;
|
||||
dict->reset();
|
||||
cW = get_next_codeword(&bits_read, source, codeword_size);
|
||||
output_root((unsigned char)cW, destination, &bytes_written);
|
||||
break;
|
||||
// end of compressed file has been reached
|
||||
case 0x101:
|
||||
end_marker_reached = true;
|
||||
break;
|
||||
// (cW <> 0x100) && (cW <> 0x101)
|
||||
default:
|
||||
if (cW < next_free_codeword) { // codeword is already in the dictionary
|
||||
// create the string associated with cW (on the stack)
|
||||
get_string(cW);
|
||||
C = stack->gettop();
|
||||
// output the string represented by cW
|
||||
while (!stack->is_empty()) {
|
||||
output_root(stack->pop(), destination, &bytes_written);
|
||||
}
|
||||
// add pW+C to the dictionary
|
||||
dict->add(C, pW);
|
||||
|
||||
next_free_codeword++;
|
||||
if (next_free_codeword >= dictionary_size) {
|
||||
if (codeword_size < max_codeword_length) {
|
||||
codeword_size += 1;
|
||||
dictionary_size *= 2;
|
||||
}
|
||||
}
|
||||
} else { // codeword is not yet defined
|
||||
// create the string associated with pW (on the stack)
|
||||
get_string(pW);
|
||||
C = stack->gettop();
|
||||
// output the string represented by pW
|
||||
while (!stack->is_empty()) {
|
||||
output_root(stack->pop(), destination, &bytes_written);
|
||||
}
|
||||
// output the char C
|
||||
output_root(C, destination, &bytes_written);
|
||||
// the new dictionary entry must correspond to cW
|
||||
// if it doesn't, something is wrong with the lzw-compressed data.
|
||||
if (cW != next_free_codeword) {
|
||||
DEBUG(0, LEVEL_ERROR, "cW != next_free_codeword!\n");
|
||||
return false;
|
||||
}
|
||||
// add pW+C to the dictionary
|
||||
dict->add(C, pW);
|
||||
|
||||
next_free_codeword++;
|
||||
if (next_free_codeword >= dictionary_size) {
|
||||
if (codeword_size < max_codeword_length) {
|
||||
codeword_size += 1;
|
||||
dictionary_size *= 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
// shift roles - the current cW becomes the new pW
|
||||
pW = cW;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// from file to file
|
||||
// -----------------
|
||||
unsigned char *U6Lzw::decompress_file(const Common::Path &filename, uint32 &destination_length) {
|
||||
unsigned char *source_buffer;
|
||||
unsigned char *destination_buffer;
|
||||
uint32 source_buffer_size;
|
||||
NuvieIOFileRead input_file;
|
||||
|
||||
destination_length = 0;
|
||||
if (input_file.open(filename) == false)
|
||||
return nullptr;
|
||||
|
||||
if (this->is_valid_lzw_file(&input_file)) {
|
||||
// determine the buffer sizes
|
||||
source_buffer_size = input_file.get_size();
|
||||
// destination_buffer_size = this->get_uncompressed_file_size(input_file);
|
||||
|
||||
// create the buffers
|
||||
source_buffer = (unsigned char *)malloc(sizeof(unsigned char) * source_buffer_size);
|
||||
// destination_buffer = (unsigned char *)malloc(sizeof(unsigned char *) * destination_buffer_size);
|
||||
|
||||
// read the input file into the source buffer
|
||||
input_file.seekStart();
|
||||
input_file.readToBuf(source_buffer, source_buffer_size);
|
||||
|
||||
// decompress the input file
|
||||
destination_buffer = this->decompress_buffer(source_buffer, source_buffer_size, destination_length);
|
||||
|
||||
// write the destination buffer to the output file
|
||||
//fwrite(destination_buffer, 1, destination_buffer_size, output_file);
|
||||
|
||||
// destroy the buffers
|
||||
free(source_buffer);
|
||||
//free(destination_buffer);
|
||||
} else {
|
||||
// uncompressed file
|
||||
uint32 destination_buffer_size = input_file.get_size();
|
||||
destination_length = destination_buffer_size - 8;
|
||||
|
||||
destination_buffer = (unsigned char *)malloc(destination_length);
|
||||
|
||||
// data starts at offset 8
|
||||
input_file.seek(8);
|
||||
input_file.readToBuf(destination_buffer, destination_length);
|
||||
}
|
||||
|
||||
return destination_buffer;
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
// Read the next code word from the source buffer
|
||||
// ----------------------------------------------
|
||||
int U6Lzw::get_next_codeword(long *bits_read, unsigned char *source, int codeword_size) {
|
||||
unsigned char b0, b1, b2;
|
||||
int codeword;
|
||||
|
||||
b0 = source[*bits_read / 8];
|
||||
b1 = source[*bits_read / 8 + 1];
|
||||
if (codeword_size + (*bits_read % 8) > 16)
|
||||
b2 = source[*bits_read / 8 + 2]; // only read next byte if necessary
|
||||
else
|
||||
b2 = 0;
|
||||
|
||||
codeword = ((b2 << 16) + (b1 << 8) + b0);
|
||||
codeword = codeword >> (*bits_read % 8);
|
||||
switch (codeword_size) {
|
||||
case 0x9:
|
||||
codeword = codeword & 0x1ff;
|
||||
break;
|
||||
case 0xa:
|
||||
codeword = codeword & 0x3ff;
|
||||
break;
|
||||
case 0xb:
|
||||
codeword = codeword & 0x7ff;
|
||||
break;
|
||||
case 0xc:
|
||||
codeword = codeword & 0xfff;
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, LEVEL_ERROR, "U6Lzw Error: weird codeword size!\n");
|
||||
break;
|
||||
}
|
||||
*bits_read += codeword_size;
|
||||
|
||||
return codeword;
|
||||
}
|
||||
|
||||
void U6Lzw::output_root(unsigned char root, unsigned char *destination, long *position) {
|
||||
destination[*position] = root;
|
||||
*position = *position + 1;
|
||||
}
|
||||
|
||||
void U6Lzw::get_string(int codeword) {
|
||||
unsigned char root;
|
||||
int current_codeword;
|
||||
|
||||
current_codeword = codeword;
|
||||
stack->reset();
|
||||
while (current_codeword > 0xff) {
|
||||
root = dict->get_root(current_codeword);
|
||||
current_codeword = dict->get_codeword(current_codeword);
|
||||
stack->push(root);
|
||||
}
|
||||
|
||||
// push the root at the leaf
|
||||
stack->push((unsigned char)current_codeword);
|
||||
}
|
||||
|
||||
|
||||
U6LzwStack::U6LzwStack() {
|
||||
memset(stack, 0, STACK_SIZE);
|
||||
this->reset();
|
||||
}
|
||||
|
||||
void U6LzwStack::reset(void) {
|
||||
contains = 0;
|
||||
}
|
||||
|
||||
bool U6LzwStack::is_empty(void) {
|
||||
if (contains == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool U6LzwStack::is_full(void) {
|
||||
if (contains == STACK_SIZE)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void U6LzwStack::push(unsigned char element) {
|
||||
if (!this->is_full()) {
|
||||
stack[contains] = element;
|
||||
contains++;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char U6LzwStack::pop(void) {
|
||||
unsigned char element;
|
||||
|
||||
if (!this->is_empty()) {
|
||||
element = stack[contains - 1];
|
||||
contains--;
|
||||
} else {
|
||||
element = 0;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
unsigned char U6LzwStack::gettop(void) {
|
||||
if (!this->is_empty()) {
|
||||
return (stack[contains - 1]);
|
||||
}
|
||||
|
||||
return '\0'; /* what should we return here!? */
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------
|
||||
a dictionary class
|
||||
--------------------------------------------------
|
||||
*/
|
||||
|
||||
U6LzwDict::U6LzwDict() {
|
||||
this->reset();
|
||||
memset(&dict, 0, sizeof(dict));
|
||||
}
|
||||
|
||||
void U6LzwDict::reset(void) {
|
||||
contains = 0x102;
|
||||
}
|
||||
|
||||
void U6LzwDict::add(unsigned char root, int codeword) {
|
||||
dict[contains].root = root;
|
||||
dict[contains].codeword = codeword;
|
||||
contains++;
|
||||
}
|
||||
|
||||
unsigned char U6LzwDict::get_root(int codeword) const {
|
||||
return dict[codeword].root;
|
||||
}
|
||||
|
||||
int U6LzwDict::get_codeword(int codeword) const {
|
||||
return dict[codeword].codeword;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
111
engines/ultima/nuvie/files/u6_lzw.h
Normal file
111
engines/ultima/nuvie/files/u6_lzw.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6_LZW_H
|
||||
#define NUVIE_FILES_U6_LZW_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class NuvieIOFileRead;
|
||||
|
||||
// LZW Stack
|
||||
|
||||
#define STACK_SIZE 10000
|
||||
|
||||
class U6LzwStack {
|
||||
protected:
|
||||
unsigned char stack[STACK_SIZE];
|
||||
int contains;
|
||||
|
||||
public:
|
||||
U6LzwStack();
|
||||
|
||||
void reset(void);
|
||||
bool is_empty(void);
|
||||
bool is_full(void);
|
||||
void push(unsigned char element);
|
||||
unsigned char pop(void);
|
||||
unsigned char gettop(void);
|
||||
};
|
||||
|
||||
// LZW dictionary
|
||||
|
||||
#define DICT_SIZE 10000
|
||||
|
||||
typedef struct {
|
||||
unsigned char root;
|
||||
int codeword;
|
||||
int contains;
|
||||
} dict_entry;
|
||||
|
||||
class U6LzwDict {
|
||||
dict_entry dict[DICT_SIZE];
|
||||
int contains;
|
||||
|
||||
public:
|
||||
|
||||
U6LzwDict();
|
||||
|
||||
void reset(void);
|
||||
void add(unsigned char root, int codeword);
|
||||
unsigned char get_root(int codeword) const;
|
||||
int get_codeword(int codeword) const;
|
||||
};
|
||||
|
||||
class U6Lzw {
|
||||
U6LzwStack *stack;
|
||||
U6LzwDict *dict;
|
||||
const char *errstr; // error string
|
||||
public:
|
||||
|
||||
U6Lzw(void);
|
||||
~U6Lzw(void);
|
||||
|
||||
unsigned char *decompress_buffer(unsigned char *source, uint32 source_length, uint32 &destination_length);
|
||||
bool decompress_buffer(unsigned char *source, uint32 source_length, unsigned char *destination, uint32 destination_length);
|
||||
unsigned char *decompress_file(const Common::Path &filename, uint32 &destination_length);
|
||||
unsigned char *compress_buffer(unsigned char *src, uint32 src_len,
|
||||
uint32 &dest_len);
|
||||
const char *strerror() const {
|
||||
return errstr; // get error string
|
||||
}
|
||||
protected:
|
||||
|
||||
bool is_valid_lzw_file(NuvieIOFileRead *input_file);
|
||||
bool is_valid_lzw_buffer(unsigned char *buf, uint32 length);
|
||||
|
||||
long get_uncompressed_file_size(NuvieIOFileRead *input_file);
|
||||
long get_uncompressed_buffer_size(unsigned char *buf, uint32 length);
|
||||
|
||||
int get_next_codeword(long *bits_read, unsigned char *source,
|
||||
int codeword_size);
|
||||
void output_root(unsigned char root, unsigned char *destination,
|
||||
long *position);
|
||||
void get_string(int codeword);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
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
|
||||
80
engines/ultima/nuvie/files/u6_shape.h
Normal file
80
engines/ultima/nuvie/files/u6_shape.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_U6_SHAPE_H
|
||||
#define NUVIE_FILES_U6_SHAPE_H
|
||||
|
||||
/*
|
||||
* ==========
|
||||
* Includes
|
||||
* ==========
|
||||
*/
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
class U6Lib_n;
|
||||
class Configuration;
|
||||
|
||||
/*
|
||||
* ==================
|
||||
* Class definition
|
||||
* ==================
|
||||
*
|
||||
* U6Shape can load Ultima VI shape files and return the shapes
|
||||
* stored into these files either as a Graphics::ManagedSurface or as raw data.
|
||||
*/
|
||||
class U6Shape {
|
||||
private:
|
||||
uint16 hotx, hoty;
|
||||
|
||||
protected:
|
||||
unsigned char *raw;
|
||||
uint16 width, height;
|
||||
|
||||
public:
|
||||
U6Shape();
|
||||
virtual ~U6Shape();
|
||||
|
||||
bool init(uint16 w, uint16 h, uint16 hx = 0, uint16 hy = 0);
|
||||
virtual bool load(const Common::Path &filename);
|
||||
bool load(U6Lib_n *file, uint32 index);
|
||||
virtual bool load(unsigned char *buf);
|
||||
bool load_from_lzc(const Common::Path &filename, uint32 idx, uint32 sub_idx);
|
||||
bool load_WoU_background(const Configuration *config, nuvie_game_t game_type);
|
||||
|
||||
const unsigned char *get_data() const;
|
||||
unsigned char *get_data();
|
||||
Graphics::ManagedSurface *get_shape_surface();
|
||||
bool get_hot_point(uint16 *x, uint16 *y);
|
||||
bool get_size(uint16 *w, uint16 *h);
|
||||
|
||||
void draw_line(uint16 sx, uint16 sy, uint16 ex, uint16 ey, uint8 color);
|
||||
bool blit(U6Shape *shp, uint16 x, uint16 y);
|
||||
void fill(uint8 color);
|
||||
};
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
58
engines/ultima/nuvie/files/utils.cpp
Normal file
58
engines/ultima/nuvie/files/utils.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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/shared/std/string.h"
|
||||
#include "ultima/nuvie/files/utils.h"
|
||||
#include "ultima/nuvie/nuvie.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/*
|
||||
* Open a file for input,
|
||||
* trying the original name (lower case), and the upper case version
|
||||
* of the name.
|
||||
*
|
||||
* Output: 0 if couldn't open.
|
||||
*/
|
||||
|
||||
bool openFile(Common::ReadStream *&in, const Common::Path &fname) {
|
||||
Common::File *f = new Common::File();
|
||||
if (f->open(fname)) {
|
||||
in = f;
|
||||
return true;
|
||||
} else {
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See if a file exists.
|
||||
*/
|
||||
|
||||
bool fileExists(const Common::Path &fname) {
|
||||
return Common::File::exists(fname);
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/nuvie/files/utils.h
Normal file
41
engines/ultima/nuvie/files/utils.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NUVIE_FILES_UTILS_H
|
||||
#define NUVIE_FILES_UTILS_H
|
||||
|
||||
#include "common/path.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
bool openFile(
|
||||
Common::ReadStream *&in, // Input stream to open.
|
||||
const Common::Path &fname // Filename
|
||||
);
|
||||
|
||||
extern bool fileExists(const Common::Path &fname);
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user