Initial commit
This commit is contained in:
375
engines/twine/resources/hqr.cpp
Normal file
375
engines/twine/resources/hqr.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
/* 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 "twine/resources/hqr.h"
|
||||
#include "twine/resources/lzss.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
namespace HQR {
|
||||
|
||||
#define wrap(cmd) \
|
||||
if ((cmd) == 0) { \
|
||||
warning("Failed to execute " #cmd); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress entry based in Yaz0r and Zink decompression code
|
||||
* @param dst destination pointer where will be the decompressed entry
|
||||
* @param compBuf compressed data pointer
|
||||
* @param compSize @p compBuf buffer size
|
||||
* @param decompsize real file size after decompression
|
||||
* @param mode compression mode used
|
||||
*/
|
||||
static void decompressEntry(uint8 *dst, const uint8 *compBuf, uint32 compSize, int32 decompsize, int32 mode) {
|
||||
Common::MemoryReadStream stream(compBuf, compSize);
|
||||
do {
|
||||
uint8 b = stream.readByte();
|
||||
for (int32 d = 0; d < 8; d++) {
|
||||
int32 length;
|
||||
if (!(b & (1 << d))) {
|
||||
const uint16 offset = stream.readUint16LE();
|
||||
length = (offset & 0x0F) + (mode + 1);
|
||||
const uint8 *ptr = dst - (offset >> 4) - 1;
|
||||
for (int32 i = 0; i < length; i++) {
|
||||
*(dst++) = *(ptr++);
|
||||
}
|
||||
} else {
|
||||
length = 1;
|
||||
*(dst++) = stream.readByte();
|
||||
}
|
||||
decompsize -= length;
|
||||
if (decompsize <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} while (decompsize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a HQR entry pointer
|
||||
* @param filename HQR file name
|
||||
* @param index entry index to extract
|
||||
* @return entry real size
|
||||
*/
|
||||
static int voxEntrySize(const char *filename, int32 index, int32 hiddenIndex) {
|
||||
if (!filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
warning("HQR: Could not open %s", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 headerSize = file.readUint32LE();
|
||||
if ((uint32)index >= headerSize / 4) {
|
||||
warning("HQR: Invalid entry index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wrap(file.seek(index * 4))
|
||||
uint32 offsetToData = file.readUint32LE();
|
||||
|
||||
wrap(file.seek(offsetToData))
|
||||
uint32 realSize = file.readUint32LE();
|
||||
uint32 compSize = file.readUint32LE();
|
||||
|
||||
// exist hidden entries
|
||||
for (int32 i = 0; i < hiddenIndex; i++) {
|
||||
wrap(file.seek(offsetToData + compSize + 10)) // hidden entry
|
||||
offsetToData = offsetToData + compSize + 10; // current hidden offset
|
||||
|
||||
realSize = file.readUint32LE();
|
||||
compSize = file.readUint32LE();
|
||||
}
|
||||
|
||||
return realSize;
|
||||
}
|
||||
|
||||
int32 getEntry(uint8 *ptr, const char *filename, int32 index) {
|
||||
if (!ptr) {
|
||||
return 0;
|
||||
}
|
||||
if (!filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
warning("HQR: Could not open %s", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 headerSize = file.readUint32LE();
|
||||
|
||||
if ((uint32)index >= headerSize / 4) {
|
||||
warning("HQR: Invalid entry index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wrap(file.seek(index * 4))
|
||||
uint32 offsetToData = file.readUint32LE();
|
||||
|
||||
wrap(file.seek(offsetToData))
|
||||
uint32 realSize = file.readUint32LE();
|
||||
uint32 compSize = file.readUint32LE();
|
||||
uint16 mode = file.readUint16LE();
|
||||
|
||||
// uncompressed
|
||||
if (mode == 0) {
|
||||
wrap(file.read(ptr, realSize))
|
||||
}
|
||||
// compressed: modes (1 & 2)
|
||||
else if (mode == 1 || mode == 2) {
|
||||
uint8 *compDataPtr = (uint8 *)malloc(compSize);
|
||||
wrap(file.read(compDataPtr, compSize))
|
||||
decompressEntry(ptr, compDataPtr, compSize, realSize, mode);
|
||||
free(compDataPtr);
|
||||
}
|
||||
debugC(1, TwinE::kDebugResources, "Loaded entry from %s for index %i with %i bytes", filename, index, realSize);
|
||||
return realSize;
|
||||
}
|
||||
|
||||
int32 entrySize(const char *filename, int32 index) {
|
||||
if (!filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
warning("HQR: Could not open %s", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 headerSize = file.readUint32LE();
|
||||
if ((uint32)index >= headerSize / 4) {
|
||||
warning("HQR: Invalid entry index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wrap(file.seek(index * 4))
|
||||
uint32 offsetToData = file.readUint32LE();
|
||||
|
||||
wrap(file.seek(offsetToData))
|
||||
uint32 realSize = file.readUint32LE();
|
||||
|
||||
return realSize;
|
||||
}
|
||||
|
||||
int32 numEntries(const char *filename) {
|
||||
if (!filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
warning("HQR: Could not open %s", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 headerSize = file.readUint32LE();
|
||||
return ((int)headerSize / 4) - 1;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *makeReadStream(const char *filename, int index) {
|
||||
Common::File *file = new Common::File();
|
||||
if (!file->open(filename)) {
|
||||
delete file;
|
||||
warning("HQR: Could not open %s", filename);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint32 headerSize = file->readUint32LE();
|
||||
if ((uint32)index >= headerSize / 4) {
|
||||
warning("HQR: Invalid entry index: %i", index);
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!file->seek(index * 4)) {
|
||||
warning("HQR: Invalid index: %i", index);
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
const uint32 offsetToData = file->readUint32LE();
|
||||
if (!file->seek(offsetToData)) {
|
||||
warning("HQR: Invalid index: %i", index);
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint32 realSize = file->readUint32LE();
|
||||
const uint32 compressedSize = file->readUint32LE();
|
||||
const uint16 mode = file->readUint16LE();
|
||||
|
||||
const uint32 begin = offsetToData + 10;
|
||||
uint32 end = 0;
|
||||
if (mode == 0) {
|
||||
end = begin + realSize;
|
||||
} else {
|
||||
end = begin + compressedSize;
|
||||
}
|
||||
Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(file, begin, end, DisposeAfterUse::YES);
|
||||
if (mode != 0) {
|
||||
stream = new LzssReadStream(stream, mode, realSize);
|
||||
}
|
||||
debugC(1, TwinE::kDebugResources, "Loaded entry from %s for index %i with %i bytes", filename, index, realSize);
|
||||
return stream;
|
||||
}
|
||||
|
||||
int32 getAllocEntry(uint8 **ptr, const char *filename, int32 index) {
|
||||
if (*ptr) {
|
||||
free(*ptr);
|
||||
}
|
||||
const int32 size = entrySize(filename, index);
|
||||
if (size <= 0) {
|
||||
*ptr = nullptr;
|
||||
warning("HQR: failed to get entry for index %i from file: %s", index, filename);
|
||||
return 0;
|
||||
}
|
||||
*ptr = (uint8 *)malloc(size * sizeof(uint8));
|
||||
if (!*ptr) {
|
||||
warning("HQR: unable to allocate entry memory");
|
||||
return 0;
|
||||
}
|
||||
const int32 entrySize = getEntry(*ptr, filename, index);
|
||||
assert(entrySize == size);
|
||||
return entrySize;
|
||||
}
|
||||
|
||||
bool dumpEntry(const char *filename, int32 index, const char *targetFileName) {
|
||||
Common::DumpFile out;
|
||||
if (!out.open(targetFileName, true)) {
|
||||
warning("Failed to save to %s", targetFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 *content = nullptr;
|
||||
const int size = getAllocEntry(&content, filename, index);
|
||||
if (size == 0) {
|
||||
warning("Could not get hqr entry in %s for index %i", filename, index);
|
||||
return false;
|
||||
}
|
||||
out.write(content, size);
|
||||
out.flush();
|
||||
out.close();
|
||||
free(content);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 getVoxEntry(uint8 *ptr, const char *filename, int32 index, int32 hiddenIndex) {
|
||||
if (!ptr) {
|
||||
return 0;
|
||||
}
|
||||
if (!filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
warning("HQR: Could not open %s", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 headerSize = file.readUint32LE();
|
||||
|
||||
if ((uint32)index >= headerSize / 4) {
|
||||
warning("HQR: Invalid entry index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wrap(file.seek(index * 4))
|
||||
uint32 offsetToData = file.readUint32LE();
|
||||
|
||||
wrap(file.seek(offsetToData))
|
||||
uint32 realSize = file.readUint32LE();
|
||||
uint32 compSize = file.readUint32LE();
|
||||
uint16 mode = file.readSint16LE();
|
||||
|
||||
// exist hidden entries
|
||||
for (int32 i = 0; i < hiddenIndex; i++) {
|
||||
wrap(file.seek(offsetToData + compSize + 10)) // hidden entry
|
||||
offsetToData = offsetToData + compSize + 10; // current hidden offset
|
||||
|
||||
realSize = file.readUint32LE();
|
||||
compSize = file.readUint32LE();
|
||||
mode = file.readUint16LE();
|
||||
}
|
||||
|
||||
// uncompressed
|
||||
if (mode == 0) {
|
||||
wrap(file.read(ptr, realSize))
|
||||
}
|
||||
// compressed: modes (1 & 2)
|
||||
else if (mode == 1 || mode == 2) {
|
||||
uint8 *compDataPtr = (uint8 *)malloc(compSize);
|
||||
wrap(file.read(compDataPtr, compSize))
|
||||
decompressEntry(ptr, compDataPtr, compSize, realSize, mode);
|
||||
free(compDataPtr);
|
||||
}
|
||||
debugC(1, TwinE::kDebugResources, "Loaded vox entry from %s for index %i with %i bytes", filename, index, realSize);
|
||||
return realSize;
|
||||
}
|
||||
|
||||
bool getPaletteEntry(Graphics::Palette &palette, const char *filename, int32 index) {
|
||||
byte paletteBuffer[NUMOFCOLORS * 3];
|
||||
int32 size = HQR::getEntry(paletteBuffer, filename, index);
|
||||
if (size <= 0) {
|
||||
debugC(1, TwinE::kDebugResources, "Failed to load palette from %s for index %i", filename, index);
|
||||
return false;
|
||||
}
|
||||
palette = Graphics::Palette(paletteBuffer, size / 3);
|
||||
debugC(1, TwinE::kDebugResources, "Loaded palette from %s for index %i with %i color entries", filename, index, (int)palette.size());
|
||||
debugC(1, TwinE::kDebugPalette, "Loaded palette from %s for index %i with %i color entries", filename, index, (int)palette.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 getAllocVoxEntry(uint8 **ptr, const char *filename, int32 index, int32 hiddenIndex) {
|
||||
const int32 size = voxEntrySize(filename, index, hiddenIndex);
|
||||
if (size == 0) {
|
||||
warning("HQR: vox entry with 0 size found for index: %d", index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ptr = (uint8 *)malloc(size * sizeof(uint8));
|
||||
if (!*ptr) {
|
||||
warning("HQR: unable to allocate entry memory of size %d for index: %d", size, index);
|
||||
return 0;
|
||||
}
|
||||
const int32 entrySize = getVoxEntry(*ptr, filename, index, hiddenIndex);
|
||||
assert(entrySize == size);
|
||||
return entrySize;
|
||||
}
|
||||
|
||||
#undef wrap
|
||||
|
||||
} // namespace HQR
|
||||
|
||||
} // namespace TwinE
|
||||
Reference in New Issue
Block a user