Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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