ScummVM API documentation
segment.h
1/* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef SCI_ENGINE_SEGMENT_H
23#define SCI_ENGINE_SEGMENT_H
24
25#include "common/serializer.h"
26#include "common/str.h"
27#include "sci/engine/object.h"
28#include "sci/engine/vm.h"
29#include "sci/engine/vm_types.h" // for reg_t
30#include "sci/util.h"
31#ifdef ENABLE_SCI32
32#include "sci/graphics/palette32.h"
33#endif
34
35namespace Sci {
36
37struct SegmentRef {
38 bool isRaw;
39 union {
40 byte *raw;
41 reg_t *reg;
42 };
43 int maxSize;
44
45 // FIXME: Perhaps a generic 'offset' is more appropriate here
46 bool skipByte;
47
48 // TODO: Add this?
49 //reg_t pointer; // Original pointer
50
51 // TODO: Add this?
52 //SegmentType type;
53
54 SegmentRef() : isRaw(true), raw(0), maxSize(0), skipByte(false) {}
55
56 bool isValid() const { return (isRaw ? raw != 0 : reg != 0); }
57};
58
59
60enum SegmentType {
61 SEG_TYPE_INVALID = 0,
62 SEG_TYPE_SCRIPT = 1,
63 SEG_TYPE_CLONES = 2,
64 SEG_TYPE_LOCALS = 3,
65 SEG_TYPE_STACK = 4,
66 // 5 used to be system strings, now obsolete
67 SEG_TYPE_LISTS = 6,
68 SEG_TYPE_NODES = 7,
69 SEG_TYPE_HUNK = 8,
70 SEG_TYPE_DYNMEM = 9,
71 // 10 used to be string fragments, now obsolete
72
73#ifdef ENABLE_SCI32
74 SEG_TYPE_ARRAY = 11,
75 // 12 used to be string, now obsolete
76 SEG_TYPE_BITMAP = 13,
77#endif
78
79 SEG_TYPE_MAX // For sanity checking
80};
81
83 SegmentType _type;
84
85public:
86 static SegmentObj *createSegmentObj(SegmentType type);
87
88public:
89 SegmentObj(SegmentType type) : _type(type) {}
90 ~SegmentObj() override {}
91
92 inline SegmentType getType() const { return _type; }
93
98 virtual bool isValidOffset(uint32 offset) const = 0;
99
105 virtual SegmentRef dereference(reg_t pointer);
106
116 virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const { return sub_addr; }
117
123 virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {}
124
130 virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const {
131 return Common::Array<reg_t>();
132 }
133
143 return Common::Array<reg_t>();
144 }
145};
146
147struct LocalVariables : public SegmentObj {
149 Common::Array<reg_t> _locals;
150
151public:
152 LocalVariables(): SegmentObj(SEG_TYPE_LOCALS), script_id(0) { }
153
154 bool isValidOffset(uint32 offset) const override {
155 return offset < _locals.size() * 2;
156 }
157 SegmentRef dereference(reg_t pointer) override;
158 reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const override;
160
161 void saveLoadWithSerializer(Common::Serializer &ser) override;
162};
163
167 reg_t *_entries;
168
169public:
170 DataStack() : SegmentObj(SEG_TYPE_STACK), _capacity(0), _entries(NULL) { }
171 ~DataStack() override {
172 free(_entries);
173 _entries = NULL;
174 }
175
176 bool isValidOffset(uint32 offset) const override {
177 return offset < _capacity * 2;
178 }
179 SegmentRef dereference(reg_t pointer) override;
180 reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const override {
181 return make_reg(addr.getSegment(), 0);
182 }
184
185 void saveLoadWithSerializer(Common::Serializer &ser) override;
186};
187
188enum {
189 CLONE_USED = -1,
190 CLONE_NONE = -1
191};
192
193typedef Object Clone;
194
195struct Node {
198 reg_t key;
199 reg_t value;
200}; /* List nodes */
201
202struct List {
203 reg_t first;
204 reg_t last;
205
206#ifdef ENABLE_SCI32
211 reg_t nextNodes[10];
212
216 int numRecursions;
217#endif
218
219 List() : first(NULL_REG), last(NULL_REG) {
220#ifdef ENABLE_SCI32
221 memset(nextNodes, 0, sizeof(nextNodes));
222 numRecursions = 0;
223#endif
224 }
225};
226
227struct Hunk {
228 void *mem;
229 uint32 size;
230 const char *type;
231};
232
233template<typename T>
235 typedef T value_type;
236 struct Entry {
237 T *data;
238 int next_free; /* Only used for free entries */
239 };
240 enum { HEAPENTRY_INVALID = -1 };
241
242 int first_free = HEAPENTRY_INVALID;
243 int entries_used = 0;
246 ArrayType _table;
247
248public:
249 SegmentObjTable(SegmentType type) : SegmentObj(type) {
250 }
251
252 ~SegmentObjTable() override {
253 for (uint i = 0; i < _table.size(); i++) {
254 if (isValidEntry(i)) {
255 freeEntry(i);
256 }
257 }
258 }
259
260 int allocEntry() {
261 entries_used++;
262 if (first_free != HEAPENTRY_INVALID) {
263 int oldff = first_free;
264 first_free = _table[oldff].next_free;
265
266 _table[oldff].next_free = oldff;
267 assert(_table[oldff].data == nullptr);
268 _table[oldff].data = new T;
269 return oldff;
270 } else {
271 uint newIdx = _table.size();
272 _table.push_back(Entry());
273 _table.back().data = new T;
274 _table[newIdx].next_free = newIdx; // Tag as 'valid'
275 return newIdx;
276 }
277 }
278
279 bool isValidOffset(uint32 offset) const override {
280 return isValidEntry(offset);
281 }
282
283 bool isValidEntry(int idx) const {
284 return idx >= 0 && (uint)idx < _table.size() && _table[idx].next_free == idx;
285 }
286
287 virtual void freeEntry(int idx) {
288 if (idx < 0 || (uint)idx >= _table.size())
289 ::error("Table::freeEntry: Attempt to release invalid table index %d", idx);
290
291 _table[idx].next_free = first_free;
292 delete _table[idx].data;
293 _table[idx].data = nullptr;
294 first_free = idx;
295 entries_used--;
296 }
297
298 Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const override {
300 for (uint i = 0; i < _table.size(); i++)
301 if (isValidEntry(i))
302 tmp.push_back(make_reg(segId, i));
303 return tmp;
304 }
305
306 uint size() const { return _table.size(); }
307
308 T &at(uint index) { return *_table[index].data; }
309 const T &at(uint index) const { return *_table[index].data; }
310
311 T &operator[](uint index) { return at(index); }
312 const T &operator[](uint index) const { return at(index); }
313};
314
315
316/* CloneTable */
317struct CloneTable : public SegmentObjTable<Clone> {
318 CloneTable() : SegmentObjTable<Clone>(SEG_TYPE_CLONES) {}
319
320 void freeAtAddress(SegManager *segMan, reg_t sub_addr) override;
322
323 void saveLoadWithSerializer(Common::Serializer &ser) override;
324};
325
326
327/* NodeTable */
328struct NodeTable : public SegmentObjTable<Node> {
329 NodeTable() : SegmentObjTable<Node>(SEG_TYPE_NODES) {}
330
331 void freeAtAddress(SegManager *segMan, reg_t sub_addr) override {
332 freeEntry(sub_addr.getOffset());
333 }
335
336 void saveLoadWithSerializer(Common::Serializer &ser) override;
337};
338
339
340/* ListTable */
341struct ListTable : public SegmentObjTable<List> {
342 ListTable() : SegmentObjTable<List>(SEG_TYPE_LISTS) {}
343
344 void freeAtAddress(SegManager *segMan, reg_t sub_addr) override {
345 freeEntry(sub_addr.getOffset());
346 }
348
349 void saveLoadWithSerializer(Common::Serializer &ser) override;
350};
351
352
353/* HunkTable */
354struct HunkTable : public SegmentObjTable<Hunk> {
355 HunkTable() : SegmentObjTable<Hunk>(SEG_TYPE_HUNK) {}
356 ~HunkTable() override {
357 for (uint i = 0; i < _table.size(); i++) {
358 if (isValidEntry(i))
359 freeEntryContents(i);
360 }
361 }
362
363 void freeEntryContents(int idx) {
364 free(at(idx).mem);
365 at(idx).mem = 0;
366 }
367
368 void freeEntry(int idx) override {
369 freeEntryContents(idx);
371 }
372
373 void freeAtAddress(SegManager *segMan, reg_t sub_addr) override {
374 freeEntry(sub_addr.getOffset());
375 }
376
377 void saveLoadWithSerializer(Common::Serializer &ser) override;
378};
379
380
381// Free-style memory
382struct DynMem : public SegmentObj {
383 uint _size;
384 Common::String _description;
385 byte *_buf;
386
387public:
388 DynMem() : SegmentObj(SEG_TYPE_DYNMEM), _size(0), _buf(0) {}
389 ~DynMem() override {
390 free(_buf);
391 _buf = NULL;
392 }
393
394 bool isValidOffset(uint32 offset) const override {
395 return offset < _size;
396 }
397 SegmentRef dereference(reg_t pointer) override;
398 reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const override {
399 return make_reg(addr.getSegment(), 0);
400 }
401 Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const override {
402 const reg_t r = make_reg(segId, 0);
403 return Common::Array<reg_t>(&r, 1);
404 }
405
406 void saveLoadWithSerializer(Common::Serializer &ser) override;
407};
408
409#ifdef ENABLE_SCI32
410
411#pragma mark -
412#pragma mark Arrays
413
414enum SciArrayType {
415 kArrayTypeInt16 = 0,
416 kArrayTypeID = 1,
417 kArrayTypeByte = 2,
418 kArrayTypeString = 3,
419 // Type 4 was for 32-bit integers; never used
420 kArrayTypeInvalid = 5
421};
422
423enum SciArrayTrim {
424 kArrayTrimRight = 1,
425 kArrayTrimCenter = 2,
426 kArrayTrimLeft = 4
427};
428
429class SciArray : public Common::Serializable {
430public:
431 SciArray() :
432 _type(kArrayTypeInvalid),
433 _size(0),
434 _data(nullptr),
435 _elementSize(0) {}
436
437 SciArray(const SciArray &array) {
438 _type = array._type;
439 _size = array._size;
440 _elementSize = array._elementSize;
441 _data = malloc(_elementSize * _size);
442 assert(_data);
443 memcpy(_data, array._data, _elementSize * _size);
444 }
445
446 SciArray &operator=(const SciArray &array) {
447 if (this == &array)
448 return *this;
449
450 free(_data);
451 _type = array._type;
452 _size = array._size;
453 _elementSize = array._elementSize;
454 _data = malloc(_elementSize * _size);
455 assert(_data);
456 memcpy(_data, array._data, _elementSize * _size);
457
458 return *this;
459 }
460
461 ~SciArray() override {
462 free(_data);
463 _size = 0;
464 _type = kArrayTypeInvalid;
465 }
466
467 void saveLoadWithSerializer(Common::Serializer &s) override;
468
472 SciArrayType getType() const {
473 return _type;
474 }
475
479 void setType(const SciArrayType type) {
480 assert(_type == kArrayTypeInvalid);
481 switch(type) {
482 case kArrayTypeInt16:
483 case kArrayTypeID:
484 _elementSize = sizeof(reg_t);
485 break;
486 case kArrayTypeString:
487 _elementSize = sizeof(char);
488 break;
489 case kArrayTypeByte:
490 _elementSize = sizeof(byte);
491 break;
492 default:
493 error("Invalid array type %d", type);
494 }
495 _type = type;
496 }
497
501 uint16 size() const {
502 return _size;
503 }
504
508 uint16 byteSize() const {
509 uint16 size1 = _size;
510 if (_type == kArrayTypeID || _type == kArrayTypeInt16) {
511 size1 *= sizeof(uint16);
512 }
513 return size1;
514 }
515
521 void resize(uint16 newSize, const bool force = false) {
522 if (force || newSize > _size) {
523 _data = realloc(_data, _elementSize * newSize);
524 if (newSize > _size) {
525 memset((byte *)_data + _elementSize * _size, 0, (newSize - _size) * _elementSize);
526 }
527 _size = newSize;
528 }
529 }
530
534 void *getRawData() { return _data; }
535 const void *getRawData() const { return _data; }
536
540 reg_t getAsID(const uint16 index) {
541 if (getSciVersion() >= SCI_VERSION_3) {
542 // SCI3 resizes arrays automatically when out-of-bounds indices are
543 // passed, but it has an off-by-one error, always passing the index
544 // instead of `index + 1` on a read. This happens to work in SSCI
545 // only because the resize method there actually allocates memory
546 // for `index + 25` elements when growing the array, and it always
547 // grows the array on its first resize because it decides whether to
548 // grow based on byte size including an extra array header.
549 resize(index + 1);
550 } else {
551 assert(index < _size);
552 }
553
554 switch(_type) {
555 case kArrayTypeInt16:
556 case kArrayTypeID:
557 return ((reg_t *)_data)[index];
558 case kArrayTypeByte:
559 case kArrayTypeString: {
560 int16 value;
561
562 if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) {
563 value = ((int8 *)_data)[index];
564 } else {
565 value = ((uint8 *)_data)[index];
566 }
567
568 return make_reg(0, value);
569 }
570 default:
571 error("Invalid array type %d", _type);
572 }
573 }
574
578 void setFromID(const uint16 index, const reg_t value) {
579 if (getSciVersion() >= SCI_VERSION_3) {
580 // This code is different from SSCI; see getAsID for an explanation
581 resize(index + 1);
582 } else {
583 assert(index < _size);
584 }
585
586 switch(_type) {
587 case kArrayTypeInt16:
588 case kArrayTypeID:
589 ((reg_t *)_data)[index] = value;
590 break;
591 case kArrayTypeByte:
592 case kArrayTypeString:
593 ((byte *)_data)[index] = value.toSint16();
594 break;
595 default:
596 error("Invalid array type %d", _type);
597 }
598 }
599
603 int16 getAsInt16(const uint16 index) {
604 assert(_type == kArrayTypeInt16);
605
606 if (getSciVersion() >= SCI_VERSION_3) {
607 // This code is different from SSCI; see getAsID for an explanation
608 resize(index + 1);
609 } else {
610 assert(index < _size);
611 }
612
613 const reg_t value = ((reg_t *)_data)[index];
614 assert(value.isNumber());
615 return value.toSint16();
616 }
617
621 void setFromInt16(const uint16 index, const int16 value) {
622 assert(_type == kArrayTypeInt16);
623
624 if (getSciVersion() >= SCI_VERSION_3) {
625 // This code is different from SSCI; see getAsID for an explanation
626 resize(index + 1);
627 } else {
628 assert(index < _size);
629 }
630
631 ((reg_t *)_data)[index] = make_reg(0, value);
632 }
633
638 byte &byteAt(const uint16 index) {
639 assert(_type == kArrayTypeString || _type == kArrayTypeByte);
640
641 if (getSciVersion() >= SCI_VERSION_3) {
642 // This code is different from SSCI; see getAsID for an explanation
643 resize(index + 1);
644 } else {
645 assert(index < _size);
646 }
647
648 return ((byte *)_data)[index];
649 }
650
655 char &charAt(const uint16 index) {
656 assert(_type == kArrayTypeString || _type == kArrayTypeByte);
657
658 if (getSciVersion() >= SCI_VERSION_3) {
659 // This code is different from SSCI; see getAsID for an explanation
660 resize(index + 1);
661 } else {
662 assert(index < _size);
663 }
664
665 return ((char *)_data)[index];
666 }
667
672 reg_t &IDAt(const uint16 index) {
673 assert(_type == kArrayTypeID || _type == kArrayTypeInt16);
674
675 if (getSciVersion() >= SCI_VERSION_3) {
676 // This code is different from SSCI; see getAsID for an explanation
677 resize(index + 1);
678 } else {
679 assert(index < _size);
680 }
681
682 return ((reg_t *)_data)[index];
683 }
684
689 void setElements(const uint16 index, uint16 count, const reg_t *values) {
690 resize(index + count);
691
692 switch (_type) {
693 case kArrayTypeInt16:
694 case kArrayTypeID: {
695 const reg_t *source = values;
696 reg_t *target = (reg_t *)_data + index;
697 while (count--) {
698 *target++ = *source++;
699 }
700 break;
701 }
702 case kArrayTypeByte:
703 case kArrayTypeString: {
704 const reg_t *source = values;
705 byte *target = (byte *)_data + index;
706 while (count--) {
707 if (!source->isNumber()) {
708 error("Non-number %04x:%04x sent to byte or string array", PRINT_REG(*source));
709 }
710 *target++ = source->getOffset();
711 ++source;
712 }
713 break;
714 }
715 default:
716 error("Attempted write to SciArray with invalid type %d", _type);
717 }
718 }
719
724 void fill(const uint16 index, uint16 count, const reg_t value) {
725 if (count == 65535 /* -1 */) {
726 count = size() - index;
727 }
728
729 if (!count) {
730 return;
731 }
732
733 resize(index + count);
734
735 switch (_type) {
736 case kArrayTypeInt16:
737 case kArrayTypeID: {
738 reg_t *target = (reg_t *)_data + index;
739 while (count--) {
740 *target++ = value;
741 }
742 break;
743 }
744 case kArrayTypeByte:
745 case kArrayTypeString: {
746 byte *target = (byte *)_data + index;
747 const byte fillValue = value.getOffset();
748 while (count--) {
749 *target++ = fillValue;
750 }
751 break;
752 }
753
754 case kArrayTypeInvalid:
755 default:
756 error("Attempted write to uninitialized SciArray");
757 break;
758 }
759 }
760
765 void copy(SciArray &source, const uint16 sourceIndex, const uint16 targetIndex, int16 count) {
766 if (count == -1) {
767 count = source.size() - sourceIndex;
768 }
769
770 if (count < 1) {
771 return;
772 }
773
774 resize(targetIndex + count);
775 source.resize(sourceIndex + count);
776
777 assert(source._elementSize == _elementSize);
778
779 const byte *sourceData = (byte *)source._data + sourceIndex * source._elementSize;
780 byte *targetData = (byte *)_data + targetIndex * _elementSize;
781 memmove(targetData, sourceData, count * _elementSize);
782 }
783
784 void byteCopy(const SciArray &source, const uint16 sourceOffset, const uint16 targetOffset, const uint16 count) {
785 error("SciArray::byteCopy not implemented");
786 }
787
791 void trim(const int8 flags, const char showChar) {
792 enum {
793 kWhitespaceBoundary = 32,
794 kAsciiBoundary = 128
795 };
796
797 byte *data = (byte *)_data;
798 byte *end = data + _size;
799 byte *source;
800 byte *target;
801
802 if (flags & kArrayTrimLeft) {
803 target = data;
804 source = data;
805 while (source < end && *source != '\0' && *source != showChar && *source <= kWhitespaceBoundary) {
806 ++source;
807 }
808 memmove(target, source, Common::strnlen((char *)source, _size - 1) + 1);
809 }
810
811 if (flags & kArrayTrimRight) {
812 source = data + Common::strnlen((char *)data, _size) - 1;
813 while (source > data && *source != showChar && *source <= kWhitespaceBoundary) {
814 *source = '\0';
815 --source;
816 }
817 }
818
819 if (flags & kArrayTrimCenter) {
820 target = data;
821 while (target < end && *target != '\0' && *target <= kWhitespaceBoundary && *target != showChar) {
822 ++target;
823 }
824
825 if (*target != '\0') {
826 while (target < end && *target != '\0' && (*target > kWhitespaceBoundary || *target == showChar)) {
827 ++target;
828 }
829
830 if (*target != '\0') {
831 source = target;
832 while (*source != '\0') {
833 while (source < end && *source != '\0' && *source <= kWhitespaceBoundary && *source != showChar) {
834 ++source;
835 }
836
837 while (source < end && *source != '\0' && (*source > kWhitespaceBoundary || *source == showChar)) {
838 *target++ = *source++;
839 }
840 }
841
842 --source;
843 while (source >= data && source > target && (*source <= kWhitespaceBoundary || *source >= kAsciiBoundary) && *source != showChar) {
844 --source;
845 }
846 ++source;
847
848 memmove(target, source, Common::strnlen((char *)source, _size - 1) + 1);
849 }
850 }
851 }
852 }
853
857 Common::String toString() const {
858 assert(_type == kArrayTypeString);
859 return Common::String((char *)_data);
860 }
861
865 void fromString(const Common::String &string) {
866 // At least LSL6hires uses a byte-type array to hold string data
867 assert(_type == kArrayTypeString || _type == kArrayTypeByte);
868 resize(string.size() + 1, true);
869 Common::strlcpy((char *)_data, string.c_str(), string.size() + 1);
870 }
871
872 Common::String toDebugString() const {
873 const char *type;
874 switch(_type) {
875 case kArrayTypeID:
876 type = "reg_t";
877 break;
878 case kArrayTypeByte:
879 type = "byte";
880 break;
881 case kArrayTypeInt16:
882 type = "int16";
883 break;
884 case kArrayTypeString:
885 type = "string";
886 break;
887 case kArrayTypeInvalid:
888 default:
889 type = "invalid";
890 break;
891 }
892
893 return Common::String::format("type %s; %u entries", type, size());
894 }
895
896protected:
897 void *_data;
898 SciArrayType _type;
899 uint16 _size;
900 uint8 _elementSize;
901};
902
903struct ArrayTable : public SegmentObjTable<SciArray> {
904 ArrayTable() : SegmentObjTable<SciArray>(SEG_TYPE_ARRAY) {}
905
906 Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
907
908 void saveLoadWithSerializer(Common::Serializer &ser) override;
909 SegmentRef dereference(reg_t pointer) override;
910};
911
912#pragma mark -
913#pragma mark Bitmaps
914
915enum {
916 kDefaultSkipColor = 250
917};
918
919#define BITMAP_PROPERTY(size, property, offset)\
920inline uint##size get##property() const {\
921 return READ_SCI11ENDIAN_UINT##size(_data + (offset));\
922}\
923inline void set##property(uint##size value) {\
924 WRITE_SCI11ENDIAN_UINT##size(_data + (offset), (value));\
925}
926
927struct BitmapTable;
928
933class SciBitmap : public Common::Serializable {
934 byte *_data;
935 int _dataSize;
936 Buffer _buffer;
937 bool _gc;
938
939public:
940 enum BitmapFlags {
941 kBitmapRemap = 2
942 };
943
948 static inline uint16 getBitmapHeaderSize() {
949 // TODO: These values are accurate for each engine, but there may be no reason
950 // to not simply just always use size 40, since SCI2.1mid does not seem to
951 // actually store any data above byte 40, and SCI2 did not allow bitmaps with
952 // scaling resolutions other than the default (320x200). Perhaps SCI3 used
953 // the extra bytes, or there is some reason why they tried to align the header
954 // size with other headers like pic headers?
955// uint32 bitmapHeaderSize;
956// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) {
957// bitmapHeaderSize = 46;
958// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) {
959// bitmapHeaderSize = 40;
960// } else {
961// bitmapHeaderSize = 36;
962// }
963// return bitmapHeaderSize;
964 return 46;
965 }
966
971 static inline uint32 getBitmapSize(const uint16 width, const uint16 height) {
972 return width * height + getBitmapHeaderSize();
973 }
974
975 inline SciBitmap() : _data(nullptr), _dataSize(0), _gc(true) {}
976
977 inline SciBitmap(const SciBitmap &other) {
978 _dataSize = other._dataSize;
979 _data = (byte *)malloc(other._dataSize);
980 memcpy(_data, other._data, other._dataSize);
981 if (_dataSize) {
982 _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8());
983 }
984 _gc = other._gc;
985 }
986
987 inline ~SciBitmap() override {
988 free(_data);
989 _data = nullptr;
990 _dataSize = 0;
991 }
992
993 inline SciBitmap &operator=(const SciBitmap &other) {
994 if (this == &other) {
995 return *this;
996 }
997
998 free(_data);
999 _dataSize = other._dataSize;
1000 _data = (byte *)malloc(other._dataSize);
1001 memcpy(_data, other._data, _dataSize);
1002 if (_dataSize) {
1003 _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8());
1004 }
1005 _gc = other._gc;
1006
1007 return *this;
1008 }
1009
1013 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) {
1014
1015 _dataSize = getBitmapSize(width, height) + paletteSize;
1016 _data = (byte *)realloc(_data, _dataSize);
1017 _gc = gc;
1018
1019 const uint16 bitmapHeaderSize = getBitmapHeaderSize();
1020
1021 setWidth(width);
1022 setHeight(height);
1023 setOrigin(Common::Point(originX, originY));
1024 setSkipColor(skipColor);
1025 _data[9] = 0;
1026 WRITE_SCI11ENDIAN_UINT16(_data + 10, 0);
1027 setRemap(remap);
1028 setDataSize(width * height);
1029 WRITE_SCI11ENDIAN_UINT32(_data + 16, 0);
1030 setHunkPaletteOffset(paletteSize > 0 ? (bitmapHeaderSize + width * height) : 0);
1031 setDataOffset(bitmapHeaderSize);
1032 setUncompressedDataOffset(bitmapHeaderSize);
1033 setControlOffset(0);
1034 setXResolution(xResolution);
1035 setYResolution(yResolution);
1036
1037 _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8());
1038 }
1039
1040 inline int getRawSize() const {
1041 return _dataSize;
1042 }
1043
1044 inline byte *getRawData() const {
1045 return _data;
1046 }
1047
1048 inline Buffer &getBuffer() {
1049 return _buffer;
1050 }
1051
1052 inline bool getShouldGC() const {
1053 return _gc;
1054 }
1055
1056 inline void enableGC() {
1057 _gc = true;
1058 }
1059
1060 inline void disableGC() {
1061 _gc = false;
1062 }
1063
1064 BITMAP_PROPERTY(16, Width, 0);
1065 BITMAP_PROPERTY(16, Height, 2);
1066
1067 inline Common::Point getOrigin() const {
1068 return Common::Point(
1069 (int16)READ_SCI11ENDIAN_UINT16(_data + 4),
1070 (int16)READ_SCI11ENDIAN_UINT16(_data + 6)
1071 );
1072 }
1073
1074 inline void setOrigin(const Common::Point &origin) {
1075 WRITE_SCI11ENDIAN_UINT16(_data + 4, (uint16)origin.x);
1076 WRITE_SCI11ENDIAN_UINT16(_data + 6, (uint16)origin.y);
1077 }
1078
1079 inline uint8 getSkipColor() const {
1080 return _data[8];
1081 }
1082
1083 inline void setSkipColor(const uint8 skipColor) {
1084 _data[8] = skipColor;
1085 }
1086
1087 inline bool getRemap() const {
1088 return READ_SCI11ENDIAN_UINT16(_data + 10) & kBitmapRemap;
1089 }
1090
1091 inline void setRemap(const bool remap) {
1092 uint16 flags = READ_SCI11ENDIAN_UINT16(_data + 10);
1093 if (remap) {
1094 flags |= kBitmapRemap;
1095 } else {
1096 flags &= ~kBitmapRemap;
1097 }
1098 WRITE_SCI11ENDIAN_UINT16(_data + 10, flags);
1099 }
1100
1101 BITMAP_PROPERTY(32, DataSize, 12);
1102
1103 BITMAP_PROPERTY(32, HunkPaletteOffset, 20);
1104
1105 BITMAP_PROPERTY(32, DataOffset, 24);
1106
1107 // This property is used as a "magic number" for validating that a block of
1108 // memory is a valid bitmap, and so is always set to the size of the header.
1109 BITMAP_PROPERTY(32, UncompressedDataOffset, 28);
1110
1111 // This property always seems to be zero in SSCI
1112 BITMAP_PROPERTY(32, ControlOffset, 32);
1113
1114 inline uint16 getXResolution() const {
1115 if (getDataOffset() >= 40) {
1116 return READ_SCI11ENDIAN_UINT16(_data + 36);
1117 }
1118
1119 // SCI2 bitmaps did not have scaling ability
1120 return 320;
1121 }
1122
1123 inline void setXResolution(uint16 xResolution) {
1124 if (getDataOffset() >= 40) {
1125 WRITE_SCI11ENDIAN_UINT16(_data + 36, xResolution);
1126 }
1127 }
1128
1129 inline uint16 getYResolution() const {
1130 if (getDataOffset() >= 40) {
1131 return READ_SCI11ENDIAN_UINT16(_data + 38);
1132 }
1133
1134 // SCI2 bitmaps did not have scaling ability
1135 return 200;
1136 }
1137
1138 inline void setYResolution(uint16 yResolution) {
1139 if (getDataOffset() >= 40) {
1140 WRITE_SCI11ENDIAN_UINT16(_data + 38, yResolution);
1141 }
1142 }
1143
1144 inline byte *getPixels() {
1145 return _data + getUncompressedDataOffset();
1146 }
1147
1148 inline byte *getHunkPalette() {
1149 if (getHunkPaletteOffset() == 0) {
1150 return nullptr;
1151 }
1152 return _data + getHunkPaletteOffset();
1153 }
1154
1155 inline void setPalette(const Palette &palette) {
1156 byte *paletteData = getHunkPalette();
1157 if (paletteData != nullptr) {
1158 SciSpan<byte> paletteSpan(paletteData, getRawSize() - getHunkPaletteOffset());
1159 HunkPalette::write(paletteSpan, palette);
1160 }
1161 }
1162
1163 void saveLoadWithSerializer(Common::Serializer &ser) override;
1164
1165 void applyRemap(SciArray &clut) {
1166 const int length = getWidth() * getHeight();
1167 uint8 *pixel = getPixels();
1168 for (int i = 0; i < length; ++i) {
1169 const int16 color = clut.getAsInt16(*pixel);
1170 assert(color >= 0 && color <= 255);
1171 *pixel++ = (uint8)color;
1172 }
1173 }
1174
1175 Common::String toString() const {
1176 return Common::String::format("%dx%d; res %dx%d; origin %dx%d; skip color %u; %s; %s)",
1177 getWidth(), getHeight(),
1178 getXResolution(), getYResolution(),
1179 getOrigin().x, getOrigin().y,
1180 getSkipColor(),
1181 getRemap() ? "remap" : "no remap",
1182 getShouldGC() ? "GC" : "no GC");
1183 }
1184};
1185
1186#undef BITMAP_PROPERTY
1187
1188struct BitmapTable : public SegmentObjTable<SciBitmap> {
1189 BitmapTable() : SegmentObjTable<SciBitmap>(SEG_TYPE_BITMAP) {}
1190
1191 SegmentRef dereference(reg_t pointer) override {
1192 SegmentRef ret;
1193 ret.isRaw = true;
1194 ret.maxSize = at(pointer.getOffset()).getRawSize();
1195 ret.raw = at(pointer.getOffset()).getRawData();
1196 return ret;
1197 }
1198
1199 void saveLoadWithSerializer(Common::Serializer &ser) override;
1200};
1201
1202#endif
1203
1204
1205} // End of namespace Sci
1206
1207#endif // SCI_ENGINE_SEGMENT_H
Definition: display_client.h:113
size_type size() const
Definition: array.h:316
void push_back(const T &element)
Definition: array.h:181
Definition: serializer.h:352
Definition: serializer.h:80
Definition: str.h:59
static String format(MSVC_PRINTF const char *fmt,...) GCC_PRINTF(1
Definition: atari-screen.h:42
Definition: seg_manager.h:48
Out copy(In first, In last, Out dst)
Definition: algorithm.h:52
signed char * fill(signed char *first, signed char *last, Value val)
Definition: algorithm.h:168
size_t strnlen(const char *src, size_t maxSize)
size_t strlcpy(char *dst, const char *src, size_t size)
void NORETURN_PRE error(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
Definition: console.h:28
T y
Definition: rect.h:49
T x
Definition: rect.h:48
Definition: rect.h:144
static constexpr PixelFormat createFormatCLUT8()
Definition: pixelformat.h:184
Definition: segment.h:317
Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const override
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:165
uint _capacity
Definition: segment.h:166
Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const override
SegmentRef dereference(reg_t pointer) override
reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const override
Definition: segment.h:180
bool isValidOffset(uint32 offset) const override
Definition: segment.h:176
Definition: segment.h:382
bool isValidOffset(uint32 offset) const override
Definition: segment.h:394
Common::Array< reg_t > listAllDeallocatable(SegmentId segId) const override
Definition: segment.h:401
reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const override
Definition: segment.h:398
SegmentRef dereference(reg_t pointer) override
Definition: segment.h:227
Definition: segment.h:354
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:373
Definition: segment.h:202
Definition: segment.h:341
Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const override
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:344
Definition: segment.h:147
Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const override
reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const override
int script_id
Definition: segment.h:148
SegmentRef dereference(reg_t pointer) override
bool isValidOffset(uint32 offset) const override
Definition: segment.h:154
Definition: segment.h:195
reg_t succ
Definition: segment.h:197
reg_t pred
Definition: segment.h:196
Definition: segment.h:328
Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const override
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:331
Definition: segment.h:82
virtual SegmentRef dereference(reg_t pointer)
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr)
Definition: segment.h:123
virtual bool isValidOffset(uint32 offset) const =0
virtual Common::Array< reg_t > listAllDeallocatable(SegmentId segId) const
Definition: segment.h:130
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const
Definition: segment.h:116
virtual Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const
Definition: segment.h:142
Definition: segment.h:236
Definition: segment.h:234
int entries_used
Definition: segment.h:243
int first_free
Definition: segment.h:242
Common::Array< reg_t > listAllDeallocatable(SegmentId segId) const override
Definition: segment.h:298
bool isValidOffset(uint32 offset) const override
Definition: segment.h:279
Definition: segment.h:37
int maxSize
number of available bytes
Definition: segment.h:43
bool skipByte
true if referencing the 2nd data byte of *reg, false otherwise
Definition: segment.h:46
bool isRaw
true if data is raw, false if it is a reg_t sequence
Definition: segment.h:38
Definition: vm_types.h:39