diff options
Diffstat (limited to 'engines/sci/engine/segment.h')
-rw-r--r-- | engines/sci/engine/segment.h | 524 |
1 files changed, 424 insertions, 100 deletions
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index add5f4c57c..361c1cb895 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -24,7 +24,7 @@ #define SCI_ENGINE_SEGMENT_H #include "common/serializer.h" - +#include "common/str.h" #include "sci/engine/object.h" #include "sci/engine/vm.h" #include "sci/engine/vm_types.h" // for reg_t @@ -70,7 +70,7 @@ enum SegmentType { #ifdef ENABLE_SCI32 SEG_TYPE_ARRAY = 11, - SEG_TYPE_STRING = 12, + // 12 used to be string, now obsolete SEG_TYPE_BITMAP = 13, #endif @@ -408,142 +408,456 @@ public: #ifdef ENABLE_SCI32 -template<typename T> -class SciArray { +#pragma mark - +#pragma mark Arrays + +enum SciArrayType { + kArrayTypeInt16 = 0, + kArrayTypeID = 1, + kArrayTypeByte = 2, + kArrayTypeString = 3, + // Type 4 was for 32-bit integers; never used + kArrayTypeInvalid = 5 +}; + +enum SciArrayTrim { + kArrayTrimRight = 1, ///< Trim whitespace after the last non-whitespace character + kArrayTrimCenter = 2, ///< Trim whitespace between non-whitespace characters + kArrayTrimLeft = 4 ///< Trim whitespace before the first non-whitespace character +}; + +class SciArray : public Common::Serializable { public: - SciArray() : _type(-1), _data(NULL), _size(0), _actualSize(0) { } + SciArray() : + _type(kArrayTypeInvalid), + _size(0), + _data(nullptr) {} - SciArray(const SciArray<T> &array) { + SciArray(const SciArray &array) { _type = array._type; _size = array._size; - _actualSize = array._actualSize; - _data = new T[_actualSize]; + _elementSize = array._elementSize; + _data = malloc(_elementSize * _size); assert(_data); - memcpy(_data, array._data, _size * sizeof(T)); + memcpy(_data, array._data, _elementSize * _size); } - SciArray<T>& operator=(const SciArray<T> &array) { + SciArray &operator=(const SciArray &array) { if (this == &array) return *this; - delete[] _data; + free(_data); _type = array._type; _size = array._size; - _actualSize = array._actualSize; - _data = new T[_actualSize]; + _elementSize = array._elementSize; + _data = malloc(_elementSize * _size); assert(_data); - memcpy(_data, array._data, _size * sizeof(T)); + memcpy(_data, array._data, _elementSize * _size); return *this; } virtual ~SciArray() { - destroy(); + free(_data); + _size = 0; + _type = kArrayTypeInvalid; } - virtual void destroy() { - delete[] _data; - _data = NULL; - _type = -1; - _size = _actualSize = 0; - } + void saveLoadWithSerializer(Common::Serializer &s); - void setType(byte type) { - if (_type >= 0) - error("SciArray::setType(): Type already set"); + /** + * Returns the type of this array. + */ + SciArrayType getType() const { + return _type; + } + /** + * Sets the type of this array. The type of the array may only be set once. + */ + void setType(const SciArrayType type) { + assert(_type == kArrayTypeInvalid); + switch(type) { + case kArrayTypeInt16: + case kArrayTypeID: + _elementSize = sizeof(reg_t); + break; + case kArrayTypeString: + _elementSize = sizeof(char); + break; + case kArrayTypeByte: + _elementSize = sizeof(byte); + break; + default: + error("Invalid array type %d", type); + } _type = type; } - void setSize(uint32 size) { - if (_type < 0) - error("SciArray::setSize(): No type set"); + /** + * Returns the size of the array, in elements. + */ + uint16 size() const { + return _size; + } - // Check if we don't have to do anything - if (_size == size) - return; + /** + * Returns the size of the array, in bytes. + */ + uint16 byteSize() const { + return _size * _elementSize; + } - // Check if we don't have to expand the array - if (size <= _actualSize) { - _size = size; - return; + /** + * Ensures the array is large enough to store at least the given number of + * values given in `newSize`. If `force` is true, the array will be resized + * to store exactly `newSize` values. New values are initialized to zero. + */ + void resize(uint16 newSize, const bool force = false) { + if (force || newSize > _size) { + _data = realloc(_data, _elementSize * newSize); + if (newSize > _size) { + memset((byte *)_data + _elementSize * _size, 0, (newSize - _size) * _elementSize); + } + _size = newSize; } + } - // So, we're going to have to create an array of some sort - T *newArray = new T[size]; - memset(newArray, 0, size * sizeof(T)); + /** + * Shrinks a string array to its optimal size. + */ + void snug() { + assert(_type == kArrayTypeString || _type == kArrayTypeByte); + resize(strlen((char *)_data) + 1, true); + } - // Check if we never created an array before - if (!_data) { - _size = _actualSize = size; - _data = newArray; - return; + /** + * Returns a pointer to the array's raw data storage. + */ + void *getRawData() { return _data; } + const void *getRawData() const { return _data; } + + /** + * Gets the value at the given index as a reg_t. + */ + reg_t getAsID(const uint16 index) { + if (getSciVersion() >= SCI_VERSION_3) { + resize(index); + } else { + assert(index < _size); } - // Copy data from the old array to the new - memcpy(newArray, _data, _size * sizeof(T)); + switch(_type) { + case kArrayTypeInt16: + case kArrayTypeID: + return ((reg_t *)_data)[index]; + case kArrayTypeByte: + case kArrayTypeString: + return make_reg(0, ((byte *)_data)[index]); + default: + error("Invalid array type %d", _type); + } + } + + /** + * Sets the value at the given index from a reg_t. + */ + void setFromID(const uint16 index, const reg_t value) { + if (getSciVersion() >= SCI_VERSION_3) { + resize(index); + } else { + assert(index < _size); + } - // Now set the new array to the old and set the sizes - delete[] _data; - _data = newArray; - _size = _actualSize = size; + switch(_type) { + case kArrayTypeInt16: + case kArrayTypeID: + ((reg_t *)_data)[index] = value; + break; + case kArrayTypeByte: + case kArrayTypeString: + ((byte *)_data)[index] = value.toSint16(); + break; + default: + error("Invalid array type %d", _type); + } } - T getValue(uint16 index) const { - if (index >= _size) - error("SciArray::getValue(): %d is out of bounds (%d)", index, _size); + /** + * Gets the value at the given index as an int16. + */ + int16 getAsInt16(const uint16 index) { + assert(_type == kArrayTypeInt16); - return _data[index]; + if (getSciVersion() >= SCI_VERSION_3) { + resize(index); + } else { + assert(index < _size); + } + + const reg_t value = ((reg_t *)_data)[index]; + assert(value.isNumber()); + return value.toSint16(); } - void setValue(uint16 index, T value) { - if (index >= _size) - error("SciArray::setValue(): %d is out of bounds (%d)", index, _size); + /** + * Sets the value at the given index from an int16. + */ + void setFromInt16(const uint16 index, const int16 value) { + assert(_type == kArrayTypeInt16); + + if (getSciVersion() >= SCI_VERSION_3) { + resize(index + 1); + } else { + assert(index < _size); + } - _data[index] = value; + ((reg_t *)_data)[index] = make_reg(0, value); } - byte getType() const { return _type; } - uint32 getSize() const { return _size; } - T *getRawData() { return _data; } - const T *getRawData() const { return _data; } + /** + * Returns a reference to the byte at the given index. Only valid for + * string and byte arrays. + */ + byte &byteAt(const uint16 index) { + assert(_type == kArrayTypeString || _type == kArrayTypeByte); -protected: - int8 _type; - T *_data; - uint32 _size; // _size holds the number of entries that the scripts have requested - uint32 _actualSize; // _actualSize is the actual numbers of entries allocated -}; + if (getSciVersion() >= SCI_VERSION_3) { + resize(index); + } else { + assert(index < _size); + } -class SciString : public SciArray<char> { -public: - SciString() : SciArray<char>() { setType(3); } + return ((byte *)_data)[index]; + } - // We overload destroy to ensure the string type is 3 after destroying - void destroy() { SciArray<char>::destroy(); _type = 3; } + /** + * Returns a reference to the char at the given index. Only valid for + * string and byte arrays. + */ + char &charAt(const uint16 index) { + assert(_type == kArrayTypeString || _type == kArrayTypeByte); - Common::String toString() const; - void fromString(const Common::String &string); -}; + if (getSciVersion() >= SCI_VERSION_3) { + resize(index); + } else { + assert(index < _size); + } -struct ArrayTable : public SegmentObjTable<SciArray<reg_t> > { - ArrayTable() : SegmentObjTable<SciArray<reg_t> >(SEG_TYPE_ARRAY) {} + return ((char *)_data)[index]; + } - virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; + /** + * Returns a reference to the reg_t at the given index. Only valid for ID + * and int16 arrays. + */ + reg_t &IDAt(const uint16 index) { + assert(_type == kArrayTypeID || _type == kArrayTypeInt16); - void saveLoadWithSerializer(Common::Serializer &ser); - SegmentRef dereference(reg_t pointer); -}; + if (getSciVersion() >= SCI_VERSION_3) { + resize(index); + } else { + assert(index < _size); + } -struct StringTable : public SegmentObjTable<SciString> { - StringTable() : SegmentObjTable<SciString>(SEG_TYPE_STRING) {} + return ((reg_t *)_data)[index]; + } - virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) { - at(sub_addr.getOffset()).destroy(); - freeEntry(sub_addr.getOffset()); + /** + * Reads values from the given reg_t pointer and sets them in the array, + * growing the array if needed to store all values. + */ + void setElements(const uint16 index, uint16 count, const reg_t *values) { + resize(index + count); + + switch (_type) { + case kArrayTypeInt16: + case kArrayTypeID: { + const reg_t *source = values; + reg_t *target = (reg_t *)_data + index; + while (count--) { + *target++ = *source++; + } + break; + } + case kArrayTypeByte: + case kArrayTypeString: { + const reg_t *source = values; + byte *target = (byte *)_data + index; + while (count--) { + if (!source->isNumber()) { + error("Non-number %04x:%04x sent to byte or string array", PRINT_REG(*source)); + } + *target++ = source->getOffset(); + ++source; + } + break; + } + default: + error("Attempted write to SciArray with invalid type %d", _type); + } + } + + /** + * Fills the array with the given value. Existing values will be + * overwritten. The array will be grown if needed to store all values. + */ + void fill(const uint16 index, uint16 count, const reg_t value) { + if (count == 65535 /* -1 */) { + count = size() - index; + } + + if (!count) { + return; + } + + resize(index + count); + + switch (_type) { + case kArrayTypeInt16: + case kArrayTypeID: { + reg_t *target = (reg_t *)_data + index; + while (count--) { + *target = value; + } + break; + } + case kArrayTypeByte: + case kArrayTypeString: { + byte *target = (byte *)_data + index; + const byte fillValue = value.getOffset(); + while (count--) { + *target = fillValue; + } + break; + } + case kArrayTypeInvalid: + error("Attempted write to uninitialized SciArray"); + } + } + + /** + * Copies values from the source array. Both arrays will be grown if needed + * to prevent out-of-bounds reads/writes. + */ + void copy(SciArray &source, const uint16 sourceIndex, const uint16 targetIndex, uint16 count) { + if (count == 65535 /* -1 */) { + count = source.size() - sourceIndex; + } + + if (!count) { + return; + } + + resize(targetIndex + count); + source.resize(sourceIndex + count); + + assert(source._elementSize == _elementSize); + + const byte *sourceData = (byte *)source._data + sourceIndex * source._elementSize; + byte *targetData = (byte *)_data + targetIndex * _elementSize; + memmove(targetData, sourceData, count * _elementSize); + } + + void byteCopy(const SciArray &source, const uint16 sourceOffset, const uint16 targetOffset, const uint16 count) { + error("SciArray::byteCopy not implemented"); } + /** + * Removes whitespace from string data held in this array. + */ + void trim(const int8 flags, const char showChar) { + enum { + kWhitespaceBoundary = 32, + kAsciiBoundary = 128 + }; + + byte *data = (byte *)_data; + byte *source; + byte *target; + + if (flags & kArrayTrimLeft) { + target = data; + source = data; + while (*source != '\0' && *source != showChar && *source <= kWhitespaceBoundary) { + ++source; + } + strcpy((char *)target, (char *)source); + } + + if (flags & kArrayTrimRight) { + source = data + strlen((char *)data) - 1; + while (source > data && *source != showChar && *source <= kWhitespaceBoundary) { + --source; + } + *source = '\0'; + } + + if (flags & kArrayTrimCenter) { + target = data; + while (*target && *target <= kWhitespaceBoundary && *target != showChar) { + ++target; + } + + if (*target) { + while (*target && (*target > kWhitespaceBoundary || *target == showChar)) { + ++target; + } + + if (*target) { + source = target; + while (*source) { + while (*source && *source <= kWhitespaceBoundary && *source != showChar) { + ++source; + } + + while (*source && (*source > kWhitespaceBoundary || *source == showChar)) { + *target++ = *source++; + } + } + + --source; + while (source > target && (*source <= kWhitespaceBoundary || *source >= kAsciiBoundary) && *source != showChar) { + --source; + } + ++source; + + memmove(target, source, strlen((char *)source) + 1); + } + } + } + } + + /** + * Copies the string data held by this array into a new Common::String. + */ + Common::String toString() const { + assert(_type == kArrayTypeString); + return Common::String((char *)_data); + } + + /** + * Copies the string from the given Common::String into this array. + */ + void fromString(const Common::String &string) { + // At least LSL6hires uses a byte-type array to hold string data + assert(_type == kArrayTypeString || _type == kArrayTypeByte); + resize(string.size() + 1, true); + Common::strlcpy((char *)_data, string.c_str(), string.size() + 1); + } + +protected: + void *_data; + SciArrayType _type; + uint16 _size; + uint8 _elementSize; +}; + +struct ArrayTable : public SegmentObjTable<SciArray> { + ArrayTable() : SegmentObjTable<SciArray>(SEG_TYPE_ARRAY) {} + + virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const; + void saveLoadWithSerializer(Common::Serializer &ser); SegmentRef dereference(reg_t pointer); }; @@ -649,7 +963,7 @@ public: /** * Allocates and initialises a new bitmap. */ - inline void create(const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 paletteSize, const bool remap, const bool gc) { + inline void create(const int16 width, const int16 height, const uint8 skipColor, const int16 originX, const int16 originY, const int16 xResolution, const int16 yResolution, const uint32 paletteSize, const bool remap, const bool gc) { _dataSize = getBitmapSize(width, height) + paletteSize; _data = (byte *)realloc(_data, _dataSize); @@ -659,7 +973,7 @@ public: setWidth(width); setHeight(height); - setDisplace(Common::Point(displaceX, displaceY)); + setOrigin(Common::Point(originX, originY)); setSkipColor(skipColor); _data[9] = 0; WRITE_SCI11ENDIAN_UINT16(_data + 10, 0); @@ -670,8 +984,8 @@ public: setDataOffset(bitmapHeaderSize); setUncompressedDataOffset(bitmapHeaderSize); setControlOffset(0); - setScaledWidth(scaledWidth); - setScaledHeight(scaledHeight); + setXResolution(xResolution); + setYResolution(yResolution); _buffer = Buffer(getWidth(), getHeight(), getPixels()); } @@ -703,16 +1017,16 @@ public: BITMAP_PROPERTY(16, Width, 0); BITMAP_PROPERTY(16, Height, 2); - inline Common::Point getDisplace() const { + inline Common::Point getOrigin() const { return Common::Point( (int16)READ_SCI11ENDIAN_UINT16(_data + 4), (int16)READ_SCI11ENDIAN_UINT16(_data + 6) ); } - inline void setDisplace(const Common::Point &displace) { - WRITE_SCI11ENDIAN_UINT16(_data + 4, (uint16)displace.x); - WRITE_SCI11ENDIAN_UINT16(_data + 6, (uint16)displace.y); + inline void setOrigin(const Common::Point &origin) { + WRITE_SCI11ENDIAN_UINT16(_data + 4, (uint16)origin.x); + WRITE_SCI11ENDIAN_UINT16(_data + 6, (uint16)origin.y); } inline uint8 getSkipColor() const { @@ -761,7 +1075,7 @@ public: // NOTE: This property always seems to be zero BITMAP_PROPERTY(32, ControlOffset, 32); - inline uint16 getScaledWidth() const { + inline uint16 getXResolution() const { if (getDataOffset() >= 40) { return READ_SCI11ENDIAN_UINT16(_data + 36); } @@ -770,13 +1084,13 @@ public: return 320; } - inline void setScaledWidth(uint16 scaledWidth) { + inline void setXResolution(uint16 xResolution) { if (getDataOffset() >= 40) { - WRITE_SCI11ENDIAN_UINT16(_data + 36, scaledWidth); + WRITE_SCI11ENDIAN_UINT16(_data + 36, xResolution); } } - inline uint16 getScaledHeight() const { + inline uint16 getYResolution() const { if (getDataOffset() >= 40) { return READ_SCI11ENDIAN_UINT16(_data + 38); } @@ -785,9 +1099,9 @@ public: return 200; } - inline void setScaledHeight(uint16 scaledHeight) { + inline void setYResolution(uint16 yResolution) { if (getDataOffset() >= 40) { - WRITE_SCI11ENDIAN_UINT16(_data + 38, scaledHeight); + WRITE_SCI11ENDIAN_UINT16(_data + 38, yResolution); } } @@ -803,6 +1117,16 @@ public: } virtual void saveLoadWithSerializer(Common::Serializer &ser); + + void applyRemap(SciArray &clut) { + const int length = getWidth() * getHeight(); + uint8 *pixel = getPixels(); + for (int i = 0; i < length; ++i) { + const int16 color = clut.getAsInt16(*pixel); + assert(color >= 0 && color <= 255); + *pixel++ = (uint8)color; + } + } }; struct BitmapTable : public SegmentObjTable<SciBitmap> { |