[Scummvm-git-logs] scummvm master -> 757aea8ed64ff4131002916bb47aec062e7b3237

bluegr noreply at scummvm.org
Wed May 11 05:59:12 UTC 2022


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
dd1ba7c85c TINSEL: Implement a container for accessing the INV_OBJECTs
1839780513 TINSEL: Convert inventory object attributes to enum class.
757aea8ed6 TINSEL: Implement basic menu support (NOIR)


Commit: dd1ba7c85c9867cba24c2c4efe4a0edbad5c1248
    https://github.com/scummvm/scummvm/commit/dd1ba7c85c9867cba24c2c4efe4a0edbad5c1248
Author: Einar Johan Trøan Sømåen (einarjohants at gmail.com)
Date: 2022-05-11T08:59:07+03:00

Commit Message:
TINSEL: Implement a container for accessing the INV_OBJECTs

This way we encapsulate the iteration, thus making sure that
the appropriate stride is used (Noir has more fields).

Changed paths:
  A engines/tinsel/inv_objects.cpp
  A engines/tinsel/inv_objects.h
    engines/tinsel/dialogs.cpp
    engines/tinsel/dialogs.h
    engines/tinsel/module.mk
    engines/tinsel/noir/notebook.cpp
    engines/tinsel/pcode.cpp
    engines/tinsel/pcode.h
    engines/tinsel/tinlib.cpp
    engines/tinsel/tinsel.cpp


diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp
index 170af0f7f16..a2f84b2d046 100644
--- a/engines/tinsel/dialogs.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -743,7 +743,7 @@ enum {
 
 /*-------------------------------------------------------------------------*/
 
-static void InvTinselEvent(INV_OBJECT *pinvo, TINSEL_EVENT event, PLR_EVENT be, int index);
+static void InvTinselEvent(const InventoryObject *pinvo, TINSEL_EVENT event, PLR_EVENT be, int index);
 static void InvPdProcess(CORO_PARAM, const void *param);
 
 Dialogs::Dialogs() {
@@ -756,7 +756,6 @@ Dialogs::Dialogs() {
 	memset(_configStrings, 0, sizeof(_configStrings));
 
 	_invObjects = nullptr;
-	_numObjects = 0;
 	_invFilms = nullptr;
 	_noLanguage = false;
 
@@ -843,6 +842,7 @@ Dialogs::Dialogs() {
 }
 
 Dialogs::~Dialogs() {
+	delete _invObjects;
 	if (_objArray[0] != NULL) {
 		DumpObjArray();
 		DumpDobjArray();
@@ -1067,43 +1067,31 @@ void Dialogs::DumpObjArray() {
  * Convert item ID number to pointer to item's compiled data
  * i.e. Image data and Glitter code.
  */
-INV_OBJECT *Dialogs::GetInvObject(int id) {
-	INV_OBJECT *pObject = _invObjects;
-
-	for (int i = 0; i < _numObjects; i++, pObject++) {
-		if (pObject->id == id)
-			return pObject;
+const InventoryObject *Dialogs::GetInvObject(int id) {
+	auto object = _invObjects->GetInvObject(id);
+	if (!object) {
+		error("GetInvObject(%d): Trying to manipulate undefined inventory icon", id);
 	}
-
-	error("GetInvObject(%d): Trying to manipulate undefined inventory icon", id);
+	return object;
 }
 
 /**
  * Returns true if the given id represents a valid inventory object
  */
 bool Dialogs::GetIsInvObject(int id) {
-	INV_OBJECT *pObject = _invObjects;
-
-	for (int i = 0; i < _numObjects; i++, pObject++) {
-		if (pObject->id == id)
-			return true;
-	}
-
-	return false;
+	int index = _invObjects->GetObjectIndexIfExists(id);
+	return index != -1;
 }
 
 /**
  * Convert item ID number to index.
  */
 int Dialogs::GetObjectIndex(int id) {
-	INV_OBJECT *pObject = _invObjects;
-
-	for (int i = 0; i < _numObjects; i++, pObject++) {
-		if (pObject->id == id)
-			return i;
+	int index = _invObjects->GetObjectIndexIfExists(id);
+	if (index == -1) {
+		error("GetObjectIndex(%d): Trying to manipulate undefined inventory icon", id);
 	}
-
-	error("GetObjectIndex(%d): Trying to manipulate undefined inventory icon", id);
+	return index;
 }
 
 /**
@@ -1156,9 +1144,9 @@ void Dialogs::InventoryIconCursor(bool bNewItem) {
 				int objIndex = GetObjectIndex(_heldItem);
 
 				if (TinselVersion == 3) {
-					INV_OBJECT *invObj = GetInvObject(_heldItem);
+					auto invObj = GetInvObject(_heldItem);
 
-					if (invObj->attribute & V3ATTR_X200) {
+					if (invObj->getAttribute() & V3ATTR_X200) {
 						_heldFilm = _vm->_systemReel->Get((SysReel)objIndex);
 					} else {
 						_heldFilm = _invFilms[objIndex];
@@ -1169,8 +1157,8 @@ void Dialogs::InventoryIconCursor(bool bNewItem) {
 			}
 			_vm->_cursor->SetAuxCursor(_heldFilm);
 		} else {
-			INV_OBJECT *invObj = GetInvObject(_heldItem);
-			_vm->_cursor->SetAuxCursor(invObj->hIconFilm);
+			auto invObj = GetInvObject(_heldItem);
+			_vm->_cursor->SetAuxCursor(invObj->getIconFilm());
 		}
 	}
 }
@@ -1461,7 +1449,6 @@ void Dialogs::ClearInventory(int invno) {
 void Dialogs::AddToInventory(int invno, int icon, bool hold) {
 	int i;
 	bool bOpen;
-	INV_OBJECT *invObj;
 
 	// Validate trying to add to a legal inventory
 	assert(invno == INV_1 || invno == INV_2 || invno == INV_3 || invno == INV_CONV || invno == INV_OPEN || (invno == INV_DEFAULT && TinselVersion >= 2));
@@ -1477,10 +1464,10 @@ void Dialogs::AddToInventory(int invno, int icon, bool hold) {
 		bOpen = false;
 
 		if ((TinselVersion >= 2) && invno == INV_DEFAULT) {
-			invObj = GetInvObject(icon);
-			if (invObj->attribute & DEFINV2)
+			auto invObj = GetInvObject(icon);
+			if (invObj->getAttribute() & DEFINV2)
 				invno = INV_2;
-			else if (invObj->attribute & DEFINV1)
+			else if (invObj->getAttribute() & DEFINV1)
 				invno = INV_1;
 			else
 				invno = SysVar(SV_DEFAULT_INV);
@@ -1507,8 +1494,8 @@ void Dialogs::AddToInventory(int invno, int icon, bool hold) {
 
 					// Count how many current contents have end attribute
 					for (i = 0, nei = 0; i < _invD[INV_CONV].NoofItems; i++) {
-						invObj = GetInvObject(_invD[INV_CONV].contents[i]);
-						if (invObj->attribute & CONVENDITEM)
+						auto invObj = GetInvObject(_invD[INV_CONV].contents[i]);
+						if (invObj->getAttribute() & CONVENDITEM)
 							nei++;
 					}
 
@@ -1592,8 +1579,6 @@ bool Dialogs::RemFromInventory(int invno, int icon) {
  * If the item is not already held, hold it.
  */
 void Dialogs::HoldItem(int item, bool bKeepFilm) {
-	INV_OBJECT *invObj;
-
 	if (_heldItem != item) {
 		if ((TinselVersion >= 2) && (_heldItem != INV_NOICON)) {
 			// No longer holding previous item
@@ -1602,14 +1587,14 @@ void Dialogs::HoldItem(int item, bool bKeepFilm) {
 			// If old held object is not in an inventory, and
 			// has a default, stick it in its default inventory.
 			if (!IsInInventory(_heldItem, INV_1) && !IsInInventory(_heldItem, INV_2)) {
-				invObj = GetInvObject(_heldItem);
+				auto invObj = GetInvObject(_heldItem);
 
-				if (invObj->attribute & DEFINV1)
+				if (invObj->getAttribute() & DEFINV1)
 					AddToInventory(INV_1, _heldItem);
-				else if (invObj->attribute & DEFINV2)
+				else if (invObj->getAttribute() & DEFINV2)
 					AddToInventory(INV_2, _heldItem);
 				else {
-					if ((TinselVersion < 3) || (!(invObj->attribute & V3ATTR_X200) && !(invObj->attribute & V3ATTR_X400))) {
+					if ((TinselVersion < 3) || (!(invObj->getAttribute() & V3ATTR_X200) && !(invObj->getAttribute() & V3ATTR_X400))) {
 						// Hook for definable default inventory
 						AddToInventory(INV_1, _heldItem);
 					}
@@ -1621,8 +1606,8 @@ void Dialogs::HoldItem(int item, bool bKeepFilm) {
 				_vm->_cursor->DelAuxCursor(); // no longer aux cursor
 
 			if (item != INV_NOICON) {
-				invObj = GetInvObject(item);
-				_vm->_cursor->SetAuxCursor(invObj->hIconFilm); // and is aux. cursor
+				auto invObj = GetInvObject(item);
+				_vm->_cursor->SetAuxCursor(invObj->getIconFilm()); // and is aux. cursor
 			}
 
 			// WORKAROUND: If a held item is being removed that's not in either inventory (i.e. it was picked up
@@ -2056,7 +2041,6 @@ void Dialogs::InvBoxes(bool InBody, int curX, int curY) {
  */
 void Dialogs::InvLabels(bool InBody, int aniX, int aniY) {
 	int index; // Icon pointed to on this call
-	INV_OBJECT *invObj;
 
 	// Find out which icon is currently pointed to
 	if (!InBody)
@@ -2077,8 +2061,8 @@ void Dialogs::InvLabels(bool InBody, int aniX, int aniY) {
 		_pointedIcon = INV_NOICON;
 	} else if (index != _pointedIcon) {
 		// A new icon is pointed to - run its script with POINTED event
-		invObj = GetInvObject(index);
-		if (invObj->hScript)
+		auto invObj = GetInvObject(index);
+		if (invObj->getScript())
 			InvTinselEvent(invObj, POINTED, PLR_NOEVENT, index);
 		_pointedIcon = index;
 	}
@@ -2157,8 +2141,8 @@ void Dialogs::AdjustTop() {
  * Insert an inventory icon object onto the display list.
  */
 OBJECT *Dialogs::AddInvObject(int num, const FREEL **pfreel, const FILM **pfilm) {
-	INV_OBJECT *invObj = GetInvObject(num);
-	const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(invObj->hIconFilm);
+	auto invObj = GetInvObject(num);
+	const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(invObj->getIconFilm());
 	const FREEL *pfr = (const FREEL *)&pFilm->reels[0];
 	const MULTI_INIT *pmi = (MULTI_INIT *)_vm->_handle->LockMem(FROM_32(pfr->mobj));
 	OBJECT *pPlayObj; // The object we insert
@@ -4556,8 +4540,6 @@ void Dialogs::InvPutDown(int index) {
 }
 
 void Dialogs::InvPickup(int index) {
-	INV_OBJECT *invObj;
-
 	// Do nothing if not clicked on anything
 	if (index == NOOBJECT)
 		return;
@@ -4566,22 +4548,22 @@ void Dialogs::InvPickup(int index) {
 	if (_heldItem == INV_NOICON && _invD[_activeInv].contents[index] &&
 	    ((TinselVersion <= 1) || _invD[_activeInv].contents[index] != _heldItem)) {
 		// Pick-up
-		invObj = GetInvObject(_invD[_activeInv].contents[index]);
+		auto invObj = GetInvObject(_invD[_activeInv].contents[index]);
 		_thisIcon = _invD[_activeInv].contents[index];
 		if (TinselVersion >= 2)
 			InvTinselEvent(invObj, PICKUP, INV_PICKUP, index);
-		else if (invObj->hScript)
+		else if (invObj->getScript())
 			InvTinselEvent(invObj, WALKTO, INV_PICKUP, index);
 
 	} else if (_heldItem != INV_NOICON) {
 		// Put-down
-		invObj = GetInvObject(_heldItem);
+		auto invObj = GetInvObject(_heldItem);
 
 		// If DROPCODE set, send event, otherwise it's a putdown
-		if (invObj->attribute & IO_DROPCODE && invObj->hScript)
+		if (invObj->getAttribute() & IO_DROPCODE && invObj->getScript())
 			InvTinselEvent(invObj, PUTDOWN, INV_PICKUP, index);
 
-		else if (!(invObj->attribute & IO_ONLYINV1 && _activeInv != INV_1) && !(invObj->attribute & IO_ONLYINV2 && _activeInv != INV_2)) {
+		else if (!(invObj->getAttribute() & IO_ONLYINV1 && _activeInv != INV_1) && !(invObj->getAttribute() & IO_ONLYINV2 && _activeInv != INV_2)) {
 			if (TinselVersion >= 2)
 				InvPutDown(index);
 			else
@@ -4679,7 +4661,7 @@ void Dialogs::InvWalkTo(const Common::Point &coOrds) {
 
 void Dialogs::InvAction() {
 	int index;
-	INV_OBJECT *invObj;
+	const InventoryObject *invObj;
 	int aniX, aniY;
 	int i;
 
@@ -4700,7 +4682,7 @@ void Dialogs::InvAction() {
 					invObj = GetInvObject(_invD[_activeInv].contents[index]);
 					if (TinselVersion >= 2)
 						_thisIcon = _invD[_activeInv].contents[index];
-					if ((TinselVersion >= 2) || (invObj->hScript))
+					if ((TinselVersion >= 2) || (invObj->getScript()))
 						InvTinselEvent(invObj, ACTION, INV_ACTION, index);
 				}
 			}
@@ -4763,7 +4745,6 @@ void Dialogs::InvAction() {
 
 void Dialogs::InvLook(const Common::Point &coOrds) {
 	int index;
-	INV_OBJECT *invObj;
 	Common::Point pt = coOrds;
 
 	switch (InvArea(pt.x, pt.y)) {
@@ -4771,8 +4752,8 @@ void Dialogs::InvLook(const Common::Point &coOrds) {
 		index = InvItem(pt, false);
 		if (index != INV_NOICON) {
 			if (_invD[_activeInv].contents[index] && _invD[_activeInv].contents[index] != _heldItem) {
-				invObj = GetInvObject(_invD[_activeInv].contents[index]);
-				if (invObj->hScript)
+				auto invObj = GetInvObject(_invD[_activeInv].contents[index]);
+				if (invObj->getScript())
 					InvTinselEvent(invObj, LOOK, INV_LOOK, index);
 			}
 		}
@@ -4939,10 +4920,7 @@ void Dialogs::EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) {
  * Changes (permanently) the animation film for that object.
  */
 void Dialogs::SetObjectFilm(int object, SCNHANDLE hFilm) {
-	INV_OBJECT *invObj;
-
-	invObj = GetInvObject(object);
-	invObj->hIconFilm = hFilm;
+	_invObjects->SetObjectFilm(object, hFilm);
 
 	if (_heldItem != object)
 		_ItemsChanged = true;
@@ -4978,7 +4956,7 @@ void Dialogs::syncInvInfo(Common::Serializer &s) {
 	}
 
 	if (TinselVersion >= 2) {
-		for (int i = 0; i < _numObjects; ++i)
+		for (int i = 0; i < _invObjects->numObjects(); ++i)
 			s.syncAsUint32LE(_invFilms[i]);
 		s.syncAsUint32LE(_heldFilm);
 	}
@@ -4994,45 +4972,31 @@ void Dialogs::syncInvInfo(Common::Serializer &s) {
  */
 // Note: the SCHANDLE type here has been changed to a void*
 void Dialogs::RegisterIcons(void *cptr, int num) {
-	_numObjects = num;
-	_invObjects = (INV_OBJECT *)cptr;
-
-	if (TinselVersion == 0) {
-		// In Tinsel 0, the INV_OBJECT structure doesn't have an attributes field, so we
-		// need to 'unpack' the source structures into the standard Tinsel v1/v2 format
-		MEM_NODE *node = MemoryAllocFixed(_numObjects * sizeof(INV_OBJECT));
-		assert(node);
-		_invObjects = (INV_OBJECT *)MemoryDeref(node);
-		assert(_invObjects);
-		byte *srcP = (byte *)cptr;
-		INV_OBJECT *destP = _invObjects;
-
-		for (int i = 0; i < num; ++i, ++destP, srcP += 12) {
-			memmove(destP, srcP, 12);
-			destP->attribute = 0;
-		}
-	} else if (TinselVersion >= 2) {
+	int numObjects = num;
+	_invObjects = InstantiateInventoryObjects((const byte*)cptr, numObjects);
+	if (TinselVersion >= 2) {
 		if (_invFilms == NULL) {
 			// First time - allocate memory
-			MEM_NODE *node = MemoryAllocFixed(_numObjects * sizeof(SCNHANDLE));
+			MEM_NODE *node = MemoryAllocFixed(numObjects * sizeof(SCNHANDLE));
 			assert(node);
 			_invFilms = (SCNHANDLE *)MemoryDeref(node);
 			if (_invFilms == NULL)
 				error(NO_MEM, "inventory scripts");
-			memset(_invFilms, 0, _numObjects * sizeof(SCNHANDLE));
+			memset(_invFilms, 0, numObjects * sizeof(SCNHANDLE));
 		}
 
 		// Add defined permanent conversation icons
 		// and store all the films separately
-		int i;
-		INV_OBJECT *pio;
-		for (i = 0, pio = _invObjects; i < _numObjects; i++, pio++) {
-			if (pio->attribute & PERMACONV)
-				PermaConvIcon(pio->id, pio->attribute & CONVENDITEM);
+		for (int i = 0; i < numObjects; i++) {
+			auto pio = _invObjects->GetObjectByIndex(i);
+			if (pio->getAttribute() & PERMACONV)
+				PermaConvIcon(pio->getId(), pio->getAttribute() & CONVENDITEM);
 
-			_invFilms[i] = pio->hIconFilm;
+			_invFilms[i] = pio->getIconFilm();
 		}
 	}
+
+
 }
 
 /**
@@ -5572,7 +5536,7 @@ extern void InventoryProcess(CORO_PARAM, const void *) {
 /**************************************************************************/
 
 struct OP_INIT {
-	INV_OBJECT *pinvo;
+	const InventoryObject *pinvo;
 	TINSEL_EVENT event;
 	PLR_EVENT bev;
 	int myEscape;
@@ -5596,7 +5560,7 @@ static void ObjectProcess(CORO_PARAM, const void *param) {
 	if (TinselVersion <= 1)
 		CORO_INVOKE_1(AllowDclick, to->bev);
 
-	_ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->hScript, to->event, NOPOLY, 0, to->pinvo,
+	_ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->getScript(), to->event, NOPOLY, 0, to->pinvo,
 	                                 to->myEscape);
 	CORO_INVOKE_1(Interpret, _ctx->pic);
 
@@ -5606,7 +5570,7 @@ static void ObjectProcess(CORO_PARAM, const void *param) {
 			CORO_SLEEP(1);
 			int x, y;
 			_vm->_cursor->GetCursorXY(&x, &y, false);
-			if (_vm->_dialogs->InvItemId(x, y) != to->pinvo->id)
+			if (_vm->_dialogs->InvItemId(x, y) != to->pinvo->getId())
 				break;
 
 			// Fix the 'repeated pressing bug'
@@ -5614,7 +5578,7 @@ static void ObjectProcess(CORO_PARAM, const void *param) {
 				CORO_KILL_SELF();
 		}
 
-		_ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->hScript, UNPOINT, NOPOLY, 0, to->pinvo);
+		_ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->getScript(), UNPOINT, NOPOLY, 0, to->pinvo);
 		CORO_INVOKE_1(Interpret, _ctx->pic);
 	}
 
@@ -5624,10 +5588,10 @@ static void ObjectProcess(CORO_PARAM, const void *param) {
 /**
  * Run inventory item's Glitter code
  */
-static void InvTinselEvent(INV_OBJECT *pinvo, TINSEL_EVENT event, PLR_EVENT be, int index) {
+static void InvTinselEvent(const InventoryObject *pinvo, TINSEL_EVENT event, PLR_EVENT be, int index) {
 	OP_INIT to = {pinvo, event, be, 0};
 
-	if (_vm->_dialogs->InventoryIsHidden() || ((TinselVersion >= 2) && !pinvo->hScript))
+	if (_vm->_dialogs->InventoryIsHidden() || ((TinselVersion >= 2) && !pinvo->getScript()))
 		return;
 
 	_vm->_dialogs->_glitterIndex = index;
@@ -5638,7 +5602,7 @@ extern void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, i
 	// COROUTINE
 	CORO_BEGIN_CONTEXT;
 	Common::PROCESS *pProc;
-	INV_OBJECT *pInvo;
+	const InventoryObject *pInvo;
 	OP_INIT op;
 	CORO_END_CONTEXT(_ctx);
 
@@ -5647,7 +5611,7 @@ extern void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, i
 	if (result)
 		*result = false;
 	_ctx->pInvo = _vm->_dialogs->GetInvObject(objId);
-	if (!_ctx->pInvo->hScript)
+	if (!_ctx->pInvo->getScript())
 		return;
 
 	_ctx->op.pinvo = _ctx->pInvo;
diff --git a/engines/tinsel/dialogs.h b/engines/tinsel/dialogs.h
index bcd71187fc0..ac6b93dc85a 100644
--- a/engines/tinsel/dialogs.h
+++ b/engines/tinsel/dialogs.h
@@ -26,6 +26,7 @@
 
 #include "tinsel/dw.h"
 #include "tinsel/events.h"	// for PLR_EVENT, PLR_EVENT
+#include "tinsel/inv_objects.h"
 #include "tinsel/object.h"
 #include "tinsel/movers.h"
 
@@ -143,24 +144,6 @@ enum CONFTYPE {
 	TOP_WINDOW
 };
 
-/** structure of each inventory object */
-struct INV_OBJECT {
-	int32 id;		// inventory objects id
-	SCNHANDLE hIconFilm;	// inventory objects animation film
-	SCNHANDLE hScript;	// inventory objects event handling script
-	int32 attribute;		// inventory object's attribute
-
-	// TODO: Commented out because there are variables
-	// with this struct type that are cast from memory blobs,
-	// so this breaks DW1 and DW2. We need to read these
-	// struct members individually instead of casting the blobs
-	// to this struct
-
-	// Noir
-	//int32 unknown;
-	//int32 title;	// id of associated notebook title
-};
-
 struct INV_DEF {
 	int MinHicons; // }
 	int MinVicons; // } Dimension limits
@@ -379,7 +362,7 @@ public:
 	void Select(int i, bool force);
 	void FillInInventory();
 	void InvCursor(InvCursorFN fn, int CurX, int CurY);
-	INV_OBJECT *GetInvObject(int id);
+	const InventoryObject *GetInvObject(int id);
 	bool UpdateString(const Common::KeyState &kbd);
 	bool InventoryIsActive() { return _inventoryState == ACTIVE_INV; }
 	bool IsMixingDeskControl() { return _invDragging == ID_MDCONT; }
@@ -473,8 +456,7 @@ private:
 
 	INV_DEF _invD[MAX_NUM_INV];        // Conversation + 2 inventories + ...
 	int _activeInv;                      // Which inventory is currently active
-	INV_OBJECT *_invObjects; // Inventory objects' data
-	int _numObjects;               // Number of inventory objects
+	InventoryObjects *_invObjects; // Inventory objects' data
 	SCNHANDLE *_invFilms;
 	DIRECTION _initialDirection;
 
diff --git a/engines/tinsel/inv_objects.cpp b/engines/tinsel/inv_objects.cpp
new file mode 100644
index 00000000000..af0ba5f7c73
--- /dev/null
+++ b/engines/tinsel/inv_objects.cpp
@@ -0,0 +1,124 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "tinsel/inv_objects.h"
+#include "common/memstream.h"
+#include "tinsel/tinsel.h"
+
+namespace Tinsel {
+
+/** structure of each inventory object */
+int32 InventoryObject::getUnknown() const {
+	error("Querying Noir-value from non-Noir game");
+}
+
+int32 InventoryObject::getTitle() const {
+	error("Querying Noir-value from non-Noir game");
+}
+
+class InventoryObjectT1 : public InventoryObject {
+public:
+	InventoryObjectT1(Common::MemoryReadStreamEndian &stream) : InventoryObject(stream) {
+		_attribute = stream.readUint32();
+	}
+	// Tinsel1+
+	virtual int32 getAttribute() const {
+		return _attribute;
+	};
+	static const int SIZE = InventoryObject::SIZE + 4;
+private:
+	int32 _attribute;
+};
+
+class InventoryObjectT3 : public InventoryObjectT1 {
+public:
+	InventoryObjectT3(Common::MemoryReadStreamEndian &stream) : InventoryObjectT1(stream) {
+		_unknown = stream.readUint32();
+		_title = stream.readUint32();
+	}
+	// Noir:
+	virtual int32 getUnknown() const {
+		return _unknown;
+	}
+	virtual int32 getTitle() const {
+		return _title;
+	}
+	static const int SIZE = InventoryObjectT1::SIZE + 8;
+private:
+	int32 _unknown;
+	int32 _title;
+};
+
+template<typename T>
+class InventoryObjectsImpl : public InventoryObjects {
+public:
+	InventoryObjectsImpl(const byte *objects, int numObjects) {
+		bool bigEndian = (TinselV1Mac || TinselV1Saturn);
+		auto stream = new Common::MemoryReadStreamEndian(objects, T::SIZE * numObjects, bigEndian, DisposeAfterUse::NO);
+		for (int i = 0; i < numObjects; i++) {
+			_objects.push_back(T(*stream));
+		}
+		assert((!stream->eos()) && stream->pos() == stream->size());
+		delete stream;
+	}
+	~InventoryObjectsImpl(){};
+	const InventoryObject *GetInvObject(int id) {
+		auto index = GetObjectIndexIfExists(id);
+		if (index != -1) {
+			return _objects.data() + index;
+		}
+		return nullptr;
+	}
+	const InventoryObject *GetObjectByIndex(int index) const {
+		assert(index >= 0 && index < numObjects());
+		return _objects.data() + index;
+	}
+	void SetObjectFilm(int id, SCNHANDLE hFilm) {
+		int index = GetObjectIndexIfExists(id);
+		_objects[index].setIconFilm(hFilm);
+	}
+	int GetObjectIndexIfExists(int id) const {
+		for (int i = 0; i < _objects.size(); i++) {
+			if (_objects[i].getId() == id) {
+				return i;
+			}
+		}
+		return -1;
+	};
+	int numObjects() const {
+		return _objects.size();
+	}
+private:
+	Common::Array<T> _objects;
+};
+
+InventoryObjects *InstantiateInventoryObjects(const byte *invObjects, int numObjects) {
+	switch (TinselVersion) {
+	case 0:
+		return new InventoryObjectsImpl<InventoryObject>(invObjects, numObjects);
+	case 3:
+		return new InventoryObjectsImpl<InventoryObjectT3>(invObjects, numObjects);
+	default:
+		return new InventoryObjectsImpl<InventoryObjectT1>(invObjects, numObjects);
+	}
+}
+
+} // End of namespace Tinsel
diff --git a/engines/tinsel/inv_objects.h b/engines/tinsel/inv_objects.h
new file mode 100644
index 00000000000..a4bb84c8adf
--- /dev/null
+++ b/engines/tinsel/inv_objects.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+#ifndef TINSEL_INV_OBJECT_H
+#define TINSEL_INV_OBJECT_H
+
+#include "common/memstream.h"
+#include "tinsel/dw.h"
+
+namespace Tinsel {
+
+class InventoryObject {
+public:
+	InventoryObject(Common::MemoryReadStreamEndian &stream) {
+		_id = stream.readUint32();
+		_hIconFilm = stream.readUint32();
+		_hScript = stream.readUint32();
+	}
+	virtual ~InventoryObject() {}
+	int32 getId() const { return _id; }
+	SCNHANDLE getIconFilm() const { return _hIconFilm; };
+	void setIconFilm(SCNHANDLE hIconFilm) { _hIconFilm = hIconFilm; }
+	SCNHANDLE getScript() const { return _hScript; }
+	// Tinsel1+
+	virtual int32 getAttribute() const {
+		return 0;
+	};
+	// Noir:
+	virtual int32 getUnknown() const;
+	virtual int32 getTitle() const;
+	static const int SIZE = 12;
+private:
+	int32 _id;            // inventory objects id
+	SCNHANDLE _hIconFilm; // inventory objects animation film
+	SCNHANDLE _hScript;   // inventory objects event handling script
+};
+
+class InventoryObjects {
+public:
+	virtual ~InventoryObjects() {};
+	virtual const InventoryObject *GetInvObject(int id) = 0;
+	virtual const InventoryObject *GetObjectByIndex(int index) const = 0;
+	virtual void SetObjectFilm(int id, SCNHANDLE hFilm) = 0;
+	virtual int GetObjectIndexIfExists(int id) const = 0;
+	virtual int numObjects() const = 0;
+};
+
+InventoryObjects *InstantiateInventoryObjects(const byte *invObjects, int numObjects);
+
+} // End of namespace Tinsel
+
+#endif // TINSEL_INV_OBJECT_H
diff --git a/engines/tinsel/module.mk b/engines/tinsel/module.mk
index cfbe50200d9..7edd3eacb3d 100644
--- a/engines/tinsel/module.mk
+++ b/engines/tinsel/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS := \
 	graphics.o \
 	handle.o \
 	heapmem.o \
+	inv_objects.o \
 	mareels.o \
 	metaengine.o \
 	move.o \
diff --git a/engines/tinsel/noir/notebook.cpp b/engines/tinsel/noir/notebook.cpp
index 8a0d5879c85..b8022c0319d 100644
--- a/engines/tinsel/noir/notebook.cpp
+++ b/engines/tinsel/noir/notebook.cpp
@@ -26,21 +26,19 @@
 namespace Tinsel {
 
 void Notebook::AddHyperlink(int32 id1, int32 id2) {
-#if 0
-	INV_OBJECT *invObject = _vm->_dialogs->GetInvObject(id1);
+	auto *invObject = _vm->_dialogs->GetInvObject(id1);
 
-	if (invObject->title != 0) {
+	if (invObject->getTitle() != 0) {
 		error("A clue can only be hyperlinked if it only has one title!");
 		return;
 	}
 
 	invObject = _vm->_dialogs->GetInvObject(id2);
 
-	if (invObject->title != 0) {
+	if (invObject->getTitle() != 0) {
 		error("A clue can only be hyperlinked if it only has one title!");
 		return;
 	}
-#endif
 
 	uint32 i;
 	for (i = 0; i < MAX_HYPERS; ++i) {
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index 49d7b992642..7b61f9ee520 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -385,7 +385,7 @@ void FreeMasterInterpretContext() {
  * @param pinvo			Associated inventory object
  */
 INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode,	TINSEL_EVENT event,
-		HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) {
+		HPOLYGON hpoly, int actorid, const InventoryObject *pinvo, int myEscape) {
 	INT_CONTEXT *ic;
 
 	ic = AllocateInterpretContext(gsort);
diff --git a/engines/tinsel/pcode.h b/engines/tinsel/pcode.h
index 519781c669d..4ecc9f12029 100644
--- a/engines/tinsel/pcode.h
+++ b/engines/tinsel/pcode.h
@@ -32,8 +32,7 @@ class Serializer;
 
 namespace Tinsel {
 
-// forward declaration
-struct INV_OBJECT;
+class InventoryObject;
 
 enum RESUME_STATE {
 	RES_NOT, RES_1, RES_2, RES_SAVEGAME
@@ -64,7 +63,7 @@ struct INT_CONTEXT {
 	TINSEL_EVENT	event;		///< causal event
 	HPOLYGON	hPoly;		///< associated polygon (if any)
 	int			idActor;	///< associated actor (if any)
-	INV_OBJECT	*pinvo;		///< associated inventory object
+	const InventoryObject	*pinvo;		///< associated inventory object
 
 	// Previously local variables in Interpret()
 	int32 stack[PCODE_STACK_SIZE];	///< interpeters run time stack
@@ -99,7 +98,7 @@ INT_CONTEXT *InitInterpretContext(
 	TINSEL_EVENT	event,		// causal event
 	HPOLYGON	hpoly,		// associated polygon (if any)
 	int		actorid,	// associated actor (if any)
-	INV_OBJECT	*pinvo,
+	const InventoryObject	*pinvo,
 	int myEscape = -1);		// associated inventory object
 
 INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric);
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 5617adf151d..508f3b825f0 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -1520,7 +1520,7 @@ void Offset(EXTREME extreme, int x, int y) {
 /**
  * OtherObject()
  */
-int OtherObject(INV_OBJECT *pinvo) {
+int OtherObject(const InventoryObject *pinvo) {
 	assert(pinvo != NULL);
 
 	// return held object or object clicked on - whichever is not the calling object
@@ -1529,9 +1529,9 @@ int OtherObject(INV_OBJECT *pinvo) {
 	// WhichItemHeld() gives the held object
 	// GetIcon() gives the object clicked on
 
-	assert(_vm->_dialogs->GetIcon() == pinvo->id || _vm->_dialogs->WhichItemHeld() == pinvo->id);
+	assert(_vm->_dialogs->GetIcon() == pinvo->getId() || _vm->_dialogs->WhichItemHeld() == pinvo->getId());
 
-	if (_vm->_dialogs->GetIcon() == pinvo->id)
+	if (_vm->_dialogs->GetIcon() == pinvo->getId())
 		return _vm->_dialogs->WhichItemHeld();
 	else
 		return _vm->_dialogs->GetIcon();
@@ -2101,13 +2101,13 @@ static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSust
 }
 
 
-static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
+static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const InventoryObject *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
 static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText);
 
 /**
  * Print the given inventory object's name or whatever.
  */
-static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo, const int event, int myEscape) {
+static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const InventoryObject *pinvo, const int event, int myEscape) {
 	CORO_BEGIN_CONTEXT;
 		OBJECT *pText;		// text object pointer
 		int	textx, texty;
@@ -2219,7 +2219,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
 				int x, y;
 				do {
 					// Give up if this item gets picked up
-					if (_vm->_dialogs->WhichItemHeld() == pinvo->id)
+					if (_vm->_dialogs->WhichItemHeld() == pinvo->getId())
 						break;
 
 					// Give way to non-POINTED-generated text
@@ -2250,7 +2250,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
 					// Carry on until the cursor leaves this icon
 					_vm->_cursor->GetCursorXY(&x, &y, false);
 
-				} while (_vm->_dialogs->InvItemId(x, y) == pinvo->id);
+				} while (_vm->_dialogs->InvItemId(x, y) == pinvo->getId());
 			} else {
 				/*
 				 * PrintObj() called from other event
@@ -2324,7 +2324,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
 	CORO_END_CODE;
 }
 
-static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
+static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const InventoryObject *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
 	CORO_BEGIN_CONTEXT;
 	CORO_END_CONTEXT(_ctx);
 
@@ -2334,7 +2334,7 @@ static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
 		int	x, y;
 		do {
 			// Give up if this item gets picked up
-		    if (_vm->_dialogs->WhichItemHeld() == pinvo->id)
+		    if (_vm->_dialogs->WhichItemHeld() == pinvo->getId())
 				break;
 
 			// Give way to non-POINTED-generated text
@@ -2360,7 +2360,7 @@ static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
 
 			// Carry on until the cursor leaves this icon
 		    _vm->_cursor->GetCursorXY(&x, &y, false);
-	    } while (_vm->_dialogs->InvItemId(x, y) == pinvo->id);
+	    } while (_vm->_dialogs->InvItemId(x, y) == pinvo->getId());
 
 	CORO_END_CODE;
 }
@@ -3677,10 +3677,10 @@ static void TalkVia(int actor) {
 /**
  * ThisObject
  */
-static int ThisObject(INV_OBJECT *pinvo) {
+static int ThisObject(const InventoryObject *pinvo) {
 	assert(pinvo != NULL);
 
-	return pinvo->id;
+	return pinvo->getId();
 }
 
 /**
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 0fd70a370d3..13daadb9006 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -809,16 +809,6 @@ void LoadBasicChunks() {
 	RegisterGlobals(game.numGlobals);
 
 	cptr = FindChunk(INV_OBJ_SCNHANDLE, CHUNK_OBJECTS);
-
-	// Convert to native endianness
-	INV_OBJECT *io = (INV_OBJECT *)cptr;
-	for (int i = 0; i < game.numObjects; i++, io++) {
-		io->id        = FROM_32(io->id);
-		io->hIconFilm = FROM_32(io->hIconFilm);
-		io->hScript   = FROM_32(io->hScript);
-		io->attribute = FROM_32(io->attribute);
-	}
-
 	_vm->_dialogs->RegisterIcons(cptr, game.numObjects);
 
 	// Max polygons are 0 in the original DW1 V0 demo and in DW1 Mac (both in the demo and the full version)


Commit: 1839780513c05ad7b3e7cbc98a36e546021f8222
    https://github.com/scummvm/scummvm/commit/1839780513c05ad7b3e7cbc98a36e546021f8222
Author: Einar Johan Trøan Sømåen (einarjohants at gmail.com)
Date: 2022-05-11T08:59:07+03:00

Commit Message:
TINSEL: Convert inventory object attributes to enum class.

Changed paths:
    engines/tinsel/dialogs.cpp
    engines/tinsel/dialogs.h
    engines/tinsel/inv_objects.cpp
    engines/tinsel/inv_objects.h


diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp
index a2f84b2d046..8875c3a24bf 100644
--- a/engines/tinsel/dialogs.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -72,13 +72,6 @@ namespace Tinsel {
 #define INV_LOOK PLR_SRIGHT  //	for button events
 #define INV_ACTION PLR_DLEFT //
 
-/** attribute values - may become bit field if further attributes are added */
-enum {
-	IO_ONLYINV1 = 0x01,
-	IO_ONLYINV2 = 0x02,
-	IO_DROPCODE = 0x04
-};
-
 //-----------------------
 // Moveable window translucent rectangle position limits
 enum {
@@ -1146,7 +1139,7 @@ void Dialogs::InventoryIconCursor(bool bNewItem) {
 				if (TinselVersion == 3) {
 					auto invObj = GetInvObject(_heldItem);
 
-					if (invObj->getAttribute() & V3ATTR_X200) {
+					if (invObj->hasAttribute(InvObjAttr::V3ATTR_X200)) {
 						_heldFilm = _vm->_systemReel->Get((SysReel)objIndex);
 					} else {
 						_heldFilm = _invFilms[objIndex];
@@ -1465,9 +1458,9 @@ void Dialogs::AddToInventory(int invno, int icon, bool hold) {
 
 		if ((TinselVersion >= 2) && invno == INV_DEFAULT) {
 			auto invObj = GetInvObject(icon);
-			if (invObj->getAttribute() & DEFINV2)
+			if (invObj->hasAttribute(InvObjAttr::DEFINV2))
 				invno = INV_2;
-			else if (invObj->getAttribute() & DEFINV1)
+			else if (invObj->hasAttribute(InvObjAttr::DEFINV1))
 				invno = INV_1;
 			else
 				invno = SysVar(SV_DEFAULT_INV);
@@ -1495,7 +1488,7 @@ void Dialogs::AddToInventory(int invno, int icon, bool hold) {
 					// Count how many current contents have end attribute
 					for (i = 0, nei = 0; i < _invD[INV_CONV].NoofItems; i++) {
 						auto invObj = GetInvObject(_invD[INV_CONV].contents[i]);
-						if (invObj->getAttribute() & CONVENDITEM)
+						if (invObj->hasAttribute(InvObjAttr::CONVENDITEM))
 							nei++;
 					}
 
@@ -1589,12 +1582,12 @@ void Dialogs::HoldItem(int item, bool bKeepFilm) {
 			if (!IsInInventory(_heldItem, INV_1) && !IsInInventory(_heldItem, INV_2)) {
 				auto invObj = GetInvObject(_heldItem);
 
-				if (invObj->getAttribute() & DEFINV1)
+				if (invObj->hasAttribute(InvObjAttr::DEFINV1))
 					AddToInventory(INV_1, _heldItem);
-				else if (invObj->getAttribute() & DEFINV2)
+				else if (invObj->hasAttribute(InvObjAttr::DEFINV2))
 					AddToInventory(INV_2, _heldItem);
 				else {
-					if ((TinselVersion < 3) || (!(invObj->getAttribute() & V3ATTR_X200) && !(invObj->getAttribute() & V3ATTR_X400))) {
+					if ((TinselVersion < 3) || (!(invObj->hasAttribute(InvObjAttr::V3ATTR_X200)) && !(invObj->hasAttribute(InvObjAttr::V3ATTR_X400)))) {
 						// Hook for definable default inventory
 						AddToInventory(INV_1, _heldItem);
 					}
@@ -4560,10 +4553,10 @@ void Dialogs::InvPickup(int index) {
 		auto invObj = GetInvObject(_heldItem);
 
 		// If DROPCODE set, send event, otherwise it's a putdown
-		if (invObj->getAttribute() & IO_DROPCODE && invObj->getScript())
+		if (invObj->hasAttribute(InvObjAttr::IO_DROPCODE) && invObj->getScript())
 			InvTinselEvent(invObj, PUTDOWN, INV_PICKUP, index);
 
-		else if (!(invObj->getAttribute() & IO_ONLYINV1 && _activeInv != INV_1) && !(invObj->getAttribute() & IO_ONLYINV2 && _activeInv != INV_2)) {
+		else if (!(invObj->hasAttribute(InvObjAttr::IO_ONLYINV1) && _activeInv != INV_1) && !(invObj->hasAttribute(InvObjAttr::IO_ONLYINV2) && _activeInv != INV_2)) {
 			if (TinselVersion >= 2)
 				InvPutDown(index);
 			else
@@ -4989,8 +4982,8 @@ void Dialogs::RegisterIcons(void *cptr, int num) {
 		// and store all the films separately
 		for (int i = 0; i < numObjects; i++) {
 			auto pio = _invObjects->GetObjectByIndex(i);
-			if (pio->getAttribute() & PERMACONV)
-				PermaConvIcon(pio->getId(), pio->getAttribute() & CONVENDITEM);
+			if (pio->hasAttribute(InvObjAttr::PERMACONV))
+				PermaConvIcon(pio->getId(), pio->hasAttribute(InvObjAttr::CONVENDITEM));
 
 			_invFilms[i] = pio->getIconFilm();
 		}
diff --git a/engines/tinsel/dialogs.h b/engines/tinsel/dialogs.h
index ac6b93dc85a..808bb1a39d3 100644
--- a/engines/tinsel/dialogs.h
+++ b/engines/tinsel/dialogs.h
@@ -97,22 +97,6 @@ enum InventoryType { EMPTY,
 enum InvCursorFN { IC_AREA,
 	               IC_DROP };
 
-// attribute values - not a bit bit field to prevent portability problems
-#define DROPCODE 0x01
-#define ONLYINV1 0x02
-#define ONLYINV2 0x04
-#define DEFINV1 0x08
-#define DEFINV2 0x10
-#define PERMACONV 0x20
-#define CONVENDITEM 0x40
-// Noir only
-#define V3ATTR_X80 0x80
-#define V3ATTR_X200 0x200
-#define V3ATTR_X400 0x400
-#define NOTEBOOK_TITLE 0x800 // is a notebook title
-#define V3ATTR_X1000 0x1000
-#define V3ATTR_X2000 0x2000
-
 #define sliderRange (_sliderYmax - _sliderYmin)
 #define MAXSLIDES 4
 #define MAX_PERMICONS 10 // Max permanent conversation icons
diff --git a/engines/tinsel/inv_objects.cpp b/engines/tinsel/inv_objects.cpp
index af0ba5f7c73..917dfc0ba81 100644
--- a/engines/tinsel/inv_objects.cpp
+++ b/engines/tinsel/inv_objects.cpp
@@ -25,7 +25,12 @@
 
 namespace Tinsel {
 
-/** structure of each inventory object */
+InventoryObject::InventoryObject(Common::MemoryReadStreamEndian &stream) {
+	_id = stream.readUint32();
+	_hIconFilm = stream.readUint32();
+	_hScript = stream.readUint32();
+}
+
 int32 InventoryObject::getUnknown() const {
 	error("Querying Noir-value from non-Noir game");
 }
diff --git a/engines/tinsel/inv_objects.h b/engines/tinsel/inv_objects.h
index a4bb84c8adf..daa9fb5a45d 100644
--- a/engines/tinsel/inv_objects.h
+++ b/engines/tinsel/inv_objects.h
@@ -27,26 +27,46 @@
 
 namespace Tinsel {
 
+// attribute values - not a bit bit field to prevent portability problems
+enum class InvObjAttr {
+	IO_DROPCODE = 0x01,
+	IO_ONLYINV1 = 0x02,
+	IO_ONLYINV2 = 0x04,
+	DEFINV1 = 0x08,
+	DEFINV2 = 0x10,
+	PERMACONV = 0x20,
+	CONVENDITEM = 0x40,
+
+	// Noir only
+	V3ATTR_X80 = 0x80,
+	V3ATTR_X200 = 0x200,
+	V3ATTR_X400 = 0x400,
+	NOTEBOOK_TITLE = 0x800, // is a notebook title
+	V3ATTR_X1000 = 0x1000,
+	V3ATTR_X2000 = 0x2000,
+};
+
 class InventoryObject {
 public:
-	InventoryObject(Common::MemoryReadStreamEndian &stream) {
-		_id = stream.readUint32();
-		_hIconFilm = stream.readUint32();
-		_hScript = stream.readUint32();
-	}
+	InventoryObject(Common::MemoryReadStreamEndian &stream);
 	virtual ~InventoryObject() {}
 	int32 getId() const { return _id; }
 	SCNHANDLE getIconFilm() const { return _hIconFilm; };
 	void setIconFilm(SCNHANDLE hIconFilm) { _hIconFilm = hIconFilm; }
 	SCNHANDLE getScript() const { return _hScript; }
 	// Tinsel1+
-	virtual int32 getAttribute() const {
-		return 0;
-	};
+	bool hasAttribute(InvObjAttr attribute) const {
+		return getAttribute() & (int32)attribute;
+	}
 	// Noir:
 	virtual int32 getUnknown() const;
 	virtual int32 getTitle() const;
 	static const int SIZE = 12;
+protected:
+	// Tinsel 1+
+	virtual int32 getAttribute() const {
+		return 0;
+	};
 private:
 	int32 _id;            // inventory objects id
 	SCNHANDLE _hIconFilm; // inventory objects animation film


Commit: 757aea8ed64ff4131002916bb47aec062e7b3237
    https://github.com/scummvm/scummvm/commit/757aea8ed64ff4131002916bb47aec062e7b3237
Author: Einar Johan Trøan Sømåen (einarjohants at gmail.com)
Date: 2022-05-11T08:59:07+03:00

Commit Message:
TINSEL: Implement basic menu support (NOIR)

Changed paths:
    engines/tinsel/dialogs.cpp
    engines/tinsel/dialogs.h
    engines/tinsel/dw.h
    engines/tinsel/multiobj.cpp
    engines/tinsel/multiobj.h
    engines/tinsel/noir/sysreel.h


diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp
index 8875c3a24bf..54951a8ae61 100644
--- a/engines/tinsel/dialogs.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -158,6 +158,9 @@ enum PARTS_INDEX {
 	IX2_RIGHT1 = 40,
 	IX2_RIGHT2 = 41,
 
+	IX3_TICK = 27,
+	IX3_CROSS = 28,
+
 	T1_HOPEDFORREELS = 50,
 	T2_HOPEDFORREELS = 42
 };
@@ -196,7 +199,7 @@ enum PARTS_INDEX {
 #define ITEM_HEIGHT ((TinselVersion >= 2) ? 50 : 25) //
 #define I_SEPARATION ((TinselVersion >= 2) ? 2 : 1)  // Item separation
 
-#define NM_TOFF 11                // Title text Y offset from top
+#define NM_TOFF ((TinselVersion == 3) ? 21 : 11) // Title text Y offset from top
 #define NM_TBT ((TinselVersion >= 2) ? 4 : 0) // Y, title box top
 #define NM_TBB 33
 #define NM_LSX ((TinselVersion >= 2) ? 4 : 0) // X, left side
@@ -331,6 +334,9 @@ struct CONFINIT {
 #define BW 44 // Width of crosses and ticks etc. buttons
 #define BH 41 // Height of crosses and ticks etc. buttons
 
+#define BW_T3 49
+#define BH_T3 45
+
 /*-------------------------------------------------------------*\
 | This is the main menu (that comes up when you hit F1 on a PC)	|
 \*-------------------------------------------------------------*/
@@ -349,10 +355,13 @@ struct CONFINIT {
 
 #define BOXX 56 // X-position of text boxes
 #define BOXY 50 // Y-position of text boxes
+#define T3_BOXX 60
+#define T3_BOXY 69
 #define T2_OPTX 33
 #define T2_OPTY 36
 #define T2_BOX_V_SEP 12
 #define T2_BOX_V2_SEP 6
+#define T3_BOX_V2_SEP 7
 
 static CONFBOX t1OptionBox[] = {
 
@@ -387,8 +396,32 @@ static CONFBOX t2OptionBox[] = {
 
 static CONFINIT t2ciOption = {6, 4, 144, 60, false, t2OptionBox, sizeof(t2OptionBox) / sizeof(CONFBOX), NO_HEADING};
 
-#define ciOption ((TinselVersion >= 2) ? t2ciOption : t1ciOption)
-#define optionBox ((TinselVersion >= 2) ? t2OptionBox : t1OptionBox)
+static CONFBOX t3OptionBox[] = {
+	{ARSBUT, OPENLOAD, TM_INDEX, NULL, SS_LOAD_OPTION, T2_OPTX, T2_OPTY, T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{ARSBUT, OPENSAVE, TM_INDEX, NULL, SS_SAVE_OPTION, T2_OPTX, T2_OPTY + (T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{ARSBUT, OPENREST, TM_INDEX, NULL, SS_RESTART_OPTION, T2_OPTX, T2_OPTY + 2 * (T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{ARSBUT, OPENSOUND, TM_INDEX, NULL, SS_SOUND_OPTION, T2_OPTX, T2_OPTY + 3 * (T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{ARSBUT, OPENQUIT, TM_INDEX, NULL, SS_QUIT_OPTION, T2_OPTX, T2_OPTY + 4 * (T2_BOX_HEIGHT + T2_BOX_V_SEP), T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0}
+};
+
+static CONFINIT t3ciOption = {6, 4, 144, 60, false, t3OptionBox, sizeof(t3OptionBox) / sizeof(CONFBOX), NO_HEADING};
+
+static CONFINIT* ciOptionLookup[] = {
+	&t1ciOption,
+	&t1ciOption,
+	&t2ciOption,
+	&t3ciOption
+};
+
+static CONFBOX* ciOptionBoxLookup[] = {
+	t1OptionBox,
+	t1OptionBox,
+	t2OptionBox,
+	t3OptionBox
+};
+
+#define ciOption (*ciOptionLookup[TinselVersion])
+#define optionBox (ciOptionBoxLookup[TinselVersion])
 
 /*-------------------------------------------------------------*\
 | These are the load and save game menus.			|
@@ -433,8 +466,22 @@ static CONFBOX t2LoadBox[] = {
 	{ARSGBUT, LOADGAME, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1},
 	{AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100 + 100, BW, BH, NULL, IX2_CROSS1}};
 
+static CONFBOX t3LoadBox[] = {
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY, T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 2 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 3 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 4 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 5 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 6 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, LOADGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 7 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+
+	{AABUT, LOADGAME, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1},
+	{AATBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100 + 100, BW, BH, NULL, IX2_CROSS1}};
+
 static CONFINIT t1ciLoad = {10, 6, 20, 16, true, t1LoadBox, ARRAYSIZE(t1LoadBox), SIX_LOAD_HEADING};
 static CONFINIT t2ciLoad = {10, 6, 40, 16, true, t2LoadBox, sizeof(t2LoadBox) / sizeof(CONFBOX), SS_LOAD_HEADING};
+static CONFINIT t3ciLoad = {10, 6, 40, 16, true, t3LoadBox, sizeof(t3LoadBox) / sizeof(CONFBOX), SS_LOAD_HEADING};
 
 static CONFBOX t1SaveBox[NUM_RGROUP_BOXES + 2] = {
 	{RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28, SY, EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0},
@@ -465,13 +512,55 @@ static CONFBOX t2SaveBox[] = {
 	{ARSGBUT, SAVEGAME, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1},
 	{AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100 + 100, BW, BH, NULL, IX2_CROSS1}};
 
+static CONFBOX t3SaveBox[] = {
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY, T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 2 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 3 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 4 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 5 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 6 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+	{RGROUP, SAVEGAME, TM_POINTER, NULL, 0, T3_BOXX, T3_BOXY + 7 * (T2_BOX_HEIGHT + T3_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0},
+
+	{AABUT, SAVEGAME, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1},
+	{AATBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100 + 100, BW, BH, NULL, IX2_CROSS1}};
+
 static CONFINIT t1ciSave = {10, 6, 20, 16, true, t1SaveBox, ARRAYSIZE(t1SaveBox), SIX_SAVE_HEADING};
 static CONFINIT t2ciSave = {10, 6, 40, 16, true, t2SaveBox, sizeof(t2SaveBox) / sizeof(CONFBOX), SS_SAVE_HEADING};
+static CONFINIT t3ciSave = {10, 6, 40, 16, true, t3SaveBox, sizeof(t3SaveBox) / sizeof(CONFBOX), SS_SAVE_HEADING};
+
+static CONFINIT* ciLoadLookup[] = {
+	&t1ciLoad,
+	&t1ciLoad,
+	&t2ciLoad,
+	&t3ciLoad
+};
+
+static CONFBOX* ciLoadBoxLookup[] = {
+	t1LoadBox,
+	t1LoadBox,
+	t2LoadBox,
+	t3LoadBox
+};
+
+static CONFINIT* ciSaveLookup[] = {
+	&t1ciSave,
+	&t1ciSave,
+	&t2ciSave,
+	&t3ciSave
+};
+
+static CONFBOX* ciSaveBoxLookup[] = {
+	t1SaveBox,
+	t1SaveBox,
+	t2SaveBox,
+	t3SaveBox
+};
 
-#define ciLoad ((TinselVersion >= 2) ? t2ciLoad : t1ciLoad)
-#define loadBox ((TinselVersion >= 2) ? t2LoadBox : t1LoadBox)
-#define ciSave ((TinselVersion >= 2) ? t2ciSave : t1ciSave)
-#define saveBox ((TinselVersion >= 2) ? t2SaveBox : t1SaveBox)
+#define ciLoad (*ciLoadLookup[TinselVersion])
+#define loadBox (ciLoadBoxLookup[TinselVersion])
+#define ciSave (*ciSaveLookup[TinselVersion])
+#define saveBox (ciSaveBoxLookup[TinselVersion])
 
 /*-------------------------------------------------------------*\
 | This is the restart confirmation 'menu'.			|
@@ -495,6 +584,10 @@ static CONFBOX t2RestartBox[] = {
 	{AAGBUT, INITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1},
 	{AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1}};
 
+static CONFBOX t3RestartBox[] = {
+	{AATBUT, INITGAME, TM_UNK4, NULL, 0, 140, 64, BW_T3, BH_T3, NULL, IX2_TICK1},
+	{AATBUT, CLOSEWIN, TM_UNK4, NULL, 0, 60, 64, BW_T3, BH_T3, NULL, IX2_CROSS1}};
+
 #ifdef JAPAN
 static CONFINIT t1ciRestart = {6, 2, 72, 53, false, t1RestartBox, ARRAYSIZE(t1RestartBox), SIX_RESTART_HEADING};
 #else
@@ -502,8 +595,16 @@ static CONFINIT t1ciRestart = {4, 2, 98, 53, false, t1RestartBox, ARRAYSIZE(t1Re
 #endif
 static CONFINIT t1ciRestartPSX = {8, 2, 46, 53, false, t1RestartBoxPSX, ARRAYSIZE(t1RestartBoxPSX), SIX_RESTART_HEADING};
 static CONFINIT t2ciRestart = {4, 2, 196, 53, false, t2RestartBox, sizeof(t2RestartBox) / sizeof(CONFBOX), SS_RESTART_HEADING};
+static CONFINIT t3ciRestart = {4, 2, 196, 53, false, t3RestartBox, sizeof(t3RestartBox) / sizeof(CONFBOX), SS_RESTART_HEADING};
+
+static CONFINIT* ciRestartLookup[] = {
+	&t1ciRestart,
+	&t1ciRestart,
+	&t2ciRestart,
+	&t3ciRestart
+};
 
-#define ciRestart ((TinselVersion >= 2) ? t2ciRestart : (TinselV1PSX ? t1ciRestartPSX : t1ciRestart))
+#define ciRestart (TinselV1PSX ? t1ciRestartPSX : *ciRestartLookup[TinselVersion])
 
 /*-------------------------------------------------------------*\
 | This is the sound control 'menu'. In Discworld 2, it also		|
@@ -524,10 +625,27 @@ static CONFBOX t2SoundBox[] = {
 	{TOGGLE2, NOFUNC, TM_INDEX, NULL, SS_STITLE_TOGGLE, 100, 220, BW, BH, 0 /*&_vm->_config->_useSubtitles*/, 0},
 	{ROTATE, NOFUNC, TM_INDEX, NULL, SS_LANGUAGE_SELECT, 320, 220, BW, BH, NULL, 0}};
 
+static CONFBOX t3SoundBox[] = {
+	{ARSGBUT, MUSICVOL, TM_INDEX, NULL, SS_MVOL_SLIDER, 280, 50, 100, 2, /*&_vm->_config->_musicVolume*/ 0, 0},
+	{ARSGBUT, NOFUNC, TM_INDEX, NULL, SS_SVOL_SLIDER, 280, 50 + 30 * 1, 100, 2, /*&_vm->_config->_soundVolume*/ 0, 0},
+	{ARSGBUT, NOFUNC, TM_INDEX, NULL, SS_VVOL_SLIDER, 280, 50 + 30 * 2, 100, 2, /*&_vm->_config->_voiceVolume*/ 0, 0},
+	{ARSGBUT, NOFUNC, TM_INDEX, NULL, SS_TSPEED_SLIDER, 280, 160, 100, 2, /*&_vm->_config->_textSpeed*/ 0, 0},
+	{SLIDER, NOFUNC, TM_INDEX, NULL, SS_STITLE_TOGGLE, 100, 220, BW_T3, BH_T3, /*&_vm->_config->_useSubtitles*/ 0, 0}, // Should have type 7
+	{TOGGLE1, NOFUNC, TM_INDEX, NULL, SS_LANGUAGE_SELECT, 320, 220, BW_T3, BH_T3, NULL, 0}
+};
+
 static CONFINIT t1ciSound = {10, 5, 20, 16, false, t1SoundBox, ARRAYSIZE(t1SoundBox), NO_HEADING};
 static CONFINIT t2ciSound = {10, 5, 40, 16, false, t2SoundBox, sizeof(t2SoundBox) / sizeof(CONFBOX), SS_SOUND_HEADING};
+static CONFINIT t3ciSound = {10, 5, 40, 16, false, t3SoundBox, sizeof(t3SoundBox) / sizeof(CONFBOX), SS_SOUND_HEADING};
 
-#define ciSound ((TinselVersion >= 2) ? t2ciSound : t1ciSound)
+static CONFINIT* ciSoundLookup[] = {
+	&t1ciSound,
+	&t1ciSound,
+	&t2ciSound,
+	&t3ciSound
+};
+
+#define ciSound (*ciSoundLookup[TinselVersion])
 
 /*-------------------------------------------------------------*\
 | This is the (mouse) control 'menu'.				|
@@ -610,11 +728,22 @@ static CONFBOX t2QuitBox[] = {
 	{AAGBUT, IQUITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1},
 	{AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78, BW, BH, NULL, IX2_CROSS1}};
 
+static CONFBOX t3QuitBox[] = {
+	{AATBUT, IQUITGAME, TM_NONE, NULL, 0, 140, 64, BW_T3, BH_T3, NULL, IX3_CROSS},
+	{AATBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 64, BW_T3, BH_T3, NULL, IX3_TICK}};
+
 static CONFINIT t1ciQuit = {4, 2, 98, 53, false, t1QuitBox, ARRAYSIZE(t1QuitBox), SIX_QUIT_HEADING};
 static CONFINIT t2ciQuit = {4, 2, 196, 53, false, t2QuitBox, sizeof(t2QuitBox) / sizeof(CONFBOX), SS_QUIT_HEADING};
+static CONFINIT t3ciQuit = {4, 2, 196, 53, false, t3QuitBox, sizeof(t3QuitBox) / sizeof(CONFBOX), SS_QUIT_HEADING};
 
-#define quitBox ((TinselVersion >= 2) ? t2QuitBox : t1QuitBox)
-#define ciQuit ((TinselVersion >= 2) ? t2ciQuit : t1ciQuit)
+static CONFINIT* ciQuitLookup[] = {
+	&t1ciQuit,
+	&t1ciQuit,
+	&t2ciQuit,
+	&t3ciQuit
+};
+
+#define ciQuit (*ciQuitLookup[TinselVersion])
 
 /***************************************************************************\
 |************************    Startup and shutdown    ***********************|
@@ -2201,23 +2330,26 @@ enum { FROM_HANDLE,
  * Set up a rectangle as the background to the inventory window.
  *  Additionally, sticks the window title up.
  */
-void Dialogs::AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV, int textFrom) {
+void Dialogs::AddBackground(OBJECT **rect, const Common::Rect &bounds, OBJECT **title, int textFrom) {
 	// Why not 2 ????
-	int width = _TLwidth + extraH + _TRwidth + NM_BG_SIZ_X;
-	int height = _TLheight + extraV + _BLheight + NM_BG_SIZ_Y;
+	int width = bounds.width();
+	int height = bounds.height();
 
 	// Create a rectangle object
 	_rectObject = *rect = TranslucentObject(width, height);
 
 	// add it to display list and position it
 	MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), *rect);
-	MultiSetAniXYZ(*rect, _invD[_activeInv].inventoryX + NM_BG_POS_X,
-	               _invD[_activeInv].inventoryY + NM_BG_POS_Y,
-	               Z_INV_BRECT);
+	PositionInventory(*rect,
+	                  (TinselVersion < 3 ? NM_BG_POS_X : 0),
+	                  (TinselVersion < 3 ? NM_BG_POS_Y : 0),
+	                  Z_INV_BRECT);
 
 	if (title == NULL)
 		return;
 
+	assert(TinselVersion < 3);
+
 	// Create text object using title string
 	if (textFrom == FROM_HANDLE) {
 		LoadStringRes(_invD[_activeInv].hInvTitle, _vm->_font->TextBufferAddr(), TBUFSZ);
@@ -2236,13 +2368,6 @@ void Dialogs::AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extra
 	}
 }
 
-/**
- * Set up a rectangle as the background to the inventory window.
- */
-void Dialogs::AddBackground(OBJECT **rect, int extraH, int extraV) {
-	AddBackground(rect, NULL, extraH, extraV, 0);
-}
-
 Common::Rect MultiBounds(OBJECT *obj) {
 	Common::Rect bounds;
 	bounds.left = MultiLeftmost(obj);
@@ -2255,15 +2380,14 @@ Common::Rect MultiBounds(OBJECT *obj) {
 /**
  * Adds a title for a dialog
  */
-void Dialogs::AddTitle(OBJECT **title, int extraH) {
-	int width = _TLwidth + extraH + _TRwidth + NM_BG_SIZ_X;
-
-	// Create text object using title string
+void Dialogs::AddTitle(OBJECT **title, const Common::Rect &bounds) {
 	if (_invD[_activeInv].hInvTitle != (SCNHANDLE)NO_HEADING) {
 		LoadStringRes(_invD[_activeInv].hInvTitle, _vm->_font->TextBufferAddr(), TBUFSZ);
+		
+		int xOffset = (TinselVersion == 3) ? 0 : NM_BG_POS_X;
 		*title = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(), 0,
-		                       _invD[_activeInv].inventoryX + (width / 2) + NM_BG_POS_X, _invD[_activeInv].inventoryY + NM_TOFF,
-		                       _vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
+							   _invD[_activeInv].inventoryX + (bounds.width() / 2) + xOffset, _invD[_activeInv].inventoryY + NM_TOFF,
+							   _vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
 		assert(*title);
 		MultiSetZPosition(*title, Z_INV_HTEXT);
 	}
@@ -2658,6 +2782,87 @@ int Dialogs::AddExtraWindow(int x, int y, OBJECT **retObj) {
 	return n;
 }
 
+void Dialogs::ConstructInventoryCommon(SysReel reel, bool hasTitle) {
+	DumpObjArray();
+
+	// Get the frame's data
+	_objArray[0] = InsertSystemReelObj(reel);
+
+	// Center the inventory.
+	auto bounds = MultiBounds(_objArray[0]);
+	_invD[_activeInv].inventoryX = (SCREEN_WIDTH - bounds.width()) / 2;
+	_invD[_activeInv].inventoryY = (SCREEN_HEIGHT - bounds.height()) / 2;
+    PositionInventory(_objArray[0], 0, 0, Z_INV_MFRAME);
+	MultiSetZPosition(_objArray[0], 16);
+
+	AddBackground(&_objArray[1], bounds);
+	if (hasTitle) {
+		AddTitle(&_objArray[2], bounds);
+		if (_objArray[2]) {
+			// We currently skip this, as AddTitle still needs ObjTextOut updates.
+			warning("TODO: Align title");
+		}
+	}
+}
+
+void Dialogs::ConstructMainInventory() {
+	warning("TODO: Complete implementation of ConstructMainInventory");
+	ConstructInventoryCommon(SysReel::INVMAIN, false);
+	_invD[_activeInv].FirstDisp = 0;
+
+	// TODO: Slider, Scrolling
+
+	FillInInventory();
+}
+
+void Dialogs::PositionInventory(OBJECT *pMultiObj, int xOffset, int yOffset, int zPosition) {
+	MultiSetAniXYZ(pMultiObj, _invD[_activeInv].inventoryX + xOffset, _invD[_activeInv].inventoryY + yOffset, zPosition);
+}
+
+SysReel GetSysReelForMenu(int menuId) {
+	switch(menuId) {
+	case MAIN_MENU:
+		return SysReel::OPTIONS_MENU;
+		break;
+	case LOAD_MENU:
+	case SAVE_MENU:
+		return SysReel::LOADSAVE_MENU;
+		break;
+	case QUIT_MENU:
+		return SysReel::QUIT_MENU;
+		break;
+	case SOUND_MENU:
+		return SysReel::SUBTITLES_MENU;
+		break;
+	default:
+		error("Unknown menu: %d", menuId);
+	}
+}
+
+void Dialogs::ConstructConversationInventory() {
+	warning("TODO: Complete implementation of ConstructConversationInventory");
+	ConstructInventoryCommon(SysReel::CONVERSATION_FRAME, true);
+}
+
+void Dialogs::ConstructOtherInventory(int menuId) {
+	warning("TODO: Complete implementation of ConstructOtherInventory");
+	SysReel reel = GetSysReelForMenu(menuId);
+	ConstructInventoryCommon(reel, true);
+
+	if (cd.bExtraWin) {
+		warning("TODO: Complete scrollbar implementation");
+		SCNHANDLE sliderReel = _vm->_systemReel->Get(SysReel::SLIDER);
+		const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(sliderReel);
+		_objArray[3] = _slideObject = InsertReelObj(pfilm->reels);
+		MultiSetAniXYZ(_slideObject,
+					   _invD[_activeInv].inventoryX + 420,
+					   _sliderYpos,
+					   Z_INV_MFRAME - 1);
+	}
+	AddBoxes(true);
+
+}
+
 /**
  * Construct an inventory window - either a standard one, with
  * background, slider and icons, or a re-sizing window.
@@ -2682,10 +2887,7 @@ void Dialogs::ConstructInventory(InventoryType filling) {
 
 	// Dispose of anything it may be replacing
 	for (int i = 0; i < MAX_WCOMP; i++) {
-		if (retObj[i] != NULL) {
-			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), retObj[i]);
-			retObj[i] = nullptr;
-		}
+		MultiDeleteObjectIfExists(FIELD_STATUS, &retObj[i]);
 	}
 
 	// Get the frame's data
@@ -2831,10 +3033,13 @@ void Dialogs::ConstructInventory(InventoryType filling) {
 
 	OBJECT **rect, **title;
 
+	Common::Rect bounds;
+	bounds.right = _TLwidth + eH + _TRwidth + NM_BG_SIZ_X;
+	bounds.bottom = _TLheight + eV + _BLheight + NM_BG_SIZ_Y;
 	// Draw background, slider and icons
 	if ((TinselVersion >= 2) && (filling != EMPTY)) {
-		AddBackground(&retObj[n++], eH, eV);
-		AddTitle(&retObj[n++], eH);
+		AddBackground(&retObj[n++], bounds);
+		AddTitle(&retObj[n++], bounds);
 	}
 
 	if (filling == FULL) {
@@ -2842,7 +3047,7 @@ void Dialogs::ConstructInventory(InventoryType filling) {
 			rect = &retObj[n++];
 			title = &retObj[n++];
 
-			AddBackground(rect, title, eH, eV, FROM_HANDLE);
+			AddBackground(rect, bounds, title, FROM_HANDLE);
 		}
 
 		if (_activeInv == INV_CONV) {
@@ -2868,7 +3073,7 @@ void Dialogs::ConstructInventory(InventoryType filling) {
 			rect = &retObj[n++];
 			title = &retObj[n++];
 
-			AddBackground(rect, title, eH, eV, FROM_STRING);
+			AddBackground(rect, bounds, title, FROM_STRING);
 			if (cd.bExtraWin)
 				n += AddExtraWindow(invX, invY, &retObj[n]);
 		} else {
@@ -3341,7 +3546,7 @@ bool Dialogs::ConvIsHidden() {
 /**
  * Start up an inventory window.
  */
-void Dialogs::PopUpInventory(int invno) {
+void Dialogs::PopUpInventory(int invno, int menuId) {
 	assert(invno == INV_1 || invno == INV_2 || invno == INV_CONV || invno == INV_CONF || invno == INV_MENU); // Trying to open illegal inventory
 
 	if (_inventoryState == IDLE_INV) {
@@ -3376,17 +3581,36 @@ void Dialogs::PopUpInventory(int invno) {
 		_inventoryState = ACTIVE_INV; // Inventory actiive
 		_InventoryHidden = false;     // Not hidden
 		_InventoryMaximised = _invD[_activeInv].bMax;
-		if (invno != INV_CONF)        // Configuration window?
-			ConstructInventory(FULL); // Draw it up
-		else {
-			ConstructInventory(CONF); // Draw it up
+		if (TinselVersion == 3) {
+			switch (invno) {
+			case INV_CONV:
+				ConstructConversationInventory();
+				 break;
+			case INV_1:
+			case INV_2:
+			case INV_3:
+			case INV_4:
+				ConstructMainInventory();
+				break;
+			default: // Should be menu.
+				ConstructOtherInventory(menuId);
+				break;
+			}
+		} else {
+			if (invno != INV_CONF)        // Configuration window?
+				ConstructInventory(FULL); // Draw it up
+			else {
+				ConstructInventory(CONF); // Draw it up
+			}
 		}
 	}
 }
 
 void Dialogs::SetMenuGlobals(CONFINIT *ci) {
-	_invD[INV_CONF].MinHicons = _invD[INV_CONF].MaxHicons = _invD[INV_CONF].NoofHicons = ci->h;
-	_invD[INV_CONF].MaxVicons = _invD[INV_CONF].MinVicons = _invD[INV_CONF].NoofVicons = ci->v;
+	if (TinselVersion < 3) {
+		_invD[INV_CONF].MinHicons = _invD[INV_CONF].MaxHicons = _invD[INV_CONF].NoofHicons = ci->h;
+		_invD[INV_CONF].MaxVicons = _invD[INV_CONF].MinVicons = _invD[INV_CONF].NoofVicons = ci->v;
+	}
 	_invD[INV_CONF].inventoryX = ci->x;
 	_invD[INV_CONF].inventoryY = ci->y;
 	cd.bExtraWin = ci->bExtraWin;
@@ -3454,7 +3678,13 @@ void Dialogs::OpenMenu(CONFTYPE menuType) {
 			_displayedLanguage = TextLanguage();
 #if 1
 		// FIXME: Hack to setup CONFBOX pointer to data in the global Config object
-		if (TinselVersion >= 2) {
+		if (TinselVersion == 3) {
+			t3SoundBox[0].ival = &_vm->_config->_musicVolume;
+			t3SoundBox[1].ival = &_vm->_config->_soundVolume;
+			t3SoundBox[2].ival = &_vm->_config->_voiceVolume;
+			t3SoundBox[3].ival = &_vm->_config->_textSpeed;
+			t3SoundBox[4].ival = &_vm->_config->_useSubtitles;
+		} else if (TinselVersion >= 2) {
 			t2SoundBox[0].ival = &_vm->_config->_musicVolume;
 			t2SoundBox[1].ival = &_vm->_config->_soundVolume;
 			t2SoundBox[2].ival = &_vm->_config->_voiceVolume;
@@ -3550,7 +3780,7 @@ void Dialogs::OpenMenu(CONFTYPE menuType) {
 	if (_heldItem != INV_NOICON)
 		_vm->_cursor->DelAuxCursor(); // no longer aux cursor
 
-	PopUpInventory(INV_CONF);
+	PopUpInventory(INV_CONF, menuType);
 
 	// Make initial box selections if appropriate
 	if (menuType == SAVE_MENU || menuType == LOAD_MENU || menuType == HOPPER_MENU1 || menuType == HOPPER_MENU2)
diff --git a/engines/tinsel/dialogs.h b/engines/tinsel/dialogs.h
index 808bb1a39d3..9bba1f88786 100644
--- a/engines/tinsel/dialogs.h
+++ b/engines/tinsel/dialogs.h
@@ -47,7 +47,7 @@ enum {
 	INV_CONV 		= 0,
 	INV_1 			= 1,
 	INV_2 			= 2,
-	INV_CONF 		= 3,
+	DW0_INV_CONF	= 3,
 	INV_MENU 		= 3, // DW2 constant
 	NUM_INV_V0 		= 4,
 
@@ -58,7 +58,8 @@ enum {
 	// Noir constants
 	INV_3		 	= 3,
 	INV_4 		 	= 4,
-	NUM_INV_V3 	 	= 5,
+	NOIR_INV_CONF   = 5,
+	NUM_INV_V3 	 	= 6,
 	INV_7NOINV 	 	= 7,
 	INV_8NOINV 	 	= 8,
 	INV_NOTEBOOK 	= 9,
@@ -66,6 +67,7 @@ enum {
 	MAX_NUM_INV 	= NUM_INV_V3 // For determination of _invD array size
 };
 
+#define INV_CONF ((TinselVersion == 3) ? NOIR_INV_CONF : DW0_INV_CONF)
 #define NUM_INV ((TinselVersion == 3) ? NUM_INV_V3 : NUM_INV_V0)
 
 enum {
@@ -226,6 +228,7 @@ enum BFUNC {
 enum TM { TM_POINTER,
 	      TM_INDEX,
 	      TM_STRINGNUM,
+	      TM_UNK4,
 	      TM_NONE };
 
 // For SlideSlider() and similar
@@ -259,12 +262,14 @@ struct BUTTONEFFECT {
 	bool press; // true = button press; false = button toggle
 };
 
+enum class SysReel;
+
 class Dialogs {
 public:
 	Dialogs();
 	virtual ~Dialogs();
 
-	void PopUpInventory(int invno);
+	void PopUpInventory(int invno, int menuId = -1);
 	void OpenMenu(CONFTYPE type);
 
 	void Xmovement(int x);
@@ -394,14 +399,18 @@ private:
 	void InvLabels(bool InBody, int aniX, int aniY);
 	void AdjustTop();
 	OBJECT *AddInvObject(int num, const FREEL **pfreel, const FILM **pfilm);
-	void AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV, int textFrom);
-	void AddBackground(OBJECT **rect, int extraH, int extraV);
-	void AddTitle(OBJECT **title, int extraH);
+	void AddBackground(OBJECT **rect, const Common::Rect &bounds, OBJECT **title = nullptr, int textFrom = 0);
+	void AddTitle(OBJECT **title, const Common::Rect &rect);
 	void AddSlider(OBJECT **slide, const FILM *pfilm);
 	void AddBox(int *pi, const int i);
 	void AddEWSlider(OBJECT **slide, const FILM *pfilm);
+	void PositionInventory(OBJECT *pMultiObj, int xOffset, int yOffset, int zPosition);
 	int AddExtraWindow(int x, int y, OBJECT **retObj);
+	void ConstructInventoryCommon(SysReel reel, bool hasTitle);
+	void ConstructConversationInventory();
 	void ConstructInventory(InventoryType filling);
+	void ConstructOtherInventory(int menuId);
+	void ConstructMainInventory();
 	void AlterCursor(int num);
 	void SetMenuGlobals(CONFINIT *ci);
 	void CloseInventory();
diff --git a/engines/tinsel/dw.h b/engines/tinsel/dw.h
index 34f6130a0aa..c016b5b0d54 100644
--- a/engines/tinsel/dw.h
+++ b/engines/tinsel/dw.h
@@ -64,7 +64,7 @@ typedef int HPOLYGON;
 // i.e. it gets a Z position of 0
 
 #define Z_INV_BRECT	10	// Inventory background rectangle
-#define Z_INV_MFRAME	15	// Inventory window frame
+#define Z_INV_MFRAME	((TinselVersion == 3) ? 16 : 15)	// Inventory window frame
 #define Z_INV_HTEXT	15	// Inventory heading text
 #define Z_INV_ICONS	16	// Icons in inventory
 #define Z_INV_ITEXT	995	// Icon text
diff --git a/engines/tinsel/multiobj.cpp b/engines/tinsel/multiobj.cpp
index d0af57ee61a..1ec2c944b41 100644
--- a/engines/tinsel/multiobj.cpp
+++ b/engines/tinsel/multiobj.cpp
@@ -21,10 +21,12 @@
  */
 
 #include "tinsel/background.h"
+#include "tinsel/film.h"
 #include "tinsel/multiobj.h"
 #include "tinsel/handle.h"
 #include "tinsel/object.h"
 #include "tinsel/tinsel.h"
+#include "tinsel/noir/sysreel.h"
 
 namespace Tinsel {
 
@@ -83,6 +85,28 @@ OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) {
 	return pFirst;
 }
 
+OBJECT *InsertReelObj(const FREEL *reels) {
+	const MULTI_INIT *pmi = (const MULTI_INIT*)_vm->_handle->LockMem(reels->mobj);
+	// Verify that there is an image defined
+	const FRAME *frame = (const FRAME*)_vm->_handle->LockMem(pmi->hMulFrame);
+	const IMAGE *image = (const IMAGE*)_vm->_handle->LockMem(*frame);
+	assert(image);
+
+	auto pInsObj = MultiInitObject(pmi);
+	MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), pInsObj);
+	return pInsObj; // Result
+}
+
+const FILM *GetSystemReelFilm(SysReel reelIndex) {
+	SCNHANDLE hFilm = _vm->_systemReel->Get(reelIndex);
+	const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
+	return pfilm;
+}
+
+OBJECT *InsertSystemReelObj(SysReel reelIndex) {
+	return InsertReelObj(GetSystemReelFilm(reelIndex)->reels);
+}
+
 /**
  * Inserts the multi-part object onto the specified object list.
  * @param pObjList			List to insert multi-part object onto
diff --git a/engines/tinsel/multiobj.h b/engines/tinsel/multiobj.h
index 897b3872817..67efe282127 100644
--- a/engines/tinsel/multiobj.h
+++ b/engines/tinsel/multiobj.h
@@ -132,6 +132,13 @@ bool MultiHasShape(		  // Returns TRUE if the object currently has an image
 void MultiForceRedraw(
 	OBJECT *pMultiObj);	// multi-part object to be forced
 
+struct FREEL;
+OBJECT* InsertReelObj(const FREEL *reels);
+struct FILM;
+enum class SysReel;
+const FILM* GetSystemReelFilm(SysReel reelIndex);
+OBJECT* InsertSystemReelObj(SysReel reelIndex);
+
 } // End of namespace Tinsel
 
 #endif	// TINSEL_MULTIOBJ_H
diff --git a/engines/tinsel/noir/sysreel.h b/engines/tinsel/noir/sysreel.h
index b5f23c539ec..a003751fcb7 100644
--- a/engines/tinsel/noir/sysreel.h
+++ b/engines/tinsel/noir/sysreel.h
@@ -35,6 +35,7 @@ enum class SysReel {
 	CURSOR = 11,
 	INVMAIN = 15,
 	SLIDER = 16,
+	CONVERSATION_FRAME = 19,
 	OPTIONS_MENU = 21,
 	LOADSAVE_MENU = 22,
 	QUIT_MENU = 23,
@@ -52,6 +53,7 @@ public:
 
 	SCNHANDLE Get(SysReel index);
 	void Set(int32 index, SCNHANDLE reel);
+
 private:
 	const static int32 MAX_SYSREELS = 0x28;
 




More information about the Scummvm-git-logs mailing list