aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine')
-rw-r--r--engines/sci/engine/gc.cpp29
-rw-r--r--engines/sci/engine/kernel.cpp1
-rw-r--r--engines/sci/engine/kernel_tables.h4
-rw-r--r--engines/sci/engine/kgraphics32.cpp64
-rw-r--r--engines/sci/engine/savegame.cpp46
-rw-r--r--engines/sci/engine/savegame.h3
-rw-r--r--engines/sci/engine/seg_manager.cpp55
-rw-r--r--engines/sci/engine/seg_manager.h10
-rw-r--r--engines/sci/engine/segment.cpp3
-rw-r--r--engines/sci/engine/segment.h300
-rw-r--r--engines/sci/engine/workarounds.cpp2
11 files changed, 468 insertions, 49 deletions
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp
index b229490570..6c1713bed9 100644
--- a/engines/sci/engine/gc.cpp
+++ b/engines/sci/engine/gc.cpp
@@ -143,22 +143,31 @@ AddrSet *findAllActiveReferences(EngineState *s) {
const Common::Array<SegmentObj *> &heap = s->_segMan->getSegments();
uint heapSize = heap.size();
- // Init: Explicitly loaded scripts
for (uint i = 1; i < heapSize; i++) {
- if (heap[i] && heap[i]->getType() == SEG_TYPE_SCRIPT) {
- Script *script = (Script *)heap[i];
+ if (heap[i]) {
+ // Init: Explicitly loaded scripts
+ if (heap[i]->getType() == SEG_TYPE_SCRIPT) {
+ Script *script = (Script *)heap[i];
- if (script->getLockers()) { // Explicitly loaded?
- wm.pushArray(script->listObjectReferences());
+ if (script->getLockers()) { // Explicitly loaded?
+ wm.pushArray(script->listObjectReferences());
+ }
}
- }
- }
#ifdef ENABLE_SCI32
- // Init: ScrollWindows
- if (g_sci->_gfxControls32)
- wm.pushArray(g_sci->_gfxControls32->listObjectReferences());
+ // Init: Explicitly opted-out bitmaps
+ else if (heap[i]->getType() == SEG_TYPE_BITMAP) {
+ BitmapTable *bt = static_cast<BitmapTable *>(heap[i]);
+
+ for (uint j = 0; j < bt->_table.size(); j++) {
+ if (bt->_table[j].data && bt->_table[j].data->getShouldGC() == false) {
+ wm.push(make_reg(i, j));
+ }
+ }
+ }
#endif
+ }
+ }
debugC(kDebugLevelGC, "[GC] -- Finished explicitly loaded scripts, done with root set");
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 2afb8b73d1..d764a88a0a 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -408,6 +408,7 @@ uint16 Kernel::findRegType(reg_t reg) {
#ifdef ENABLE_SCI32
case SEG_TYPE_ARRAY:
case SEG_TYPE_STRING:
+ case SEG_TYPE_BITMAP:
#endif
result |= SIG_TYPE_REFERENCE;
break;
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 8a1176eed8..e0e4dcc233 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -772,7 +772,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(EditText), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(MakeSaveCatName), SIG_EVERYWHERE, "rr", NULL, NULL },
{ MAP_CALL(MakeSaveFileName), SIG_EVERYWHERE, "rri", NULL, NULL },
- { MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiiii(i)", NULL, NULL },
+ { MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiii(i)(i)", NULL, NULL },
{ MAP_CALL(PalCycle), SIG_EVERYWHERE, "(.*)", kPalCycle_subops, NULL },
// SCI2 Empty functions
@@ -823,7 +823,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
{ MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL },
- { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii(i)(i)", NULL, NULL },
{ MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(GetConfig), SIG_EVERYWHERE, "ro", NULL, NULL },
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 9cfe53255b..e458109cc2 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -56,6 +56,7 @@
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
#include "sci/graphics/text32.h"
+#include "sci/graphics/transitions32.h"
#endif
namespace Sci {
@@ -123,8 +124,9 @@ reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
int16 x = argv[2].toSint16();
int16 y = argv[3].toSint16();
bool mirrorX = argc > 4 ? argv[4].toSint16() : false;
+ bool deleteDuplicate = argc > 5 ? argv[5].toSint16() : true;
- g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, x, y, mirrorX);
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, x, y, mirrorX, deleteDuplicate);
return s->r_acc;
}
@@ -139,7 +141,7 @@ reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16());
+ g_sci->_gfxTransitions32->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16());
return s->r_acc;
}
@@ -195,14 +197,14 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
if (subop == 0) {
TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode));
- return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true);
+ return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true, true);
} else {
CelInfo32 celInfo;
celInfo.type = kCelTypeView;
celInfo.resourceId = readSelectorValue(segMan, object, SELECTOR(view));
celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel));
- return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed);
+ return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed, true);
}
}
@@ -310,7 +312,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// NOTE: The order of planeObj and showStyle are reversed
// because this is how SCI3 called the corresponding method
// on the KernelMgr
- g_sci->_gfxFrameout->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
+ g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
return s->r_acc;
}
@@ -534,13 +536,14 @@ reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) {
int16 scaledHeight = argc > 5 ? argv[5].toSint16() : g_sci->_gfxText32->_scaledHeight;
bool useRemap = argc > 6 ? argv[6].toSint16() : false;
- BitmapResource bitmap(s->_segMan, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap);
+ reg_t bitmapId;
+ SciBitmap &bitmap = *s->_segMan->allocateBitmap(&bitmapId, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap, true);
memset(bitmap.getPixels(), backColor, width * height);
- return bitmap.getObject();
+ return bitmapId;
}
reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv) {
- s->_segMan->freeHunkEntry(argv[0]);
+ s->_segMan->freeBitmap(argv[0]);
return s->r_acc;
}
@@ -550,7 +553,7 @@ reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv) {
}
reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) {
- BitmapResource bitmap(argv[0]);
+ SciBitmap &bitmap = *s->_segMan->lookupBitmap(argv[0]);
CelObjView view(argv[1].toUint16(), argv[2].toSint16(), argv[3].toSint16());
const int16 x = argc > 4 ? argv[4].toSint16() : 0;
@@ -580,7 +583,7 @@ reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) {
reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) {
// called e.g. from TextButton::createBitmap() in Torin's Passage, script 64894
- BitmapResource bitmap(argv[0]);
+ SciBitmap &bitmap = *s->_segMan->lookupBitmap(argv[0]);
Common::String text = s->_segMan->getString(argv[1]);
Common::Rect textRect(
argv[2].toSint16(),
@@ -604,10 +607,10 @@ reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) {
// Then clips. But this seems stupid.
textRect.clip(Common::Rect(bitmap.getWidth(), bitmap.getHeight()));
- reg_t textBitmapObject = g_sci->_gfxText32->createFontBitmap(textRect.width(), textRect.height(), Common::Rect(textRect.width(), textRect.height()), text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, false);
+ reg_t textBitmapObject = g_sci->_gfxText32->createFontBitmap(textRect.width(), textRect.height(), Common::Rect(textRect.width(), textRect.height()), text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, false, false);
CelObjMem textCel(textBitmapObject);
textCel.draw(bitmap.getBuffer(), textRect, Common::Point(textRect.left, textRect.top), false);
- s->_segMan->freeHunkEntry(textBitmapObject);
+ s->_segMan->freeBitmap(textBitmapObject);
return s->r_acc;
}
@@ -615,7 +618,7 @@ reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) {
reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) {
// called e.g. from TextView::init() and TextView::draw() in Torin's Passage, script 64890
- BitmapResource bitmap(argv[0]);
+ SciBitmap &bitmap = *s->_segMan->lookupBitmap(argv[0]);
Common::Rect fillRect(
argv[1].toSint16(),
argv[2].toSint16(),
@@ -640,7 +643,7 @@ reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv) {
}
reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv) {
- BitmapResource bitmap(argv[0]);
+ SciBitmap &bitmap = *s->_segMan->lookupBitmap(argv[0]);
bitmap.setDisplace(Common::Point(argv[1].toSint16(), argv[2].toSint16()));
return s->r_acc;
}
@@ -760,28 +763,19 @@ reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// Used by LSL6hires intro (room 110)
reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
- // Called in the intro of LSL6 hires (room 110)
- // The end effect of this is the same as the old screen scroll transition
-
- // 7 parameters
- reg_t planeObject = argv[0];
- //int16 x = argv[1].toSint16();
- //int16 y = argv[2].toSint16();
- uint16 pictureId = argv[3].toUint16();
- // param 4: int (0 in LSL6, probably scroll direction? The picture in LSL6 scrolls down)
- // param 5: int (first call is 1, then the subsequent one is 0 in LSL6)
- // param 6: optional int (0 in LSL6)
-
- // Set the new picture directly for now
- //writeSelectorValue(s->_segMan, planeObject, SELECTOR(left), x);
- //writeSelectorValue(s->_segMan, planeObject, SELECTOR(top), y);
- writeSelectorValue(s->_segMan, planeObject, SELECTOR(picture), pictureId);
- // and update our draw list
- g_sci->_gfxFrameout->kernelUpdatePlane(planeObject);
-
- // TODO
- return kStub(s, argc, argv);
+ const reg_t plane = argv[0];
+ const int16 deltaX = argv[1].toSint16();
+ const int16 deltaY = argv[2].toSint16();
+ const GuiResourceId pictureId = argv[3].toUint16();
+ const bool animate = argv[4].toUint16();
+ // NOTE: speed was accepted as an argument, but then never actually used
+ // const int16 speed = argc > 5 ? (bool)argv[5].toSint16() : -1;
+ const bool mirrorX = argc > 6 ? (bool)argv[6].toUint16() : false;
+
+ g_sci->_gfxTransitions32->kernelSetScroll(plane, deltaX, deltaY, pictureId, animate, mirrorX);
+ return s->r_acc;
}
// Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 31fb848a2c..c4d53a2dc9 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -158,6 +158,24 @@ void syncWithSerializer(Common::Serializer &s, SciString &obj) {
obj.setValue(i, value);
}
}
+
+void syncWithSerializer(Common::Serializer &s, SciBitmap *&obj) {
+ bool hasEntry;
+ if (s.isSaving()) {
+ hasEntry = obj != nullptr;
+ }
+ s.syncAsByte(hasEntry);
+
+ if (hasEntry) {
+ if (s.isLoading()) {
+ obj = new SciBitmap;
+ }
+
+ obj->saveLoadWithSerializer(s);
+ } else {
+ obj = nullptr;
+ }
+}
#endif
#pragma mark -
@@ -273,6 +291,8 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
} else if (type == SEG_TYPE_STRING) {
// Set the correct segment for SCI32 strings
_stringSegId = i;
+ } else if (s.getVersion() >= 36 && type == SEG_TYPE_BITMAP) {
+ _bitmapSegId = i;
#endif
}
@@ -402,6 +422,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2) {
g_sci->_gfxPalette32->saveLoadWithSerializer(s);
+ g_sci->_gfxRemap32->saveLoadWithSerializer(s);
} else
#endif
g_sci->_gfxPalette16->saveLoadWithSerializer(s);
@@ -687,6 +708,31 @@ void StringTable::saveLoadWithSerializer(Common::Serializer &ser) {
sync_Table<StringTable>(ser, *this);
}
+
+void BitmapTable::saveLoadWithSerializer(Common::Serializer &ser) {
+ if (ser.getVersion() < 36) {
+ return;
+ }
+
+ sync_Table(ser, *this);
+}
+
+void SciBitmap::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 36) {
+ return;
+ }
+
+ s.syncAsByte(_gc);
+ s.syncAsUint32LE(_dataSize);
+ if (s.isLoading()) {
+ _data = (byte *)malloc(_dataSize);
+ }
+ s.syncBytes(_data, _dataSize);
+
+ if (s.isLoading()) {
+ _buffer = Buffer(getWidth(), getHeight(), getPixels());
+ }
+}
#endif
void GfxPalette::palVarySaveLoadPalette(Common::Serializer &s, Palette *palette) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 43909accf2..c5c2bcef08 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,6 +37,7 @@ struct EngineState;
*
* Version - new/changed feature
* =============================
+ * 36 - SCI32 bitmap segment
* 35 - SCI32 remap
* 34 - SCI32 palettes, and store play time in ticks
* 33 - new overridePriority flag in MusicEntry
@@ -60,7 +61,7 @@ struct EngineState;
*/
enum {
- CURRENT_SAVEGAME_VERSION = 35,
+ CURRENT_SAVEGAME_VERSION = 36,
MINIMUM_SAVEGAME_VERSION = 14
};
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 95e3cd15f9..608452983a 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -43,6 +43,7 @@ SegManager::SegManager(ResourceManager *resMan, ScriptPatcher *scriptPatcher)
#ifdef ENABLE_SCI32
_arraysSegId = 0;
_stringSegId = 0;
+ _bitmapSegId = 0;
#endif
createClassTable();
@@ -72,6 +73,7 @@ void SegManager::resetSegMan() {
#ifdef ENABLE_SCI32
_arraysSegId = 0;
_stringSegId = 0;
+ _bitmapSegId = 0;
#endif
// Reinitialize class table
@@ -941,6 +943,59 @@ void SegManager::freeString(reg_t addr) {
stringTable.freeEntry(addr.getOffset());
}
+#pragma mark -
+#pragma mark Bitmaps
+
+SciBitmap *SegManager::allocateBitmap(reg_t *addr, 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) {
+ BitmapTable *table;
+ int offset;
+
+ if (!_bitmapSegId) {
+ table = (BitmapTable *)allocSegment(new BitmapTable(), &(_bitmapSegId));
+ } else {
+ table = (BitmapTable *)_heap[_bitmapSegId];
+ }
+
+ offset = table->allocEntry();
+
+ *addr = make_reg(_bitmapSegId, offset);
+ SciBitmap *bitmap = table->at(offset);
+
+ if (bitmap == nullptr) {
+ *addr = NULL_REG;
+ }
+
+ bitmap->create(width, height, skipColor, displaceX, displaceY, scaledWidth, scaledHeight, paletteSize, remap, gc);
+
+ return bitmap;
+}
+
+SciBitmap *SegManager::lookupBitmap(const reg_t addr) {
+ if (_heap[addr.getSegment()]->getType() != SEG_TYPE_BITMAP)
+ error("Attempt to use non-bitmap %04x:%04x as bitmap", PRINT_REG(addr));
+
+ BitmapTable &bitmapTable = *(BitmapTable *)_heap[addr.getSegment()];
+
+ if (!bitmapTable.isValidEntry(addr.getOffset()))
+ error("Attempt to use invalid entry %04x:%04x as bitmap", PRINT_REG(addr));
+
+ return (bitmapTable.at(addr.getOffset()));
+}
+
+void SegManager::freeBitmap(const reg_t addr) {
+ if (_heap[addr.getSegment()]->getType() != SEG_TYPE_BITMAP)
+ error("Attempt to free non-bitmap %04x:%04x as bitmap", PRINT_REG(addr));
+
+ BitmapTable &bitmapTable = *(BitmapTable *)_heap[addr.getSegment()];
+
+ if (!bitmapTable.isValidEntry(addr.getOffset()))
+ error("Attempt to free invalid entry %04x:%04x as bitmap", PRINT_REG(addr));
+
+ bitmapTable.freeEntry(addr.getOffset());
+}
+
+#pragma mark -
+
#endif
void SegManager::createClassTable() {
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index 9da7e58770..acebecea97 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -29,6 +29,10 @@
#include "sci/engine/vm.h"
#include "sci/engine/vm_types.h"
#include "sci/engine/segment.h"
+#ifdef ENABLE_SCI32
+// TODO: Baaaad?
+#include "sci/graphics/celobj32.h"
+#endif
namespace Sci {
@@ -433,10 +437,15 @@ public:
SciArray<reg_t> *allocateArray(reg_t *addr);
SciArray<reg_t> *lookupArray(reg_t addr);
void freeArray(reg_t addr);
+
SciString *allocateString(reg_t *addr);
SciString *lookupString(reg_t addr);
void freeString(reg_t addr);
SegmentId getStringSegmentId() { return _stringSegId; }
+
+ SciBitmap *allocateBitmap(reg_t *addr, const int16 width, const int16 height, const uint8 skipColor = kDefaultSkipColor, const int16 displaceX = 0, const int16 displaceY = 0, const int16 scaledWidth = kLowResX, const int16 scaledHeight = kLowResY, const uint32 paletteSize = 0, const bool remap = false, const bool gc = true);
+ SciBitmap *lookupBitmap(reg_t addr);
+ void freeBitmap(reg_t addr);
#endif
const Common::Array<SegmentObj *> &getSegments() const { return _heap; }
@@ -462,6 +471,7 @@ private:
#ifdef ENABLE_SCI32
SegmentId _arraysSegId;
SegmentId _stringSegId;
+ SegmentId _bitmapSegId;
#endif
public:
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index 2cff799f4b..7943946ee4 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -70,6 +70,9 @@ SegmentObj *SegmentObj::createSegmentObj(SegmentType type) {
case SEG_TYPE_STRING:
mem = new StringTable();
break;
+ case SEG_TYPE_BITMAP:
+ mem = new BitmapTable();
+ break;
#endif
default:
error("Unknown SegmentObj type %d", type);
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 50c77d0538..7c39050f18 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -71,6 +71,7 @@ enum SegmentType {
#ifdef ENABLE_SCI32
SEG_TYPE_ARRAY = 11,
SEG_TYPE_STRING = 12,
+ SEG_TYPE_BITMAP = 13,
#endif
SEG_TYPE_MAX // For sanity checking
@@ -519,6 +520,305 @@ struct StringTable : public SegmentObjTable<SciString> {
SegmentRef dereference(reg_t pointer);
};
+#pragma mark -
+#pragma mark Bitmaps
+
+enum {
+ kDefaultSkipColor = 250
+};
+
+#define BITMAP_PROPERTY(size, property, offset)\
+inline uint##size get##property() const {\
+ return READ_SCI11ENDIAN_UINT##size(_data + (offset));\
+}\
+inline void set##property(uint##size value) {\
+ WRITE_SCI11ENDIAN_UINT##size(_data + (offset), (value));\
+}
+
+struct BitmapTable;
+
+/**
+ * A convenience class for creating and modifying in-memory
+ * bitmaps.
+ */
+class SciBitmap : public Common::Serializable {
+ byte *_data;
+ int _dataSize;
+ Buffer _buffer;
+ bool _gc;
+
+public:
+ enum BitmapFlags {
+ kBitmapRemap = 2
+ };
+
+ /**
+ * Gets the size of the bitmap header for the current
+ * engine version.
+ */
+ static inline uint16 getBitmapHeaderSize() {
+ // TODO: These values are accurate for each engine, but there may be no reason
+ // to not simply just always use size 40, since SCI2.1mid does not seem to
+ // actually store any data above byte 40, and SCI2 did not allow bitmaps with
+ // scaling resolutions other than the default (320x200). Perhaps SCI3 used
+ // the extra bytes, or there is some reason why they tried to align the header
+ // size with other headers like pic headers?
+// uint32 bitmapHeaderSize;
+// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) {
+// bitmapHeaderSize = 46;
+// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) {
+// bitmapHeaderSize = 40;
+// } else {
+// bitmapHeaderSize = 36;
+// }
+// return bitmapHeaderSize;
+ return 46;
+ }
+
+ /**
+ * Gets the byte size of a bitmap with the given width
+ * and height.
+ */
+ static inline uint32 getBitmapSize(const uint16 width, const uint16 height) {
+ return width * height + getBitmapHeaderSize();
+ }
+
+ inline SciBitmap() : _data(nullptr), _dataSize(0), _gc(true) {}
+
+ inline SciBitmap(const SciBitmap &other) {
+ _dataSize = other._dataSize;
+ _data = (byte *)malloc(other._dataSize);
+ memcpy(_data, other._data, other._dataSize);
+ if (_dataSize) {
+ _buffer = Buffer(getWidth(), getHeight(), getPixels());
+ }
+ _gc = other._gc;
+ }
+
+ inline ~SciBitmap() {
+ free(_data);
+ _data = nullptr;
+ _dataSize = 0;
+ }
+
+ inline SciBitmap &operator=(const SciBitmap &other) {
+ if (this == &other) {
+ return *this;
+ }
+
+ free(_data);
+ _dataSize = other._dataSize;
+ _data = (byte *)malloc(other._dataSize);
+ memcpy(_data, other._data, _dataSize);
+ if (_dataSize) {
+ _buffer = Buffer(getWidth(), getHeight(), getPixels());
+ }
+ _gc = other._gc;
+
+ return *this;
+ }
+
+ /**
+ * 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) {
+
+ _dataSize = getBitmapSize(width, height) + paletteSize;
+ _data = (byte *)realloc(_data, _dataSize);
+ _gc = gc;
+
+ const uint16 bitmapHeaderSize = getBitmapHeaderSize();
+
+ setWidth(width);
+ setHeight(height);
+ setDisplace(Common::Point(displaceX, displaceY));
+ setSkipColor(skipColor);
+ _data[9] = 0;
+ WRITE_SCI11ENDIAN_UINT16(_data + 10, 0);
+ setRemap(remap);
+ setDataSize(width * height);
+ WRITE_SCI11ENDIAN_UINT32(_data + 16, 0);
+ setHunkPaletteOffset(paletteSize > 0 ? (width * height) : 0);
+ setDataOffset(bitmapHeaderSize);
+ setUncompressedDataOffset(bitmapHeaderSize);
+ setControlOffset(0);
+ setScaledWidth(scaledWidth);
+ setScaledHeight(scaledHeight);
+
+ _buffer = Buffer(getWidth(), getHeight(), getPixels());
+ }
+
+ inline int getRawSize() const {
+ return _dataSize;
+ }
+
+ inline byte *getRawData() const {
+ return _data;
+ }
+
+ inline Buffer &getBuffer() {
+ return _buffer;
+ }
+
+ inline bool getShouldGC() const {
+ return _gc;
+ }
+
+ inline void enableGC() {
+ _gc = true;
+ }
+
+ inline void disableGC() {
+ _gc = false;
+ }
+
+ BITMAP_PROPERTY(16, Width, 0);
+ BITMAP_PROPERTY(16, Height, 2);
+
+ inline Common::Point getDisplace() 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 uint8 getSkipColor() const {
+ return _data[8];
+ }
+
+ inline void setSkipColor(const uint8 skipColor) {
+ _data[8] = skipColor;
+ }
+
+ inline bool getRemap() const {
+ return READ_SCI11ENDIAN_UINT16(_data + 10) & kBitmapRemap;
+ }
+
+ inline void setRemap(const bool remap) {
+ uint16 flags = READ_SCI11ENDIAN_UINT16(_data + 10);
+ if (remap) {
+ flags |= kBitmapRemap;
+ } else {
+ flags &= ~kBitmapRemap;
+ }
+ WRITE_SCI11ENDIAN_UINT16(_data + 10, flags);
+ }
+
+ BITMAP_PROPERTY(32, DataSize, 12);
+
+ inline uint32 getHunkPaletteOffset() const {
+ return READ_SCI11ENDIAN_UINT32(_data + 20);
+ }
+
+ inline void setHunkPaletteOffset(uint32 hunkPaletteOffset) {
+ if (hunkPaletteOffset) {
+ hunkPaletteOffset += getBitmapHeaderSize();
+ }
+
+ WRITE_SCI11ENDIAN_UINT32(_data + 20, hunkPaletteOffset);
+ }
+
+ BITMAP_PROPERTY(32, DataOffset, 24);
+
+ // NOTE: This property is used as a "magic number" for
+ // validating that a block of memory is a valid bitmap,
+ // and so is always set to the size of the header.
+ BITMAP_PROPERTY(32, UncompressedDataOffset, 28);
+
+ // NOTE: This property always seems to be zero
+ BITMAP_PROPERTY(32, ControlOffset, 32);
+
+ inline uint16 getScaledWidth() const {
+ if (getDataOffset() >= 40) {
+ return READ_SCI11ENDIAN_UINT16(_data + 36);
+ }
+
+ // SCI2 bitmaps did not have scaling ability
+ return 320;
+ }
+
+ inline void setScaledWidth(uint16 scaledWidth) {
+ if (getDataOffset() >= 40) {
+ WRITE_SCI11ENDIAN_UINT16(_data + 36, scaledWidth);
+ }
+ }
+
+ inline uint16 getScaledHeight() const {
+ if (getDataOffset() >= 40) {
+ return READ_SCI11ENDIAN_UINT16(_data + 38);
+ }
+
+ // SCI2 bitmaps did not have scaling ability
+ return 200;
+ }
+
+ inline void setScaledHeight(uint16 scaledHeight) {
+ if (getDataOffset() >= 40) {
+ WRITE_SCI11ENDIAN_UINT16(_data + 38, scaledHeight);
+ }
+ }
+
+ inline byte *getPixels() {
+ return _data + getUncompressedDataOffset();
+ }
+
+ inline byte *getHunkPalette() {
+ if (getHunkPaletteOffset() == 0) {
+ return nullptr;
+ }
+ return _data + getHunkPaletteOffset();
+ }
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
+};
+
+struct BitmapTable : public SegmentObjTable<SciBitmap *> {
+ BitmapTable() : SegmentObjTable<SciBitmap *>(SEG_TYPE_BITMAP) {}
+
+ virtual ~BitmapTable() {
+ for (uint i = 0; i < _table.size(); i++) {
+ if (isValidEntry(i)) {
+ freeEntryContents(i);
+ }
+ }
+ }
+
+ int allocEntry() {
+ int offset = SegmentObjTable<SciBitmap *>::allocEntry();
+ at(offset) = new SciBitmap;
+ return offset;
+ }
+
+ void freeEntryContents(const int offset) {
+ delete at(offset);
+ at(offset) = nullptr;
+ }
+
+ virtual void freeEntry(const int offset) override {
+ SegmentObjTable<SciBitmap *>::freeEntry(offset);
+ freeEntryContents(offset);
+ }
+
+ virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) override {
+ freeEntry(sub_addr.getOffset());
+ }
+
+ SegmentRef dereference(reg_t pointer) {
+ SegmentRef ret;
+ ret.isRaw = true;
+ ret.maxSize = at(pointer.getOffset())->getRawSize();
+ ret.raw = at(pointer.getOffset())->getRawData();
+ return ret;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &ser);
+};
+
#endif
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index f304f774af..9b3b329418 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -492,7 +492,7 @@ const SciWorkaroundEntry kDisplay_workarounds[] = {
{ GID_PQ2, 23, 23, 0, "rm23Script", "elements", sig_kDisplay_pq2_1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id - bug #5223
{ GID_QFG1, 11, 11, 0, "battle", "init", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
{ GID_SQ4, 397, 0, 0, "", "export 12", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227
- { GID_SQ4, 391, 391, 0, "doCatalog", "mode", sig_kDisplay_sq4_1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
+ { GID_SQ4, 391, 391, 0, "doCatalog", "changeState", sig_kDisplay_sq4_1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
{ GID_SQ4, 391, 391, 0, "choosePlug", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object
SCI_WORKAROUNDENTRY_TERMINATOR
};