Files
scummvm-cursorfix/engines/icb/common/datapacker.cpp
2026-02-02 04:50:13 +01:00

310 lines
7.7 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* 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 "engines/icb/common/datapacker.h"
#include "common/stream.h"
#include "common/textconsole.h"
namespace ICB {
// Just initialise the buffer position and the min, max values
// used for packing and error detection
DataPacker::DataPacker() {
readMode = false;
iMode = NO_MODE;
iPackMode = NO_PACKMODE;
pos = 0;
packMin = -(1 << (PACK_BIT_SIZE - 1));
packMax = +((1 << PACK_BIT_SIZE) - 1);
// clear out the data buffer
ClearBuffer();
}
// Could do something here - but not sure what !
// Could error check on pos = 0 : but might lead to trouble
DataPacker::~DataPacker() {}
// Start the bit-packing process : say if we are in READ or WRITE mode
DataPacker::ReturnCodes DataPacker::open(const ModeEnum mode, const PackModeEnum packMode) {
if (pos != 0) {
return BAD_POS;
}
if ((mode != READ) && (mode != WRITE)) {
return BAD_MODE;
}
if ((packMode != PACK) && (packMode != DONT_PACK)) {
return BAD_PACKMODE;
}
if (mode == READ) {
readMode = true;
pos = PACK_CHUNK_SIZE;
}
if (mode == WRITE) {
readMode = false;
pos = 0;
}
// clear out the data buffer
ClearBuffer();
iMode = mode;
iPackMode = packMode;
return OK;
}
// Put a value into the bit-stream
DataPacker::ReturnCodes DataPacker::put(const int32 value, Common::WriteStream *stream) {
if (iMode != WRITE) {
return BAD_MODE;
}
if ((iPackMode != PACK) && (iPackMode != DONT_PACK)) {
return BAD_PACKMODE;
}
if ((pos < 0) || (pos >= PACK_CHUNK_SIZE)) {
return BAD_POS;
}
// For DONT_PACK mode just write the data straight out
if (iPackMode == DONT_PACK) {
// Check it is a legal value : 16-bits
int32 lvarMin = -(1 << 15);
int32 lvarMax = +((1 << 15) - 1);
if ((value < lvarMin) || (value > lvarMax)) {
return BAD_VALUE;
}
int32 nItems = 2;
int16 v16 = (int16)value;
int32 ret = stream->write((const void *)&v16, nItems);
if (ret != nItems) {
return WRITE_ERROR;
}
return OK;
}
// Convert the value to be within limits
int32 v = value - packMin;
// Check the value is within range
if ((v < 0) || (v > packMax)) {
return BAD_VALUE;
}
// Add the value in
if (pos == 0) {
buffer[0] = (uint8)((v >> 6) & 0xFF); // v's top 8-bits
buffer[1] = (uint8)((v & 0x3F) << 2); // v's bottom 6-bits into top 6-bits
} else if (pos == 1) {
buffer[1] |= ((v >> 12) & 0x03); // v's top 2-bits into bottom 2
buffer[2] = (uint8)((v >> 4) & 0xFF); // v's middle 8-bits
buffer[3] = (uint8)((v & 0x0F) << 4); // v's bottom 4-bits into top 4
} else if (pos == 2) {
buffer[3] |= ((v >> 10) & 0x0F); // v's top 4-bits into bottom 4
buffer[4] = (uint8)((v >> 2) & 0xFF); // v's middle 8-bits
buffer[5] = (uint8)((v & 0x03) << 6); // v's bottom 2-bits into top 2
} else if (pos == 3) {
buffer[5] |= ((v >> 8) & 0x3F); // v's top 6-bits into bottom 6
buffer[6] = (uint8)(v & 0xFF); // v's bottom 8-bits
}
// Put data into the next position !
pos++;
// Do we need to output the current buffer ?
if (pos == PACK_CHUNK_SIZE) {
#if 0
printf("WRITE %X %X %X %X %X %X %X",
buffer[0], buffer[1], buffer[2], buffer[3],
buffer[4], buffer[5], buffer[6]);
#endif // #if 0
// Write out the buffer
int32 nItems = BUFFER_BYTE_SIZE;
int32 ret = stream->write((const void *)buffer, nItems);
if (ret != nItems) {
return WRITE_ERROR;
}
pos = 0;
ClearBuffer();
}
return OK;
}
// Get a value from the bit-stream
DataPacker::ReturnCodes DataPacker::Get(int32 &value, Common::SeekableReadStream *stream) {
if (iMode != READ) {
return BAD_MODE;
}
if ((iPackMode != PACK) && (iPackMode != DONT_PACK)) {
return BAD_PACKMODE;
}
if ((pos < 0) || (pos > PACK_CHUNK_SIZE)) {
return BAD_POS;
}
// For DONT_PACK mode just read the data straight in
if (iPackMode == DONT_PACK) {
int32 nItems = 2;
int16 v16;
int32 ret = stream->read((void *)&v16, nItems);
value = v16;
if (ret != nItems) {
return READ_ERROR;
}
return OK;
}
// Do we need to fill up the current buffer ?
if (pos == PACK_CHUNK_SIZE) {
// Read into the buffer
int32 nItems = BUFFER_BYTE_SIZE;
int32 ret = stream->read((void *)buffer, nItems);
if (ret != nItems) {
return READ_ERROR;
}
#if 0
printf("READ %X %X %X %X %X %X %X",
buffer[0], buffer[1], buffer[2], buffer[3],
buffer[4], buffer[5], buffer[6]);
#endif
pos = 0;
}
int32 v = 0;
// Get the value out of the buffer
if (pos == 0) {
v = (buffer[0] << 6); // v's top 8-bits
v |= (buffer[1] >> 2); // v's bottom 6-bits into top 6-bits
} else if (pos == 1) {
v = ((buffer[1] & 0x03) << 12); // v's top 2-bits into bottom 2
v |= (buffer[2] << 4); // v's middle 8-bits
v |= (buffer[3] >> 4); // v's bottom 4-bits into top 4
} else if (pos == 2) {
v = ((buffer[3] & 0x0F) << 10); // v's top 4-bits into bottom 4
v |= (buffer[4] << 2); // v's middle 8-bits
v |= (buffer[5] >> 6); // v's bottom 2-bits into top 2
} else if (pos == 3) {
v = (buffer[5] & 0x3F) << 8; // v's top 6-bits into bottom 6
v |= buffer[6]; // v's bottom 8-bits
}
// Get data from the next position !
pos++;
// Check the value is within range
// This should just not be possible !
if ((v < 0) || (v > packMax)) {
return BAD_VALUE;
}
// Convert the extracted value to be in normal signed/unsigned limits
value = v + packMin;
return OK;
}
// Stop the bit-packing process : will output any remaining data
DataPacker::ReturnCodes DataPacker::close(Common::WriteStream *stream) {
if ((iMode == WRITE) && (pos != 0)) {
// Write out the remaining data items
int32 nItems = BUFFER_BYTE_SIZE;
int32 ret = stream->write((const void *)buffer, nItems);
if (ret != nItems) {
return WRITE_ERROR;
}
} else {
error("Wrong close-function called, passed WriteStream without being in WRITE-mode");
}
iMode = NO_MODE;
iPackMode = NO_PACKMODE;
pos = 0;
ClearBuffer();
return OK;
}
DataPacker::ReturnCodes DataPacker::close(Common::SeekableReadStream *stream) {
// TODO: If write mode.
if ((iMode == WRITE) && (pos != 0)) {
error("Wrong close-function called, passed ReadStream in WRITE-mode");
}
iMode = NO_MODE;
iPackMode = NO_PACKMODE;
pos = 0;
ClearBuffer();
return OK;
}
// Copy constructor
DataPacker::DataPacker(DataPacker &src) { *this = src; }
// Assignment operator
DataPacker &DataPacker::operator=(DataPacker &b) {
readMode = b.readMode;
iMode = b.iMode;
iPackMode = b.iPackMode;
pos = b.pos;
packMin = b.packMin;
packMax = b.packMax;
for (int32 i = 0; i < BUFFER_BYTE_SIZE; i++) {
buffer[i] = b.buffer[i];
}
return *this;
}
// Clear out the data buffer
void DataPacker::ClearBuffer() {
for (int32 i = 0; i < BUFFER_BYTE_SIZE; i++) {
buffer[i] = 0x00;
}
}
} // End of namespace ICB