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 
35 namespace Sci {
36 
37 struct 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 
60 enum 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 
85 public:
86  static SegmentObj *createSegmentObj(SegmentType type);
87 
88 public:
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 
147 struct LocalVariables : public SegmentObj {
148  int script_id;
149  Common::Array<reg_t> _locals;
150 
151 public:
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;
159  Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
160 
161  void saveLoadWithSerializer(Common::Serializer &ser) override;
162 };
163 
166  uint _capacity;
167  reg_t *_entries;
168 
169 public:
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  }
183  Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
184 
185  void saveLoadWithSerializer(Common::Serializer &ser) override;
186 };
187 
188 enum {
189  CLONE_USED = -1,
190  CLONE_NONE = -1
191 };
192 
193 typedef Object Clone;
194 
195 struct Node {
198  reg_t key;
199  reg_t value;
200 }; /* List nodes */
201 
202 struct List {
203  reg_t first;
204  reg_t last;
205 
206 #ifdef ENABLE_SCI32
207 
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 
227 struct Hunk {
228  void *mem;
229  uint32 size;
230  const char *type;
231 };
232 
233 template<typename T>
234 struct SegmentObjTable : public SegmentObj {
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 
248 public:
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 */
317 struct CloneTable : public SegmentObjTable<Clone> {
318  CloneTable() : SegmentObjTable<Clone>(SEG_TYPE_CLONES) {}
319 
320  void freeAtAddress(SegManager *segMan, reg_t sub_addr) override;
321  Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
322 
323  void saveLoadWithSerializer(Common::Serializer &ser) override;
324 };
325 
326 
327 /* NodeTable */
328 struct 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  }
334  Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
335 
336  void saveLoadWithSerializer(Common::Serializer &ser) override;
337 };
338 
339 
340 /* ListTable */
341 struct 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  }
347  Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
348 
349  void saveLoadWithSerializer(Common::Serializer &ser) override;
350 };
351 
352 
353 /* HunkTable */
354 struct 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
382 struct DynMem : public SegmentObj {
383  uint _size;
384  Common::String _description;
385  byte *_buf;
386 
387 public:
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 
414 enum 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 
423 enum SciArrayTrim {
424  kArrayTrimRight = 1,
425  kArrayTrimCenter = 2,
426  kArrayTrimLeft = 4
427 };
428 
429 class SciArray : public Common::Serializable {
430 public:
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 
896 protected:
897  void *_data;
898  SciArrayType _type;
899  uint16 _size;
900  uint8 _elementSize;
901 };
902 
903 struct 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 
915 enum {
916  kDefaultSkipColor = 250
917 };
918 
919 #define BITMAP_PROPERTY(size, property, offset)\
920 inline uint##size get##property() const {\
921  return READ_SCI11ENDIAN_UINT##size(_data + (offset));\
922 }\
923 inline void set##property(uint##size value) {\
924  WRITE_SCI11ENDIAN_UINT##size(_data + (offset), (value));\
925 }
926 
927 struct BitmapTable;
928 
933 class SciBitmap : public Common::Serializable {
934  byte *_data;
935  int _dataSize;
936  Buffer _buffer;
937  bool _gc;
938 
939 public:
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 
1188 struct 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
bool skipByte
true if referencing the 2nd data byte of *reg, false otherwise
Definition: segment.h:46
bool isValidOffset(uint32 offset) const override
Definition: segment.h:154
Definition: str.h:59
static String format(MSVC_PRINTF const char *fmt,...) GCC_PRINTF(1
const T * data() const
Definition: array.h:210
virtual Common::Array< reg_t > listAllDeallocatable(SegmentId segId) const
Definition: segment.h:130
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: segment.h:227
Definition: segment.h:37
Definition: segment.h:341
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:331
Definition: segment.h:354
static constexpr PixelFormat createFormatCLUT8()
Definition: pixelformat.h:184
bool isValidOffset(uint32 offset) const override
Definition: segment.h:176
Definition: segment.h:234
Definition: serializer.h:79
Definition: segment.h:382
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:373
Definition: segment.h:202
reg_t succ
Definition: segment.h:197
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override
Definition: segment.h:344
Definition: segment.h:317
Out copy(In first, In last, Out dst)
Definition: algorithm.h:52
virtual Common::Array< reg_t > listAllOutgoingReferences(reg_t object) const
Definition: segment.h:142
Common::Array< reg_t > listAllDeallocatable(SegmentId segId) const override
Definition: segment.h:401
bool isValidOffset(uint32 offset) const override
Definition: segment.h:279
int maxSize
number of available bytes
Definition: segment.h:43
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr)
Definition: segment.h:123
void push_back(const T &element)
Definition: array.h:183
Definition: segment.h:82
Definition: segment.h:147
T y
Definition: rect.h:49
Definition: segment.h:236
Definition: object.h:69
Definition: segment.h:328
T x
Definition: rect.h:48
Definition: rect.h:144
reg_t pred
Definition: segment.h:196
int script_id
Definition: segment.h:148
reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const override
Definition: segment.h:180
Definition: console.h:28
Definition: serializer.h:308
bool isRaw
true if data is raw, false if it is a reg_t sequence
Definition: segment.h:38
Common::Array< reg_t > listAllDeallocatable(SegmentId segId) const override
Definition: segment.h:298
size_type size() const
Definition: array.h:318
void NORETURN_PRE error(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
Definition: seg_manager.h:48
signed char * fill(signed char *first, signed char *last, Value val)
Definition: algorithm.h:168
reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const override
Definition: segment.h:398
Definition: display_client.h:113
size_t strnlen(const char *src, size_t maxSize)
Definition: helpers.h:247
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const
Definition: segment.h:116
Definition: segment.h:195
Definition: vm_types.h:39
Definition: segment.h:165
T & back()
Definition: array.h:232
bool isValidOffset(uint32 offset) const override
Definition: segment.h:394
uint _capacity
Definition: segment.h:166