[Scummvm-git-logs] scummvm master -> 532828c143e0872525dadfe8b0ebe804f7071633

csnover csnover at users.noreply.github.com
Tue Mar 28 02:44:08 CEST 2017


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

Summary:
31daa956d6 SCI: Implement bounds-checked reads of game resources
6600fb77ea SCI: Use containers in GfxView and remove some SCI32 code
816296fd25 SCI: Remove unused boilerplate from SciSpan
323e74bda9 SCI: Simplify calculation of locals size in SCI0/1
532828c143 SCI: Remove old SCI32 check from SCI16 drawing code


Commit: 31daa956d62b39429cb6638ed3fb549ac488833a
    https://github.com/scummvm/scummvm/commit/31daa956d62b39429cb6638ed3fb549ac488833a
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-03-27T19:42:31-05:00

Commit Message:
SCI: Implement bounds-checked reads of game resources

Changed paths:
    engines/sci/console.cpp
    engines/sci/detection.cpp
    engines/sci/engine/features.cpp
    engines/sci/engine/kernel.cpp
    engines/sci/engine/kernel.h
    engines/sci/engine/kparse.cpp
    engines/sci/engine/kvideo.cpp
    engines/sci/engine/message.cpp
    engines/sci/engine/object.cpp
    engines/sci/engine/object.h
    engines/sci/engine/savegame.cpp
    engines/sci/engine/script.cpp
    engines/sci/engine/script.h
    engines/sci/engine/script_patches.cpp
    engines/sci/engine/script_patches.h
    engines/sci/engine/scriptdebug.cpp
    engines/sci/engine/seg_manager.cpp
    engines/sci/engine/workarounds.cpp
    engines/sci/graphics/animate.cpp
    engines/sci/graphics/celobj32.cpp
    engines/sci/graphics/celobj32.h
    engines/sci/graphics/compare.cpp
    engines/sci/graphics/cursor.cpp
    engines/sci/graphics/cursor.h
    engines/sci/graphics/cursor32.cpp
    engines/sci/graphics/font.cpp
    engines/sci/graphics/font.h
    engines/sci/graphics/maciconbar.cpp
    engines/sci/graphics/palette.cpp
    engines/sci/graphics/palette.h
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/palette32.h
    engines/sci/graphics/picture.cpp
    engines/sci/graphics/picture.h
    engines/sci/graphics/portrait.cpp
    engines/sci/graphics/portrait.h
    engines/sci/graphics/ports.cpp
    engines/sci/graphics/ports.h
    engines/sci/graphics/screen.cpp
    engines/sci/graphics/screen.h
    engines/sci/graphics/screen_item32.cpp
    engines/sci/graphics/view.cpp
    engines/sci/graphics/view.h
    engines/sci/parser/vocabulary.cpp
    engines/sci/parser/vocabulary.h
    engines/sci/resource.cpp
    engines/sci/resource.h
    engines/sci/resource_audio.cpp
    engines/sci/sci.cpp
    engines/sci/sound/audio.cpp
    engines/sci/sound/drivers/adlib.cpp
    engines/sci/sound/drivers/amigamac.cpp
    engines/sci/sound/drivers/cms.cpp
    engines/sci/sound/drivers/fb01.cpp
    engines/sci/sound/drivers/fmtowns.cpp
    engines/sci/sound/drivers/midi.cpp
    engines/sci/sound/midiparser_sci.cpp
    engines/sci/sound/music.cpp
    engines/sci/sound/sync.cpp
    engines/sci/util.cpp
    engines/sci/util.h


diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index ae8ab14..eed0be1 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -526,16 +526,16 @@ bool Console::cmdOpcodes(int argc, const char **argv) {
 		return true;
 	}
 
-	int count = READ_LE_UINT16(r->data);
+	int count = r->getUint16LEAt(0);
 
 	debugPrintf("Opcode names in numeric order [index: type name]:\n");
 
 	for (int i = 0; i < count; i++) {
-		int offset = READ_LE_UINT16(r->data + 2 + i * 2);
-		int len = READ_LE_UINT16(r->data + offset) - 2;
-		int type = READ_LE_UINT16(r->data + offset + 2);
+		int offset = r->getUint16LEAt(2 + i * 2);
+		int len = r->getUint16LEAt(offset) - 2;
+		int type = r->getUint16LEAt(offset + 2);
 		// QFG3 has empty opcodes
-		Common::String name = len > 0 ? Common::String((const char *)r->data + offset + 4, len) : "Dummy";
+		Common::String name = len > 0 ? r->getStringAt(offset + 4, len) : "Dummy";
 		debugPrintf("%03x: %03x %20s | ", i, type, name.c_str());
 		if ((i % 3) == 2)
 			debugPrintf("\n");
@@ -816,7 +816,7 @@ bool Console::cmdHexDump(int argc, const char **argv) {
 	else {
 		Resource *resource = _engine->getResMan()->findResource(ResourceId(res, resNum), 0);
 		if (resource) {
-			Common::hexdump(resource->data, resource->size, 16, 0);
+			Common::hexdump(resource->getUnsafeDataAt(0), resource->size(), 16, 0);
 			debugPrintf("Resource %s.%03d has been dumped to standard output\n", argv[1], resNum);
 		} else {
 			debugPrintf("Resource %s.%03d not found\n", argv[1], resNum);
@@ -954,7 +954,7 @@ bool Console::cmdResourceInfo(int argc, const char **argv) {
 	else {
 		Resource *resource = _engine->getResMan()->findResource(ResourceId(res, resNum), 0);
 		if (resource) {
-			debugPrintf("Resource size: %d\n", resource->size);
+			debugPrintf("Resource size: %lu\n", resource->size());
 			debugPrintf("Resource location: %s\n", resource->getResourceLocation().c_str());
 		} else {
 			debugPrintf("Resource %s.%03d not found\n", argv[1], resNum);
@@ -1015,8 +1015,8 @@ bool Console::cmdHexgrep(int argc, const char **argv) {
 			uint32 comppos = 0;
 			int output_script_name = 0;
 
-			while (seeker < script->size) {
-				if (script->data[seeker] == byteString[comppos]) {
+			while (seeker < script->size()) {
+				if (script->getUint8At(seeker) == byteString[comppos]) {
 					if (comppos == 0)
 						seekerold = seeker;
 
@@ -1066,13 +1066,13 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
 			if (!heap)
 				debugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->getNumber());
 
-			if (script && heap && (script->size + heap->size > 65535))
-				debugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n",
-				itr->getNumber(), script->size + heap->size);
+			if (script && heap && (script->size() + heap->size() > 65535))
+				debugPrintf("Error: script and heap %d together are larger than 64KB (%lu bytes)\n",
+				itr->getNumber(), script->size() + heap->size());
 		} else {	// SCI3
-			if (script && script->size > 65535)
-				debugPrintf("Error: script %d is larger than 64KB (%d bytes)\n",
-				itr->getNumber(), script->size);
+			if (script && script->size() > 65535)
+				debugPrintf("Error: script %d is larger than 64KB (%lu bytes)\n",
+				itr->getNumber(), script->size());
 		}
 	}
 
@@ -1566,7 +1566,7 @@ bool Console::cmdSaid(int argc, const char **argv) {
 	spec[len++] = 0xFF;
 
 	debugN("Matching '%s' against:", string);
-	_engine->getVocabulary()->debugDecipherSaidBlock(spec);
+	_engine->getVocabulary()->debugDecipherSaidBlock(SciSpan<const byte>(spec, len));
 	debugN("\n");
 
 	ResultWordListList words;
@@ -2113,9 +2113,10 @@ bool Console::segmentInfo(int nr) {
 	case SEG_TYPE_SCRIPT: {
 		Script *scr = (Script *)mobj;
 		debugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->getScriptNumber(), scr->getLockers(), (uint)scr->getBufSize(), (uint)scr->getBufSize());
-		if (scr->getExportTable())
-			debugPrintf("  Exports: %4d at %d\n", scr->getExportsNr(), (int)(((const byte *)scr->getExportTable()) - ((const byte *)scr->getBuf())));
-		else
+		if (scr->getExportsNr()) {
+			const uint location = scr->getExportsOffset();
+			debugPrintf("  Exports: %4d at %d\n", scr->getExportsNr(), location);
+		} else
 			debugPrintf("  Exports: none\n");
 
 		debugPrintf("  Synonyms: %4d\n", scr->getSynonymsNr());
@@ -3171,7 +3172,7 @@ void Console::printOffsets(int scriptNr, uint16 showType) {
 					saidPtr = curScriptData + arrayIterator->offset;
 					debugPrintf(" %03d:%04x:\n", arrayIterator->id, arrayIterator->offset);
 					debugN(" %03d:%04x: ", arrayIterator->id, arrayIterator->offset);
-					vocab->debugDecipherSaidBlock(saidPtr);
+					vocab->debugDecipherSaidBlock(SciSpan<const byte>(saidPtr, (arrayIterator + 1)->offset - arrayIterator->offset));
 					debugN("\n");
 					break;
 				default:
@@ -3512,7 +3513,7 @@ void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) {
 				byte opcode;
 				uint16 maxJmpOffset = 0;
 
-				while (true) {
+				for (;;) {
 					offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
 					opcode = extOpcode >> 1;
 
@@ -3946,7 +3947,7 @@ bool Console::cmdSfx01Header(int argc, const char **argv) {
 		return true;
 	}
 
-	Resource *song = _engine->getResMan()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), 0);
+	Resource *song = _engine->getResMan()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), false);
 
 	if (!song) {
 		debugPrintf("Doesn't exist\n");
@@ -3957,36 +3958,36 @@ bool Console::cmdSfx01Header(int argc, const char **argv) {
 
 	debugPrintf("SCI01 song track mappings:\n");
 
-	if (*song->data == 0xf0) // SCI1 priority spec
+	if (song->getUint8At(0) == 0xf0) // SCI1 priority spec
 		offset = 8;
 
-	if (song->size <= 0)
+	if (song->size() <= 0)
 		return 1;
 
-	while (song->data[offset] != 0xff) {
-		byte device_id = song->data[offset];
+	while (song->getUint8At(offset) != 0xff) {
+		byte device_id = song->getUint8At(offset);
 		debugPrintf("* Device %02x:\n", device_id);
 		offset++;
 
-		if (offset + 1 >= song->size)
+		if (offset + 1 >= song->size())
 			return 1;
 
-		while (song->data[offset] != 0xff) {
+		while (song->getUint8At(offset) != 0xff) {
 			int track_offset;
 			int end;
 			byte header1, header2;
 
-			if (offset + 7 >= song->size)
+			if (offset + 7 >= song->size())
 				return 1;
 
 			offset += 2;
 
-			track_offset = READ_LE_UINT16(song->data + offset);
-			header1 = song->data[track_offset];
-			header2 = song->data[track_offset+1];
+			track_offset = song->getUint16LEAt(offset);
+			header1 = song->getUint8At(track_offset);
+			header2 = song->getUint8At(track_offset + 1);
 			track_offset += 2;
 
-			end = READ_LE_UINT16(song->data + offset + 2);
+			end = song->getUint16LEAt(offset + 2);
 			debugPrintf("  - %04x -- %04x", track_offset, track_offset + end);
 
 			if (track_offset == 0xfe)
@@ -4002,7 +4003,7 @@ bool Console::cmdSfx01Header(int argc, const char **argv) {
 	return true;
 }
 
-static int _parse_ticks(byte *data, int *offset_p, int size) {
+static int _parse_ticks(const byte *data, int *offset_p, int size) {
 	int ticks = 0;
 	int tempticks;
 	int offset = 0;
@@ -4019,7 +4020,7 @@ static int _parse_ticks(byte *data, int *offset_p, int size) {
 }
 
 // Specialised for SCI01 tracks (this affects the way cumulative cues are treated)
-static void midi_hexdump(byte *data, int size, int notational_offset) {
+static void midi_hexdump(const byte *data, int size, int notational_offset) {
 	int offset = 0;
 	int prev = 0;
 	const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0};
@@ -4120,7 +4121,7 @@ bool Console::cmdSfx01Track(int argc, const char **argv) {
 		return true;
 	}
 
-	midi_hexdump(song->data + offset, song->size, offset);
+	midi_hexdump(song->getUnsafeDataAt(offset), song->size() - offset, offset);
 
 	return true;
 }
@@ -4141,9 +4142,9 @@ bool Console::cmdMapVocab994(int argc, const char **argv) {
 		return true;
 	}
 
-	Resource *resource = _engine->_resMan->findResource(ResourceId(kResourceTypeVocab, 994), 0);
+	Resource *resource = _engine->_resMan->findResource(ResourceId(kResourceTypeVocab, 994), false);
 	const Object *obj = s->_segMan->getObject(reg);
-	uint16 *data = (uint16 *) resource->data;
+	SciSpan<const uint16> data = resource->subspan<const uint16>(0);
 	uint32 first = atoi(argv[2]);
 	uint32 last  = atoi(argv[3]);
 	Common::Array<bool> markers;
@@ -4152,8 +4153,8 @@ bool Console::cmdMapVocab994(int argc, const char **argv) {
 	if (!obj->isClass() && getSciVersion() != SCI_VERSION_3)
 		obj = s->_segMan->getObject(obj->getSuperClassSelector());
 
-	first = MIN(first, (uint32) (resource->size / 2 - 2));
-	last =  MIN(last, (uint32) (resource->size / 2 - 2));
+	first = MIN<uint32>(first, resource->size() / 2 - 2);
+	last =  MIN<uint32>(last, resource->size() / 2 - 2);
 
 	for (uint32 i = first; i <= last; ++i) {
 		uint16 ofs = data[i];
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 5270398..13b3326 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -648,18 +648,18 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
 	// As far as we know, these games store the messages of each language in separate
 	// resources, and it's not possible to detect that easily
 	// Also look for "%J" which is used in japanese games
-	Resource *text = resMan.findResource(ResourceId(kResourceTypeText, 0), 0);
+	Resource *text = resMan.findResource(ResourceId(kResourceTypeText, 0), false);
 	uint seeker = 0;
 	if (text) {
-		while (seeker < text->size) {
-			if (text->data[seeker] == '#')  {
-				if (seeker + 1 < text->size)
-					s_fallbackDesc.language = charToScummVMLanguage(text->data[seeker + 1]);
+		while (seeker < text->size()) {
+			if (text->getUint8At(seeker) == '#')  {
+				if (seeker + 1 < text->size())
+					s_fallbackDesc.language = charToScummVMLanguage(text->getUint8At(seeker + 1));
 				break;
 			}
-			if (text->data[seeker] == '%') {
-				if ((seeker + 1 < text->size) && (text->data[seeker + 1] == 'J')) {
-					s_fallbackDesc.language = charToScummVMLanguage(text->data[seeker + 1]);
+			if (text->getUint8At(seeker) == '%') {
+				if ((seeker + 1 < text->size()) && (text->getUint8At(seeker + 1) == 'J')) {
+					s_fallbackDesc.language = charToScummVMLanguage(text->getUint8At(seeker + 1));
 					break;
 				}
 			}
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index e37a165..40d3801 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -448,7 +448,7 @@ SciVersion GameFeatures::detectMessageFunctionType() {
 	// Only v2 Message resources use the kGetMessage kernel function.
 	// v3-v5 use the kMessage kernel function.
 
-	if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2)
+	if (res->getUint32SEAt(0) / 1000 == 2)
 		_messageFunctionType = SCI_VERSION_1_LATE;
 	else
 		_messageFunctionType = SCI_VERSION_1_1;
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index c7732c6..d029923 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -149,13 +149,13 @@ void Kernel::loadSelectorNames() {
 		return;
 	}
 
-	int count = (isBE ? READ_BE_UINT16(r->data) : READ_LE_UINT16(r->data)) + 1; // Counter is slightly off
+	int count = (isBE ? r->getUint16BEAt(0) : r->getUint16LEAt(0)) + 1; // Counter is slightly off
 
 	for (int i = 0; i < count; i++) {
-		int offset = isBE ? READ_BE_UINT16(r->data + 2 + i * 2) : READ_LE_UINT16(r->data + 2 + i * 2);
-		int len = isBE ? READ_BE_UINT16(r->data + offset) : READ_LE_UINT16(r->data + offset);
+		int offset = isBE ? r->getUint16BEAt(2 + i * 2) : r->getUint16LEAt(2 + i * 2);
+		int len = isBE ? r->getUint16BEAt(offset) : r->getUint16LEAt(offset);
 
-		Common::String tmp((const char *)r->data + offset + 2, len);
+		Common::String tmp = r->getStringAt(offset + 2, len);
 		_selectorNames.push_back(tmp);
 		//debug("%s", tmp.c_str());
 
@@ -940,33 +940,27 @@ void Kernel::loadKernelNames(GameFeatures *features) {
 }
 
 Common::String Kernel::lookupText(reg_t address, int index) {
-	char *seeker;
-	Resource *textres;
-
 	if (address.getSegment())
 		return _segMan->getString(address);
 
-	int textlen;
-	int _index = index;
-	textres = _resMan->findResource(ResourceId(kResourceTypeText, address.getOffset()), 0);
+	Resource *textres = _resMan->findResource(ResourceId(kResourceTypeText, address.getOffset()), false);
 
 	if (!textres) {
 		error("text.%03d not found", address.getOffset());
-		return NULL; /* Will probably segfault */
 	}
 
-	textlen = textres->size;
-	seeker = (char *) textres->data;
+	int textlen = textres->size();
+	const char *seeker = (const char *)textres->getUnsafeDataAt(0);
 
+	int _index = index;
 	while (index--)
-		while ((textlen--) && (*seeker++))
+		while (textlen-- && *seeker++)
 			;
 
 	if (textlen)
 		return seeker;
 
 	error("Index %d out of bounds in text.%03d", _index, address.getOffset());
-	return NULL;
 }
 
 // TODO: script_adjust_opcode_formats should probably be part of the
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 335fec0..51f4b5d 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -171,8 +171,8 @@ public:
 
 	// Script dissection/dumping functions
 	void dissectScript(int scriptNumber, Vocabulary *vocab);
-	void dumpScriptObject(char *data, int seeker, int objsize);
-	void dumpScriptClass(char *data, int seeker, int objsize);
+	void dumpScriptObject(const SciSpan<const byte> &script, SciSpan<const byte> object);
+	void dumpScriptClass(const SciSpan<const byte> &script, SciSpan<const byte> clazz);
 
 	SelectorCache _selectorCache; /**< Shortcut list for important selectors. */
 	typedef Common::Array<KernelFunction> KernelFunctionArray;
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index f85f33e..d3bf2d7 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -188,7 +188,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
 			numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr();
 
 		if (numSynonyms) {
-			const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms();
+			const SciSpan<const byte> &synonyms = s->_segMan->getScript(seg)->getSynonyms();
 
 			if (synonyms) {
 				debugC(kDebugLevelParser, "Setting %d synonyms for script.%d",
@@ -202,8 +202,8 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
 				} else
 					for (int i = 0; i < numSynonyms; i++) {
 						synonym_t tmp;
-						tmp.replaceant = READ_LE_UINT16(synonyms + i * 4);
-						tmp.replacement = READ_LE_UINT16(synonyms + i * 4 + 2);
+						tmp.replaceant = synonyms.getUint16LEAt(i * 4);
+						tmp.replacement = synonyms.getUint16LEAt(i * 4 + 2);
 						voc->addSynonym(tmp);
 					}
 			} else
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 11378d7..3d689f2 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -27,8 +27,10 @@
 #include "sci/graphics/cursor.h"
 #include "sci/graphics/palette.h"
 #include "sci/graphics/screen.h"
+#include "sci/util.h"
 #include "common/events.h"
 #include "common/keyboard.h"
+#include "common/span.h"
 #include "common/str.h"
 #include "common/system.h"
 #include "common/textconsole.h"
@@ -53,19 +55,21 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
 
 	videoDecoder->start();
 
-	byte *scaleBuffer = 0;
+	Common::SpanOwner<SciSpan<byte> > scaleBuffer;
 	byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel;
 	uint16 width = videoDecoder->getWidth();
 	uint16 height = videoDecoder->getHeight();
 	uint16 pitch = videoDecoder->getWidth() * bytesPerPixel;
 	uint16 screenWidth = g_sci->_gfxScreen->getDisplayWidth();
 	uint16 screenHeight = g_sci->_gfxScreen->getDisplayHeight();
+	uint32 numPixels;
 
 	if (screenWidth == 640 && width <= 320 && height <= 240) {
 		width *= 2;
 		height *= 2;
 		pitch *= 2;
-		scaleBuffer = new byte[width * height * bytesPerPixel];
+		numPixels = width * height * bytesPerPixel;
+		scaleBuffer->allocate(numPixels, videoState.fileName + " scale buffer");
 	}
 
 	uint16 x = (screenWidth - width) / 2;
@@ -84,9 +88,10 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
 
 			if (frame) {
 				if (scaleBuffer) {
+					const SciSpan<const byte> input((const byte *)frame->getPixels(), frame->w * frame->h * bytesPerPixel);
 					// TODO: Probably should do aspect ratio correction in KQ6
-					g_sci->_gfxScreen->scale2x((const byte *)frame->getPixels(), scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
-					g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height);
+					g_sci->_gfxScreen->scale2x(input, *scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
+					g_system->copyRectToScreen(scaleBuffer->getUnsafeDataAt(0, pitch * height), pitch, x, y, width, height);
 				} else {
 					g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, width, height);
 				}
@@ -111,7 +116,6 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
 		g_system->delayMillis(10);
 	}
 
-	delete[] scaleBuffer;
 	delete videoDecoder;
 }
 
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 5e07ead..c30ad3a 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -39,13 +39,13 @@ struct MessageRecord {
 class MessageReader {
 public:
 	bool init() {
-		if (_headerSize > _size)
+		if (_headerSize > _data.size())
 			return false;
 
 		// Read message count from last word in header
-		_messageCount = READ_SCI11ENDIAN_UINT16(_data + _headerSize - 2);
+		_messageCount = _data.getUint16SEAt(_headerSize - 2);
 
-		if (_messageCount * _recordSize + _headerSize > _size)
+		if (_messageCount * _recordSize + _headerSize > _data.size())
 			return false;
 
 		return true;
@@ -56,11 +56,10 @@ public:
 	virtual ~MessageReader() { }
 
 protected:
-	MessageReader(const byte *data, uint size, uint headerSize, uint recordSize)
-		: _data(data), _size(size), _headerSize(headerSize), _recordSize(recordSize), _messageCount(0) { }
+	MessageReader(const SciSpan<const byte> &data, uint headerSize, uint recordSize)
+		: _data(data), _headerSize(headerSize), _recordSize(recordSize), _messageCount(0) { }
 
-	const byte *_data;
-	const uint _size;
+	const SciSpan<const byte> _data;
 	const uint _headerSize;
 	const uint _recordSize;
 	uint _messageCount;
@@ -68,22 +67,22 @@ protected:
 
 class MessageReaderV2 : public MessageReader {
 public:
-	MessageReaderV2(byte *data, uint size) : MessageReader(data, size, 6, 4) { }
+	MessageReaderV2(const SciSpan<const byte> &data) : MessageReader(data, 6, 4) { }
 
 	bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
-		const byte *recordPtr = _data + _headerSize;
+		SciSpan<const byte> recordPtr = _data.subspan(_headerSize);
 
 		for (uint i = 0; i < _messageCount; i++) {
 			if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)) {
 				record.tuple = tuple;
 				record.refTuple = MessageTuple();
 				record.talker = 0;
-				const uint16 stringOffset = READ_LE_UINT16(recordPtr + 2);
-				const uint32 maxSize = _size - stringOffset;
-				record.string = (const char *)_data + stringOffset;
+				const uint16 stringOffset = recordPtr.getUint16LEAt(2);
+				const uint32 maxSize = _data.size() - stringOffset;
+				record.string = (const char *)_data.getUnsafeDataAt(stringOffset, maxSize);
 				record.length = Common::strnlen(record.string, maxSize);
 				if (record.length == maxSize) {
-					warning("Message %s appears truncated at %ld", tuple.toString().c_str(), recordPtr - _data);
+					warning("Message %s from %s appears truncated at %ld", tuple.toString().c_str(), _data.name().c_str(), recordPtr - _data);
 				}
 				return true;
 			}
@@ -96,23 +95,22 @@ public:
 
 class MessageReaderV3 : public MessageReader {
 public:
-	MessageReaderV3(byte *data, uint size) : MessageReader(data, size, 8, 10) { }
+	MessageReaderV3(const SciSpan<const byte> &data) : MessageReader(data, 8, 10) { }
 
 	bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
-		const byte *recordPtr = _data + _headerSize;
-
+		SciSpan<const byte> recordPtr = _data.subspan(_headerSize);
 		for (uint i = 0; i < _messageCount; i++) {
 			if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)
 				&& (recordPtr[2] == tuple.cond) && (recordPtr[3] == tuple.seq)) {
 				record.tuple = tuple;
 				record.refTuple = MessageTuple();
 				record.talker = recordPtr[4];
-				const uint16 stringOffset = READ_LE_UINT16(recordPtr + 5);
-				const uint32 maxSize = _size - stringOffset;
-				record.string = (const char *)_data + stringOffset;
+				const uint16 stringOffset = recordPtr.getUint16LEAt(5);
+				const uint32 maxSize = _data.size() - stringOffset;
+				record.string = (const char *)_data.getUnsafeDataAt(stringOffset, maxSize);
 				record.length = Common::strnlen(record.string, maxSize);
 				if (record.length == maxSize) {
-					warning("Message %s appears truncated at %ld", tuple.toString().c_str(), recordPtr - _data);
+					warning("Message %s from %s appears truncated at %ld", tuple.toString().c_str(), _data.name().c_str(), recordPtr - _data);
 				}
 				return true;
 			}
@@ -125,23 +123,22 @@ public:
 
 class MessageReaderV4 : public MessageReader {
 public:
-	MessageReaderV4(byte *data, uint size) : MessageReader(data, size, 10, 11) { }
+	MessageReaderV4(const SciSpan<const byte> &data) : MessageReader(data, 10, 11) { }
 
 	bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
-		const byte *recordPtr = _data + _headerSize;
-
+		SciSpan<const byte> recordPtr = _data.subspan(_headerSize);
 		for (uint i = 0; i < _messageCount; i++) {
 			if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)
 				&& (recordPtr[2] == tuple.cond) && (recordPtr[3] == tuple.seq)) {
 				record.tuple = tuple;
 				record.refTuple = MessageTuple(recordPtr[7], recordPtr[8], recordPtr[9]);
 				record.talker = recordPtr[4];
-				const uint16 stringOffset = READ_SCI11ENDIAN_UINT16(recordPtr + 5);
-				const uint32 maxSize = _size - stringOffset;
-				record.string = (const char *)_data + stringOffset;
+				const uint16 stringOffset = recordPtr.getUint16SEAt(5);
+				const uint32 maxSize = _data.size() - stringOffset;
+				record.string = (const char *)_data.getUnsafeDataAt(stringOffset, maxSize);
 				record.length = Common::strnlen(record.string, maxSize);
 				if (record.length == maxSize) {
-					warning("Message %s appears truncated at %ld", tuple.toString().c_str(), recordPtr - _data);
+					warning("Message %s from %s appears truncated at %ld", tuple.toString().c_str(), _data.name().c_str(), recordPtr - _data);
 				}
 				return true;
 			}
@@ -157,23 +154,22 @@ public:
 // the talker and the string...
 class MessageReaderV4_MacSCI32 : public MessageReader {
 public:
-	MessageReaderV4_MacSCI32(byte *data, uint size) : MessageReader(data, size, 10, 12) { }
+	MessageReaderV4_MacSCI32(const SciSpan<const byte> &data) : MessageReader(data, 10, 12) { }
 
 	bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
-		const byte *recordPtr = _data + _headerSize;
-
+		SciSpan<const byte> recordPtr = _data.subspan(_headerSize);
 		for (uint i = 0; i < _messageCount; i++) {
 			if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)
 				&& (recordPtr[2] == tuple.cond) && (recordPtr[3] == tuple.seq)) {
 				record.tuple = tuple;
 				record.refTuple = MessageTuple(recordPtr[8], recordPtr[9], recordPtr[10]);
 				record.talker = recordPtr[4];
-				const uint16 stringOffset = READ_BE_UINT16(recordPtr + 6);
-				const uint32 maxSize = _size - stringOffset;
-				record.string = (const char *)_data + stringOffset;
+				const uint16 stringOffset = recordPtr.getUint16BEAt(6);
+				const uint32 maxSize = _data.size() - stringOffset;
+				record.string = (const char *)_data.getUnsafeDataAt(stringOffset, maxSize);
 				record.length = Common::strnlen(record.string, maxSize);
 				if (record.length == maxSize) {
-					warning("Message %s appears truncated at %ld", tuple.toString().c_str(), recordPtr - _data);
+					warning("Message %s from %s appears truncated at %ld", tuple.toString().c_str(), _data.name().c_str(), recordPtr - _data);
 				}
 				return true;
 			}
@@ -194,24 +190,24 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
 	}
 
 	MessageReader *reader;
-	int version = READ_SCI11ENDIAN_UINT32(res->data) / 1000;
+	int version = res->getUint32SEAt(0) / 1000;
 
 	switch (version) {
 	case 2:
-		reader = new MessageReaderV2(res->data, res->size);
+		reader = new MessageReaderV2(*res);
 		break;
 	case 3:
-		reader = new MessageReaderV3(res->data, res->size);
+		reader = new MessageReaderV3(*res);
 		break;
 	case 4:
 #ifdef ENABLE_SCI32
 	case 5: // v5 seems to be compatible with v4
 		// SCI32 Mac is different than SCI32 DOS/Win here
 		if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2_1_EARLY)
-			reader = new MessageReaderV4_MacSCI32(res->data, res->size);
+			reader = new MessageReaderV4_MacSCI32(*res);
 		else
 #endif
-			reader = new MessageReaderV4(res->data, res->size);
+			reader = new MessageReaderV4(*res);
 		break;
 	default:
 		error("Message: unsupported resource version %d", version);
diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp
index 0566d69..2a6c966 100644
--- a/engines/sci/engine/object.cpp
+++ b/engines/sci/engine/object.cpp
@@ -51,24 +51,25 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
 	return true;
 }
 
-void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
-	byte *data = buf + obj_pos.getOffset();
+void Object::init(const SciSpan<const byte> &buf, reg_t obj_pos, bool initVariables) {
+	const SciSpan<const byte> data = buf.subspan(obj_pos.getOffset());
 	_baseObj = data;
 	_pos = obj_pos;
 
 	if (getSciVersion() <= SCI_VERSION_1_LATE) {
-		_variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter));
-		_baseVars = (const uint16 *)(_baseObj + _variables.size() * 2);
-		_methodCount = READ_LE_UINT16(data + READ_LE_UINT16(data + kOffsetFunctionArea) - 2);
+		const SciSpan<const byte> header = buf.subspan(obj_pos.getOffset() - kOffsetHeaderSize);
+		_variables.resize(header.getUint16LEAt(kOffsetHeaderSelectorCounter));
+		_baseVars = _baseObj.subspan<const uint16>(_variables.size() * sizeof(uint16));
+		_methodCount = data.getUint16LEAt(header.getUint16LEAt(kOffsetHeaderFunctionArea) - 2);
 		for (int i = 0; i < _methodCount * 2 + 2; ++i) {
-			_baseMethod.push_back(READ_SCI11ENDIAN_UINT16(data + READ_LE_UINT16(data + kOffsetFunctionArea) + i * 2));
+			_baseMethod.push_back(data.getUint16SEAt(header.getUint16LEAt(kOffsetHeaderFunctionArea) + i * 2));
 		}
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-		_variables.resize(READ_SCI11ENDIAN_UINT16(data + 2));
-		_baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4));
-		_methodCount = READ_SCI11ENDIAN_UINT16(buf + READ_SCI11ENDIAN_UINT16(data + 6));
+		_variables.resize(data.getUint16SEAt(2));
+		_baseVars = buf.subspan<const uint16>(data.getUint16SEAt(4), _variables.size() * sizeof(uint16));
+		_methodCount = buf.getUint16SEAt(data.getUint16SEAt(6));
 		for (int i = 0; i < _methodCount * 2 + 3; ++i) {
-			_baseMethod.push_back(READ_SCI11ENDIAN_UINT16(buf + READ_SCI11ENDIAN_UINT16(data + 6) + i * 2));
+			_baseMethod.push_back(buf.getUint16SEAt(data.getUint16SEAt(6) + i * 2));
 		}
 	} else if (getSciVersion() == SCI_VERSION_3) {
 		initSelectorsSci3(buf);
@@ -77,9 +78,9 @@ void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
 	if (initVariables) {
 		if (getSciVersion() <= SCI_VERSION_2_1_LATE) {
 			for (uint i = 0; i < _variables.size(); i++)
-				_variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2)));
+				_variables[i] = make_reg(0, data.getUint16SEAt(i * 2));
 		} else {
-			_infoSelectorSci3 = make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 10));
+			_infoSelectorSci3 = make_reg(0, _baseObj.getUint16SEAt(10));
 		}
 	}
 }
@@ -89,20 +90,20 @@ const Object *Object::getClass(SegManager *segMan) const {
 }
 
 int Object::locateVarSelector(SegManager *segMan, Selector slc) const {
-	const byte *buf = 0;
+	SciSpan<const byte> buf;
 	uint varnum = 0;
 
 	if (getSciVersion() <= SCI_VERSION_2_1_LATE) {
 		const Object *obj = getClass(segMan);
 		varnum = getSciVersion() <= SCI_VERSION_1_LATE ? getVarCount() : obj->getVariable(1).toUint16();
-		buf = (const byte *)obj->_baseVars;
+		buf = obj->_baseVars.subspan<const byte>(0);
 	} else if (getSciVersion() == SCI_VERSION_3) {
 		varnum = _variables.size();
-		buf = (const byte *)_baseVars;
+		buf = _baseVars.subspan<const byte>(0);
 	}
 
 	for (uint i = 0; i < varnum; i++)
-		if (READ_SCI11ENDIAN_UINT16(buf + (i << 1)) == slc) // Found it?
+		if (buf.getUint16SEAt(i << 1) == slc) // Found it?
 			return i; // report success
 
 	return -1; // Failed
@@ -136,14 +137,14 @@ int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const {
 	}
 
 	if (getSciVersion() < SCI_VERSION_1_1) {
-		const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2;
-		return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset);
+		const SciSpan<const byte> selectoroffset = _baseObj.subspan(kOffsetSelectorSegment + selectors * 2);
+		return selectoroffset.getUint16SEAt(propertyOffset);
 	} else {
 		const Object *obj = this;
 		if (!isClass())
 			obj = segMan->getObject(getSuperClassSelector());
 
-		return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset);
+		return obj->_baseVars.subspan<const byte>(0).getUint16SEAt(propertyOffset);
 	}
 }
 
@@ -246,9 +247,9 @@ bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClas
 
 const int EXTRA_GROUPS = 3;
 
-void Object::initSelectorsSci3(const byte *buf) {
-	const byte *groupInfo = _baseObj + 16;
-	const byte *selectorBase = groupInfo + EXTRA_GROUPS * 32 * 2;
+void Object::initSelectorsSci3(const SciSpan<const byte> &buf) {
+	const SciSpan<const byte> groupInfo = _baseObj.subspan(16);
+	const SciSpan<const byte> selectorBase = groupInfo.subspan(EXTRA_GROUPS * 32 * 2);
 	int groups = g_sci->getKernel()->getSelectorNamesSize()/32;
 	int methods, properties;
 
@@ -266,16 +267,16 @@ void Object::initSelectorsSci3(const byte *buf) {
 	// there are, so we count them first.
 	for (int groupNr = 0; groupNr < groups; ++groupNr) {
 		byte groupLocation = groupInfo[groupNr];
-		const byte *seeker = selectorBase + groupLocation * 32 * 2;
+		const SciSpan<const byte> seeker = selectorBase.subspan(groupLocation * 32 * 2);
 
 		if (groupLocation != 0)	{
 			// This object actually has selectors belonging to this group
-			int typeMask = READ_SCI11ENDIAN_UINT32(seeker);
+			int typeMask = seeker.getUint32SEAt(0);
 
 			_mustSetViewVisible[groupNr] = (typeMask & 1);
 
 			 for (int bit = 2; bit < 32; ++bit) {
-				int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2);
+				int value = seeker.getUint16SEAt(bit * 2);
 				if (typeMask & (1 << bit)) { // Property
 					++properties;
 				} else if (value != 0xffff) { // Method
@@ -300,15 +301,15 @@ void Object::initSelectorsSci3(const byte *buf) {
 	// and method pointers
 	for (int groupNr = 0; groupNr < groups; ++groupNr) {
 		byte groupLocation = groupInfo[groupNr];
-		const byte *seeker = selectorBase + groupLocation * 32 * 2;
+		const SciSpan<const byte> seeker = selectorBase.subspan(groupLocation * 32 * 2);
 
 		if (groupLocation != 0)	{
 			// This object actually has selectors belonging to this group
-			int typeMask = READ_SCI11ENDIAN_UINT32(seeker);
+			int typeMask = seeker.getUint32SEAt(0);
 			int groupBaseId = groupNr * 32;
 
 			for (int bit = 2; bit < 32; ++bit) {
-				int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2);
+				int value = seeker.getUint16SEAt(bit * 2);
 				if (typeMask & (1 << bit)) { // Property
 
 					// FIXME: We really shouldn't be doing endianness
@@ -325,7 +326,7 @@ void Object::initSelectorsSci3(const byte *buf) {
 					++propertyCounter;
 				} else if (value != 0xffff) { // Method
 					_baseMethod.push_back(groupBaseId + bit);
-					_baseMethod.push_back(value + READ_SCI11ENDIAN_UINT32(buf));
+					_baseMethod.push_back(value + buf.getUint32SEAt(0));
 //					methodOffsets[methodCounter] = (seeker + bit * 2) - buf;
 					++methodCounter;
 				} else {
@@ -336,10 +337,10 @@ void Object::initSelectorsSci3(const byte *buf) {
 		}
 	}
 
-	_speciesSelectorSci3 = make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 4));
-	_superClassPosSci3 = make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 8));
+	_speciesSelectorSci3 = make_reg(0, _baseObj.getUint16SEAt(4));
+	_superClassPosSci3 = make_reg(0, _baseObj.getUint16SEAt(8));
 
-	_baseVars = propertyIds;
+	_baseVars = SciSpan<const uint16>(propertyIds, properties);
 	_methodCount = methods;
 	_propertyOffsetsSci3 = propertyOffsets;
 	//_methodOffsetsSci3 = methodOffsets;
diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h
index 74a908a..61f942c 100644
--- a/engines/sci/engine/object.h
+++ b/engines/sci/engine/object.h
@@ -59,9 +59,11 @@ enum infoSelectorFlags {
 };
 
 enum ObjectOffsets {
-	kOffsetLocalVariables = -6,
-	kOffsetFunctionArea = -4,
-	kOffsetSelectorCounter = -2,
+	kOffsetHeaderSize = 6,
+	kOffsetHeaderLocalVariables = 0,
+	kOffsetHeaderFunctionArea = 2,
+	kOffsetHeaderSelectorCounter = 4,
+
 	kOffsetSelectorSegment = 0,
 	kOffsetInfoSelectorSci0 = 4,
 	kOffsetNamePointerSci0 = 6,
@@ -74,21 +76,48 @@ public:
 	Object() {
 		_offset = getSciVersion() < SCI_VERSION_1_1 ? 0 : 5;
 		_flags = 0;
-		_baseObj = 0;
-		_baseVars = 0;
+		_baseObj.clear();
+		_baseVars.clear();
 		_methodCount = 0;
-		_propertyOffsetsSci3 = 0;
+		_propertyOffsetsSci3 = nullptr;
 	}
 
 	~Object() {
 		if (getSciVersion() == SCI_VERSION_3) {
-			// FIXME: memory leak! Commented out because of reported heap
-			// corruption by MSVC (e.g. in LSL7, when it starts)
-			//free(_baseVars);
-			//_baseVars = 0;
-			//free(_propertyOffsetsSci3);
-			//_propertyOffsetsSci3 = 0;
+			// TODO: This is super gross
+			free(const_cast<uint16 *>(_baseVars.data()));
+			_baseVars.clear();
+			free(_propertyOffsetsSci3);
+			_propertyOffsetsSci3 = nullptr;
+		}
+	}
+
+	Object &operator=(const Object &other) {
+		_baseObj = other._baseObj;
+		_baseMethod = other._baseMethod;
+		_variables = other._variables;
+		_methodCount = other._methodCount;
+		_flags = other._flags;
+		_offset = other._offset;
+		_pos = other._pos;
+
+		if (getSciVersion() == SCI_VERSION_3) {
+			uint16 *baseVars = (uint16 *)malloc(other._baseVars.byteSize());
+			other._baseVars.unsafeCopyDataTo(baseVars);
+			_baseVars = SciSpan<const uint16>(baseVars, other._baseVars.size());
+
+			_propertyOffsetsSci3 = (uint32 *)malloc(sizeof(uint32) * _variables.size());
+			memcpy(_propertyOffsetsSci3, other._propertyOffsetsSci3, sizeof(uint32) * _variables.size());
+
+			_superClassPosSci3 = other._superClassPosSci3;
+			_speciesSelectorSci3 = other._speciesSelectorSci3;
+			_infoSelectorSci3 = other._infoSelectorSci3;
+			_mustSetViewVisible = other._mustSetViewVisible;
+		} else {
+			_baseVars = other._baseVars;
 		}
+
+		return *this;
 	}
 
 	reg_t getSpeciesSelector() const {
@@ -181,7 +210,7 @@ public:
 		if (getSciVersion() < SCI_VERSION_3)
 			return _variables[4];
 		else	// SCI3
-			return make_reg(0, READ_SCI11ENDIAN_UINT16(_baseObj + 6));
+			return make_reg(0, _baseObj.getUint16SEAt(6));
 	}
 
 	void setClassScriptSelector(reg_t value) {
@@ -192,7 +221,7 @@ public:
 			error("setClassScriptSelector called for SCI3");
 	}
 
-	Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); }
+	Selector getVarSelector(uint16 i) const { return _baseVars.getUint16SEAt(i); }
 
 	reg_t getFunction(uint16 i) const {
 		uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2;
@@ -236,7 +265,7 @@ public:
 
 	uint getVarCount() const { return _variables.size(); }
 
-	void init(byte *buf, reg_t obj_pos, bool initVariables = true);
+	void init(const SciSpan<const byte> &buf, reg_t obj_pos, bool initVariables = true);
 
 	reg_t getVariable(uint var) const { return _variables[var]; }
 	reg_t &getVariableRef(uint var) { return _variables[var]; }
@@ -247,9 +276,9 @@ public:
 	void saveLoadWithSerializer(Common::Serializer &ser);
 
 	void cloneFromObject(const Object *obj) {
-		_baseObj = obj ? obj->_baseObj : NULL;
+		_baseObj = obj ? obj->_baseObj : SciSpan<const byte>();
 		_baseMethod = obj ? obj->_baseMethod : Common::Array<uint16>();
-		_baseVars = obj ? obj->_baseVars : NULL;
+		_baseVars = obj ? obj->_baseVars : SciSpan<const uint16>();
 	}
 
 	bool relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize);
@@ -260,17 +289,17 @@ public:
 	void initSpecies(SegManager *segMan, reg_t addr);
 	void initSuperClass(SegManager *segMan, reg_t addr);
 	bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
-	void syncBaseObject(const byte *ptr) { _baseObj = ptr; }
+	void syncBaseObject(const SciSpan<const byte> &ptr) { _baseObj = ptr; }
 
 	bool mustSetViewVisibleSci3(int selector) const { return _mustSetViewVisible[selector/32]; }
 
 private:
-	void initSelectorsSci3(const byte *buf);
+	void initSelectorsSci3(const SciSpan<const byte> &buf);
 
-	const byte *_baseObj; /**< base + object offset within base */
-	const uint16 *_baseVars; /**< Pointer to the varselector area for this object */
+	SciSpan<const byte> _baseObj; /**< base + object offset within base */
+	SciSpan<const uint16> _baseVars; /**< Pointer to the varselector area for this object */
 	Common::Array<uint16> _baseMethod; /**< Pointer to the method selector area for this object */
-	uint32 *_propertyOffsetsSci3; /**< This is used to enable relocation of property valuesa in SCI3 */
+	uint32 *_propertyOffsetsSci3; /**< This is used to enable relocation of property values in SCI3 */
 
 	Common::Array<reg_t> _variables;
 	uint16 _methodCount;
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index a3a690b..f05fdc5 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -255,7 +255,7 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
 
 				ObjMap objects = scr->getObjectMap();
 				for (ObjMap::iterator it = objects.begin(); it != objects.end(); ++it)
-					it->_value.syncBaseObject(scr->getBuf(it->_value.getPos().getOffset()));
+					it->_value.syncBaseObject(SciSpan<const byte>(scr->getBuf(it->_value.getPos().getOffset()), scr->getBufSize() - it->_value.getPos().getOffset()));
 
 			}
 
@@ -437,37 +437,38 @@ void HunkTable::saveLoadWithSerializer(Common::Serializer &s) {
 void Script::syncStringHeap(Common::Serializer &s) {
 	if (getSciVersion() < SCI_VERSION_1_1) {
 		// Sync all of the SCI_OBJ_STRINGS blocks
-		byte *buf = _buf;
+		SciSpan<byte> buf = (SciSpan<byte> &)*_buf;
 		bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
 
 		if (oldScriptHeader)
 			buf += 2;
 
-		do {
-			int blockType = READ_LE_UINT16(buf);
+		for (;;) {
+			int blockType = buf.getUint16LEAt(0);
 			int blockSize;
 			if (blockType == 0)
 				break;
 
-			blockSize = READ_LE_UINT16(buf + 2);
+			blockSize = buf.getUint16LEAt(2);
 			assert(blockSize > 0);
 
 			if (blockType == SCI_OBJ_STRINGS)
-				s.syncBytes(buf, blockSize);
+				s.syncBytes(buf.getUnsafeDataAt(0, blockSize), blockSize);
 
 			buf += blockSize;
-		} while (1);
+		}
 
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE){
 		// Strings in SCI1.1 come after the object instances
-		byte *buf = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+		SciSpan<byte> buf = _heap.subspan<byte>(4 + _heap.getUint16SEAt(2) * 2);
 
 		// Skip all of the objects
-		while (READ_SCI11ENDIAN_UINT16(buf) == SCRIPT_OBJECT_MAGIC_NUMBER)
-			buf += READ_SCI11ENDIAN_UINT16(buf + 2) * 2;
+		while (buf.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER)
+			buf += buf.getUint16SEAt(2) * 2;
 
 		// Now, sync everything till the end of the buffer
-		s.syncBytes(buf, _heapSize - (buf - _heapStart));
+		const int length = _heap.size() - (buf - _heap);
+		s.syncBytes(buf.getUnsafeDataAt(0, length), length);
 	} else if (getSciVersion() == SCI_VERSION_3) {
 		warning("TODO: syncStringHeap(): Implement SCI3 variant");
 	}
@@ -1062,7 +1063,7 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin
 	meta.saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
 
 	Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
-	meta.script0Size = script0->size;
+	meta.script0Size = script0->size();
 	meta.gameObjectOffset = g_sci->getGameObject().getOffset();
 
 	// Checking here again
@@ -1199,7 +1200,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
 
 	if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
 		Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
-		if (script0->size != meta.script0Size || g_sci->getGameObject().getOffset() != meta.gameObjectOffset) {
+		if (script0->size() != meta.script0Size || g_sci->getGameObject().getOffset() != meta.gameObjectOffset) {
 			showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
 
 			s->r_acc = TRUE_REG;	// signal failure
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 8a973bd..f790b41 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -33,8 +33,13 @@
 
 namespace Sci {
 
+const char *sciObjectTypeNames[] = {
+	"terminator", "object", "code", "synonyms", "said", "strings", "class",
+	"exports", "pointers", "preload text", "local vars"
+};
+
 Script::Script()
-	: SegmentObj(SEG_TYPE_SCRIPT), _buf(NULL) {
+	: SegmentObj(SEG_TYPE_SCRIPT), _buf() {
 	freeScript();
 }
 
@@ -45,16 +50,12 @@ Script::~Script() {
 void Script::freeScript() {
 	_nr = 0;
 
-	free(_buf);
-	_buf = NULL;
-	_bufSize = 0;
-	_scriptSize = 0;
-	_heapStart = NULL;
-	_heapSize = 0;
-
-	_exportTable = NULL;
+	_buf.clear();
+	_script.clear();
+	_heap.clear();
+	_exports.clear();
 	_numExports = 0;
-	_synonyms = NULL;
+	_synonyms.clear();
 	_numSynonyms = 0;
 
 	_localsOffset = 0;
@@ -80,15 +81,16 @@ enum {
 void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) {
 	freeScript();
 
-	Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
+	Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), false);
 	if (!script)
 		error("Script %d not found", script_nr);
 
 	_nr = script_nr;
-	_bufSize = _scriptSize = script->size;
+	uint32 scriptSize = script->size();
+	uint32 bufSize = scriptSize;
 
 	if (getSciVersion() == SCI_VERSION_0_EARLY) {
-		_bufSize += READ_LE_UINT16(script->data) * 2;
+		bufSize += script->getUint16LEAt(0) * 2;
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
 		// In SCI1.1 - SCI2.1, the heap was in a separate space from the script. We append
 		// it to the end of the script, and adjust addressing accordingly.
@@ -97,18 +99,17 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 		// worked for SCI11, SCI2 and SCI21 games. SCI3 games use a different
 		// script format, and theoretically they can exceed the 64KB boundary
 		// using relocation.
-		Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
-		_bufSize += heap->size;
-		_heapSize = heap->size;
+		Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), false);
+		bufSize += heap->size();
 
 		// Ensure that the start of the heap resource can be word-aligned.
-		if (script->size & 2) {
-			_bufSize++;
-			_scriptSize++;
+		if (script->size() & 2) {
+			++bufSize;
+			++scriptSize;
 		}
 
 		// As mentioned above, the script and the heap together should not exceed 64KB
-		if (script->size + heap->size > 65535)
+		if (script->size() + heap->size() > 65535)
 			error("Script and heap sizes combined exceed 64K. This means a fundamental "
 					"design bug was made regarding SCI1.1 and newer games.\n"
 					"Please report this error to the ScummVM team");
@@ -125,13 +126,13 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 		// RAMA: 70
 		//
 		// TODO: Remove this once such a mechanism is in place
-		if (script->size > 65535)
-			warning("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't "
-			      "be fully handled at the moment", script_nr, script->size);
+		if (script->size() > 65535)
+			warning("TODO: SCI script %d is over 64KB - it's %lu bytes long. This can't "
+			      "be fully handled at the moment", script_nr, script->size());
 	}
 
 	uint extraLocalsWorkaround = 0;
-	if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) {
+	if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size() == 11140) {
 		// WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to
 		// fit the string showing how many shots are left (a nasty script bug,
 		// corrupting heap memory). We add 10 more locals so that it has enough
@@ -139,60 +140,71 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 		// #3059871.
 		extraLocalsWorkaround = 10;
 	}
-	_bufSize += extraLocalsWorkaround * 2;
+	bufSize += extraLocalsWorkaround * 2;
 
-	_buf = (byte *)malloc(_bufSize);
-	assert(_buf);
-
-	assert(_bufSize >= script->size);
-	memcpy(_buf, script->data, script->size);
+	SciSpan<byte> outBuffer = _buf->allocate(bufSize, script->name() + " buffer");
+	script->copyDataTo(outBuffer);
+	// The word-aligned script size is used here because other parts of the code
+	// currently rely on finding the start of the heap by reading the script
+	// size
+	_script = _buf->subspan(0, scriptSize, script->name());
 
 	if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-		Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
-		assert(heap != 0);
-
-		_heapStart = _buf + _scriptSize;
+		Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), false);
+		assert(heap);
 
-		assert(_bufSize - _scriptSize >= heap->size);
-		memcpy(_heapStart, heap->data, heap->size);
+		SciSpan<byte> outHeap = outBuffer.subspan(scriptSize, heap->size(), heap->name(), 0);
+		heap->copyDataTo(outHeap);
+		_heap = outHeap;
 	}
 
 	// Check scripts (+ possibly SCI 1.1 heap) for matching signatures and patch those, if found
-	scriptPatcher->processScript(_nr, _buf, _bufSize);
+	scriptPatcher->processScript(_nr, outBuffer);
 
 	if (getSciVersion() <= SCI_VERSION_1_LATE) {
-		_exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS);
-		if (_exportTable) {
-			_numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1);
-			_exportTable += 3;	// skip header plus 2 bytes (_exportTable is a uint16 pointer)
+		SciSpan<const uint16> exportTable = findBlockSCI0(SCI_OBJ_EXPORTS).subspan<const uint16>(0);
+		if (exportTable) {
+			// The export table is after the block header (4 bytes / 2 uint16s)
+			// and the number of exports (2 bytes / 1 uint16).
+			// The exports span does not need to be explicitly sized since the
+			// maximum size was already determined by findBlockSCI0
+			_exports = exportTable.subspan(3);
+			_numExports = exportTable.getUint16SEAt(2);
 		}
-		_synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS);
-		if (_synonyms) {
-			_numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4;
-			_synonyms += 4;	// skip header
+
+		SciSpan<const byte> synonymTable = findBlockSCI0(SCI_OBJ_SYNONYMS);
+		if (synonymTable) {
+			// the synonyms table is after the block header (4 bytes),
+			// and each synonym entry is 4 bytes
+			_synonyms = synonymTable.subspan(4);
+			_numSynonyms = _synonyms.size() / 4;
 		}
-		const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS);
-		if (localsBlock) {
-			_localsOffset = localsBlock - _buf + 4;
-			_localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1;	// half block size
+
+		SciSpan<const byte> localsTable = findBlockSCI0(SCI_OBJ_LOCALVARS);
+		if (localsTable) {
+			// skip header (4 bytes)
+			_localsOffset = localsTable - *_buf + 4;
+			_localsCount = (_buf->getUint16LEAt(_localsOffset - 2) - 4) >> 1; // half block size
 		}
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-		_numExports = READ_SCI11ENDIAN_UINT16(_buf + kSci11NumExportsOffset);
+		_numExports = _buf->getUint16SEAt(kSci11NumExportsOffset);
 		if (_numExports) {
-			_exportTable = (const uint16 *)(_buf + kSci11ExportTableOffset);
+			_exports = _buf->subspan<const uint16>(kSci11ExportTableOffset, _numExports * sizeof(uint16));
 		}
 
-		_localsOffset = _scriptSize + 4;
-		_localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
+		_localsOffset = _script.size() + 4;
+		_localsCount = _buf->getUint16SEAt(_localsOffset - 2);
 	} else if (getSciVersion() == SCI_VERSION_3) {
-		_localsCount = READ_LE_UINT16(_buf + 12);
-		_exportTable = (const uint16 *)(_buf + 22);
-		_numExports = READ_LE_UINT16(_buf + 20);
-		// SCI3 local variables always start dword-aligned
-		if (_numExports % 2)
-			_localsOffset = 22 + _numExports * 2;
-		else
-			_localsOffset = 24 + _numExports * 2;
+		_localsCount = _buf->getUint16LEAt(12);
+		_numExports = _buf->getUint16LEAt(20);
+		if (_numExports) {
+			_exports = _buf->subspan<const uint16>(22, _numExports * sizeof(uint16));
+			// SCI3 local variables always start dword-aligned
+			if (_numExports % 2)
+				_localsOffset = 22 + _numExports * 2;
+			else
+				_localsOffset = 24 + _numExports * 2;
+		}
 	}
 
 	// WORKAROUND: Increase locals, if needed (check above)
@@ -203,7 +215,7 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 		// Old script block. There won't be a localvar block in this case.
 		// Instead, the script starts with a 16 bit int specifying the
 		// number of locals we need; these are then allocated and zeroed.
-		_localsCount = READ_LE_UINT16(_buf);
+		_localsCount = _buf->getUint16LEAt(0);
 		_localsOffset = -_localsCount * 2; // Make sure it's invalid
 	} else {
 		// SCI0 late and newer
@@ -211,8 +223,8 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 		if (!_localsCount)
 			_localsOffset = 0;
 
-		if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
-			error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, (int)_bufSize);
+		if (_localsOffset + _localsCount * 2 + 1 >= (int)_buf->size()) {
+			error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, (int)_buf->size());
 			//_localsCount = (_bufSize - _localsOffset) >> 1;
 		}
 	}
@@ -223,11 +235,9 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 
 void Script::identifyOffsets() {
 	offsetLookupArrayEntry arrayEntry;
-	const byte *scriptDataPtr  = NULL;
-	const byte *stringStartPtr = NULL;
-	const byte *stringDataPtr  = NULL;
-	uint32 scriptDataLeft   = 0;
-	uint32 stringDataLeft   = 0;
+	SciSpan<const byte> scriptDataPtr;
+	SciSpan<const byte> stringStartPtr;
+	SciSpan<const byte> stringDataPtr;
 	byte stringDataByte  = 0;
 	uint16 typeObject_id = 0;
 	uint16 typeString_id = 0;
@@ -244,38 +254,34 @@ void Script::identifyOffsets() {
 
 	if (getSciVersion() < SCI_VERSION_1_1) {
 		// SCI0 + SCI1
-		scriptDataPtr  = _buf;
-		scriptDataLeft = _bufSize;
+		scriptDataPtr = *_buf;
 
 		// Go through all blocks
 		if (getSciVersion() == SCI_VERSION_0_EARLY) {
-			if (scriptDataLeft < 2)
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script %d", _nr);
-			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
+			scriptDataPtr += 2;
 		}
 
-		do {
-			if (scriptDataLeft < 2)
+		for (;;) {
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script %d", _nr);
 
-			blockType = READ_LE_UINT16(scriptDataPtr);
-			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
+			blockType = scriptDataPtr.getUint16LEAt(0);
+			scriptDataPtr += 2;
 			if (blockType == 0) // end of blocks detected
 				break;
 
-			if (scriptDataLeft < 2)
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script %d", _nr);
 
-			blockSize = READ_LE_UINT16(scriptDataPtr);
+			blockSize = scriptDataPtr.getUint16LEAt(0);
 			if (blockSize < 4)
 				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
-			blockSize      -= 4; // block size includes block-type UINT16 and block-size UINT16
-			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
+			blockSize     -= 4; // block size includes block-type UINT16 and block-size UINT16
+			scriptDataPtr += 2;
 
-			if (scriptDataLeft < blockSize)
+			if (scriptDataPtr.size() < blockSize)
 				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
 
 			switch (blockType) {
@@ -284,7 +290,7 @@ void Script::identifyOffsets() {
 				typeObject_id++;
 				arrayEntry.type       = SCI_SCR_OFFSET_TYPE_OBJECT;
 				arrayEntry.id         = typeObject_id;
-				arrayEntry.offset     = scriptDataPtr - _buf + 8; // Calculate offset inside script data (VM uses +8)
+				arrayEntry.offset     = scriptDataPtr - *_buf + 8; // Calculate offset inside script data (VM uses +8)
 				arrayEntry.stringSize = 0;
 				_offsetLookupArray.push_back(arrayEntry);
 				_offsetLookupObjectCount++;
@@ -292,18 +298,17 @@ void Script::identifyOffsets() {
 
 			case SCI_OBJ_STRINGS:
 				// string block detected, we now grab all NUL terminated strings out of this block
-				stringDataPtr  = scriptDataPtr;
-				stringDataLeft = blockSize;
+				stringDataPtr = scriptDataPtr.subspan(0, blockSize);
 
 				arrayEntry.type       = SCI_SCR_OFFSET_TYPE_STRING;
 
-				do {
-					if (stringDataLeft < 1) // no more bytes left
+				for (;;) {
+					if (stringDataPtr.size() < 1) // no more bytes left
 						break;
 
 					stringStartPtr = stringDataPtr;
 
-					if (stringDataLeft == 1) {
+					if (stringDataPtr.size() == 1) {
 						// only 1 byte left and that byte is a [00], in that case we also exit
 						stringDataByte = *stringStartPtr;
 						if (stringDataByte == 0x00)
@@ -311,46 +316,44 @@ void Script::identifyOffsets() {
 					}
 
 					// now look for terminating [NUL]
-					do {
+					for (;;) {
 						stringDataByte = *stringDataPtr;
 						stringDataPtr++;
-						stringDataLeft--;
 						if (!stringDataByte) // NUL found, exit this loop
 							break;
-						if (stringDataLeft < 1) {
+						if (stringDataPtr.size() < 1) {
 							// no more bytes left
 							warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr);
 							break;
 						}
-					} while (1);
+					}
 
 					if (stringDataByte)
 						break;
 
 					typeString_id++;
 					arrayEntry.id         = typeString_id;
-					arrayEntry.offset     = stringStartPtr - _buf; // Calculate offset inside script data
+					arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
 					arrayEntry.stringSize = stringDataPtr - stringStartPtr;
 					_offsetLookupArray.push_back(arrayEntry);
 					_offsetLookupStringCount++;
-				} while (1);
+				}
 				break;
 
 			case SCI_OBJ_SAID:
 				// said block detected, we now try to find every single said "string" inside this block
 				// said strings are terminated with a 0xFF, the string itself may contain words (2 bytes), where
 				//  the second byte of a word may also be a 0xFF.
-				stringDataPtr  = scriptDataPtr;
-				stringDataLeft = blockSize;
+				stringDataPtr  = scriptDataPtr.subspan(0, blockSize);
 
 				arrayEntry.type       = SCI_SCR_OFFSET_TYPE_SAID;
 
-				do {
-					if (stringDataLeft < 1) // no more bytes left
+				for (;;) {
+					if (stringDataPtr.size() < 1) // no more bytes left
 						break;
 
 					stringStartPtr = stringDataPtr;
-					if (stringDataLeft == 1) {
+					if (stringDataPtr.size() == 1) {
 						// only 1 byte left and that byte is a [00], in that case we also exit
 						// happens in some scripts, for example Conquests of Camelot, script 997
 						// may have been a bug in the compiler or just an intentional filler byte
@@ -360,30 +363,28 @@ void Script::identifyOffsets() {
 					}
 
 					// now look for terminating 0xFF
-					do {
+					for (;;) {
 						stringDataByte = *stringDataPtr;
 						stringDataPtr++;
-						stringDataLeft--;
 						if (stringDataByte == 0xFF) // Terminator found, exit this loop
 							break;
-						if (stringDataLeft < 1) // no more bytes left
+						if (stringDataPtr.size() < 1) // no more bytes left
 							error("Script::identifyOffsets(): said-string without terminator in script %d", _nr);
 						if (stringDataByte < 0xF0) {
 							// Part of a word, skip second byte
 							stringDataPtr++;
-							stringDataLeft--;
-							if (stringDataLeft < 1) // no more bytes left
+							if (stringDataPtr.size() < 1) // no more bytes left
 								error("Script::identifyOffsets(): said-string without terminator in script %d", _nr);
 						}
-					} while (1);
+					}
 
 					typeSaid_id++;
 					arrayEntry.id         = typeSaid_id;
-					arrayEntry.offset     = stringStartPtr - _buf; // Calculate offset inside script data
+					arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
 					arrayEntry.stringSize = 0;
 					_offsetLookupArray.push_back(arrayEntry);
 					_offsetLookupSaidCount++;
-				} while (1);
+				}
 				break;
 
 			default:
@@ -391,48 +392,44 @@ void Script::identifyOffsets() {
 			}
 
 			scriptDataPtr  += blockSize;
-			scriptDataLeft -= blockSize;
-		} while (1);
+		}
 
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
 		// Strings in SCI1.1 up to SCI2 come after the object instances
-		scriptDataPtr = _heapStart;
-		scriptDataLeft = _heapSize;
+		scriptDataPtr = _heap;
 
 		enum {
-			kExportSize = 2,
-			kPropertySize = 2,
-			kNumMethodsSize = 2,
+			kExportSize = sizeof(uint16),
+			kPropertySize = sizeof(uint16),
+			kNumMethodsSize = sizeof(uint16),
 			kPropDictEntrySize = 2,
 			kMethDictEntrySize = 4
 		};
 
-		const byte *hunkPtr = _buf + kSci11ExportTableOffset + _numExports * kExportSize;
+		SciSpan<const byte> hunkPtr = _buf->subspan(kSci11ExportTableOffset + _numExports * kExportSize);
 
-		if (scriptDataLeft < 4)
+		if (scriptDataPtr.size() < 4)
 			error("Script::identifyOffsets(): unexpected end of script in script %d", _nr);
 
-		uint16 endOfStringOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
-		uint16 objectStartOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr + 2) * 2 + 4;
+		uint16 endOfStringOffset = scriptDataPtr.getUint16SEAt(0);
+		uint16 objectStartOffset = scriptDataPtr.getUint16SEAt(2) * 2 + 4;
 
-		if (scriptDataLeft < objectStartOffset)
+		if (scriptDataPtr.size() < objectStartOffset)
 			error("Script::identifyOffsets(): object start is beyond heap size in script %d", _nr);
-		if (scriptDataLeft < endOfStringOffset)
+		if (scriptDataPtr.size() < endOfStringOffset)
 			error("Script::identifyOffsets(): end of string is beyond heap size in script %d", _nr);
 
-		const byte *endOfStringPtr    = scriptDataPtr + endOfStringOffset;
+		SciSpan<const byte> endOfStringPtr = scriptDataPtr.subspan(endOfStringOffset);
 
 		scriptDataPtr  += objectStartOffset;
-		scriptDataLeft -= objectStartOffset;
 
 		// go through all objects
-		do {
-			if (scriptDataLeft < 2)
+		for (;;) {
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script %d", _nr);
 
-			blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
+			blockType = scriptDataPtr.getUint16SEAt(0);
 			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
 			if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER)
 				break;
 
@@ -440,77 +437,73 @@ void Script::identifyOffsets() {
 			typeObject_id++;
 			arrayEntry.type       = SCI_SCR_OFFSET_TYPE_OBJECT;
 			arrayEntry.id         = typeObject_id;
-			arrayEntry.offset     = scriptDataPtr - _buf - 2; // the VM uses a pointer to the Magic-Number
+			arrayEntry.offset     = scriptDataPtr - *_buf - 2; // the VM uses a pointer to the Magic-Number
 			arrayEntry.stringSize = 0;
 			_offsetLookupArray.push_back(arrayEntry);
 			_offsetLookupObjectCount++;
 
-			if (scriptDataLeft < 2)
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script in script %d", _nr);
 
-			const uint16 numProperties = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
+			const uint16 numProperties = scriptDataPtr.getUint16SEAt(0);
 			blockSize = numProperties * kPropertySize;
 			if (blockSize < 4)
 				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
 			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
 
-			const uint16 scriptNum = READ_SCI11ENDIAN_UINT16(scriptDataPtr + 6);
+			const uint16 scriptNum = scriptDataPtr.getUint16SEAt(6);
 
 			if (scriptNum != 0xFFFF) {
 				hunkPtr += numProperties * kPropDictEntrySize;
 			}
 
-			const uint16 numMethods = READ_SCI11ENDIAN_UINT16(hunkPtr);
+			const uint16 numMethods = hunkPtr.getUint16SEAt(0);
 			hunkPtr += kNumMethodsSize + numMethods * kMethDictEntrySize;
 
 			blockSize -= 4; // blocksize contains UINT16 type and UINT16 size
-			if (scriptDataLeft < blockSize)
+			if (scriptDataPtr.size() < blockSize)
 				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
 
 			scriptDataPtr  += blockSize;
-			scriptDataLeft -= blockSize;
-		} while (1);
+		}
 
-		_codeOffset = hunkPtr - _buf;
+		_codeOffset = hunkPtr - *_buf;
 
 		// now scriptDataPtr points to right at the start of the strings
 		if (scriptDataPtr > endOfStringPtr)
 			error("Script::identifyOffsets(): string block / end-of-string block mismatch in script %d", _nr);
 
-		stringDataPtr  = scriptDataPtr;
-		stringDataLeft = endOfStringPtr - scriptDataPtr; // Calculate byte count within string-block
+		stringDataPtr = scriptDataPtr.subspan(0, endOfStringPtr - scriptDataPtr);
 
 		arrayEntry.type       = SCI_SCR_OFFSET_TYPE_STRING;
-		do {
-			if (stringDataLeft < 1) // no more bytes left
+		for (;;) {
+			if (stringDataPtr.size() < 1) // no more bytes left
 				break;
 
 			stringStartPtr = stringDataPtr;
 			// now look for terminating [NUL]
-			do {
+			for (;;) {
 				stringDataByte = *stringDataPtr;
 				stringDataPtr++;
-				stringDataLeft--;
 				if (!stringDataByte) // NUL found, exit this loop
 					break;
-				if (stringDataLeft < 1) {
+				if (stringDataPtr.size() < 1) {
 				    // no more bytes left
 					warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr);
 					break;
 				}
-			} while (1);
+			}
 
 			if (stringDataByte)
 				break;
 
 			typeString_id++;
 			arrayEntry.id         = typeString_id;
-			arrayEntry.offset     = stringStartPtr - _buf; // Calculate offset inside script data
+			arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
 			arrayEntry.stringSize = stringDataPtr - stringStartPtr;
 			_offsetLookupArray.push_back(arrayEntry);
 			_offsetLookupStringCount++;
-		} while (1);
+		}
 
 	} else if (getSciVersion() == SCI_VERSION_3) {
 		// SCI3
@@ -518,25 +511,23 @@ void Script::identifyOffsets() {
 		uint32 sci3RelocationOffset = 0;
 		uint32 sci3BoundaryOffset = 0;
 
-		if (_bufSize < 22)
+		if (_buf->size() < 22)
 			error("Script::identifyOffsets(): script %d smaller than expected SCI3-header", _nr);
 
-		sci3StringOffset = READ_LE_UINT32(_buf + 4);
-		sci3RelocationOffset = READ_LE_UINT32(_buf + 8);
+		sci3StringOffset = _buf->getUint32LEAt(4);
+		sci3RelocationOffset = _buf->getUint32LEAt(8);
 
-		if (sci3RelocationOffset > _bufSize)
+		if (sci3RelocationOffset > _buf->size())
 			error("Script::identifyOffsets(): relocation offset is beyond end of script %d", _nr);
 
 		// First we get all the objects
 		scriptDataPtr = getSci3ObjectsPointer();
-		scriptDataLeft = _bufSize - (scriptDataPtr - _buf);
-		do {
-			if (scriptDataLeft < 2)
+		for (;;) {
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script %d", _nr);
 
-			blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
-			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
+			blockType = scriptDataPtr.getUint16SEAt(0);
+			scriptDataPtr += 2;
 			if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER)
 				break;
 
@@ -544,48 +535,45 @@ void Script::identifyOffsets() {
 			typeObject_id++;
 			arrayEntry.type       = SCI_SCR_OFFSET_TYPE_OBJECT;
 			arrayEntry.id         = typeObject_id;
-			arrayEntry.offset     = scriptDataPtr - _buf - 2; // the VM uses a pointer to the Magic-Number
+			arrayEntry.offset     = scriptDataPtr - *_buf - 2; // the VM uses a pointer to the Magic-Number
 			arrayEntry.stringSize = 0;
 			_offsetLookupArray.push_back(arrayEntry);
 			_offsetLookupObjectCount++;
 
-			if (scriptDataLeft < 2)
+			if (scriptDataPtr.size() < 2)
 				error("Script::identifyOffsets(): unexpected end of script in script %d", _nr);
 
-			blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr);
+			blockSize = scriptDataPtr.getUint16SEAt(0);
 			if (blockSize < 4)
 				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
 			scriptDataPtr  += 2;
-			scriptDataLeft -= 2;
 			blockSize -= 4; // blocksize contains UINT16 type and UINT16 size
-			if (scriptDataLeft < blockSize)
+			if (scriptDataPtr.size() < blockSize)
 				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
 
 			scriptDataPtr  += blockSize;
-			scriptDataLeft -= blockSize;
-		} while (1);
+		}
 
 		// And now we get all the strings
 		if (sci3StringOffset > 0) {
 			// string offset set, we expect strings
-			if (sci3StringOffset > _bufSize)
+			if (sci3StringOffset > _buf->size())
 				error("Script::identifyOffsets(): string offset is beyond end of script %d", _nr);
 
 			if (sci3RelocationOffset < sci3StringOffset)
 				error("Script::identifyOffsets(): string offset points beyond relocation offset in script %d", _nr);
 
-			stringDataPtr  = _buf + sci3StringOffset;
-			stringDataLeft = sci3RelocationOffset - sci3StringOffset;
+			stringDataPtr  = _buf->subspan(sci3StringOffset, sci3RelocationOffset - sci3StringOffset);
 
 			arrayEntry.type       = SCI_SCR_OFFSET_TYPE_STRING;
 
-			do {
-				if (stringDataLeft < 1) // no more bytes left
+			for (;;) {
+				if (stringDataPtr.size() < 1) // no more bytes left
 					break;
 
 				stringStartPtr = stringDataPtr;
 
-				if (stringDataLeft == 1) {
+				if (stringDataPtr.size() == 1) {
 					// only 1 byte left and that byte is a [00], in that case we also exit
 					stringDataByte = *stringStartPtr;
 					if (stringDataByte == 0x00)
@@ -593,60 +581,57 @@ void Script::identifyOffsets() {
 				}
 
 				// now look for terminating [NUL]
-				do {
+				for (;;) {
 					stringDataByte = *stringDataPtr;
 					stringDataPtr++;
-					stringDataLeft--;
 					if (!stringDataByte) // NUL found, exit this loop
 						break;
-					if (stringDataLeft < 1) {
+					if (stringDataPtr.size() < 1) {
 						// no more bytes left
 						warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr);
 						break;
 					}
-				} while (1);
+				}
 
 				if (stringDataByte)
 					break;
 
 				typeString_id++;
 				arrayEntry.id         = typeString_id;
-				arrayEntry.offset     = stringStartPtr - _buf; // Calculate offset inside script data
+				arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
 				arrayEntry.stringSize = stringDataPtr - stringStartPtr;
 				_offsetLookupArray.push_back(arrayEntry);
 				_offsetLookupStringCount++;
 
 				// SCI3 seems to have aligned all string on DWORD boundaries
-				sci3BoundaryOffset = stringDataPtr - _buf; // Calculate current offset inside script data
+				sci3BoundaryOffset = stringDataPtr - *_buf; // Calculate current offset inside script data
 				sci3BoundaryOffset = sci3BoundaryOffset & 3; // Check boundary offset
 				if (sci3BoundaryOffset) {
 					// lower 2 bits are set? Then we have to adjust the offset
 					sci3BoundaryOffset = 4 - sci3BoundaryOffset;
-					if (stringDataLeft < sci3BoundaryOffset)
+					if (stringDataPtr.size() < sci3BoundaryOffset)
 						error("Script::identifyOffsets(): SCI3 string boundary adjustment goes beyond end of string block in script %d", _nr);
-					stringDataLeft -= sci3BoundaryOffset;
 					stringDataPtr += sci3BoundaryOffset;
 				}
-			} while (1);
+			}
 		}
-		return;
 	}
 }
 
-const byte *Script::getSci3ObjectsPointer() {
-	const byte *ptr = 0;
+SciSpan<const byte> Script::getSci3ObjectsPointer() {
+	SciSpan<const byte> ptr;
 
 	// SCI3 local variables always start dword-aligned
 	if (_numExports % 2)
-		ptr = _buf + 22 + _numExports * 2;
+		ptr = _buf->subspan(22 + _numExports * sizeof(uint16));
 	else
-		ptr = _buf + 24 + _numExports * 2;
+		ptr = _buf->subspan(24 + _numExports * sizeof(uint16));
 
 	// SCI3 object structures always start dword-aligned
 	if (_localsCount % 2)
-		ptr += 2 + _localsCount * 2;
+		ptr += 2 + _localsCount * sizeof(uint16);
 	else
-		ptr += _localsCount * 2;
+		ptr += _localsCount * sizeof(uint16);
 
 	return ptr;
 }
@@ -669,13 +654,13 @@ Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
 	if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
 		obj_pos.incOffset(8);	// magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
 
-	if (obj_pos.getOffset() >= _bufSize)
+	if (obj_pos.getOffset() >= _buf->size())
 		error("Attempt to initialize object beyond end of script");
 
 	// Get the object at the specified position and init it. This will
 	// automatically "allocate" space for it in the _objects map if necessary.
 	Object *obj = &_objects[obj_pos.getOffset()];
-	obj->init(_buf, obj_pos, fullObjectInit);
+	obj->init(*_buf, obj_pos, fullObjectInit);
 
 	return obj;
 }
@@ -705,14 +690,14 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
 }
 
 int Script::relocateOffsetSci3(uint32 offset) const {
-	int relocStart = READ_LE_UINT32(_buf + 8);
-	int relocCount = READ_LE_UINT16(_buf + 18);
-	const byte *seeker = _buf + relocStart;
+	int relocStart = _buf->getUint32LEAt(8);
+	int relocCount = _buf->getUint16LEAt(18);
+	SciSpan <const byte> seeker = _buf->subspan(relocStart);
 
 	for (int i = 0; i < relocCount; ++i) {
-		if (READ_SCI11ENDIAN_UINT32(seeker) == offset) {
+		if (seeker.getUint32SEAt(0) == offset) {
 			// TODO: Find out what UINT16 at (seeker + 8) means
-			return READ_SCI11ENDIAN_UINT16(_buf + offset) + READ_SCI11ENDIAN_UINT32(seeker + 4);
+			return _buf->getUint16SEAt(offset) + seeker.getUint32SEAt(4);
 		}
 		seeker += 10;
 	}
@@ -722,39 +707,37 @@ int Script::relocateOffsetSci3(uint32 offset) const {
 
 bool Script::relocateLocal(SegmentId segment, int location) {
 	if (_localsBlock)
-		return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
+		return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _script.size());
 	else
 		return false;
 }
 
 void Script::relocateSci0Sci21(reg_t block) {
-	const byte *heap = _buf;
-	uint16 heapSize = (uint16)_bufSize;
+	SciSpan<const byte> heap = *_buf;
 	uint16 heapOffset = 0;
 
 	if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-		heap = _heapStart;
-		heapSize = (uint16)_heapSize;
-		heapOffset = _scriptSize;
+		heap = _heap;
+		heapOffset = _script.size();
 	}
 
-	if (block.getOffset() >= (uint16)heapSize ||
-		READ_SCI11ENDIAN_UINT16(heap + block.getOffset()) * 2 + block.getOffset() >= (uint16)heapSize)
+	if (block.getOffset() >= (uint16)heap.size() ||
+		heap.getUint16SEAt(block.getOffset()) * 2 + block.getOffset() >= (uint16)heap.size())
 	    error("Relocation block outside of script");
 
-	int count = READ_SCI11ENDIAN_UINT16(heap + block.getOffset());
+	int count = heap.getUint16SEAt(block.getOffset());
 	int exportIndex = 0;
 	int pos = 0;
 
 	for (int i = 0; i < count; i++) {
-		pos = READ_SCI11ENDIAN_UINT16(heap + block.getOffset() + 2 + (exportIndex * 2)) + heapOffset;
+		pos = heap.getUint16SEAt(block.getOffset() + 2 + (exportIndex * 2)) + heapOffset;
 		// This occurs in SCI01/SCI1 games where usually one export value is
 		// zero. It seems that in this situation, we should skip the export and
 		// move to the next one, though the total count of valid exports remains
 		// the same
 		if (!pos) {
 			exportIndex++;
-			pos = READ_SCI11ENDIAN_UINT16(heap + block.getOffset() + 2 + (exportIndex * 2)) + heapOffset;
+			pos = heap.getUint16SEAt(block.getOffset() + 2 + (exportIndex * 2)) + heapOffset;
 			if (!pos)
 				error("Script::relocate(): Consecutive zero exports found");
 		}
@@ -768,7 +751,7 @@ void Script::relocateSci0Sci21(reg_t block) {
 			// object, relocate it.
 			const ObjMap::iterator end = _objects.end();
 			for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
-				if (it->_value.relocateSci0Sci21(block.getSegment(), pos, _scriptSize))
+				if (it->_value.relocateSci0Sci21(block.getSegment(), pos, _script.size()))
 					break;
 		}
 
@@ -777,18 +760,18 @@ void Script::relocateSci0Sci21(reg_t block) {
 }
 
 void Script::relocateSci3(reg_t block) {
-	const byte *relocStart = _buf + READ_SCI11ENDIAN_UINT32(_buf + 8);
+	SciSpan<const byte> relocStart = _buf->subspan(_buf->getUint32SEAt(8));
 	//int count = _bufSize - READ_SCI11ENDIAN_UINT32(_buf + 8);
 
 	ObjMap::iterator it;
 	for (it = _objects.begin(); it != _objects.end(); ++it) {
-		const byte *seeker = relocStart;
-		while (seeker < _buf + _bufSize) {
+		SciSpan<const byte> seeker = relocStart;
+		while (seeker.size()) {
 			// TODO: Find out what UINT16 at (seeker + 8) means
 			it->_value.relocateSci3(block.getSegment(),
-						READ_SCI11ENDIAN_UINT32(seeker),
-						READ_SCI11ENDIAN_UINT32(seeker + 4),
-						_scriptSize);
+						seeker.getUint32SEAt(0),
+						seeker.getUint32SEAt(4),
+						_script.size());
 			seeker += 10;
 		}
 	}
@@ -816,7 +799,7 @@ void Script::setLockers(int lockers) {
 uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) {
 	bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
 
-	if (_numExports <= pubfunct) {
+	if (_numExports <= (uint)pubfunct) {
 		error("validateExportFunc(): pubfunct is invalid");
 		return 0;
 	}
@@ -827,10 +810,10 @@ uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) {
 	uint32 offset;
 
 	if (getSciVersion() != SCI_VERSION_3) {
-		offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
+		offset = _exports.getUint16SEAt(pubfunct);
 	} else {
 		if (!relocSci3)
-			offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct) + getCodeBlockOffsetSci3();
+			offset = _exports.getUint16SEAt(pubfunct) + getCodeBlockOffsetSci3();
 		else
 			offset = relocateOffsetSci3(pubfunct * 2 + 22);
 	}
@@ -842,11 +825,11 @@ uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) {
 	// is located at a specific address, thus findBlockSCI0() won't work.
 	// Fixes bugs #3039785 and #3037595.
 	if (offset < 10 && getSciVersion() <= SCI_VERSION_1_LATE) {
-		const uint16 *secondExportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS, 0);
+		const SciSpan<const uint16> secondExportTable = findBlockSCI0(SCI_OBJ_EXPORTS, 0).subspan<const uint16>(0);
 
 		if (secondExportTable) {
-			secondExportTable += 3;	// skip header plus 2 bytes (secondExportTable is a uint16 pointer)
-			offset = READ_SCI11ENDIAN_UINT16(secondExportTable + pubfunct);
+			// 3 skips header plus 2 bytes (secondExportTable is a uint16 pointer)
+			offset = secondExportTable.getUint16SEAt(3 + pubfunct);
 		}
 	}
 
@@ -855,61 +838,58 @@ uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) {
 		offset = _codeOffset;
 	}
 
-	if (offset >= _bufSize)
+	if (offset >= _buf->size())
 		error("Invalid export function pointer");
 
 	return offset;
 }
 
-byte *Script::findBlockSCI0(int type, int startBlockIndex) {
-	byte *buf = _buf;
+SciSpan<const byte> Script::findBlockSCI0(ScriptObjectTypes type, int startBlockIndex) {
+	SciSpan<const byte> buf = *_buf;
 	bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
 	int blockIndex = 0;
 
 	if (oldScriptHeader)
 		buf += 2;
 
-	do {
-		int blockType = READ_LE_UINT16(buf);
+	for (;;) {
+		const int blockType = buf.getUint16LEAt(0);
 
 		if (blockType == 0)
 			break;
-		if (blockType == type && blockIndex > startBlockIndex)
-			return buf;
 
-		int blockSize = READ_LE_UINT16(buf + 2);
+		// the size in the block header includes the size of the header itself
+		const int blockSize = buf.getUint16LEAt(2);
 		assert(blockSize > 0);
+
+		if (blockType == type && blockIndex > startBlockIndex) {
+			return buf.subspan(0, blockSize, Common::String::format("%s, %s block", _buf->name().c_str(), sciObjectTypeNames[type]));
+		}
+
 		buf += blockSize;
 		blockIndex++;
-	} while (1);
+	}
 
-	return NULL;
+	return SciSpan<const byte>();
 }
 
 // memory operations
 
-void Script::mcpyInOut(int dst, const void *src, size_t n) {
-	if (_buf) {
-		assert(dst + n <= _bufSize);
-		memcpy(_buf + dst, src, n);
-	}
-}
-
 bool Script::isValidOffset(uint16 offset) const {
-	return offset < _bufSize;
+	return offset < _buf->size();
 }
 
 SegmentRef Script::dereference(reg_t pointer) {
-	if (pointer.getOffset() > _bufSize) {
-		error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
-				  PRINT_REG(pointer), (uint)_bufSize);
+	if (pointer.getOffset() > _buf->size()) {
+		error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%lu)",
+				  PRINT_REG(pointer), _buf->size());
 		return SegmentRef();
 	}
 
 	SegmentRef ret;
 	ret.isRaw = true;
-	ret.maxSize = _bufSize - pointer.getOffset();
-	ret.raw = _buf + pointer.getOffset();
+	ret.maxSize = _buf->size() - pointer.getOffset();
+	ret.raw = const_cast<byte *>(_buf->getUnsafeDataAt(pointer.getOffset(), ret.maxSize));
 	return ret;
 }
 
@@ -938,10 +918,10 @@ void Script::initializeLocals(SegManager *segMan) {
 	LocalVariables *locals = allocLocalsSegment(segMan);
 	if (locals) {
 		if (getSciVersion() > SCI_VERSION_0_EARLY) {
-			const byte *base = (const byte *)(_buf + getLocalsOffset());
+			const SciSpan<const byte> base = _buf->subspan(getLocalsOffset());
 
 			for (uint16 i = 0; i < getLocalsCount(); i++)
-				locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2));
+				locals->_locals[i] = make_reg(0, base.getUint16SEAt(i * 2));
 		} else {
 			// In SCI0 early, locals are set at run time, thus zero them all here
 			for (uint16 i = 0; i < getLocalsCount(); i++)
@@ -955,14 +935,19 @@ void Script::syncLocalsBlock(SegManager *segMan) {
 }
 
 void Script::initializeClasses(SegManager *segMan) {
-	const byte *seeker = 0;
+	SciSpan<const byte> seeker;
 	uint16 mult = 0;
 
 	if (getSciVersion() <= SCI_VERSION_1_LATE) {
-		seeker = findBlockSCI0(SCI_OBJ_CLASS);
+		seeker = _script;
 		mult = 1;
+
+		// SCI0 early has an extra two bytes of header
+		if (getSciVersion() == SCI_VERSION_0_EARLY) {
+			seeker += 2;
+		}
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-		seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+		seeker = _heap.subspan(4 + _heap.getUint16SEAt(2) * 2);
 		mult = 2;
 	} else if (getSciVersion() == SCI_VERSION_3) {
 		seeker = getSci3ObjectsPointer();
@@ -977,10 +962,10 @@ void Script::initializeClasses(SegManager *segMan) {
 	uint32 classpos;
 	int16 species = 0;
 
-	while (true) {
+	for (;;) {
 		// In SCI0-SCI1, this is the segment type. In SCI11, it's a marker (0x1234)
-		marker = READ_SCI11ENDIAN_UINT16(seeker);
-		classpos = seeker - _buf;
+		marker = seeker.getUint16SEAt(0);
+		classpos = seeker - *_buf;
 
 		if (getSciVersion() <= SCI_VERSION_1_LATE && !marker)
 			break;
@@ -991,14 +976,14 @@ void Script::initializeClasses(SegManager *segMan) {
 		if (getSciVersion() <= SCI_VERSION_1_LATE) {
 			isClass = (marker == SCI_OBJ_CLASS);
 			if (isClass)
-				species = READ_SCI11ENDIAN_UINT16(seeker + 12);
+				species = seeker.getUint16SEAt(12);
 			classpos += 12;
 		} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-			isClass = (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass);	// -info- selector
-			species = READ_SCI11ENDIAN_UINT16(seeker + 10);
+			isClass = (seeker.getUint16SEAt(14) & kInfoFlagClass);	// -info- selector
+			species = seeker.getUint16SEAt(10);
 		} else if (getSciVersion() == SCI_VERSION_3) {
-			isClass = (READ_SCI11ENDIAN_UINT16(seeker + 10) & kInfoFlagClass);
-			species = READ_SCI11ENDIAN_UINT16(seeker + 4);
+			isClass = (seeker.getUint16SEAt(10) & kInfoFlagClass);
+			species = seeker.getUint16SEAt(4);
 		}
 
 		if (isClass) {
@@ -1022,7 +1007,7 @@ void Script::initializeClasses(SegManager *segMan) {
 			segMan->setClassOffset(species, make_reg(segmentId, classpos));
 		}
 
-		seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * mult;
+		seeker += seeker.getUint16SEAt(2) * mult;
 	}
 }
 
@@ -1032,10 +1017,10 @@ void Script::initializeObjectsSci0(SegManager *segMan, SegmentId segmentId) {
 	// We need to make two passes, as the objects in the script might be in the
 	// wrong order (e.g. in the demo of Iceman) - refer to bug #3034713
 	for (int pass = 1; pass <= 2; pass++) {
-		const byte *seeker = _buf + (oldScriptHeader ? 2 : 0);
+		SciSpan<const byte> seeker = _buf->subspan(oldScriptHeader ? 2 : 0);
 
 		do {
-			uint16 objType = READ_SCI11ENDIAN_UINT16(seeker);
+			uint16 objType = seeker.getUint16SEAt(0);
 			if (!objType)
 				break;
 
@@ -1043,7 +1028,7 @@ void Script::initializeObjectsSci0(SegManager *segMan, SegmentId segmentId) {
 			case SCI_OBJ_OBJECT:
 			case SCI_OBJ_CLASS:
 				{
-					reg_t addr = make_reg(segmentId, seeker - _buf + 4);
+					reg_t addr = make_reg(segmentId, seeker - *_buf + 4);
 					Object *obj = scriptObjInit(addr);
 					obj->initSpecies(segMan, addr);
 
@@ -1069,20 +1054,20 @@ void Script::initializeObjectsSci0(SegManager *segMan, SegmentId segmentId) {
 				break;
 			}
 
-			seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
-		} while ((uint32)(seeker - _buf) < getScriptSize() - 2);
+			seeker += seeker.getUint16SEAt(2);
+		} while ((uint32)(seeker - *_buf) < getScriptSize() - 2);
 	}
 
-	byte *relocationBlock = findBlockSCI0(SCI_OBJ_POINTERS);
+	const SciSpan<const byte> relocationBlock = findBlockSCI0(SCI_OBJ_POINTERS);
 	if (relocationBlock)
-		relocateSci0Sci21(make_reg(segmentId, relocationBlock - getBuf() + 4));
+		relocateSci0Sci21(make_reg(segmentId, relocationBlock - *_buf + 4));
 }
 
 void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId) {
-	const byte *seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+	SciSpan<const byte> seeker = _heap.subspan(4 + _heap.getUint16SEAt(2) * 2);
 
-	while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
-		reg_t reg = make_reg(segmentId, seeker - _buf);
+	while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
+		reg_t reg = make_reg(segmentId, seeker - *_buf);
 		Object *obj = scriptObjInit(reg);
 
 		// Copy base from species class, as we need its selector IDs
@@ -1113,26 +1098,26 @@ void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId) {
 		// to be sufficient.
 		obj->setClassScriptSelector(make_reg(0, _nr));
 
-		seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+		seeker += seeker.getUint16SEAt(2) * 2;
 	}
 
-	relocateSci0Sci21(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(_heapStart)));
+	relocateSci0Sci21(make_reg(segmentId, _heap.getUint16SEAt(0)));
 }
 
 void Script::initializeObjectsSci3(SegManager *segMan, SegmentId segmentId) {
-	const byte *seeker = getSci3ObjectsPointer();
+	SciSpan<const byte> seeker = getSci3ObjectsPointer();
 
-	while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
+	while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
 		// We call setSegment and setOffset directly here, instead of using
 		// make_reg, as in large scripts, seeker - _buf can be larger than
 		// a 16-bit integer
 		reg_t reg;
 		reg.setSegment(segmentId);
-		reg.setOffset(seeker - _buf);
+		reg.setOffset(seeker - *_buf);
 
 		Object *obj = scriptObjInit(reg);
 		obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0));
-		seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
+		seeker += seeker.getUint16SEAt(2);
 	}
 
 	relocateSci3(make_reg(segmentId, 0));
@@ -1170,7 +1155,7 @@ Common::Array<reg_t> Script::listAllDeallocatable(SegmentId segId) const {
 
 Common::Array<reg_t> Script::listAllOutgoingReferences(reg_t addr) const {
 	Common::Array<reg_t> tmp;
-	if (addr.getOffset() <= _bufSize && addr.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET && offsetIsObject(addr.getOffset())) {
+	if (addr.getOffset() <= _buf->size() && addr.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET && offsetIsObject(addr.getOffset())) {
 		const Object *obj = getObject(addr.getOffset());
 		if (obj) {
 			// Note all local variables, if we have a local variable environment
@@ -1207,7 +1192,7 @@ Common::Array<reg_t> Script::listObjectReferences() const {
 }
 
 bool Script::offsetIsObject(uint16 offset) const {
-	return (READ_SCI11ENDIAN_UINT16((const byte *)_buf + offset + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER);
+	return _buf->getUint16SEAt(offset + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER;
 }
 
 } // End of namespace Sci
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index 677b367..52b58ee 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -24,6 +24,7 @@
 #define SCI_ENGINE_SCRIPT_H
 
 #include "common/str.h"
+#include "sci/util.h"
 #include "sci/engine/segment.h"
 #include "sci/engine/script_patches.h"
 
@@ -67,19 +68,16 @@ typedef Common::Array<offsetLookupArrayEntry> offsetLookupArrayType;
 class Script : public SegmentObj {
 private:
 	int _nr; /**< Script number */
-	byte *_buf; /**< Static data buffer, or NULL if not used */
-	byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
+	Common::SpanOwner<SciSpan<const byte> > _buf; /**< Static data buffer, or NULL if not used */
+	SciSpan<const byte> _script; /**< Script size includes alignment byte */
+	SciSpan<const byte> _heap; /**< Start of heap if SCI1.1, NULL otherwise */
 
 	int _lockers; /**< Number of classes and objects that require this script */
-	size_t _scriptSize;
-	size_t _heapSize;
-	size_t _bufSize;
 
-	const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
-	uint16 _numExports; /**< Number of entries in the exports table */
-
-	const byte *_synonyms; /**< Synonyms block or 0 if not present */
-	uint16 _numSynonyms; /**< Number of entries in the synonyms block */
+	SciSpan<const uint16> _exports; /**< Exports block or 0 if not present */
+	uint16 _numExports; /**< Number of export entries */
+	SciSpan<const byte> _synonyms; /**< Synonyms block or 0 if not present */
+	uint16 _numSynonyms; /**< Number of synonym entries */
 
 	int _codeOffset; /**< The absolute offset of the VM code block */
 
@@ -104,10 +102,11 @@ public:
 	int getLocalsOffset() const { return _localsOffset; }
 	uint16 getLocalsCount() const { return _localsCount; }
 
-	uint32 getScriptSize() const { return _scriptSize; }
-	uint32 getHeapSize() const { return _heapSize; }
-	uint32 getBufSize() const { return _bufSize; }
-	const byte *getBuf(uint offset = 0) const { return _buf + offset; }
+	uint32 getScriptSize() const { return _script.size(); }
+	uint32 getHeapSize() const { return _heap.size(); }
+	uint32 getBufSize() const { return _buf->size(); }
+
+	const byte *getBuf(uint offset = 0) const { return _buf->getUnsafeDataAt(offset); }
 
 	int getScriptNumber() const { return _nr; }
 	SegmentId getLocalsSegment() const { return _localsSegment; }
@@ -192,10 +191,10 @@ public:
 	void setLockers(int lockers);
 
 	/**
-	 * Retrieves a pointer to the exports of this script
-	 * @return	pointer to the exports.
+	 * Retrieves the offset of the export table in the script
+	 * @return	the exports offset.
 	 */
-	const uint16 *getExportTable() const { return _exportTable; }
+	uint getExportsOffset() const { return _exports.sourceByteOffset(); }
 
 	/**
 	 * Retrieves the number of exports of script.
@@ -207,7 +206,7 @@ public:
 	 * Retrieves a pointer to the synonyms associated with this script
 	 * @return	pointer to the synonyms, in non-parsed format.
 	 */
-	const byte *getSynonyms() const { return _synonyms; }
+	const SciSpan<const byte> &getSynonyms() const { return _synonyms; }
 
 	/**
 	 * Retrieves the number of synonyms associated with this script.
@@ -244,18 +243,10 @@ public:
 	}
 
 	/**
-	 * Copies a byte string into a script's heap representation.
-	 * @param dst	script-relative offset of the destination area
-	 * @param src	pointer to the data source location
-	 * @param n		number of bytes to copy
-	 */
-	void mcpyInOut(int dst, const void *src, size_t n);
-
-	/**
 	 * Finds the pointer where a block of a specific type starts from,
 	 * in SCI0 - SCI1 games
 	 */
-	byte *findBlockSCI0(int type, int startBlockIndex = -1);
+	SciSpan<const byte> findBlockSCI0(ScriptObjectTypes type, int startBlockIndex = -1);
 
 	/**
 	 * Syncs the string heap of a script. Used when saving/loading.
@@ -271,7 +262,7 @@ public:
 	/**
 	 * Gets an offset to the beginning of the code block in a SCI3 script
 	 */
-	int getCodeBlockOffsetSci3() { return READ_SCI11ENDIAN_UINT32(_buf); }
+	int getCodeBlockOffsetSci3() { return _buf->getInt32SEAt(0); }
 
 	/**
 	 * Get the offset array
@@ -303,7 +294,7 @@ private:
 	/**
 	 * Gets a pointer to the beginning of the objects in a SCI3 script
 	 */
-	const byte *getSci3ObjectsPointer();
+	SciSpan<const byte> getSci3ObjectsPointer();
 
 	/**
 	 * Initializes the script's objects (SCI0)
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index cf3a981..d84d2ab 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -4975,7 +4975,7 @@ ScriptPatcher::~ScriptPatcher() {
 }
 
 // will actually patch previously found signature area
-void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) {
+void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, SciSpan<byte> scriptData, int32 signatureOffset) {
 	const uint16 *patchData = patchEntry->patchData;
 	byte orgData[PATCH_VALUELIMIT];
 	int32 offset = signatureOffset;
@@ -4983,10 +4983,10 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc
 	uint16 patchSelector = 0;
 
 	// Copy over original bytes from script
-	uint32 orgDataSize = scriptSize - offset;
+	uint32 orgDataSize = scriptData.size() - offset;
 	if (orgDataSize > PATCH_VALUELIMIT)
 		orgDataSize = PATCH_VALUELIMIT;
-	memcpy(&orgData, &scriptData[offset], orgDataSize);
+	scriptData.subspan(offset, orgDataSize).unsafeCopyDataTo(orgData);
 
 	while (patchWord != PATCH_END) {
 		uint16 patchCommand = patchWord & PATCH_COMMANDMASK;
@@ -5082,7 +5082,7 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc
 	}
 }
 
-bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize) {
+bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const SciSpan<const byte> &scriptData) {
 	uint16 sigSelector = 0;
 
 	uint16 sigWord = *signatureData;
@@ -5097,7 +5097,7 @@ bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureDa
 		}
 		case SIG_CODE_UINT16:
 		case SIG_CODE_SELECTOR16: {
-			if ((byteOffset + 1) < scriptSize) {
+			if (byteOffset + 1 < scriptData.size()) {
 				byte byte1;
 				byte byte2;
 
@@ -5134,7 +5134,7 @@ bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureDa
 			break;
 		}
 		case SIG_CODE_SELECTOR8: {
-			if (byteOffset < scriptSize) {
+			if (byteOffset < scriptData.size()) {
 				sigSelector = _selectorIdTable[sigValue];
 				if (sigSelector & 0xFF00)
 					error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty signature: '%s'", signatureDescription);
@@ -5147,7 +5147,7 @@ bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureDa
 			break;
 		}
 		case SIG_CODE_BYTE:
-			if (byteOffset < scriptSize) {
+			if (byteOffset < scriptData.size()) {
 				if (scriptData[byteOffset] != sigWord)
 					sigWord = SIG_MISMATCH;
 				byteOffset++;
@@ -5169,20 +5169,20 @@ bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureDa
 }
 
 // will return -1 if no match was found, otherwise an offset to the start of the signature match
-int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize) {
-	if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
+int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const SciSpan<const byte> &scriptData) {
+	if (scriptData.size() < 4) // we need to find a DWORD, so less than 4 bytes is not okay
 		return -1;
 
 	// magicDWord is in platform-specific BE/LE form, so that the later match will work, this was done for performance
-	const uint32 searchLimit = scriptSize - 3;
+	const uint32 searchLimit = scriptData.size() - 3;
 	uint32 DWordOffset = 0;
 	// first search for the magic DWORD
 	while (DWordOffset < searchLimit) {
-		if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
+		if (magicDWord == scriptData.getUint32At(DWordOffset)) {
 			// magic DWORD found, check if actual signature matches
 			uint32 offset = DWordOffset + magicOffset;
 
-			if (verifySignature(offset, signatureData, patchDescription, scriptData, scriptSize))
+			if (verifySignature(offset, signatureData, patchDescription, scriptData))
 				return offset;
 		}
 		DWordOffset++;
@@ -5191,8 +5191,8 @@ int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uin
 	return -1;
 }
 
-int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
-	return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize);
+int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const SciSpan<const byte> &scriptData) {
+	return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData);
 }
 
 // Attention: Magic DWord is returned using platform specific byte order. This is done on purpose for performance.
@@ -5380,7 +5380,7 @@ void ScriptPatcher::enablePatch(const SciScriptPatcherEntry *patchTable, const c
 		error("Script-Patcher: no patch found to enable");
 }
 
-void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
+void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) {
 	const SciScriptPatcherEntry *signatureTable = NULL;
 	const SciScriptPatcherEntry *curEntry = NULL;
 	SciScriptPatcherRuntimeEntry *curRuntimeEntry = NULL;
@@ -5552,11 +5552,11 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
 				int32 foundOffset = 0;
 				int16 applyCount = curEntry->applyCount;
 				do {
-					foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize);
+					foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData);
 					if (foundOffset != -1) {
 						// found, so apply the patch
 						debugC(kDebugLevelScriptPatcher, "Script-Patcher: '%s' on script %d offset %d", curEntry->description, scriptNr, foundOffset);
-						applyPatch(curEntry, scriptData, scriptSize, foundOffset);
+						applyPatch(curEntry, scriptData, foundOffset);
 					}
 					applyCount--;
 				} while ((foundOffset != -1) && (applyCount));
diff --git a/engines/sci/engine/script_patches.h b/engines/sci/engine/script_patches.h
index b5797be..69f9794 100644
--- a/engines/sci/engine/script_patches.h
+++ b/engines/sci/engine/script_patches.h
@@ -97,14 +97,14 @@ public:
 	void calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset);
 
 	// Called when a script is loaded to check for signature matches and apply patches in such cases
-	void processScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize);
+	void processScript(uint16 scriptNr, SciSpan<byte> scriptData);
 
 	// Verifies, if a given signature matches the given script data (pointed to by additional byte offset)
-	bool verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize);
+	bool verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const SciSpan<const byte> &scriptData);
 
 	// searches for a given signature inside script data
 	// returns -1 in case it was not found or an offset to the matching data
-	int32 findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize);
+	int32 findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const SciSpan<const byte> &scriptData);
 
 private:
 	// Initializes a patch table and creates run time information for it (for enabling/disabling), also calculates magic DWORD)
@@ -115,10 +115,10 @@ private:
 
 	// Searches for a given signature entry inside script data
 	// returns -1 in case it was not found or an offset to the matching data
-	int32 findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize);
+	int32 findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const SciSpan<const byte> &scriptData);
 
 	// Applies a patch to a given script + offset (overwrites parts)
-	void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset);
+	void applyPatch(const SciScriptPatcherEntry *patchEntry, SciSpan<byte> scriptData, int32 signatureOffset);
 
 	Selector *_selectorIdTable;
 	SciScriptPatcherRuntimeEntry *_runtimeTable;
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 6002cbd..d15cf83 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -71,8 +71,6 @@ const char *opcodeNames[] = {
 reg_t disassemble(EngineState *s, reg32_t pos, reg_t objAddr, bool printBWTag, bool printBytecode) {
 	SegmentObj *mobj = s->_segMan->getSegment(pos.getSegment(), SEG_TYPE_SCRIPT);
 	Script *script_entity = NULL;
-	const byte *scr;
-	uint32 scr_size;
 	reg_t retval = make_reg(pos.getSegment(), pos.getOffset() + 1);
 	uint16 param_value = 0xffff; // Suppress GCC warning by setting default value, chose value as invalid to getKernelName etc.
 	uint i = 0;
@@ -84,14 +82,15 @@ reg_t disassemble(EngineState *s, reg32_t pos, reg_t objAddr, bool printBWTag, b
 	} else
 		script_entity = (Script *)mobj;
 
-	scr = script_entity->getBuf();
-	scr_size = script_entity->getBufSize();
+	uint scr_size = script_entity->getBufSize();
 
 	if (pos.getOffset() >= scr_size) {
 		warning("Trying to disassemble beyond end of script");
 		return NULL_REG;
 	}
 
+	const byte *scr = script_entity->getBuf();
+
 	int16 opparams[4];
 	byte opsize;
 	uint bytecount = readPMachineInstruction(scr + pos.getOffset(), opsize, opparams);
@@ -348,12 +347,13 @@ bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpTarget) {
 		return false;
 	Script *script_entity = (Script *)mobj;
 
-	const byte *scr = script_entity->getBuf();
 	uint scr_size = script_entity->getScriptSize();
 
 	if (pos.getOffset() >= scr_size)
 		return false;
 
+	const byte *scr = script_entity->getBuf();
+
 	int16 opparams[4];
 	byte opsize;
 	int bytecount = readPMachineInstruction(scr + pos.getOffset(), opsize, opparams);
@@ -449,107 +449,114 @@ void SciEngine::scriptDebug() {
 	_console->attach();
 }
 
-void Kernel::dumpScriptObject(char *data, int seeker, int objsize) {
-	int selectors, overloads, selectorsize;
-	int species = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 8 + seeker);
-	int superclass = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 10 + seeker);
-	int namepos = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 14 + seeker);
+void Kernel::dumpScriptObject(const SciSpan<const byte> &script, SciSpan <const byte> object) {
+	const int16 species = object.getInt16SEAt(8);
+	const int16 superclass = object.getInt16SEAt(10);
+	const int16 namepos = object.getInt16SEAt(14);
 	int i = 0;
 
 	debugN("Object\n");
 
-	Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker);
 	//-4 because the size includes the two-word header
+	Common::hexdump(object.getUnsafeDataAt(0, object.size() - 4), object.size() - 4, 16, object.sourceByteOffset());
 
-	debugN("Name: %s\n", namepos ? ((char *)(data + namepos)) : "<unknown>");
+	debugN("Name: %s\n", namepos ? script.getStringAt(namepos).c_str() : "<unknown>");
 	debugN("Superclass: %x\n", superclass);
 	debugN("Species: %x\n", species);
-	debugN("-info-:%x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 12 + seeker) & 0xffff);
+	debugN("-info-: %x\n", object.getInt16SEAt(12) & 0xFFFF);
+
+	debugN("Function area offset: %x\n", object.getInt16SEAt(4));
 
-	debugN("Function area offset: %x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + seeker + 4));
-	debugN("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + seeker + 6)));
+	int16 selectors = object.getInt16SEAt(6);
+	debugN("Selectors [%x]:\n", selectors);
 
-	seeker += 8;
+	object += 8;
 
 	while (selectors--) {
-		debugN("  [#%03x] = 0x%x\n", i++, (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker) & 0xffff);
-		seeker += 2;
+		debugN("  [#%03x] = 0x%x\n", i++, object.getInt16SEAt(0) & 0xFFFF);
+		object += 2;
 	}
 
-	debugN("Overridden functions: %x\n", selectors = overloads = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker));
+	selectors = object.getInt16SEAt(0);
+	int16 overloads = selectors;
+	debugN("Overridden functions: %x\n", overloads);
 
-	seeker += 2;
+	object += 2;
 
-	if (overloads < 100)
+	if (overloads < 100) {
 		while (overloads--) {
-			int selector = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + (seeker));
+			const int16 selector = object.getInt16SEAt(0);
 
-			debugN("  [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>");
-			debugN("%04x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + selectors*2 + 2) & 0xffff);
+			debugN("  [%03x] %s: @", selector & 0xFFFF, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>");
+			debugN("%04x\n", object.getInt16SEAt(selectors * 2 + 2) & 0xFFFF);
 
-			seeker += 2;
+			object += 2;
 		}
+	}
 }
 
-void Kernel::dumpScriptClass(char *data, int seeker, int objsize) {
-	int selectors, overloads, selectorsize;
-	int species = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 8 + seeker);
-	int superclass = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 10 + seeker);
-	int namepos = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + 14 + seeker);
+void Kernel::dumpScriptClass(const SciSpan<const byte> &script, SciSpan<const byte> clazz) {
+	const int16 species = clazz.getInt16SEAt(8);
+	const int16 superclass = clazz.getInt16SEAt(10);
+	const int16 namepos = clazz.getInt16SEAt(14);
 
 	debugN("Class\n");
 
-	Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker);
+	Common::hexdump(clazz.getUnsafeDataAt(0, clazz.size() - 4), clazz.size() - 4, 16, clazz.sourceByteOffset());
 
-	debugN("Name: %s\n", namepos ? ((char *)data + namepos) : "<unknown>");
+	debugN("Name: %s\n", namepos ? script.getStringAt(namepos).c_str() : "<unknown>");
 	debugN("Superclass: %x\n", superclass);
 	debugN("Species: %x\n", species);
-	debugN("-info-:%x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + 12 + seeker) & 0xffff);
+	debugN("-info-: %x\n", clazz.getInt16SEAt(12) & 0xFFFF);
+
+	debugN("Function area offset: %x\n", clazz.getInt16SEAt(4));
 
-	debugN("Function area offset: %x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + 4));
-	debugN("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + 6)));
+	int16 selectors = clazz.getInt16SEAt(6);
+	int16 selectorsize = selectors;
+	debugN("Selectors [%x]:\n", selectors);
 
-	seeker += 8;
+	clazz += 8;
 	selectorsize <<= 1;
 
 	while (selectors--) {
-		int selector = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *) data + (seeker) + selectorsize);
+		const int16 selector = clazz.getInt16SEAt(selectorsize);
 
-		debugN("  [%03x] %s = 0x%x\n", 0xffff & selector, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>",
-		          (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker) & 0xffff);
+		debugN("  [%03x] %s = 0x%x\n", selector & 0xFFFF, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>", clazz.getInt16SEAt(0) & 0xFFFF);
 
-		seeker += 2;
+		clazz += 2;
 	}
 
-	seeker += selectorsize;
+	clazz += selectorsize;
 
-	debugN("Overloaded functions: %x\n", selectors = overloads = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker));
+	int16 overloads = clazz.getInt16SEAt(0);
+	selectors = overloads;
+	debugN("Overloaded functions: %x\n", overloads);
 
-	seeker += 2;
+	clazz += 2;
 
 	while (overloads--) {
-		int selector = (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + (seeker));
+		int16 selector = clazz.getInt16SEAt(0);
 		debugN("selector=%d; selectorNames.size() =%d\n", selector, _selectorNames.size());
-		debugN("  [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ?
+		debugN("  [%03x] %s: @", selector & 0xFFFF, (selector >= 0 && selector < (int)_selectorNames.size()) ?
 		          _selectorNames[selector].c_str() : "<?>");
-		debugN("%04x\n", (int16)READ_SCI11ENDIAN_UINT16((unsigned char *)data + seeker + selectors * 2 + 2) & 0xffff);
+		debugN("%04x\n", clazz.getInt16SEAt(selectors * 2 + 2) & 0xFFFF);
 
-		seeker += 2;
+		clazz += 2;
 	}
 }
 
 void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
 	int objectctr[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 	uint32 _seeker = 0;
-	Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), 0);
+	Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), false);
 
 	if (!script) {
 		warning("dissectScript(): Script not found!\n");
 		return;
 	}
 
-	while (_seeker < script->size) {
-		int objType = (int16)READ_SCI11ENDIAN_UINT16(script->data + _seeker);
+	while (_seeker < script->size()) {
+		int objType = script->getInt16SEAt(_seeker);
 		int objsize;
 		uint32 seeker = _seeker + 4;
 
@@ -562,7 +569,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
 
 		debugN("\n");
 
-		objsize = (int16)READ_SCI11ENDIAN_UINT16(script->data + _seeker + 2);
+		objsize = script->getInt16SEAt(_seeker + 2);
 
 		debugN("Obj type #%x, size 0x%x: ", objType, objsize);
 
@@ -573,34 +580,35 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
 
 		switch (objType) {
 		case SCI_OBJ_OBJECT:
-			dumpScriptObject((char *)script->data, seeker, objsize);
+			dumpScriptObject(*script, script->subspan(seeker, objsize));
 			break;
 
 		case SCI_OBJ_CODE:
 			debugN("Code\n");
-			Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 			break;
 
 		case SCI_OBJ_SYNONYMS:
 			debugN("Synonyms\n");
-			Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 			break;
 
 		case SCI_OBJ_SAID:
 			debugN("Said\n");
-			Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 
 			debugN("%04x: ", seeker);
-			vocab->debugDecipherSaidBlock(script->data + seeker);
+			vocab->debugDecipherSaidBlock(script->subspan(seeker));
 			debugN("\n");
 			break;
 
 		case SCI_OBJ_STRINGS:
 			debugN("Strings\n");
-			while (script->data [seeker]) {
-				debugN("%04x: %s", seeker, script->data + seeker);
-				seeker += Common::strnlen((char *)script->data + seeker, script->size - seeker) + 1;
-				if (seeker > script->size) {
+			while (script->getUint8At(seeker)) {
+				const Common::String string = script->getStringAt(seeker);
+				debugN("%04x: %s", seeker, string.c_str());
+				seeker += string.size() + 1;
+				if (seeker > script->size()) {
 					debugN("[TRUNCATED]");
 				}
 				debugN("\n");
@@ -609,27 +617,27 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
 			break;
 
 		case SCI_OBJ_CLASS:
-			dumpScriptClass((char *)script->data, seeker, objsize);
+			dumpScriptClass(*script, script->subspan(seeker, objsize));
 			break;
 
 		case SCI_OBJ_EXPORTS:
 			debugN("Exports\n");
-			Common::hexdump((unsigned char *)script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 			break;
 
 		case SCI_OBJ_POINTERS:
 			debugN("Pointers\n");
-			Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 			break;
 
 		case 9:
 			debugN("<unknown>\n");
-			Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 			break;
 
 		case SCI_OBJ_LOCALVARS:
 			debugN("Local vars\n");
-			Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
+			Common::hexdump(script->getUnsafeDataAt(seeker, objsize - 4), objsize - 4, 16, seeker);
 			break;
 
 		default:
@@ -821,7 +829,7 @@ void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *ke
 							SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]);
 							if (saidSpec.isRaw) {
 								debugN(" ('");
-								g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw);
+								g_sci->getVocabulary()->debugDecipherSaidBlock(SciSpan<const byte>(saidSpec.raw, saidSpec.maxSize, Common::String::format("said %04x:%04x", PRINT_REG(argv[parmNr]))));
 								debugN("')");
 							} else {
 								debugN(" (non-raw said-spec)");
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 9ccd109..3157c84 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -976,11 +976,11 @@ void SegManager::createClassTable() {
 	if (!vocab996)
 		error("SegManager: failed to open vocab 996");
 
-	int totalClasses = vocab996->size >> 2;
+	int totalClasses = vocab996->size() >> 2;
 	_classTable.resize(totalClasses);
 
 	for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
-		uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
+		uint16 scriptNr = vocab996->getUint16SEAt(classNr * 4 + 2);
 
 		_classTable[classNr].reg = NULL_REG;
 		_classTable[classNr].script = scriptNr;
@@ -993,15 +993,13 @@ reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, uint16 calle
 
 	if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) {
 		error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size());
-		return NULL_REG;
 	} else {
 		Class *the_class = &_classTable[classnr];
 		if (!the_class->reg.getSegment()) {
 			getScriptSegment(the_class->script, lock);
 
 			if (!the_class->reg.getSegment()) {
-				error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
-				return NULL_REG;
+				error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed", classnr, the_class->script, the_class->script);
 			}
 		} else
 			if (callerSegment != the_class->reg.getSegment())
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index ed913b2..84211fd 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -929,7 +929,7 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
 							}
 
 							// now actually check for signature match
-							if (g_sci->getScriptPatcher()->verifySignature(curLocalCallOffset, workaround->localCallSignature, "workaround signature", curScriptPtr, curScriptSize)) {
+							if (g_sci->getScriptPatcher()->verifySignature(curLocalCallOffset, workaround->localCallSignature, "workaround signature", SciSpan<const byte>(curScriptPtr, curScriptSize))) {
 								matched = true;
 							}
 
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 8d92cb9..317e98f 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -122,7 +122,7 @@ bool GfxAnimate::detectFastCast() {
 	// within that game. Which means even though we detect it as having the capability, it's never actually used.
 	// The original multilingual KQ5 interpreter did have this feature disabled.
 	// Sierra probably used latest system scripts and that's why we detect it.
-	if (_scriptPatcher->findSignature(magicDWord, magicDWordOffset, fastCastSignature, "fast cast detection", scriptData, scriptSize) >= 0) {
+	if (_scriptPatcher->findSignature(magicDWord, magicDWordOffset, fastCastSignature, "fast cast detection", SciSpan<const byte>(scriptData, scriptSize)) >= 0) {
 		// Signature found, game seems to use fast cast for kAnimate
 		return true;
 	}
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 8540b63..75f6280 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -30,6 +30,7 @@
 #include "sci/graphics/remap32.h"
 #include "sci/graphics/text32.h"
 #include "sci/engine/workarounds.h"
+#include "sci/util.h"
 
 namespace Sci {
 #pragma mark CelScaler
@@ -273,8 +274,15 @@ public:
 	_sourceHeight(celObj._height),
 #endif
 	_sourceWidth(celObj._width) {
-		const byte *resource = celObj.getResPointer();
-		_pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24);
+		const SciSpan<const byte> resource = celObj.getResPointer();
+		const uint32 pixelsOffset = resource.getUint32SEAt(celObj._celHeaderOffset + 24);
+		const int32 numPixels = MIN<int32>(resource.size() - pixelsOffset, celObj._width * celObj._height);
+
+		if (numPixels < celObj._width * celObj._height) {
+			warning("%s is truncated", celObj._info.toString().c_str());
+		}
+
+		_pixels = resource.getUnsafeDataAt(pixelsOffset, numPixels);
 	}
 
 	inline const byte *getRow(const int16 y) const {
@@ -285,7 +293,7 @@ public:
 
 struct READER_Compressed {
 private:
-	const byte *const _resource;
+	const SciSpan<const byte> _resource;
 	byte _buffer[kCelScalerTableSize];
 	uint32 _controlOffset;
 	uint32 _dataOffset;
@@ -304,20 +312,38 @@ public:
 	_maxWidth(maxWidth) {
 		assert(maxWidth <= celObj._width);
 
-		const byte *const celHeader = _resource + celObj._celHeaderOffset;
-		_dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24);
-		_uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28);
-		_controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32);
+		const SciSpan<const byte> celHeader = _resource.subspan(celObj._celHeaderOffset);
+		_dataOffset = celHeader.getUint32SEAt(24);
+		_uncompressedDataOffset = celHeader.getUint32SEAt(28);
+		_controlOffset = celHeader.getUint32SEAt(32);
 	}
 
 	inline const byte *getRow(const int16 y) {
 		assert(y >= 0 && y < _sourceHeight);
 		if (y != _y) {
 			// compressed data segment for row
-			const byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4);
+			const uint32 rowOffset = _resource.getUint32SEAt(_controlOffset + y * sizeof(uint32));
+
+			uint32 rowCompressedSize;
+			if (y + 1 < _sourceHeight) {
+				rowCompressedSize = _resource.getUint32SEAt(_controlOffset + (y + 1) * sizeof(uint32)) - rowOffset;
+			} else {
+				rowCompressedSize = _resource.size() - rowOffset - _dataOffset;
+			}
+
+			const byte *row = _resource.getUnsafeDataAt(_dataOffset + rowOffset, rowCompressedSize);
 
 			// uncompressed data segment for row
-			const byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4);
+			const uint32 literalOffset = _resource.getUint32SEAt(_controlOffset + _sourceHeight * sizeof(uint32) + y * sizeof(uint32));
+
+			uint32 literalRowSize;
+			if (y + 1 < _sourceHeight) {
+				literalRowSize = _resource.getUint32SEAt(_controlOffset + _sourceHeight * sizeof(uint32) + (y + 1) * sizeof(uint32)) - literalOffset;
+			} else {
+				literalRowSize = _resource.size() - literalOffset - _uncompressedDataOffset;
+			}
+
+			const byte *literal = _resource.getUnsafeDataAt(_uncompressedDataOffset + literalOffset, literalRowSize);
 
 			uint8 length;
 			for (int16 i = 0; i < _maxWidth; i += length) {
@@ -573,7 +599,8 @@ uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const {
 
 void CelObj::submitPalette() const {
 	if (_hunkPaletteOffset) {
-		const HunkPalette palette(getResPointer() + _hunkPaletteOffset);
+		const SciSpan<const byte> data = getResPointer();
+		const HunkPalette palette(data.subspan(_hunkPaletteOffset));
 		g_sci->_gfxPalette32->submit(palette);
 	}
 }
@@ -817,8 +844,7 @@ int16 CelObjView::getNumLoops(const GuiResourceId viewId) {
 		return 0;
 	}
 
-	assert(resource->size >= 3);
-	return resource->data[2];
+	return resource->getUint8At(2);
 }
 
 int16 CelObjView::getNumCels(const GuiResourceId viewId, int16 loopNo) {
@@ -828,7 +854,7 @@ int16 CelObjView::getNumCels(const GuiResourceId viewId, int16 loopNo) {
 		return 0;
 	}
 
-	const byte *const data = resource->data;
+	const SciSpan<const byte> &data = *resource;
 
 	const uint16 loopCount = data[2];
 
@@ -849,19 +875,14 @@ int16 CelObjView::getNumCels(const GuiResourceId viewId, int16 loopNo) {
 		return 0;
 	}
 
-	const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data);
+	const uint16 viewHeaderSize = data.getUint16SEAt(0);
 	const uint8 loopHeaderSize = data[12];
 	const uint8 viewHeaderFieldSize = 2;
 
-#ifndef NDEBUG
-	const byte *const dataMax = data + resource->size;
-#endif
-	const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopNo);
-	assert(loopHeader + 3 <= dataMax);
+	SciSpan<const byte> loopHeader = data.subspan(viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopNo));
 
-	if ((int8)loopHeader[0] != -1) {
-		loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]);
-		assert(loopHeader >= data && loopHeader + 3 <= dataMax);
+	if (loopHeader.getInt8At(0) != -1) {
+		loopHeader = data.subspan(viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopHeader.getInt8At(0)));
 	}
 
 	return loopHeader[2];
@@ -889,10 +910,6 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 		return;
 	}
 
-	// TODO: The next code should be moved to a common file that
-	// generates view resource metadata for both SCI16 and SCI32
-	// implementations
-
 	const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
 
 	// NOTE: SCI2.1/SQ6 just silently returns here.
@@ -900,10 +917,10 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 		error("View resource %d not found", viewId);
 	}
 
-	const byte *const data = resource->data;
+	const Resource &data = *resource;
 
-	_xResolution = READ_SCI11ENDIAN_UINT16(data + 14);
-	_yResolution = READ_SCI11ENDIAN_UINT16(data + 16);
+	_xResolution = data.getUint16SEAt(14);
+	_yResolution = data.getUint16SEAt(16);
 
 	if (_xResolution == 0 && _yResolution == 0) {
 		byte sizeFlag = data[5];
@@ -930,18 +947,18 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 		error("Loop is less than 0");
 	}
 
-	const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data);
+	const uint16 viewHeaderSize = data.getUint16SEAt(0);
 	const uint8 loopHeaderSize = data[12];
 	const uint8 viewHeaderFieldSize = 2;
 
-	const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
+	SciSpan<const byte> loopHeader = data.subspan(viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo));
 
-	if ((int8)loopHeader[0] != -1) {
+	if (loopHeader.getInt8At(0) != -1) {
 		if (loopHeader[1] == 1) {
 			_mirrorX = true;
 		}
 
-		loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]);
+		loopHeader = data.subspan(viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopHeader.getInt8At(0)));
 	}
 
 	uint8 celCount = loopHeader[2];
@@ -962,18 +979,19 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 		error("Cel is less than 0 on loop 0");
 	}
 
-	_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8);
-	_celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo);
+	_hunkPaletteOffset = data.getUint32SEAt(8);
+	_celHeaderOffset = loopHeader.getUint32SEAt(12) + (data[13] * _info.celNo);
 
-	const byte *const celHeader = data + _celHeaderOffset;
+	const SciSpan<const byte> celHeader = data.subspan(_celHeaderOffset);
 
-	_width = READ_SCI11ENDIAN_UINT16(celHeader);
-	_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
-	_origin.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4);
+	_width = celHeader.getUint16SEAt(0);
+	_height = celHeader.getUint16SEAt(2);
+	assert(_width <= kCelScalerTableSize && _height <= kCelScalerTableSize);
+	_origin.x = _width / 2 - celHeader.getInt16SEAt(4);
 	if (g_sci->_features->usesAlternateSelectors() && _mirrorX) {
 		_origin.x = _width - _origin.x - 1;
 	}
-	_origin.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1;
+	_origin.y = _height - celHeader.getInt16SEAt(6) - 1;
 	_skipColor = celHeader[8];
 	_compressionType = (CelCompressionType)celHeader[9];
 
@@ -984,7 +1002,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 	if (celHeader[10] & 128) {
 		// NOTE: This is correct according to SCI2.1/SQ6/DOS;
 		// the engine re-reads the byte value as a word value
-		uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
+		const uint16 flags = celHeader.getUint16SEAt(10);
 		_transparent = flags & 1 ? true : false;
 		_remap = flags & 2 ? true : false;
 	} else if (_compressionType == kCelCompressionNone) {
@@ -997,7 +1015,9 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
 }
 
 bool CelObjView::analyzeUncompressedForRemap() const {
-	const byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
+	const SciSpan<const byte> data = getResPointer();
+	const uint32 pixelsOffset = data.getUint32SEAt(_celHeaderOffset + 24);
+	const byte *pixels = data.getUnsafeDataAt(pixelsOffset, _width * _height);
 	for (int i = 0; i < _width * _height; ++i) {
 		const byte pixel = pixels[i];
 		if (
@@ -1038,12 +1058,12 @@ CelObjView *CelObjView::duplicate() const {
 	return new CelObjView(*this);
 }
 
-byte *CelObjView::getResPointer() const {
+const SciSpan<const byte> CelObjView::getResPointer() const {
 	Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false);
 	if (resource == nullptr) {
 		error("Failed to load view %d from resource manager", _info.resourceId);
 	}
-	return resource->data;
+	return *resource;
 }
 
 #pragma mark -
@@ -1079,31 +1099,31 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
 		error("Pic resource %d not found", picId);
 	}
 
-	const byte *const data = resource->data;
+	const Resource &data = *resource;
 
-	_celCount = data[2];
+	_celCount = data.getUint8At(2);
 
 	if (_info.celNo >= _celCount) {
 		error("Cel number %d greater than cel count %d", _info.celNo, _celCount);
 	}
 
-	_celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo);
-	_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6);
+	_celHeaderOffset = data.getUint16SEAt(0) + (data.getUint16SEAt(4) * _info.celNo);
+	_hunkPaletteOffset = data.getUint32SEAt(6);
 
-	const byte *const celHeader = data + _celHeaderOffset;
+	const SciSpan<const byte> celHeader = data.subspan(_celHeaderOffset);
 
-	_width = READ_SCI11ENDIAN_UINT16(celHeader);
-	_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
-	_origin.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4);
-	_origin.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6);
+	_width = celHeader.getUint16SEAt(0);
+	_height = celHeader.getUint16SEAt(2);
+	_origin.x = celHeader.getInt16SEAt(4);
+	_origin.y = celHeader.getInt16SEAt(6);
 	_skipColor = celHeader[8];
 	_compressionType = (CelCompressionType)celHeader[9];
-	_priority = READ_SCI11ENDIAN_UINT16(celHeader + 36);
-	_relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38);
-	_relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40);
+	_priority = celHeader.getInt16SEAt(36);
+	_relativePosition.x = celHeader.getInt16SEAt(38);
+	_relativePosition.y = celHeader.getInt16SEAt(40);
 
-	const uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
-	const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
+	const uint16 sizeFlag1 = data.getUint16SEAt(10);
+	const uint16 sizeFlag2 = data.getUint16SEAt(12);
 
 	if (sizeFlag2) {
 		_xResolution = sizeFlag1;
@@ -1119,10 +1139,10 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
 		_yResolution = 400;
 	}
 
-	if (celHeader[10] & 128) {
+	if (celHeader.getUint8At(10) & 128) {
 		// NOTE: This is correct according to SCI2.1/SQ6/DOS;
 		// the engine re-reads the byte value as a word value
-		const uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
+		const uint16 flags = celHeader.getUint16SEAt(10);
 		_transparent = flags & 1 ? true : false;
 		_remap = flags & 2 ? true : false;
 	} else {
@@ -1137,9 +1157,16 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
 }
 
 bool CelObjPic::analyzeUncompressedForSkip() const {
-	const byte *const resource = getResPointer();
-	const byte *const pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
-	for (int i = 0; i < _width * _height; ++i) {
+	const SciSpan<const byte> resource = getResPointer();
+	const uint32 pixelsOffset = resource.getUint32SEAt(_celHeaderOffset + 24);
+	const int32 numPixels = MIN<int32>(resource.size() - pixelsOffset, _width * _height);
+
+	if (numPixels < _width * _height) {
+		warning("%s is truncated", _info.toString().c_str());
+	}
+
+	const byte *const pixels = resource.getUnsafeDataAt(pixelsOffset, numPixels);
+	for (int32 i = 0; i < numPixels; ++i) {
 		uint8 pixel = pixels[i];
 		if (pixel == _skipColor) {
 			return true;
@@ -1159,12 +1186,12 @@ CelObjPic *CelObjPic::duplicate() const {
 	return new CelObjPic(*this);
 }
 
-byte *CelObjPic::getResPointer() const {
+const SciSpan<const byte> CelObjPic::getResPointer() const {
 	const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false);
 	if (resource == nullptr) {
 		error("Failed to load pic %d from resource manager", _info.resourceId);
 	}
-	return resource->data;
+	return *resource;
 }
 
 #pragma mark -
@@ -1199,8 +1226,9 @@ CelObjMem *CelObjMem::duplicate() const {
 	return new CelObjMem(*this);
 }
 
-byte *CelObjMem::getResPointer() const {
-	return g_sci->getEngineState()->_segMan->lookupBitmap(_info.bitmap)->getRawData();
+const SciSpan<const byte> CelObjMem::getResPointer() const {
+	SciBitmap &bitmap = *g_sci->getEngineState()->_segMan->lookupBitmap(_info.bitmap);
+	return SciSpan<const byte>(bitmap.getRawData(), bitmap.getRawSize(), Common::String::format("bitmap %04x:%04x", PRINT_REG(_info.bitmap)));
 }
 
 #pragma mark -
@@ -1237,7 +1265,7 @@ CelObjColor *CelObjColor::duplicate() const {
 	return new CelObjColor(*this);
 }
 
-byte *CelObjColor::getResPointer() const {
+const SciSpan<const byte> CelObjColor::getResPointer() const {
 	error("Unsupported method");
 }
 } // End of namespace Sci
diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h
index 3e3f81a..351a885 100644
--- a/engines/sci/graphics/celobj32.h
+++ b/engines/sci/graphics/celobj32.h
@@ -27,6 +27,7 @@
 #include "common/rect.h"
 #include "sci/resource.h"
 #include "sci/engine/vm_types.h"
+#include "sci/util.h"
 
 namespace Sci {
 typedef Common::Rational Ratio;
@@ -413,7 +414,7 @@ public:
 	 * Retrieves a pointer to the raw resource data for this
 	 * cel. This method cannot be used with a CelObjColor.
 	 */
-	virtual byte *getResPointer() const = 0;
+	virtual const SciSpan<const byte> getResPointer() const = 0;
 
 	/**
 	 * Reads the pixel at the given coordinates. This method
@@ -535,7 +536,7 @@ public:
 	void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX, const Ratio &scaleX, const Ratio &scaleY);
 
 	virtual CelObjView *duplicate() const override;
-	virtual byte *getResPointer() const override;
+	virtual const SciSpan<const byte> getResPointer() const override;
 };
 
 #pragma mark -
@@ -580,7 +581,7 @@ public:
 	virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) override;
 
 	virtual CelObjPic *duplicate() const override;
-	virtual byte *getResPointer() const override;
+	virtual const SciSpan<const byte> getResPointer() const override;
 };
 
 #pragma mark -
@@ -598,7 +599,7 @@ public:
 	virtual ~CelObjMem() override {};
 
 	virtual CelObjMem *duplicate() const override;
-	virtual byte *getResPointer() const override;
+	virtual const SciSpan<const byte> getResPointer() const override;
 };
 
 #pragma mark -
@@ -622,7 +623,7 @@ public:
 	virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) override;
 
 	virtual CelObjColor *duplicate() const override;
-	virtual byte *getResPointer() const override;
+	virtual const SciSpan<const byte> getResPointer() const override;
 };
 } // End of namespace Sci
 
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 36026a8..582dd32 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -197,7 +197,7 @@ bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo,
 	const CelInfo *celInfo = tmpView->getCelInfo(loopNo, celNo);
 	position.x = CLIP<int>(position.x, 0, celInfo->width - 1);
 	position.y = CLIP<int>(position.y, 0, celInfo->height - 1);
-	const byte *celData = tmpView->getBitmap(loopNo, celNo);
+	const SciSpan<const byte> &celData = tmpView->getBitmap(loopNo, celNo);
 	bool result = (celData[position.y * celInfo->width + position.x] == celInfo->clearKey);
 	return result;
 }
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 7cf9a57..c29e547 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -58,7 +58,6 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc
 	_zoomPicView = 0;
 	_zoomColor = 0;
 	_zoomMultiplier = 0;
-	_cursorSurface = 0;
 
 	if (g_sci && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformWindows)
 		_useOriginalKQ6WinCursors = ConfMan.getBool("windows_cursors");
@@ -110,20 +109,17 @@ void GfxCursor::purgeCache() {
 
 void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
 	Resource *resource;
-	byte *resourceData;
 	Common::Point hotspot = Common::Point(0, 0);
 	byte colorMapping[4];
 	int16 x, y;
 	byte color;
 	int16 maskA, maskB;
 	byte *pOut;
-	byte *rawBitmap = new byte[SCI_CURSOR_SCI0_HEIGHTWIDTH * SCI_CURSOR_SCI0_HEIGHTWIDTH];
 	int16 heightWidth;
 
 	if (resourceId == -1) {
 		// no resourceId given, so we actually hide the cursor
 		kernelHide();
-		delete[] rawBitmap;
 		return;
 	}
 
@@ -131,20 +127,18 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
 	resource = _resMan->findResource(ResourceId(kResourceTypeCursor, resourceId), false);
 	if (!resource)
 		error("cursor resource %d not found", resourceId);
-	if (resource->size != SCI_CURSOR_SCI0_RESOURCESIZE)
+	if (resource->size() != SCI_CURSOR_SCI0_RESOURCESIZE)
 		error("cursor resource %d has invalid size", resourceId);
 
-	resourceData = resource->data;
-
 	if (getSciVersion() <= SCI_VERSION_01) {
 		// SCI0 cursors contain hotspot flags, not actual hotspot coordinates.
 		// If bit 0 of resourceData[3] is set, the hotspot should be centered,
 		// otherwise it's in the top left of the mouse cursor.
-		hotspot.x = hotspot.y = resourceData[3] ? SCI_CURSOR_SCI0_HEIGHTWIDTH / 2 : 0;
+		hotspot.x = hotspot.y = resource->getUint8At(3) ? SCI_CURSOR_SCI0_HEIGHTWIDTH / 2 : 0;
 	} else {
 		// Cursors in newer SCI versions contain actual hotspot coordinates.
-		hotspot.x = READ_LE_UINT16(resourceData);
-		hotspot.y = READ_LE_UINT16(resourceData + 2);
+		hotspot.x = resource->getUint16LEAt(0);
+		hotspot.y = resource->getUint16LEAt(2);
 	}
 
 	// Now find out what colors we are supposed to use
@@ -160,13 +154,13 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
 	if (g_sci->getGameId() == GID_LONGBOW)
 		colorMapping[3] = _palette->matchColor(223, 223, 223) & SCI_PALETTE_MATCH_COLORMASK; // Light Grey
 
-	// Seek to actual data
-	resourceData += 4;
+	Common::SpanOwner<SciSpan<byte> > rawBitmap;
+	rawBitmap->allocate(SCI_CURSOR_SCI0_HEIGHTWIDTH * SCI_CURSOR_SCI0_HEIGHTWIDTH, resource->name() + " copy");
 
-	pOut = rawBitmap;
+	pOut = rawBitmap->getUnsafeDataAt(0, SCI_CURSOR_SCI0_HEIGHTWIDTH * SCI_CURSOR_SCI0_HEIGHTWIDTH);
 	for (y = 0; y < SCI_CURSOR_SCI0_HEIGHTWIDTH; y++) {
-		maskA = READ_LE_UINT16(resourceData + (y << 1));
-		maskB = READ_LE_UINT16(resourceData + 32 + (y << 1));
+		maskA = resource->getUint16LEAt(4 + (y << 1));
+		maskB = resource->getUint16LEAt(4 + 32 + (y << 1));
 
 		for (x = 0; x < SCI_CURSOR_SCI0_HEIGHTWIDTH; x++) {
 			color = (((maskA << x) & 0x8000) | (((maskB << x) >> 1) & 0x4000)) >> 14;
@@ -181,9 +175,10 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
 		heightWidth *= 2;
 		hotspot.x *= 2;
 		hotspot.y *= 2;
-		byte *upscaledBitmap = new byte[heightWidth * heightWidth];
-		_screen->scale2x(rawBitmap, upscaledBitmap, SCI_CURSOR_SCI0_HEIGHTWIDTH, SCI_CURSOR_SCI0_HEIGHTWIDTH);
-		delete[] rawBitmap;
+
+		Common::SpanOwner<SciSpan<byte> > upscaledBitmap;
+		upscaledBitmap->allocate(heightWidth * heightWidth, "upscaled cursor bitmap");
+		_screen->scale2x(*rawBitmap, *upscaledBitmap, SCI_CURSOR_SCI0_HEIGHTWIDTH, SCI_CURSOR_SCI0_HEIGHTWIDTH);
 		rawBitmap = upscaledBitmap;
 	}
 
@@ -192,10 +187,8 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
 				resourceId, hotspot.x, hotspot.y, heightWidth, heightWidth);
 	}
 
-	CursorMan.replaceCursor(rawBitmap, heightWidth, heightWidth, hotspot.x, hotspot.y, SCI_CURSOR_SCI0_TRANSPARENCYCOLOR);
+	CursorMan.replaceCursor(rawBitmap->getUnsafeDataAt(0, heightWidth * heightWidth), heightWidth, heightWidth, hotspot.x, hotspot.y, SCI_CURSOR_SCI0_TRANSPARENCYCOLOR);
 	kernelShow();
-
-	delete[] rawBitmap;
 }
 
 void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Common::Point *hotspot) {
@@ -261,19 +254,19 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
 		return;
 	}
 
-	const byte *rawBitmap = cursorView->getBitmap(loopNum, celNum);
+	const SciSpan<const byte> &rawBitmap = cursorView->getBitmap(loopNum, celNum);
 	if (_upscaledHires && !_useOriginalKQ6WinCursors) {
 		// Scale cursor by 2x - note: sierra didn't do this, but it looks much better
 		width *= 2;
 		height *= 2;
 		cursorHotspot->x *= 2;
 		cursorHotspot->y *= 2;
-		byte *cursorBitmap = new byte[width * height];
-		_screen->scale2x(rawBitmap, cursorBitmap, celInfo->width, celInfo->height);
-		CursorMan.replaceCursor(cursorBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
-		delete[] cursorBitmap;
+		Common::SpanOwner<SciSpan<byte> > cursorBitmap;
+		cursorBitmap->allocate(width * height, "upscaled cursor bitmap");
+		_screen->scale2x(rawBitmap, *cursorBitmap, celInfo->width, celInfo->height);
+		CursorMan.replaceCursor(cursorBitmap->getUnsafeDataAt(0, width * height), width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
 	} else {
-		CursorMan.replaceCursor(rawBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
+		CursorMan.replaceCursor(rawBitmap.getUnsafeDataAt(0, width * height), width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
 	}
 
 	kernelShow();
@@ -386,10 +379,10 @@ void GfxCursor::refreshPosition() {
 	if (_zoomZoneActive) {
 		// Cursor
 		const CelInfo *cursorCelInfo = _zoomCursorView->getCelInfo(_zoomCursorLoop, _zoomCursorCel);
-		const byte *cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel);
+		const SciSpan<const byte> &cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel);
 		// Pic
 		const CelInfo *picCelInfo = _zoomPicView->getCelInfo(0, 0);
-		const byte *rawPicBitmap = _zoomPicView->getBitmap(0, 0);
+		const SciSpan<const byte> &rawPicBitmap = _zoomPicView->getBitmap(0, 0);
 
 		// Compute hotspot of cursor
 		Common::Point cursorHotspot = Common::Point((cursorCelInfo->width >> 1) - cursorCelInfo->displaceX, cursorCelInfo->height - cursorCelInfo->displaceY - 1);
@@ -426,7 +419,7 @@ void GfxCursor::refreshPosition() {
 			}
 		}
 
-		CursorMan.replaceCursor(_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
+		CursorMan.replaceCursor(_cursorSurface->getUnsafeDataAt(0, cursorCelInfo->width * cursorCelInfo->height), cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
 	}
 }
 
@@ -449,8 +442,7 @@ void GfxCursor::kernelClearZoomZone() {
 	_zoomCursorView = 0;
 	delete _zoomPicView;
 	_zoomPicView = 0;
-	delete[] _cursorSurface;
-	_cursorSurface = 0;
+	_cursorSurface.clear();
 }
 
 void GfxCursor::kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor) {
@@ -474,10 +466,7 @@ void GfxCursor::kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourc
 	_zoomCursorLoop = (byte)loopNum;
 	_zoomCursorCel = (byte)celNum;
 	_zoomPicView = new GfxView(_resMan, _screen, _palette, picNum);
-	const CelInfo *cursorCelInfo = _zoomCursorView->getCelInfo(_zoomCursorLoop, _zoomCursorCel);
-	const byte *cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel);
-	_cursorSurface = new byte[cursorCelInfo->width * cursorCelInfo->height];
-	memcpy(_cursorSurface, cursorBitmap, cursorCelInfo->width * cursorCelInfo->height);
+	_cursorSurface->allocateFromSpan(_zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel));
 
 	_zoomZone = zone;
 	kernelSetMoveZone(_zoomZone);
@@ -537,7 +526,7 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
 
 	assert(resource);
 
-	Common::MemoryReadStream resStream(resource->data, resource->size);
+	Common::MemoryReadStream resStream(resource->toStream());
 	Graphics::MacCursor *macCursor = new Graphics::MacCursor();
 
 	if (!macCursor->readFromStream(resStream)) {
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 36518ea..8d9ce7c 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -27,13 +27,15 @@
 #include "common/hashmap.h"
 #include "sci/sci.h"
 #include "sci/graphics/helpers.h"
+#include "sci/util.h"
 
 namespace Sci {
 
-#define SCI_CURSOR_SCI0_HEIGHTWIDTH 16
-#define SCI_CURSOR_SCI0_RESOURCESIZE 68
-
-#define SCI_CURSOR_SCI0_TRANSPARENCYCOLOR 1
+enum {
+	SCI_CURSOR_SCI0_HEIGHTWIDTH = 16,
+	SCI_CURSOR_SCI0_RESOURCESIZE = 68,
+	SCI_CURSOR_SCI0_TRANSPARENCYCOLOR = 1
+};
 
 class GfxView;
 class GfxPalette;
@@ -117,7 +119,7 @@ private:
 	GfxView *_zoomPicView;
 	byte _zoomColor;
 	byte _zoomMultiplier;
-	byte *_cursorSurface;
+	Common::SpanOwner<SciSpan<byte> > _cursorSurface;
 
 	CursorCache _cachedCursors;
 
diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp
index 34a6d54..e8450b7 100644
--- a/engines/sci/graphics/cursor32.cpp
+++ b/engines/sci/graphics/cursor32.cpp
@@ -244,7 +244,7 @@ void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const
 			debug(0, "Mac cursor %d not found", viewNum);
 			return;
 		}
-		Common::MemoryReadStream resStream(resource->data, resource->size);
+		Common::MemoryReadStream resStream(resource->toStream());
 		Graphics::MacCursor *macCursor = new Graphics::MacCursor();
 
 		if (!macCursor->readFromStream(resStream)) {
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index 2268ec0..ff2f361 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -40,16 +40,15 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr
 	if (!_resource) {
 		error("font resource %d not found", resourceId);
 	}
-	_resourceData = _resource->data;
 
-	_numChars = READ_SCI32ENDIAN_UINT16(_resourceData + 2);
-	_fontHeight = READ_SCI32ENDIAN_UINT16(_resourceData + 4);
+	_numChars = _resource->getUint16SE32At(2);
+	_fontHeight = _resource->getUint16SE32At(4);
 	_chars = new Charinfo[_numChars];
 	// filling info for every char
 	for (int16 i = 0; i < _numChars; i++) {
-		_chars[i].offset = READ_SCI32ENDIAN_UINT16(_resourceData + 6 + i * 2);
-		_chars[i].width = _resourceData[_chars[i].offset];
-		_chars[i].height = _resourceData[_chars[i].offset + 1];
+		_chars[i].offset = _resource->getUint16SE32At(6 + i * 2);
+		_chars[i].width = _resource->getUint8At(_chars[i].offset);
+		_chars[i].height = _resource->getUint8At(_chars[i].offset + 1);
 	}
 }
 
@@ -62,20 +61,33 @@ GuiResourceId GfxFontFromResource::getResourceId() {
 	return _resourceId;
 }
 
-byte GfxFontFromResource::getHeight() {
+uint8 GfxFontFromResource::getHeight() {
 	return _fontHeight;
 }
-byte GfxFontFromResource::getCharWidth(uint16 chr) {
+uint8 GfxFontFromResource::getCharWidth(uint16 chr) {
 	return chr < _numChars ? _chars[chr].width : 0;
 }
-byte GfxFontFromResource::getCharHeight(uint16 chr) {
+uint8 GfxFontFromResource::getCharHeight(uint16 chr) {
 	return chr < _numChars ? _chars[chr].height : 0;
 }
-byte *GfxFontFromResource::getCharData(uint16 chr) {
-	return chr < _numChars ? _resourceData + _chars[chr].offset + 2 : 0;
+SciSpan<const byte> GfxFontFromResource::getCharData(uint16 chr) {
+	if (chr >= _numChars) {
+		return SciSpan<const byte>();
+	}
+
+	const uint32 size = (chr + 1 >= _numChars ? _resource->size() : _chars[chr + 1].offset) - _chars[chr].offset - 2;
+	return _resource->subspan(_chars[chr].offset + 2, size);
 }
 
 void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput) {
+	if (chr >= _numChars) {
+		// SSCI silently ignores attempts to draw characters that do not exist
+		// in the font; for now, emit warnings if this happens, to learn if
+		// it leads to any bugs
+		warning("%s is missing glyph %d", _resource->name().c_str(), chr);
+		return;
+	}
+
 	// Make sure we're comparing against the correct dimensions
 	// If the font we're drawing is already upscaled, make sure we use the full screen width/height
 	uint16 screenWidth = _screen->fontIsUpscaled() ? _screen->getDisplayWidth() : _screen->getWidth();
@@ -87,13 +99,13 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
 	int y = 0;
 	int16 greyedTop = top;
 
-	byte *pIn = getCharData(chr);
+	SciSpan<const byte> charData = getCharData(chr);
 	for (int i = 0; i < charHeight; i++, y++) {
 		if (greyedOutput)
 			mask = ((greyedTop++) % 2) ? 0xAA : 0x55;
 		for (int done = 0; done < charWidth; done++) {
 			if ((done & 7) == 0) // fetching next data byte
-				b = *(pIn++) & mask;
+				b = *(charData++) & mask;
 			if (b & 0x80) // if MSB is set - paint it
 				_screen->putFontPixel(top, left + done, y, color);
 			b = b << 1;
@@ -104,19 +116,27 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
 #ifdef ENABLE_SCI32
 
 void GfxFontFromResource::drawToBuffer(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput, byte *buffer, int16 bufWidth, int16 bufHeight) {
+	if (chr >= _numChars) {
+		// SSCI silently ignores attempts to draw characters that do not exist
+		// in the font; for now, emit warnings if this happens, to learn if
+		// it leads to any bugs
+		warning("%s is missing glyph %d", _resource->name().c_str(), chr);
+		return;
+	}
+
 	int charWidth = MIN<int>(getCharWidth(chr), bufWidth - left);
 	int charHeight = MIN<int>(getCharHeight(chr), bufHeight - top);
 	byte b = 0, mask = 0xFF;
 	int y = 0;
 	int16 greyedTop = top;
 
-	byte *pIn = getCharData(chr);
+	SciSpan<const byte> charData = getCharData(chr);
 	for (int i = 0; i < charHeight; i++, y++) {
 		if (greyedOutput)
 			mask = ((greyedTop++) % 2) ? 0xAA : 0x55;
 		for (int done = 0; done < charWidth; done++) {
 			if ((done & 7) == 0) // fetching next data byte
-				b = *(pIn++) & mask;
+				b = *(charData++) & mask;
 			if (b & 0x80) {	// if MSB is set - paint it
 				int offset = (top + y) * bufWidth + (left + done);
 				buffer[offset] = color;
diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h
index b79fb2f..4e26510 100644
--- a/engines/sci/graphics/font.h
+++ b/engines/sci/graphics/font.h
@@ -24,6 +24,7 @@
 #define SCI_GRAPHICS_FONT_H
 
 #include "sci/graphics/helpers.h"
+#include "sci/util.h"
 
 namespace Sci {
 
@@ -51,8 +52,8 @@ public:
 	~GfxFontFromResource();
 
 	GuiResourceId getResourceId();
-	byte getHeight();
-	byte getCharWidth(uint16 chr);
+	uint8 getHeight();
+	uint8 getCharWidth(uint16 chr);
 	void draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput);
 #ifdef ENABLE_SCI32
 	// SCI2/2.1 equivalent
@@ -60,23 +61,21 @@ public:
 #endif
 
 private:
-	byte getCharHeight(uint16 chr);
-	byte *getCharData(uint16 chr);
+	uint8 getCharHeight(uint16 chr);
+	SciSpan<const byte> getCharData(uint16 chr);
 
 	ResourceManager *_resMan;
 	GfxScreen *_screen;
 
 	Resource *_resource;
 	GuiResourceId _resourceId;
-	byte *_resourceData;
 
 	struct Charinfo {
-		byte width;
-		byte height;
+		uint8 width, height;
 		int16 offset;
 	};
 
-	byte _fontHeight;
+	uint8 _fontHeight;
 	uint16 _numChars;
 	Charinfo *_chars;
 };
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index e10b9fd..3a62760 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -203,11 +203,11 @@ void GfxMacIconBar::setInventoryIcon(int16 icon) {
 Graphics::Surface *GfxMacIconBar::loadPict(ResourceId id) {
 	Resource *res = g_sci->getResMan()->findResource(id, false);
 
-	if (!res || res->size == 0)
+	if (!res || res->size() == 0)
 		return 0;
 
 	Image::PICTDecoder pictDecoder;
-	Common::MemoryReadStream stream(res->data, res->size);
+	Common::MemoryReadStream stream(res->toStream());
 	if (!pictDecoder.loadStream(stream))
 		return 0;
 
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 6fe1fb1..307c8ae 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -134,12 +134,12 @@ void GfxPalette::setDefault() {
 #define SCI_PAL_FORMAT_CONSTANT 1
 #define SCI_PAL_FORMAT_VARIABLE 0
 
-void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) const {
+void GfxPalette::createFromData(const SciSpan<const byte> &data, Palette *paletteOut) const {
 	int palFormat = 0;
-	int palOffset = 0;
-	int palColorStart = 0;
-	int palColorCount = 0;
-	int colorNo = 0;
+	uint palOffset = 0;
+	uint palColorStart = 0;
+	uint palColorCount = 0;
+	uint colorNo = 0;
 
 	memset(paletteOut, 0, sizeof(Palette));
 
@@ -148,16 +148,16 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut)
 		paletteOut->mapping[colorNo] = colorNo;
 	}
 
-	if (bytesLeft < 37) {
+	if (data.size() < 37) {
 		// This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full
 		//  palette
-		debugC(kDebugLevelResMan, "GfxPalette::createFromData() - not enough bytes in resource (%d), expected palette header", bytesLeft);
+		debugC(kDebugLevelResMan, "GfxPalette::createFromData() - not enough bytes in resource (%lu), expected palette header", data.size());
 		return;
 	}
 
 	// palette formats in here are not really version exclusive, we can not use sci-version to differentiate between them
 	//  they were just called that way, because they started appearing in sci1.1 for example
-	if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_SCI11ENDIAN_UINT16(data + 29) == 0)) {
+	if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && data.getUint16SEAt(29) == 0)) {
 		// SCI0/SCI1 palette
 		palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT;
 		palOffset = 260;
@@ -168,13 +168,13 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut)
 		palFormat = data[32];
 		palOffset = 37;
 		palColorStart = data[25];
-		palColorCount = READ_SCI11ENDIAN_UINT16(data + 29);
+		palColorCount = data.getUint16SEAt(29);
 	}
 
 	switch (palFormat) {
 		case SCI_PAL_FORMAT_CONSTANT:
 			// Check, if enough bytes left
-			if (bytesLeft < palOffset + (3 * palColorCount)) {
+			if (data.size() < palOffset + (3 * palColorCount)) {
 				warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
 				return;
 			}
@@ -187,7 +187,7 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut)
 			}
 			break;
 		case SCI_PAL_FORMAT_VARIABLE:
-			if (bytesLeft < palOffset + (4 * palColorCount)) {
+			if (data.size() < palOffset + (4 * palColorCount)) {
 				warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
 				return;
 			}
@@ -237,7 +237,7 @@ bool GfxPalette::setAmiga() {
 }
 
 // Called from picture class, some amiga sci1 games set half of the palette
-void GfxPalette::modifyAmigaPalette(byte *data) {
+void GfxPalette::modifyAmigaPalette(const SciSpan<const byte> &data) {
 	int16 curPos = 0;
 
 	for (int curColor = 0; curColor < 16; curColor++) {
@@ -525,7 +525,7 @@ bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) {
 	Palette palette;
 
 	if (palResource) {
-		createFromData(palResource->data, palResource->size, &palette);
+		createFromData(*palResource, &palette);
 		set(&palette, force);
 		return true;
 	}
@@ -723,7 +723,7 @@ bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) {
 	Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
 	if (palResource) {
 		// Load and initialize destination palette
-		createFromData(palResource->data, palResource->size, &_palVaryTargetPalette);
+		createFromData(*palResource, &_palVaryTargetPalette);
 		return true;
 	}
 	return false;
@@ -810,7 +810,7 @@ int16 GfxPalette::kernelPalVaryChangeTarget(GuiResourceId resourceId) {
 		Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
 		if (palResource) {
 			Palette insertPalette;
-			createFromData(palResource->data, palResource->size, &insertPalette);
+			createFromData(*palResource, &insertPalette);
 			// insert new palette into target
 			insert(&insertPalette, &_palVaryTargetPalette);
 			// update palette and set on screen
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 2178de8..af74169 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -25,6 +25,7 @@
 
 #include "common/array.h"
 #include "sci/graphics/helpers.h"
+#include "sci/util.h"
 
 namespace Sci {
 
@@ -47,9 +48,9 @@ public:
 	bool isUsing16bitColorMatch();
 
 	void setDefault();
-	void createFromData(byte *data, int bytesLeft, Palette *paletteOut) const;
+	void createFromData(const SciSpan<const byte> &data, Palette *paletteOut) const;
 	bool setAmiga();
-	void modifyAmigaPalette(byte *data);
+	void modifyAmigaPalette(const SciSpan<const byte> &data);
 	void setEGA();
 	void set(Palette *sciPal, bool force, bool forceRealMerge = false);
 	bool insert(Palette *newPalette, Palette *destPalette);
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 56e1940..febb682 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -37,15 +37,15 @@ namespace Sci {
 
 #pragma mark HunkPalette
 
-HunkPalette::HunkPalette(byte *rawPalette) :
+HunkPalette::HunkPalette(const SciSpan<const byte> &rawPalette) :
 	_version(0),
 	// NOTE: The header size in palettes is garbage. In at least KQ7 2.00b and
 	// Phant1, the 999.pal sets this value to 0. In most other palettes it is
 	// set to 14, but the *actual* size of the header structure used in SSCI is
 	// 13, which is reflected by `kHunkPaletteHeaderSize`.
 	// _headerSize(rawPalette[0]),
-	_numPalettes(rawPalette[10]),
-	_data(nullptr) {
+	_numPalettes(rawPalette.getUint8At(10)),
+	_data() {
 	assert(_numPalettes == 0 || _numPalettes == 1);
 	if (_numPalettes) {
 		_data = rawPalette;
@@ -54,7 +54,7 @@ HunkPalette::HunkPalette(byte *rawPalette) :
 }
 
 void HunkPalette::setVersion(const uint32 version) const {
-	if (_numPalettes != _data[10]) {
+	if (_numPalettes != _data.getUint8At(10)) {
 		error("Invalid HunkPalette");
 	}
 
@@ -64,20 +64,21 @@ void HunkPalette::setVersion(const uint32 version) const {
 			error("Invalid HunkPalette");
 		}
 
-		WRITE_SCI11ENDIAN_UINT32(getPalPointer() + kEntryVersionOffset, version);
+		byte *palette = const_cast<byte *>(getPalPointer().getUnsafeDataAt(kEntryVersionOffset, sizeof(uint32)));
+		WRITE_SCI11ENDIAN_UINT32(palette, version);
 		_version = version;
 	}
 }
 
 const HunkPalette::EntryHeader HunkPalette::getEntryHeader() const {
-	const byte *const data = getPalPointer();
+	const SciSpan<const byte> data(getPalPointer());
 
 	EntryHeader header;
-	header.startColor = data[10];
-	header.numColors = READ_SCI11ENDIAN_UINT16(data + 14);
-	header.used = data[16];
-	header.sharedUsed = data[17];
-	header.version = READ_SCI11ENDIAN_UINT32(data + kEntryVersionOffset);
+	header.startColor = data.getUint8At(10);
+	header.numColors = data.getUint16SEAt(14);
+	header.used = data.getUint8At(16);
+	header.sharedUsed = data.getUint8At(17);
+	header.version = data.getUint32SEAt(kEntryVersionOffset);
 
 	return header;
 }
@@ -94,7 +95,8 @@ const Palette HunkPalette::toPalette() const {
 
 	if (_numPalettes) {
 		const EntryHeader header = getEntryHeader();
-		const byte *data = getPalPointer() + kEntryHeaderSize;
+		const uint32 dataSize = header.numColors * (/* RGB */ 3 + (header.sharedUsed ? 0 : 1));
+		const byte *data = getPalPointer().getUnsafeDataAt(kEntryHeaderSize, dataSize);
 
 		const int16 end = header.startColor + header.numColors;
 		assert(end <= 256);
@@ -169,7 +171,7 @@ bool GfxPalette32::loadPalette(const GuiResourceId resourceId) {
 		return false;
 	}
 
-	const HunkPalette palette(palResource->data);
+	const HunkPalette palette(*palResource);
 	submit(palette);
 	return true;
 }
@@ -296,7 +298,7 @@ Palette GfxPalette32::getPaletteFromResource(const GuiResourceId resourceId) con
 		error("Could not load vary palette %d", resourceId);
 	}
 
-	const HunkPalette rawPalette(palResource->data);
+	const HunkPalette rawPalette(*palResource);
 	return rawPalette.toPalette();
 }
 
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index c4cfb35..d6d7d0d 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -35,7 +35,7 @@ namespace Sci {
  */
 class HunkPalette {
 public:
-	HunkPalette(byte *rawPalette);
+	HunkPalette(const SciSpan<const byte> &rawPalette);
 
 	/**
 	 * Gets the version of the palette. Used to avoid resubmitting a HunkPalette
@@ -118,7 +118,7 @@ private:
 	/**
 	 * The raw palette data for this hunk palette.
 	 */
-	byte *_data;
+	SciSpan<const byte> _data;
 
 	/**
 	 * Returns a struct that describes the palette held by this HunkPalette. The
@@ -129,8 +129,8 @@ private:
 	/**
 	 * Returns a pointer to the palette data within the hunk palette.
 	 */
-	byte *getPalPointer() const {
-		return _data + kHunkPaletteHeaderSize + (2 * _numPalettes);
+	SciSpan<const byte> getPalPointer() const {
+		return _data.subspan(kHunkPaletteHeaderSize + (2 * _numPalettes));
 	}
 };
 
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 864327f..9e9dede 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -20,6 +20,7 @@
  *
  */
 
+#include "common/span.h"
 #include "common/stack.h"
 #include "common/system.h"
 
@@ -68,22 +69,16 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1
 	_EGApaletteNo = EGApaletteNo;
 	_priority = 0;
 
-	headerSize = READ_LE_UINT16(_resource->data);
+	headerSize = _resource->getUint16LEAt(0);
 	switch (headerSize) {
 	case 0x26: // SCI 1.1 VGA picture
 		_resourceType = SCI_PICTURE_TYPE_SCI11;
 		drawSci11Vga();
 		break;
-#ifdef ENABLE_SCI32
-	case 0x0e: // SCI32 VGA picture
-		_resourceType = SCI_PICTURE_TYPE_SCI32;
-		drawSci32Vga(0, 0, 0, 0, 0, false);
-		break;
-#endif
 	default:
 		// VGA, EGA or Amiga vector data
 		_resourceType = SCI_PICTURE_TYPE_REGULAR;
-		drawVectorData(_resource->data, _resource->size);
+		drawVectorData(*_resource);
 	}
 }
 
@@ -100,16 +95,15 @@ void GfxPicture::reset() {
 }
 
 void GfxPicture::drawSci11Vga() {
-	byte *inbuffer = _resource->data;
-	int size = _resource->size;
+	SciSpan<const byte> inbuffer(*_resource);
 	int priorityBandsCount = inbuffer[3];
 	int has_cel = inbuffer[4];
-	int vector_dataPos = READ_LE_UINT32(inbuffer + 16);
-	int vector_size = size - vector_dataPos;
-	int palette_data_ptr = READ_LE_UINT32(inbuffer + 28);
-	int cel_headerPos = READ_LE_UINT32(inbuffer + 32);
-	int cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24);
-	int cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28);
+	int vector_dataPos = inbuffer.getUint32LEAt(16);
+	int vector_size = _resource->size() - vector_dataPos;
+	int palette_data_ptr = inbuffer.getUint32LEAt(28);
+	int cel_headerPos = inbuffer.getUint32LEAt(32);
+	int cel_RlePos = inbuffer.getUint32LEAt(cel_headerPos + 24);
+	int cel_LiteralPos = inbuffer.getUint32LEAt(cel_headerPos + 28);
 	Palette palette;
 
 	// Header
@@ -132,113 +126,24 @@ void GfxPicture::drawSci11Vga() {
 	// display Cel-data
 	if (has_cel) {
 		// Create palette and set it
-		_palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
+		_palette->createFromData(inbuffer.subspan(palette_data_ptr), &palette);
 		_palette->set(&palette, true);
 
-		drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0, 0, false);
+		drawCelData(inbuffer, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0, 0, false);
 	}
 
 	// process vector data
-	drawVectorData(inbuffer + vector_dataPos, vector_size);
+	drawVectorData(inbuffer.subspan(vector_dataPos, vector_size));
 
 	// Set priority band information
-	_ports->priorityBandsInitSci11(inbuffer + 40);
-}
-
-#ifdef ENABLE_SCI32
-int16 GfxPicture::getSci32celCount() {
-	byte *inbuffer = _resource->data;
-	return inbuffer[2];
-}
-
-int16 GfxPicture::getSci32celX(int16 celNo) {
-	byte *inbuffer = _resource->data;
-	int header_size = READ_SCI11ENDIAN_UINT16(inbuffer);
-	int cel_headerPos = header_size + 42 * celNo;
-	return READ_SCI11ENDIAN_UINT16(inbuffer + cel_headerPos + 38);
-}
-
-int16 GfxPicture::getSci32celY(int16 celNo) {
-	byte *inbuffer = _resource->data;
-	int header_size = READ_SCI11ENDIAN_UINT16(inbuffer);
-	int cel_headerPos = header_size + 42 * celNo;
-	return READ_SCI11ENDIAN_UINT16(inbuffer + cel_headerPos + 40);
-}
-
-int16 GfxPicture::getSci32celWidth(int16 celNo) {
-	byte *inbuffer = _resource->data;
-	int header_size = READ_SCI11ENDIAN_UINT16(inbuffer);
-	int cel_headerPos = header_size + 42 * celNo;
-	return READ_SCI11ENDIAN_UINT16(inbuffer + cel_headerPos + 0);
-}
-
-int16 GfxPicture::getSci32celHeight(int16 celNo) {
-	byte *inbuffer = _resource->data;
-	int header_size = READ_SCI11ENDIAN_UINT16(inbuffer);
-	int cel_headerPos = header_size + 42 * celNo;
-	return READ_SCI11ENDIAN_UINT16(inbuffer + cel_headerPos + 2);
-}
-
-
-int16 GfxPicture::getSci32celPriority(int16 celNo) {
-	byte *inbuffer = _resource->data;
-	int header_size = READ_SCI11ENDIAN_UINT16(inbuffer);
-	int cel_headerPos = header_size + 42 * celNo;
-	return READ_SCI11ENDIAN_UINT16(inbuffer + cel_headerPos + 36);
-}
-
-void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool mirrored) {
-	byte *inbuffer = _resource->data;
-	int size = _resource->size;
-	int header_size = READ_SCI11ENDIAN_UINT16(inbuffer);
-	int palette_data_ptr = READ_SCI11ENDIAN_UINT32(inbuffer + 6);
-//	int celCount = inbuffer[2];
-	int cel_headerPos = header_size;
-	int cel_RlePos, cel_LiteralPos;
-	Palette palette;
-
-	// HACK
-	_mirroredFlag = mirrored;
-	_addToFlag = false;
-	_resourceType = SCI_PICTURE_TYPE_SCI32;
-
-	if (celNo == 0) {
-		// Create palette and set it
-		_palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
-		_palette->set(&palette, true);
-	}
-
-	// Header
-	// 0[headerSize:WORD] 2[celCount:BYTE] 3[Unknown:BYTE] 4[celHeaderSize:WORD] 6[paletteOffset:DWORD] 10[Unknown:WORD] 12[Unknown:WORD]
-	// cel-header follow afterwards, each is 42 bytes
-	// Cel-Header
-	// 0[width:WORD] 2[height:WORD] 4[displaceX:WORD] 6[displaceY:WORD] 8[clearColor:BYTE] 9[compressed:BYTE]
-	//  offset 10-23 is unknown
-	// 24[rleOffset:DWORD] 28[literalOffset:DWORD] 32[Unknown:WORD] 34[Unknown:WORD] 36[priority:WORD] 38[relativeXpos:WORD] 40[relativeYpos:WORD]
-
-	cel_headerPos += 42 * celNo;
-
-	if (mirrored) {
-		// switch around relativeXpos
-		Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();
-		drawX = displayArea.width() - drawX - READ_SCI11ENDIAN_UINT16(inbuffer + cel_headerPos + 0);
-	}
-
-	cel_RlePos = READ_SCI11ENDIAN_UINT32(inbuffer + cel_headerPos + 24);
-	cel_LiteralPos = READ_SCI11ENDIAN_UINT32(inbuffer + cel_headerPos + 28);
-
-	drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, drawX, drawY, pictureX, pictureY, false);
-	cel_headerPos += 42;
+	_ports->priorityBandsInitSci11(inbuffer.subspan(40));
 }
-#endif
 
-extern void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData);
+extern void unpackCelData(const SciSpan<const byte> &inBuffer, SciSpan<byte> &celBitmap, byte clearColor, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData);
 
-void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA) {
-	byte *celBitmap = NULL;
-	byte *ptr = NULL;
-	byte *headerPtr = inbuffer + headerPos;
-	byte *rlePtr = inbuffer + rlePos;
+void GfxPicture::drawCelData(const SciSpan<const byte> &inbuffer, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA) {
+	const SciSpan<const byte> headerPtr = inbuffer.subspan(headerPos);
+	const SciSpan<const byte> rlePtr = inbuffer.subspan(rlePos);
 	// displaceX, displaceY fields are ignored, and may contain garbage
 	// (e.g. pic 261 in Dr. Brain 1 Spanish - bug #3614914)
 	//int16 displaceX, displaceY;
@@ -254,30 +159,16 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
 	if (!isEGA && !_addToFlag)
 		priority = 0;
 
-#ifdef ENABLE_SCI32
-	if (_resourceType != SCI_PICTURE_TYPE_SCI32) {
-#endif
-		// Width/height here are always LE, even in Mac versions
-		width = READ_LE_UINT16(headerPtr + 0);
-		height = READ_LE_UINT16(headerPtr + 2);
-		//displaceX = (signed char)headerPtr[4];
-		//displaceY = (unsigned char)headerPtr[5];
-		if (_resourceType == SCI_PICTURE_TYPE_SCI11)
-			// SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
-			clearColor = _screen->getColorWhite();
-		else
-			clearColor = headerPtr[6];
-#ifdef ENABLE_SCI32
-	} else {
-		width = READ_SCI11ENDIAN_UINT16(headerPtr + 0);
-		height = READ_SCI11ENDIAN_UINT16(headerPtr + 2);
-		//displaceX = READ_SCI11ENDIAN_UINT16(headerPtr + 4); // probably signed?!?
-		//displaceY = READ_SCI11ENDIAN_UINT16(headerPtr + 6); // probably signed?!?
-		clearColor = headerPtr[8];
-		if (headerPtr[9] == 0)
-			compression = false;
-	}
-#endif
+	// Width/height here are always LE, even in Mac versions
+	width = headerPtr.getUint16LEAt(0);
+	height = headerPtr.getUint16LEAt(2);
+	//displaceX = (signed char)headerPtr[4];
+	//displaceY = (unsigned char)headerPtr[5];
+	if (_resourceType == SCI_PICTURE_TYPE_SCI11)
+		// SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
+		clearColor = _screen->getColorWhite();
+	else
+		clearColor = headerPtr[6];
 
 	//if (displaceX || displaceY)
 	//	error("unsupported embedded cel-data in picture");
@@ -285,7 +176,8 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
 	// We will unpack cel-data into a temporary buffer and then plot it to screen
 	//  That needs to be done cause a mirrored picture may be requested
 	pixelCount = width * height;
-	celBitmap = new byte[pixelCount];
+	Common::SpanOwner<SciSpan<byte> > celBitmap;
+	celBitmap->allocate(pixelCount, _resource->name());
 
 	if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) {
 		// See GfxView::unpackCel() for why this black/white swap is done
@@ -296,22 +188,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
 			clearColor = 0;
 	}
 
-	if (compression)
-		unpackCelData(inbuffer, celBitmap, clearColor, pixelCount, rlePos, literalPos, _resMan->getViewType(), width, false);
-	else
+	if (compression) {
+		unpackCelData(inbuffer, *celBitmap, clearColor, rlePos, literalPos, _resMan->getViewType(), width, false);
+	} else
 		// No compression (some SCI32 pictures)
-		memcpy(celBitmap, rlePtr, pixelCount);
-
-	if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) {
-		// See GfxView::unpackCel() for why this black/white swap is done
-		// This picture swap is only needed in SCI32, not SCI1.1
-		for (int i = 0; i < pixelCount; i++) {
-			if (celBitmap[i] == 0)
-				celBitmap[i] = 0xff;
-			else if (celBitmap[i] == 0xff)
-				celBitmap[i] = 0;
-		}
-	}
+		memcpy(celBitmap->getUnsafeDataAt(0, pixelCount), rlePtr.getUnsafeDataAt(0, pixelCount), pixelCount);
 
 	Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();
 
@@ -356,13 +237,12 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
 		// but white and that won't matter because the screen is supposed to be already white. It seems that most (if not all)
 		// SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint
 		// the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra.
-		// SCI32 doesn't use _addToFlag at all.
-		if (!_addToFlag && _resourceType != SCI_PICTURE_TYPE_SCI32)
+		if (!_addToFlag)
 			clearColor = _screen->getColorWhite();
 
 		byte drawMask = priority > 15 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY;
 
-		ptr = celBitmap;
+		SciSpan<const byte> ptr = *celBitmap;
 		ptr += skipCelBitmapPixels;
 		ptr += skipCelBitmapLines * width;
 
@@ -440,8 +320,6 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
 			}
 		}
 	}
-
-	delete[] celBitmap;
 }
 
 enum {
@@ -549,7 +427,7 @@ static const byte vector_defaultEGApriority[PIC_EGAPRIORITY_SIZE] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
 };
 
-void GfxPicture::drawVectorData(byte *data, int dataSize) {
+void GfxPicture::drawVectorData(const SciSpan<const byte> &data) {
 	byte pic_op;
 	byte pic_color = _screen->getColorDefaultVectorData();
 	byte pic_priority = 255, pic_control = 255;
@@ -558,7 +436,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 	byte *EGApalette = &EGApalettes[_EGApaletteNo * PIC_EGAPALETTE_SIZE];
 	byte EGApriority[PIC_EGAPRIORITY_SIZE] = {0};
 	bool isEGA = false;
-	int curPos = 0;
+	uint curPos = 0;
 	uint16 size;
 	byte pixel;
 	int i;
@@ -591,7 +469,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 	}
 
 	// Drawing
-	while (curPos < dataSize) {
+	while (curPos < data.size()) {
 #ifdef DEBUG_PICTURE_DRAW
 		debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos);
 		_screen->copyToScreen();
@@ -774,7 +652,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 					break;
 				case PIC_OPX_EGA_EMBEDDED_VIEW:
 					vectorGetAbsCoordsNoMirror(data, curPos, x, y);
-					size = READ_LE_UINT16(data + curPos); curPos += 2;
+					size = data.getUint16LEAt(curPos); curPos += 2;
 					// hardcoded in SSCI, 16 for SCI1early excluding Space Quest 4, 0 for anything else
 					//  fixes sq4 pictures 546+547 (Vohaul's head and Roger Jr trapped). Bug #5250
 					//  fixes sq4 picture 631 (SQ1 view from cockpit). Bug 5249
@@ -783,11 +661,11 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 					} else {
 						_priority = 0;
 					}
-					drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0, 0, true);
+					drawCelData(data, curPos, curPos + 8, 0, x, y, 0, 0, true);
 					curPos += size;
 					break;
 				case PIC_OPX_EGA_SET_PRIORITY_TABLE:
-					_ports->priorityBandsInit(data + curPos);
+					_ports->priorityBandsInit(data.subspan(curPos, 14));
 					curPos += 14;
 					break;
 				default:
@@ -810,7 +688,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 							curPos += 256 + 4 + 1024;
 						} else {
 							// Setting half of the Amiga palette
-							_palette->modifyAmigaPalette(&data[curPos]);
+							_palette->modifyAmigaPalette(data.subspan(curPos));
 							curPos += 32;
 						}
 					} else {
@@ -824,7 +702,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 					break;
 				case PIC_OPX_VGA_EMBEDDED_VIEW: // draw cel
 					vectorGetAbsCoordsNoMirror(data, curPos, x, y);
-					size = READ_LE_UINT16(data + curPos); curPos += 2;
+					size = data.getUint16LEAt(curPos); curPos += 2;
 					if (getSciVersion() <= SCI_VERSION_1_EARLY) {
 						// During SCI1Early sierra always used 0 as priority for cels inside picture resources
 						//  fixes Space Quest 4 orange ship lifting off (bug #6446)
@@ -832,15 +710,15 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
 					} else {
 						_priority = pic_priority; // set global priority so the cel gets drawn using current priority as well
 					}
-					drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0, 0, false);
+					drawCelData(data, curPos, curPos + 8, 0, x, y, 0, 0, false);
 					curPos += size;
 					break;
 				case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST:
-					_ports->priorityBandsInit(-1, READ_LE_UINT16(data + curPos), READ_LE_UINT16(data + curPos + 2));
+					_ports->priorityBandsInit(-1, data.getUint16LEAt(curPos), data.getUint16LEAt(curPos + 2));
 					curPos += 4;
 					break;
 				case PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT:
-					_ports->priorityBandsInit(data + curPos);
+					_ports->priorityBandsInit(data.subspan(curPos, 14));
 					curPos += 14;
 					break;
 				default:
@@ -886,20 +764,20 @@ bool GfxPicture::vectorIsNonOpcode(byte pixel) {
 	return true;
 }
 
-void GfxPicture::vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y) {
+void GfxPicture::vectorGetAbsCoords(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y) {
 	byte pixel = data[curPos++];
 	x = data[curPos++] + ((pixel & 0xF0) << 4);
 	y = data[curPos++] + ((pixel & 0x0F) << 8);
 	if (_mirroredFlag) x = 319 - x;
 }
 
-void GfxPicture::vectorGetAbsCoordsNoMirror(byte *data, int &curPos, int16 &x, int16 &y) {
+void GfxPicture::vectorGetAbsCoordsNoMirror(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y) {
 	byte pixel = data[curPos++];
 	x = data[curPos++] + ((pixel & 0xF0) << 4);
 	y = data[curPos++] + ((pixel & 0x0F) << 8);
 }
 
-void GfxPicture::vectorGetRelCoords(byte *data, int &curPos, int16 &x, int16 &y) {
+void GfxPicture::vectorGetRelCoords(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y) {
 	byte pixel = data[curPos++];
 	if (pixel & 0x80) {
 		x -= ((pixel >> 4) & 7) * (_mirroredFlag ? -1 : 1);
@@ -913,7 +791,7 @@ void GfxPicture::vectorGetRelCoords(byte *data, int &curPos, int16 &x, int16 &y)
 	}
 }
 
-void GfxPicture::vectorGetRelCoordsMed(byte *data, int &curPos, int16 &x, int16 &y) {
+void GfxPicture::vectorGetRelCoordsMed(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y) {
 	byte pixel = data[curPos++];
 	if (pixel & 0x80) {
 		y -= (pixel & 0x7F);
@@ -928,7 +806,7 @@ void GfxPicture::vectorGetRelCoordsMed(byte *data, int &curPos, int16 &x, int16
 	}
 }
 
-void GfxPicture::vectorGetPatternTexture(byte *data, int &curPos, int16 pattern_Code, int16 &pattern_Texture) {
+void GfxPicture::vectorGetPatternTexture(const SciSpan<const byte> &data, uint &curPos, int16 pattern_Code, int16 &pattern_Texture) {
 	if (pattern_Code & SCI_PATTERN_CODE_USE_TEXTURE) {
 		pattern_Texture = (data[curPos++] >> 1) & 0x7f;
 	}
diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h
index 1be1ae3..48f8032 100644
--- a/engines/sci/graphics/picture.h
+++ b/engines/sci/graphics/picture.h
@@ -23,6 +23,8 @@
 #ifndef SCI_GRAPHICS_PICTURE_H
 #define SCI_GRAPHICS_PICTURE_H
 
+#include "sci/util.h"
+
 namespace Sci {
 
 #define SCI_PATTERN_CODE_RECTANGLE 0x10
@@ -54,28 +56,18 @@ public:
 	GuiResourceId getResourceId();
 	void draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
 
-#ifdef ENABLE_SCI32
-	int16 getSci32celCount();
-	int16 getSci32celY(int16 celNo);
-	int16 getSci32celX(int16 celNo);
-	int16 getSci32celWidth(int16 celNo);
-	int16 getSci32celHeight(int16 celNo);
-	int16 getSci32celPriority(int16 celNo);
-	void drawSci32Vga(int16 celNo, int16 callerX, int16 callerY, int16 pictureX, int16 pictureY, bool mirrored);
-#endif
-
 private:
 	void initData(GuiResourceId resourceId);
 	void reset();
 	void drawSci11Vga();
-	void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA);
-	void drawVectorData(byte *data, int size);
+	void drawCelData(const SciSpan<const byte> &inbuffer, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA);
+	void drawVectorData(const SciSpan<const byte> &data);
 	bool vectorIsNonOpcode(byte pixel);
-	void vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y);
-	void vectorGetAbsCoordsNoMirror(byte *data, int &curPos, int16 &x, int16 &y);
-	void vectorGetRelCoords(byte *data, int &curPos, int16 &x, int16 &y);
-	void vectorGetRelCoordsMed(byte *data, int &curPos, int16 &x, int16 &y);
-	void vectorGetPatternTexture(byte *data, int &curPos, int16 pattern_Code, int16 &pattern_Texture);
+	void vectorGetAbsCoords(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y);
+	void vectorGetAbsCoordsNoMirror(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y);
+	void vectorGetRelCoords(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y);
+	void vectorGetRelCoordsMed(const SciSpan<const byte> &data, uint &curPos, int16 &x, int16 &y);
+	void vectorGetPatternTexture(const SciSpan<const byte> &data, uint &curPos, int16 pattern_Code, int16 &pattern_Texture);
 	void vectorFloodFill(int16 x, int16 y, byte color, byte prio, byte control);
 	void vectorPattern(int16 x, int16 y, byte pic_color, byte pic_priority, byte pic_control, byte code, byte texture);
 	void vectorPatternBox(Common::Rect box, byte color, byte prio, byte control);
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 045a923..675883e 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -39,12 +39,6 @@ Portrait::Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *scre
 	init();
 }
 
-Portrait::~Portrait() {
-	delete[] _lipSyncDataOffsetTable;
-	delete[] _bitmaps;
-	delete[] _fileData;
-}
-
 void Portrait::init() {
 	// .BIN files are loaded from actors directory and from .\ directory
 	// header:
@@ -90,31 +84,28 @@ void Portrait::init() {
 	// 4 bytes appended, seem to be random
 	//   9E11120E for alex
 	//   9E9E9E9E for vizier
-	int32 fileSize = 0;
-	Common::SeekableReadStream *file =
-		SearchMan.createReadStreamForMember("actors/" + _resourceName + ".bin");
+	Common::String fileName = "actors/" + _resourceName + ".bin";
+	Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(fileName);
+		;
 	if (!file) {
-		file = SearchMan.createReadStreamForMember(_resourceName + ".bin");
+		fileName = _resourceName + ".bin";
+		file = SearchMan.createReadStreamForMember(fileName);
 		if (!file)
 			error("portrait %s.bin not found", _resourceName.c_str());
 	}
-	fileSize = file->size();
-	_fileData = new byte[fileSize];
-	file->read(_fileData, fileSize);
+	_fileData->allocateFromStream(*file, Common::kSpanMaxSize, fileName);
 	delete file;
 
-	if (strncmp((char *)_fileData, "WIN", 3)) {
+	if (strncmp((const char *)_fileData->getUnsafeDataAt(0, 3), "WIN", 3)) {
 		error("portrait %s doesn't have valid header", _resourceName.c_str());
 	}
-	_width = READ_LE_UINT16(_fileData + 3);
-	_height = READ_LE_UINT16(_fileData + 5);
-	_bitmapCount = READ_LE_UINT16(_fileData + 7);
-	_lipSyncIDCount = READ_LE_UINT16(_fileData + 11);
-
-	_bitmaps = new PortraitBitmap[_bitmapCount];
+	_width = _fileData->getUint16LEAt(3);
+	_height = _fileData->getUint16LEAt(5);
+	_bitmaps.resize(_fileData->getUint16LEAt(7));
+	_lipSyncIDCount = _fileData->getUint16LEAt(11);
 
-	uint16 portraitPaletteSize = READ_LE_UINT16(_fileData + 13);
-	byte *data = _fileData + 17;
+	uint16 portraitPaletteSize = _fileData->getUint16LEAt(13);
+	SciSpan<const byte> data = _fileData->subspan(17);
 	// Read palette
 	memset(&_portraitPalette, 0, sizeof(Palette));
 	uint16 palSize = 0, palNr = 0;
@@ -128,42 +119,40 @@ void Portrait::init() {
 	}
 
 	// Read all bitmaps
-	PortraitBitmap *curBitmap = _bitmaps;
 	uint16 bitmapNr;
 	uint16 bytesPerLine;
 
-	for (bitmapNr = 0; bitmapNr < _bitmapCount; bitmapNr++) {
-		curBitmap->width = READ_LE_UINT16(data + 2);
-		curBitmap->height = READ_LE_UINT16(data + 4);
-		bytesPerLine = READ_LE_UINT16(data + 6);
-		if (bytesPerLine < curBitmap->width)
+	for (bitmapNr = 0; bitmapNr < _bitmaps.size(); bitmapNr++) {
+		PortraitBitmap &curBitmap = _bitmaps[bitmapNr];
+		curBitmap.width = data.getUint16LEAt(2);
+		curBitmap.height = data.getUint16LEAt(4);
+		bytesPerLine = data.getUint16LEAt(6);
+		if (bytesPerLine < curBitmap.width)
 			error("kPortrait: bytesPerLine larger than actual width");
-		curBitmap->extraBytesPerLine = bytesPerLine - curBitmap->width;
-		curBitmap->rawBitmap = data + 14;
-		data += 14 + (curBitmap->height * bytesPerLine);
-		curBitmap++;
+		curBitmap.extraBytesPerLine = bytesPerLine - curBitmap.width;
+		curBitmap.rawBitmap = data.subspan(14, curBitmap.width * curBitmap.height);
+		data += 14 + (curBitmap.height * bytesPerLine);
 	}
 
 	// Offset table follows
-	curBitmap = _bitmaps;
-	int32 offsetTableSize = READ_LE_UINT32(data);
-	assert((bitmapNr + 1) * 14 <= offsetTableSize);
+	uint32 offsetTableSize = data.getUint32LEAt(0);
+	assert((bitmapNr + 1U) * 14U <= offsetTableSize);
 	data += 4;
-	byte *dataOffsetTable = data + 14; // we skip first bitmap offsets
-	for (bitmapNr = 0; bitmapNr < _bitmapCount; bitmapNr++) {
-		curBitmap->displaceX = READ_LE_UINT16(dataOffsetTable);
-		curBitmap->displaceY = READ_LE_UINT16(dataOffsetTable + 2);
+	SciSpan<const byte> dataOffsetTable = data.subspan(14); // we skip first bitmap offsets
+	for (bitmapNr = 0; bitmapNr < _bitmaps.size(); bitmapNr++) {
+		PortraitBitmap &curBitmap = _bitmaps[bitmapNr];
+		curBitmap.displaceX = dataOffsetTable.getUint16LEAt(0);
+		curBitmap.displaceY = dataOffsetTable.getUint16LEAt(2);
 		dataOffsetTable += 14;
-		curBitmap++;
 	}
 	data += offsetTableSize;
 
 	// raw lip-sync ID table follows
 	uint32 lipSyncIDTableSize;
 
-	lipSyncIDTableSize = READ_LE_UINT32(data);
+	lipSyncIDTableSize = data.getUint32LEAt(0);
 	data += 4;
-	assert( lipSyncIDTableSize == (_lipSyncIDCount * 4) );
+	assert(lipSyncIDTableSize == _lipSyncIDCount * 4);
 	_lipSyncIDTable = data;
 	data += lipSyncIDTableSize;
 
@@ -174,23 +163,23 @@ void Portrait::init() {
 	uint16 lipSyncDataNr;
 	uint16 lipSyncCurOffset;
 
-	lipSyncDataTableSize = READ_LE_UINT32(data);
+	lipSyncDataTableSize = data.getUint32LEAt(0);
 	data += 4;
-	assert( lipSyncDataTableSize == 0x220 ); // always this size, just a safety-check
+	assert(lipSyncDataTableSize == 0x220); // always this size, just a safety-check
 
 	_lipSyncData = data;
 	lipSyncDataTableLastOffset = lipSyncDataTableSize - 1;
-	_lipSyncDataOffsetTable = new uint16[ _lipSyncIDCount ];
+	_lipSyncDataOffsetTable.resize(_lipSyncIDCount);
 
 	lipSyncDataNr = 0;
 	lipSyncCurOffset = 0;
-	while ( (lipSyncCurOffset < lipSyncDataTableSize) && (lipSyncDataNr < _lipSyncIDCount) ) {
+	while (lipSyncCurOffset < lipSyncDataTableSize && lipSyncDataNr < _lipSyncIDCount) {
 		// We are currently at the start of ID-frame data
 		_lipSyncDataOffsetTable[lipSyncDataNr] = lipSyncCurOffset;
 
 		// Look for end of ID-frame data
 		lipSyncData = *data++; lipSyncCurOffset++;
-		while ( (lipSyncData != 0xFF) && (lipSyncCurOffset < lipSyncDataTableLastOffset) ) {
+		while (lipSyncData != 0xFF && lipSyncCurOffset < lipSyncDataTableLastOffset) {
 			// Either terminator (0xFF) or frame-data (1 byte tick count and 1 byte bitmap ID)
 			data++;
 			lipSyncData = *data++;
@@ -198,7 +187,6 @@ void Portrait::init() {
 		}
 		lipSyncDataNr++;
 	}
-	_lipSyncDataOffsetTableEnd = data;
 	// last 4 bytes seem to be garbage
 }
 
@@ -277,7 +265,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
 	// Do animation depending on rave resource till audio is done playing
 	int16 raveTicks;
 	uint16 raveID;
-	byte *raveLipSyncData;
+	SciSpan<const byte> raveLipSyncData;
 	byte raveLipSyncTicks;
 	byte raveLipSyncBitmapNr;
 	int timerPosition = 0;
@@ -286,7 +274,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
 	SciEvent curEvent;
 	bool userAbort = false;
 
-	while ((raveOffset < raveResource->size) && (!userAbort)) {
+	while (raveOffset < raveResource->size() && !userAbort) {
 		// rave string starts with tick count, followed by lipSyncID, tick count and so on
 		raveTicks = raveGetTicks(raveResource, &raveOffset);
 		if (raveTicks < 0)
@@ -297,7 +285,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
 		if (raveID) {
 			raveLipSyncData = raveGetLipSyncData(raveID);
 		} else {
-			raveLipSyncData = NULL;
+			raveLipSyncData = SciSpan<const byte>();
 		}
 
 #ifdef DEBUG_PORTRAIT
@@ -330,7 +318,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
 			//  Tick = 0xFF is the terminator for the data
 			timerPositionWithin = timerPosition;
 			raveLipSyncTicks = *raveLipSyncData++;
-			while ( (raveLipSyncData < _lipSyncDataOffsetTableEnd) && (raveLipSyncTicks != 0xFF) ) {
+			while (raveLipSyncData.size() && raveLipSyncTicks != 0xFF) {
 				if (raveLipSyncTicks)
 					raveLipSyncTicks--; // 1 -> wait 0 ticks, 2 -> wait 1 tick, etc.
 				timerPositionWithin += raveLipSyncTicks;
@@ -357,7 +345,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
 				// bitmap nr within sync data is base 1, we need base 0
 				raveLipSyncBitmapNr--;
 
-				if (raveLipSyncBitmapNr < _bitmapCount) {
+				if (raveLipSyncBitmapNr < _bitmaps.size()) {
 					drawBitmap(0);
 					drawBitmap(raveLipSyncBitmapNr);
 					bitsShow();
@@ -435,14 +423,14 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
 // returns ASCII ticks from lip sync string as uint16
 int16 Portrait::raveGetTicks(Resource *resource, uint *offset) {
 	uint curOffset = *offset;
-	byte *curData = resource->data + curOffset;
+	SciSpan<const byte> curData = resource->subspan(curOffset);
 	byte curByte;
 	uint16 curValue = 0;
 
-	if (curOffset >= resource->size)
+	if (curOffset >= resource->size())
 		return -1;
 
-	while (curOffset < resource->size) {
+	while (curOffset < resource->size()) {
 		curByte = *curData++; curOffset++;
 		if ( curByte == ' ' )
 			break;
@@ -460,11 +448,11 @@ int16 Portrait::raveGetTicks(Resource *resource, uint *offset) {
 // returns ASCII ID from lip sync string as uint16
 uint16 Portrait::raveGetID(Resource *resource, uint *offset) {
 	uint curOffset = *offset;
-	byte *curData = resource->data + curOffset;
+	SciSpan<const byte> curData = resource->subspan(curOffset);
 	byte curByte = 0;
 	uint16 curValue = 0;
 
-	while (curOffset < resource->size) {
+	while (curOffset < resource->size()) {
 		curByte = *curData++; curOffset++;
 		if ( curByte == ' ' )
 			break;
@@ -480,9 +468,9 @@ uint16 Portrait::raveGetID(Resource *resource, uint *offset) {
 }
 
 // Searches for a specific lip sync ID and returns pointer to lip sync data or NULL in case ID was not found
-byte *Portrait::raveGetLipSyncData(uint16 raveID) {
+SciSpan<const byte> Portrait::raveGetLipSyncData(const uint16 raveID) {
 	uint lipSyncIDNr = 0;
-	byte *lipSyncIDPtr = _lipSyncIDTable;
+	SciSpan<const byte> lipSyncIDPtr = _lipSyncIDTable;
 	byte lipSyncIDByte1, lipSyncIDByte2;
 	uint16 lipSyncID;
 
@@ -490,20 +478,19 @@ byte *Portrait::raveGetLipSyncData(uint16 raveID) {
 	while (lipSyncIDNr < _lipSyncIDCount) {
 		lipSyncIDByte1 = *lipSyncIDPtr++;
 		lipSyncIDByte2 = *lipSyncIDPtr++;
-		lipSyncID = ( lipSyncIDByte1 << 8 ) | lipSyncIDByte2;
+		lipSyncID = (lipSyncIDByte1 << 8) | lipSyncIDByte2;
 
-		if ( lipSyncID == raveID ) {
-			return _lipSyncData + _lipSyncDataOffsetTable[lipSyncIDNr];
+		if (lipSyncID == raveID) {
+			return _lipSyncData.subspan(_lipSyncDataOffsetTable[lipSyncIDNr]);
 		}
 
 		lipSyncIDNr++;
 		lipSyncIDPtr += 2; // ID is every 4 bytes
 	}
-	return NULL;
+	return SciSpan<const byte>();
 }
 
 void Portrait::drawBitmap(uint16 bitmapNr) {
-	byte *data = _bitmaps[bitmapNr].rawBitmap;
 	uint16 bitmapHeight = _bitmaps[bitmapNr].height;
 	uint16 bitmapWidth = _bitmaps[bitmapNr].width;
 	Common::Point bitmapPosition = _position;
@@ -511,6 +498,7 @@ void Portrait::drawBitmap(uint16 bitmapNr) {
 	bitmapPosition.x += _bitmaps[bitmapNr].displaceX;
 	bitmapPosition.y += _bitmaps[bitmapNr].displaceY;
 
+	const byte *data = _bitmaps[bitmapNr].rawBitmap.getUnsafeDataAt(0, bitmapWidth * bitmapHeight);
 	for (int y = 0; y < bitmapHeight; y++) {
 		for (int x = 0; x < bitmapWidth; x++) {
 			_screen->putPixelOnDisplay(bitmapPosition.x + x, bitmapPosition.y + y, _portraitPalette.mapping[*data++]);
diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h
index e0888da..b6f5a17 100644
--- a/engines/sci/graphics/portrait.h
+++ b/engines/sci/graphics/portrait.h
@@ -23,13 +23,15 @@
 #ifndef SCI_GRAPHICS_PORTRAITS_H
 #define SCI_GRAPHICS_PORTRAITS_H
 
+#include "sci/util.h"
+
 namespace Sci {
 
 struct PortraitBitmap {
 	int16 width, height;
 	int16 extraBytesPerLine;
 	uint16 displaceX, displaceY;
-	byte *rawBitmap;
+	SciSpan<const byte> rawBitmap;
 };
 
 /**
@@ -40,7 +42,6 @@ struct PortraitBitmap {
 class Portrait {
 public:
 	Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName);
-	~Portrait();
 
 	void setupAudio(uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
 	void doit(Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
@@ -54,7 +55,7 @@ private:
 
 	int16 raveGetTicks(Resource *resource, uint *offset);
 	uint16 raveGetID(Resource *resource, uint *offset);
-	byte *raveGetLipSyncData(uint16 raveID);
+	SciSpan<const byte> raveGetLipSyncData(const uint16 raveID);
 
 	ResourceManager *_resMan;
 	EventManager *_event;
@@ -66,19 +67,17 @@ private:
 	uint16 _width;
 	Palette _portraitPalette;
 
-	uint16 _bitmapCount;
-	PortraitBitmap *_bitmaps;
+	Common::Array<PortraitBitmap> _bitmaps;
 
 	Common::String _resourceName;
 
-	byte *_fileData;
+	Common::SpanOwner<SciSpan<const byte> > _fileData;
 
 	uint32 _lipSyncIDCount;
-	byte *_lipSyncIDTable;
+	SciSpan<const byte> _lipSyncIDTable;
 
-	byte *_lipSyncData;
-	uint16 *_lipSyncDataOffsetTable;
-	byte *_lipSyncDataOffsetTableEnd;
+	SciSpan<const byte> _lipSyncData;
+	Common::Array<uint16> _lipSyncDataOffsetTable;
 
 	Common::Point _position;
 };
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index 0d00ce0..eb66896 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -674,13 +674,13 @@ void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) {
 		_priorityBottom--;
 }
 
-void GfxPorts::priorityBandsInit(byte *data) {
+void GfxPorts::priorityBandsInit(const SciSpan<const byte> &data) {
 	int i = 0, inx;
 	byte priority = 0;
 
 	for (inx = 0; inx < 14; inx++) {
-		priority = *data++;
-		while (i < priority)
+		priority = data[inx];
+		while (i < priority && i < 200)
 			_priorityBands[i++] = inx;
 	}
 	while (i < 200)
@@ -688,13 +688,13 @@ void GfxPorts::priorityBandsInit(byte *data) {
 }
 
 // Gets used to read priority bands data from sci1.1 pictures
-void GfxPorts::priorityBandsInitSci11(byte *data) {
+void GfxPorts::priorityBandsInitSci11(SciSpan<const byte> data) {
 	byte priorityBands[14];
 	for (int bandNo = 0; bandNo < 14; bandNo++) {
-		priorityBands[bandNo] = READ_LE_UINT16(data);
+		priorityBands[bandNo] = data.getUint16LEAt(0);
 		data += 2;
 	}
-	priorityBandsInit(priorityBands);
+	priorityBandsInit(SciSpan<const byte>(priorityBands, 14));
 }
 
 void GfxPorts::kernelInitPriorityBands() {
diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h
index 51aca09..cd25645 100644
--- a/engines/sci/graphics/ports.h
+++ b/engines/sci/graphics/ports.h
@@ -93,8 +93,8 @@ public:
 	void clipLine(Common::Point &start, Common::Point &end);
 
 	void priorityBandsInit(int16 bandCount, int16 top, int16 bottom);
-	void priorityBandsInit(byte *data);
-	void priorityBandsInitSci11(byte *data);
+	void priorityBandsInit(const SciSpan<const byte> &data);
+	void priorityBandsInitSci11(SciSpan<const byte> data);
 
 	void kernelInitPriorityBands();
 	void kernelGraphAdjustPriority(int top, int bottom);
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index de6df39..e69c432 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -719,40 +719,41 @@ void GfxScreen::debugShowMap(int mapNo) {
 	copyToScreen();
 }
 
-void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel) {
+void GfxScreen::scale2x(const SciSpan<const byte> &src, SciSpan<byte> &dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel) {
 	assert(bytesPerPixel == 1 || bytesPerPixel == 2);
 	const int newWidth = srcWidth * 2;
 	const int pitch = newWidth * bytesPerPixel;
-	const byte *srcPtr = src;
+	const byte *srcPtr = src.getUnsafeDataAt(0, srcWidth * srcHeight * bytesPerPixel);
+	byte *dstPtr = dst.getUnsafeDataAt(0, srcWidth * srcHeight * bytesPerPixel);
 
 	if (bytesPerPixel == 1) {
 		for (int y = 0; y < srcHeight; y++) {
 			for (int x = 0; x < srcWidth; x++) {
 				const byte color = *srcPtr++;
-				dst[0] = color;
-				dst[1] = color;
-				dst[newWidth] = color;
-				dst[newWidth + 1] = color;
-				dst += 2;
+				dstPtr[0] = color;
+				dstPtr[1] = color;
+				dstPtr[newWidth] = color;
+				dstPtr[newWidth + 1] = color;
+				dstPtr += 2;
 			}
-			dst += newWidth;
+			dstPtr += newWidth;
 		}
 	} else if (bytesPerPixel == 2) {
 		for (int y = 0; y < srcHeight; y++) {
 			for (int x = 0; x < srcWidth; x++) {
 				const byte color = *srcPtr++;
 				const byte color2 = *srcPtr++;
-				dst[0] = color;
-				dst[1] = color2;
-				dst[2] = color;
-				dst[3] = color2;
-				dst[pitch] = color;
-				dst[pitch + 1] = color2;
-				dst[pitch + 2] = color;
-				dst[pitch + 3] = color2;
-				dst += 4;
+				dstPtr[0] = color;
+				dstPtr[1] = color2;
+				dstPtr[2] = color;
+				dstPtr[3] = color2;
+				dstPtr[pitch] = color;
+				dstPtr[pitch + 1] = color2;
+				dstPtr[pitch + 2] = color;
+				dstPtr[pitch + 3] = color2;
+				dstPtr += 4;
 			}
-			dst += pitch;
+			dstPtr += pitch;
 		}
 	}
 }
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 63ee4ed..46214b7 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -31,8 +31,10 @@
 
 namespace Sci {
 
-#define SCI_SCREEN_UPSCALEDMAXHEIGHT 200
-#define SCI_SCREEN_UPSCALEDMAXWIDTH  320
+enum {
+	SCI_SCREEN_UPSCALEDMAXHEIGHT = 200,
+	SCI_SCREEN_UPSCALEDMAXWIDTH  = 320
+};
 
 enum GfxScreenUpscaledMode {
 	GFX_SCREEN_UPSCALED_DISABLED	= 0,
@@ -76,11 +78,6 @@ public:
 	byte getColorWhite() { return _colorWhite; }
 	byte getColorDefaultVectorData() { return _colorDefaultVectorData; }
 
-#ifdef ENABLE_SCI32
-	byte *getDisplayScreen() { return _displayScreen; }
-	byte *getPriorityScreen() { return _priorityScreen; }
-#endif
-
 	void clearForRestoreGame();
 	void copyToScreen();
 	void copyFromScreen(byte *buffer);
@@ -120,7 +117,7 @@ public:
 	void bitsGetRect(byte *memoryPtr, Common::Rect *destRect);
 	void bitsRestore(byte *memoryPtr);
 
-	void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel = 1);
+	void scale2x(const SciSpan<const byte> &src, SciSpan<byte> &dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel = 1);
 
 	void adjustToUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewScalingType = SCI_VIEW_NATIVERES_NONE);
 	void adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewScalingType = SCI_VIEW_NATIVERES_NONE);
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
index c07e075..77ff9a3 100644
--- a/engines/sci/graphics/screen_item32.cpp
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -183,9 +183,9 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo
 
 			// NOTE: +2 because the header size field itself is excluded from
 			// the header size in the data
-			const uint16 headerSize = READ_SCI11ENDIAN_UINT16(view->data) + 2;
-			const uint8 loopCount = view->data[2];
-			const uint8 loopSize = view->data[12];
+			const uint16 headerSize = view->getUint16SEAt(0) + 2;
+			const uint8 loopCount = view->getUint8At(2);
+			const uint8 loopSize = view->getUint8At(12);
 
 			// loopNo is set to be an unsigned integer in SSCI, so if it's a
 			// negative value, it'll be fixed accordingly
@@ -195,10 +195,10 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo
 				writeSelectorValue(segMan, object, SELECTOR(loop), maxLoopNo);
 			}
 
-			byte *loopData = view->data + headerSize + (_celInfo.loopNo * loopSize);
+			SciSpan<const byte> loopData = view->subspan(headerSize + (_celInfo.loopNo * loopSize));
 			const int8 seekEntry = loopData[0];
 			if (seekEntry != -1) {
-				loopData = view->data + headerSize + (seekEntry * loopSize);
+				loopData = view->subspan(headerSize + (seekEntry * loopSize));
 			}
 
 			// celNo is set to be an unsigned integer in SSCI, so if it's a
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 0c09fcb..2dc7775 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -21,7 +21,6 @@
  */
 
 #include "sci/sci.h"
-#include "sci/util.h"
 #include "sci/engine/state.h"
 #include "sci/graphics/screen.h"
 #include "sci/graphics/palette.h"
@@ -39,16 +38,7 @@ GfxView::GfxView(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette
 }
 
 GfxView::~GfxView() {
-	// Iterate through the loops
-	for (uint16 loopNum = 0; loopNum < _loopCount; loopNum++) {
-		// and through the cells of each loop
-		for (uint16 celNum = 0; celNum < _loop[loopNum].celCount; celNum++) {
-			delete[] _loop[loopNum].cel[celNum].rawBitmap;
-		}
-		delete[] _loop[loopNum].cel;
-	}
-	delete[] _loop;
-
+	_loop.clear();
 	_resMan->unlockResource(_resource);
 }
 
@@ -111,10 +101,8 @@ void GfxView::initData(GuiResourceId resourceId) {
 	if (!_resource) {
 		error("view resource %d not found", resourceId);
 	}
-	_resourceData = _resource->data;
-	_resourceSize = _resource->size;
 
-	byte *celData, *loopData;
+	SciSpan<const byte> celData, loopData;
 	uint16 celOffset;
 	CelInfo *cel;
 	uint16 celCount = 0;
@@ -130,7 +118,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 
 	_loopCount = 0;
 	_embeddedPal = false;
-	_EGAmapping = NULL;
+	_EGAmapping.clear();
 	_sci2ScaleRes = SCI_VIEW_NATIVERES_NONE;
 	_isScaleable = true;
 
@@ -143,11 +131,10 @@ void GfxView::initData(GuiResourceId resourceId) {
 	// make them look better (like removing dithered colors that aren't caught
 	// by our undithering or even improve the graphics overall).
 	if (curViewType == kViewEga) {
-		if (_resourceData[1] == 0x80) {
+		if (_resource->getUint8At(1) == 0x80) {
 			curViewType = kViewVga;
-		} else {
-			if (READ_LE_UINT16(_resourceData + 4) == 1)
-				curViewType = kViewVga11;
+		} else if (_resource->getUint16LEAt(4) == 1) {
+			curViewType = kViewVga11;
 		}
 	}
 
@@ -159,12 +146,12 @@ void GfxView::initData(GuiResourceId resourceId) {
 	case kViewVga: // View-format SCI1
 		// LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
 
-		_loopCount = _resourceData[0];
+		_loopCount = _resource->getUint8At(0);
 		// bit 0x8000 of _resourceData[1] means palette is set
-		if (_resourceData[1] & 0x40)
+		if (_resource->getUint8At(1) & 0x40)
 			isCompressed = false;
-		mirrorBits = READ_LE_UINT16(_resourceData + 2);
-		palOffset = READ_LE_UINT16(_resourceData + 6);
+		mirrorBits = _resource->getUint16LEAt(2);
+		palOffset = _resource->getUint16LEAt(6);
 
 		if (palOffset && palOffset != 0x100) {
 			// Some SCI0/SCI01 games also have an offset set. It seems that it
@@ -174,7 +161,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 			// have this pointing to a 8x16 byte mapping table that needs to get
 			// applied then.
 			if (!isEGA) {
-				_palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
+				_palette->createFromData(_resource->subspan(palOffset), &_viewPalette);
 				_embeddedPal = true;
 			} else {
 				// Only use the EGA-mapping, when being SCI1 EGA
@@ -182,44 +169,43 @@ void GfxView::initData(GuiResourceId resourceId) {
 				//  with broken mapping tables. I guess those games won't use the mapping, so I rather disable it
 				//  for them
 				if (getSciVersion() == SCI_VERSION_1_EGA_ONLY) {
-					_EGAmapping = &_resourceData[palOffset];
 					for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
-						if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
+						const SciSpan<const byte> mapping = _resource->subspan(palOffset + EGAmapNr * SCI_VIEW_EGAMAPPING_SIZE, SCI_VIEW_EGAMAPPING_SIZE);
+						if (memcmp(mapping.getUnsafeDataAt(0, SCI_VIEW_EGAMAPPING_SIZE), EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
 							break;
-						_EGAmapping += SCI_VIEW_EGAMAPPING_SIZE;
 					}
 					// If all mappings are "straight", then we actually ignore the mapping
 					if (EGAmapNr == SCI_VIEW_EGAMAPPING_COUNT)
-						_EGAmapping = NULL;
+						_EGAmapping.clear();
 					else
-						_EGAmapping = &_resourceData[palOffset];
+						_EGAmapping = _resource->subspan(palOffset, SCI_VIEW_EGAMAPPING_COUNT * SCI_VIEW_EGAMAPPING_SIZE);
 				}
 			}
 		}
 
-		_loop = new LoopInfo[_loopCount];
+		_loop.resize(_loopCount);
 		for (loopNo = 0; loopNo < _loopCount; loopNo++) {
-			loopData = _resourceData + READ_LE_UINT16(_resourceData + 8 + loopNo * 2);
+			loopData = _resource->subspan(_resource->getUint16LEAt(8 + loopNo * 2));
 			// CelCount:WORD Unknown:WORD CelOffset0:WORD CelOffset1:WORD...
 
-			celCount = READ_LE_UINT16(loopData);
+			celCount = loopData.getUint16LEAt(0);
 			_loop[loopNo].celCount = celCount;
 			_loop[loopNo].mirrorFlag = mirrorBits & 1 ? true : false;
 			mirrorBits >>= 1;
 
 			// read cel info
-			_loop[loopNo].cel = new CelInfo[celCount];
+			_loop[loopNo].cel.resize(celCount);
 			for (celNo = 0; celNo < celCount; celNo++) {
-				celOffset = READ_LE_UINT16(loopData + 4 + celNo * 2);
-				celData = _resourceData + celOffset;
+				celOffset = loopData.getUint16LEAt(4 + celNo * 2);
+				celData = _resource->subspan(celOffset);
 
 				// For VGA
 				// Width:WORD Height:WORD DisplaceX:BYTE DisplaceY:BYTE ClearKey:BYTE Unknown:BYTE RLEData starts now directly
 				// For EGA
 				// Width:WORD Height:WORD DisplaceX:BYTE DisplaceY:BYTE ClearKey:BYTE EGAData starts now directly
 				cel = &_loop[loopNo].cel[celNo];
-				cel->scriptWidth = cel->width = READ_LE_UINT16(celData);
-				cel->scriptHeight = cel->height = READ_LE_UINT16(celData + 2);
+				cel->scriptWidth = cel->width = celData.getUint16LEAt(0);
+				cel->scriptHeight = cel->height = celData.getUint16LEAt(2);
 				cel->displaceX = (signed char)celData[4];
 				cel->displaceY = celData[5];
 				cel->clearKey = celData[6];
@@ -251,7 +237,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 						cel->offsetLiteral = celOffset + 8;
 					}
 				}
-				cel->rawBitmap = 0;
+				cel->rawBitmap.clear();
 				if (_loop[loopNo].mirrorFlag)
 					cel->displaceX = -cel->displaceX;
 			}
@@ -260,15 +246,15 @@ void GfxView::initData(GuiResourceId resourceId) {
 
 	case kViewVga11: // View-format SCI1.1+
 		// HeaderSize:WORD LoopCount:BYTE Flags:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
-		headerSize = READ_SCI11ENDIAN_UINT16(_resourceData + 0) + 2; // headerSize is not part of the header, so it's added
+		headerSize = _resource->getUint16SEAt(0) + 2; // headerSize is not part of the header, so it's added
 		assert(headerSize >= 16);
-		_loopCount = _resourceData[2];
+		_loopCount = _resource->getUint8At(2);
 		assert(_loopCount);
-		palOffset = READ_SCI11ENDIAN_UINT32(_resourceData + 8);
+		palOffset = _resource->getUint32SEAt(8);
 
 		// For SCI32, this is a scale flag
 		if (getSciVersion() >= SCI_VERSION_2) {
-			_sci2ScaleRes = (Sci32ViewNativeResolution)_resourceData[5];
+			_sci2ScaleRes = (Sci32ViewNativeResolution)_resource->getUint8At(5);
 			if (_screen->getUpscaledHires() == GFX_SCREEN_UPSCALED_DISABLED)
 				_sci2ScaleRes = SCI_VIEW_NATIVERES_NONE;
 		}
@@ -279,7 +265,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 		// we assume that if flags is 0h the view does not support flags and default to scaleable
 		// if it's 1h then we assume that the view is not to be scaled
 		// if it's 40h then we assume that the view is scaleable
-		switch (_resourceData[3]) {
+		switch (_resource->getUint8At(3)) {
 		case 1:
 			_isScaleable = false;
 			break;
@@ -288,31 +274,31 @@ void GfxView::initData(GuiResourceId resourceId) {
 		case 0:
 			break; // don't do anything, we already have _isScaleable set
 		default:
-			error("unsupported flags byte (%d) inside sci1.1 view", _resourceData[3]);
+			error("unsupported flags byte (%d) inside sci1.1 view", _resource->getUint8At(3));
 			break;
 		}
 
-		loopData = _resourceData + headerSize;
-		loopSize = _resourceData[12];
+		loopData = _resource->subspan(headerSize);
+		loopSize = _resource->getUint8At(12);
 		assert(loopSize >= 16);
-		celSize = _resourceData[13];
+		celSize = _resource->getUint8At(13);
 		assert(celSize >= 32);
 
 		if (palOffset) {
-			_palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
+			_palette->createFromData(_resource->subspan(palOffset), &_viewPalette);
 			_embeddedPal = true;
 		}
 
-		_loop = new LoopInfo[_loopCount];
+		_loop.resize(_loopCount);
 		for (loopNo = 0; loopNo < _loopCount; loopNo++) {
-			loopData = _resourceData + headerSize + (loopNo * loopSize);
+			loopData = _resource->subspan(headerSize + (loopNo * loopSize));
 
 			seekEntry = loopData[0];
 			if (seekEntry != 255) {
 				if (seekEntry >= _loopCount)
 					error("Bad loop-pointer in sci 1.1 view");
 				_loop[loopNo].mirrorFlag = true;
-				loopData = _resourceData + headerSize + (seekEntry * loopSize);
+				loopData = _resource->subspan(headerSize + (seekEntry * loopSize));
 			} else {
 				_loop[loopNo].mirrorFlag = false;
 			}
@@ -320,16 +306,18 @@ void GfxView::initData(GuiResourceId resourceId) {
 			celCount = loopData[2];
 			_loop[loopNo].celCount = celCount;
 
-			celData = _resourceData + READ_SCI11ENDIAN_UINT32(loopData + 12);
+			const uint32 celDataOffset = loopData.getUint32SEAt(12);
 
 			// read cel info
-			_loop[loopNo].cel = new CelInfo[celCount];
+			_loop[loopNo].cel.resize(celCount);
 			for (celNo = 0; celNo < celCount; celNo++) {
+				celData = _resource->subspan(celDataOffset + celNo * celSize, celSize);
+
 				cel = &_loop[loopNo].cel[celNo];
-				cel->scriptWidth = cel->width = READ_SCI11ENDIAN_UINT16(celData);
-				cel->scriptHeight = cel->height = READ_SCI11ENDIAN_UINT16(celData + 2);
-				cel->displaceX = READ_SCI11ENDIAN_UINT16(celData + 4);
-				cel->displaceY = READ_SCI11ENDIAN_UINT16(celData + 6);
+				cel->scriptWidth = cel->width = celData.getInt16SEAt(0);
+				cel->scriptHeight = cel->height = celData.getInt16SEAt(2);
+				cel->displaceX = celData.getInt16SEAt(4);
+				cel->displaceY = celData.getInt16SEAt(6);
 				if (cel->displaceY < 0)
 					cel->displaceY += 255; // sierra did this adjust in their sci1.1 getCelRect() - not sure about sci32
 
@@ -337,18 +325,16 @@ void GfxView::initData(GuiResourceId resourceId) {
 
 				cel->clearKey = celData[8];
 				cel->offsetEGA = 0;
-				cel->offsetRLE = READ_SCI11ENDIAN_UINT32(celData + 24);
-				cel->offsetLiteral = READ_SCI11ENDIAN_UINT32(celData + 28);
+				cel->offsetRLE = celData.getUint32SEAt(24);
+				cel->offsetLiteral = celData.getUint32SEAt(28);
 
 				// GK1-hires content is actually uncompressed, we need to swap both so that we process it as such
 				if ((cel->offsetRLE) && (!cel->offsetLiteral))
 					SWAP(cel->offsetRLE, cel->offsetLiteral);
 
-				cel->rawBitmap = 0;
+				cel->rawBitmap.clear();
 				if (_loop[loopNo].mirrorFlag)
 					cel->displaceX = -cel->displaceX;
-
-				celData += celSize;
 			}
 		}
 		break;
@@ -367,20 +353,16 @@ void GfxView::initData(GuiResourceId resourceId) {
 		// View 995, Loop 13, Cel 2 = "DUAL" (<- our injected view)
 		if ((g_sci->isCD()) && (resourceId == 995)) {
 			// security checks
-			if (_loopCount >= 14) {
-				if ((_loop[13].celCount == 2) && (_loop[13].cel[0].width == 46) && (_loop[13].cel[0].height == 11)) {
-					// copy current cels over
-					CelInfo *newCels = new CelInfo[3];
-					memcpy(newCels, _loop[13].cel, sizeof(CelInfo) * 2);
-					delete[] _loop[13].cel;
-					_loop[13].celCount++;
-					_loop[13].cel = newCels;
-					// Duplicate cel 0 to cel 2
-					memcpy(&_loop[13].cel[2], &_loop[13].cel[0], sizeof(CelInfo));
-					// copy over our data (which is uncompressed bitmap data)
-					_loop[13].cel[2].rawBitmap = new byte[sizeof(ViewInject_LauraBow2_Dual)];
-					memcpy(_loop[13].cel[2].rawBitmap, ViewInject_LauraBow2_Dual, sizeof(ViewInject_LauraBow2_Dual));
-				}
+			if (_loop.size() >= 14 &&
+				_loop[13].cel.size() == 2 &&
+				_loop[13].cel[0].width == 46 &&
+				_loop[13].cel[0].height == 11) {
+
+				_loop[13].cel.resize(3);
+				// Duplicate cel 0 to cel 2
+				_loop[13].cel[2] = _loop[13].cel[0];
+				// use our data (which is uncompressed bitmap data)
+				_loop[13].cel[2].rawBitmap->allocateFromSpan(SciSpan<const byte>(ViewInject_LauraBow2_Dual, sizeof(ViewInject_LauraBow2_Dual)));
 			}
 		}
 		break;
@@ -393,25 +375,18 @@ void GfxView::initData(GuiResourceId resourceId) {
 		// View 947, Loop 12, Cel 1 = "DUAL" (pressed) (<- our injected view)
 		if ((g_sci->isCD()) && (resourceId == 947)) {
 			// security checks
-			if (_loopCount == 12) {
-				if ((_loop[8].celCount == 2) && (_loop[8].cel[0].width == 50) && (_loop[8].cel[0].height == 15)) {
-					// add another loop
-					LoopInfo *newLoops = new LoopInfo[_loopCount + 1];
-					memcpy(newLoops, _loop, sizeof(LoopInfo) * _loopCount);
-					delete[] _loop;
-					_loop = newLoops;
-					_loopCount++;
-					// copy loop 8 to loop 12
-					memcpy(&_loop[12], &_loop[8], sizeof(LoopInfo));
-					_loop[12].cel = new CelInfo[2];
-					// duplicate all cels of loop 8 and into loop 12
-					memcpy(_loop[12].cel, _loop[8].cel, sizeof(CelInfo) * _loop[8].celCount);
-					// copy over our data (which is uncompressed bitmap data)
-					_loop[12].cel[0].rawBitmap = new byte[sizeof(ViewInject_KingsQuest6_Dual1)];
-					memcpy(_loop[12].cel[0].rawBitmap, ViewInject_KingsQuest6_Dual1, sizeof(ViewInject_KingsQuest6_Dual1));
-					_loop[12].cel[1].rawBitmap = new byte[sizeof(ViewInject_KingsQuest6_Dual2)];
-					memcpy(_loop[12].cel[1].rawBitmap, ViewInject_KingsQuest6_Dual2, sizeof(ViewInject_KingsQuest6_Dual2));
-				}
+			if (_loop.size() == 12 &&
+				_loop[8].cel.size() == 2 &&
+				_loop[8].cel[0].width == 50 &&
+				_loop[8].cel[0].height == 15) {
+
+				// add another loop
+				_loop.resize(_loop.size() + 1);
+				// copy loop 8 to loop 12
+				_loop[12] = _loop[8];
+				// use our data (which is uncompressed bitmap data)
+				_loop[12].cel[0].rawBitmap->allocateFromSpan(SciSpan<const byte>(ViewInject_KingsQuest6_Dual1, sizeof(ViewInject_KingsQuest6_Dual1)));
+				_loop[12].cel[1].rawBitmap->allocateFromSpan(SciSpan<const byte>(ViewInject_KingsQuest6_Dual2, sizeof(ViewInject_KingsQuest6_Dual2)));
 			}
 		}
 		break;
@@ -491,16 +466,19 @@ void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int1
 	outRect.top = outRect.bottom - scaledHeight;
 }
 
-void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) {
-	byte *outPtr = celBitmap;
+void unpackCelData(const SciSpan<const byte> &inBuffer, SciSpan<byte> &celBitmap, byte clearColor, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) {
+	const int pixelCount = celBitmap.size();
+	byte *outPtr = celBitmap.getUnsafeDataAt(0);
 	byte curByte, runLength;
-	byte *rlePtr = inBuffer + rlePos;
+	// TODO: Calculate correct maximum dimensions
+	const byte *rlePtr = inBuffer.getUnsafeDataAt(rlePos);
 	// The existence of a literal position pointer signifies data with two
 	// separate streams, most likely a SCI1.1 view
-	byte *literalPtr = inBuffer + literalPos;
+	const byte *literalPtr = inBuffer.getUnsafeDataAt(literalPos, inBuffer.size() - literalPos);
+	const byte *const endOfResource = inBuffer.getUnsafeDataAt(inBuffer.size(), 0);
 	int pixelNr = 0;
 
-	memset(celBitmap, clearColor, pixelCount);
+	memset(celBitmap.getUnsafeDataAt(0), clearColor, celBitmap.size());
 
 	// View unpacking:
 	//
@@ -545,14 +523,17 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo
 			uint32 pixelLine = pixelNr;
 
 			if (hasByteLengths) {
+				assert (rlePtr + 2 <= endOfResource);
 				pixelNr += *rlePtr++;
 				runLength = *rlePtr++;
 			} else {
+				assert (rlePtr + 4 <= endOfResource);
 				pixelNr += READ_BE_UINT16(rlePtr);
 				runLength = READ_BE_UINT16(rlePtr + 2);
 				rlePtr += 4;
 			}
 
+			assert(literalPtr + MIN<int>(runLength, pixelCount - pixelNr) <= endOfResource);
 			while (runLength-- && pixelNr < pixelCount)
 				outPtr[pixelNr++] = *literalPtr++;
 
@@ -566,7 +547,7 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo
 		while (pixelNr < pixelCount) {
 			curByte = *rlePtr++;
 			runLength = curByte >> 4;
-			memset(outPtr + pixelNr,        curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
+			memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
 			pixelNr += runLength;
 		}
 		break;
@@ -576,7 +557,7 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo
 			if (curByte & 0x07) { // fill with color
 				runLength = curByte & 0x07;
 				curByte = curByte >> 3;
-				memset(outPtr + pixelNr,           curByte, MIN<uint16>(runLength, pixelCount - pixelNr));
+				memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr));
 			} else { // skip the next pixels (transparency)
 				runLength = curByte >> 3;
 			}
@@ -589,7 +570,7 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo
 			if (curByte & 0xC0) { // fill with color
 				runLength = curByte >> 6;
 				curByte = curByte & 0x3F;
-				memset(outPtr + pixelNr,           curByte, MIN<uint16>(runLength, pixelCount - pixelNr));
+				memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr));
 			} else { // skip the next pixels (transparency)
 				runLength = curByte & 0x3F;
 			}
@@ -638,12 +619,12 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo
 	}
 }
 
-void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) {
+void GfxView::unpackCel(int16 loopNo, int16 celNo, SciSpan<byte> &outPtr) {
 	const CelInfo *celInfo = getCelInfo(loopNo, celNo);
 
 	if (celInfo->offsetEGA) {
 		// decompression for EGA views
-		unpackCelData(_resourceData, outPtr, 0, pixelCount, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false);
+		unpackCelData(*_resource, outPtr, 0, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false);
 	} else {
 		// We fill the buffer with transparent pixels, so that we can later skip
 		//  over pixels to automatically have them transparent
@@ -667,11 +648,11 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
 		}
 
 		bool isMacSci11ViewData = g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1;
-		unpackCelData(_resourceData, outPtr, clearColor, pixelCount, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData);
+		unpackCelData(*_resource, outPtr, clearColor, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData);
 
 		// Swap 0 and 0xff pixels for Mac SCI1.1+ games (see above)
 		if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
-			for (uint32 i = 0; i < pixelCount; i++) {
+			for (uint32 i = 0; i < outPtr.size(); i++) {
 				if (outPtr[i] == 0)
 					outPtr[i] = 0xff;
 				else if (outPtr[i] == 0xff)
@@ -681,39 +662,44 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
 	}
 }
 
-const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
-	loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
-	celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
-	if (_loop[loopNo].cel[celNo].rawBitmap)
-		return _loop[loopNo].cel[celNo].rawBitmap;
+const SciSpan<const byte> &GfxView::getBitmap(int16 loopNo, int16 celNo) {
+	loopNo = CLIP<int16>(loopNo, 0, _loop.size() - 1);
+	celNo = CLIP<int16>(celNo, 0, _loop[loopNo].cel.size() - 1);
 
-	uint16 width = _loop[loopNo].cel[celNo].width;
-	uint16 height = _loop[loopNo].cel[celNo].height;
-	// allocating memory to store cel's bitmap
-	int pixelCount = width * height;
-	_loop[loopNo].cel[celNo].rawBitmap = new byte[pixelCount];
-	byte *pBitmap = _loop[loopNo].cel[celNo].rawBitmap;
+	CelInfo &cel = _loop[loopNo].cel[celNo];
+
+	if (cel.rawBitmap)
+		return *cel.rawBitmap;
+
+	const uint16 width = cel.width;
+	const uint16 height = cel.height;
+	const uint pixelCount = width * height;
+	const Common::String sourceName = Common::String::format("%s loop %d cel %d", _resource->name().c_str(), loopNo, celNo);
+
+	SciSpan<byte> outBitmap = cel.rawBitmap->allocate(pixelCount, sourceName);
 
 	// unpack the actual cel bitmap data
-	unpackCel(loopNo, celNo, pBitmap, pixelCount);
+	unpackCel(loopNo, celNo, outBitmap);
 
 	if (_resMan->getViewType() == kViewEga)
-		unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey);
+		unditherBitmap(outBitmap, width, height, _loop[loopNo].cel[celNo].clearKey);
 
 	// mirroring the cel if needed
 	if (_loop[loopNo].mirrorFlag) {
+		byte *pBitmap = outBitmap.getUnsafeDataAt(0, width * height);
 		for (int i = 0; i < height; i++, pBitmap += width)
 			for (int j = 0; j < width / 2; j++)
 				SWAP(pBitmap[j], pBitmap[width - j - 1]);
 	}
-	return _loop[loopNo].cel[celNo].rawBitmap;
+
+	return *cel.rawBitmap;
 }
 
 /**
  * Called after unpacking an EGA cel, this will try to undither (parts) of the
  * cel if the dithering in here matches dithering used by the current picture.
  */
-void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) {
+void GfxView::unditherBitmap(SciSpan<byte> &bitmapPtr, int16 width, int16 height, byte clearKey) {
 	int16 *ditheredPicColors = _screen->unditherGetDitheredBgColors();
 
 	// It makes no sense to go further, if there isn't any dithered color data
@@ -731,7 +717,6 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
 
 	// Walk through the bitmap and remember all combinations of colors
 	int16 ditheredBitmapColors[DITHERED_BG_COLORS_SIZE];
-	byte *curPtr;
 	byte color1, color2;
 	byte nextColor1, nextColor2;
 	int16 y, x;
@@ -742,8 +727,8 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
 	// pixels are adjacent and check pixels in the following line as well to
 	// be the reverse pixel combination
 	int16 checkHeight = height - 1;
-	curPtr = bitmapPtr;
-	byte *nextPtr = curPtr + width;
+	byte *curPtr = bitmapPtr.getUnsafeDataAt(0, checkHeight * width);
+	const byte *nextPtr = bitmapPtr.getUnsafeDataAt(width, checkHeight * width);
 	for (y = 0; y < checkHeight; y++) {
 		color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
 		nextColor1 = nextPtr[0] << 4; nextColor2 = (nextPtr[2] << 4) | nextPtr[1];
@@ -783,9 +768,9 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
 		return;
 
 	// We now need to replace color-combinations
-	curPtr = bitmapPtr;
+	curPtr = bitmapPtr.getUnsafeDataAt(0, height * width);
 	for (y = 0; y < height; y++) {
-		color = *curPtr;
+		color = curPtr[0];
 		for (x = 1; x < width; x++) {
 			color = (color << 4) | curPtr[1];
 			if (unditherTable[color]) {
@@ -806,12 +791,11 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
 			int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires) {
 	const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
 	const CelInfo *celInfo = getCelInfo(loopNo, celNo);
-	const byte *bitmap = getBitmap(loopNo, celNo);
+	const SciSpan<const byte> &bitmap = getBitmap(loopNo, celNo);
 	const int16 celHeight = celInfo->height;
 	const int16 celWidth = celInfo->width;
 	const byte clearKey = celInfo->clearKey;
 	const byte drawMask = priority > 15 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
-	int x, y;
 
 	if (_embeddedPal)
 		// Merge view palette in...
@@ -820,12 +804,16 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
 	const int16 width = MIN(clipRect.width(), celWidth);
 	const int16 height = MIN(clipRect.height(), celHeight);
 
-	bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
+	if (!width || !height) {
+		return;
+	}
+
+	const byte *bitmapData = bitmap.getUnsafeDataAt((clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left), celWidth * (height - 1) + width);
 
 	if (!_EGAmapping) {
-		for (y = 0; y < height; y++, bitmap += celWidth) {
-			for (x = 0; x < width; x++) {
-				const byte color = bitmap[x];
+		for (int y = 0; y < height; y++, bitmapData += celWidth) {
+			for (int x = 0; x < width; x++) {
+				const byte color = bitmapData[x];
 				if (color != clearKey) {
 					const int x2 = clipRectTranslated.left + x;
 					const int y2 = clipRectTranslated.top + y;
@@ -850,10 +838,10 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
 			}
 		}
 	} else {
-		byte *EGAmapping = _EGAmapping + (EGAmappingNr * SCI_VIEW_EGAMAPPING_SIZE);
-		for (y = 0; y < height; y++, bitmap += celWidth) {
-			for (x = 0; x < width; x++) {
-				const byte color = EGAmapping[bitmap[x]];
+		const SciSpan<const byte> EGAmapping = _EGAmapping.subspan(EGAmappingNr * SCI_VIEW_EGAMAPPING_SIZE, SCI_VIEW_EGAMAPPING_SIZE);
+		for (int y = 0; y < height; y++, bitmapData += celWidth) {
+			for (int x = 0; x < width; x++) {
+				const byte color = EGAmapping[bitmapData[x]];
 				const int x2 = clipRectTranslated.left + x;
 				const int y2 = clipRectTranslated.top + y;
 				if (color != clearKey && priority >= _screen->getPriority(x2, y2))
@@ -872,7 +860,7 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
 			int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY) {
 	const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
 	const CelInfo *celInfo = getCelInfo(loopNo, celNo);
-	const byte *bitmap = getBitmap(loopNo, celNo);
+	const SciSpan<const byte> &bitmap = getBitmap(loopNo, celNo);
 	const int16 celHeight = celInfo->height;
 	const int16 celWidth = celInfo->width;
 	const byte clearKey = celInfo->clearKey;
@@ -933,15 +921,17 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
 	const int16 offsetY = clipRect.top - rect.top;
 	const int16 offsetX = clipRect.left - rect.left;
 
+	// TODO: Remove? This class is not for SCI32
 	// Happens in SQ6, first room
 	if (offsetX < 0 || offsetY < 0)
 		return;
 
 	assert(scaledHeight + offsetY <= ARRAYSIZE(scalingY));
 	assert(scaledWidth + offsetX <= ARRAYSIZE(scalingX));
+	const byte *bitmapData = bitmap.getUnsafeDataAt(0, celWidth * celHeight);
 	for (int y = 0; y < scaledHeight; y++) {
 		for (int x = 0; x < scaledWidth; x++) {
-			const byte color = bitmap[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
+			const byte color = bitmapData[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
 			const int x2 = clipRectTranslated.left + x;
 			const int y2 = clipRectTranslated.top + y;
 			if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 5e42246..808e203 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -23,6 +23,8 @@
 #ifndef SCI_GRAPHICS_VIEW_H
 #define SCI_GRAPHICS_VIEW_H
 
+#include "sci/util.h"
+
 namespace Sci {
 
 enum Sci32ViewNativeResolution {
@@ -41,17 +43,19 @@ struct CelInfo {
 	uint16 offsetEGA;
 	uint32 offsetRLE;
 	uint32 offsetLiteral;
-	byte *rawBitmap;
+	Common::SpanOwner<SciSpan<const byte> > rawBitmap;
 };
 
 struct LoopInfo {
 	bool mirrorFlag;
 	uint16 celCount;
-	CelInfo *cel;
+	Common::Array<CelInfo> cel;
 };
 
-#define SCI_VIEW_EGAMAPPING_SIZE 16
-#define SCI_VIEW_EGAMAPPING_COUNT 8
+enum {
+	SCI_VIEW_EGAMAPPING_SIZE = 16,
+	SCI_VIEW_EGAMAPPING_COUNT = 8
+};
 
 class GfxScreen;
 class GfxPalette;
@@ -73,7 +77,7 @@ public:
 	void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
 	void getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
 	void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const;
-	const byte *getBitmap(int16 loopNo, int16 celNo);
+	const SciSpan<const byte> &getBitmap(int16 loopNo, int16 celNo);
 	void draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
 	void drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY);
 	uint16 getLoopCount() const { return _loopCount; }
@@ -88,8 +92,8 @@ public:
 
 private:
 	void initData(GuiResourceId resourceId);
-	void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount);
-	void unditherBitmap(byte *bitmap, int16 width, int16 height, byte clearKey);
+	void unpackCel(int16 loopNo, int16 celNo, SciSpan<byte> &outPtr);
+	void unditherBitmap(SciSpan<byte> &bitmap, int16 width, int16 height, byte clearKey);
 
 	ResourceManager *_resMan;
 	GfxCoordAdjuster16 *_coordAdjuster;
@@ -98,18 +102,16 @@ private:
 
 	GuiResourceId _resourceId;
 	Resource *_resource;
-	byte *_resourceData;
-	int _resourceSize;
 
 	uint16 _loopCount;
-	LoopInfo *_loop;
+	Common::Array<LoopInfo> _loop;
 	bool _embeddedPal;
 	Palette _viewPalette;
 
 	// specifies scaling resolution for SCI2 views (see gk1/windows, Wolfgang in room 720)
 	Sci32ViewNativeResolution _sci2ScaleRes;
 
-	byte *_EGAmapping;
+	SciSpan<const byte> _EGAmapping;
 
 	// this is set for sci0early to adjust for the getCelRect() change
 	int16 _adjustForSci0Early;
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index ea8722a..4fefff6 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -104,14 +104,14 @@ bool Vocabulary::loadParserWords() {
 	VocabularyVersions resourceType = _vocabVersion;
 
 	if (resourceType == kVocabularySCI0) {
-		if (resource->size < 26 * 2) {
+		if (resource->size() < 26 * 2) {
 			warning("Invalid main vocabulary encountered: Much too small");
 			return false;
 		}
 		// Check the alphabet-offset table for any content
 		int alphabetNr;
 		for (alphabetNr = 0; alphabetNr < 26; alphabetNr++) {
-			if (READ_LE_UINT16(resource->data + alphabetNr * 2))
+			if (resource->getUint16LEAt(alphabetNr * 2))
 				break;
 		}
 		// If all of them were empty, we are definitely seeing SCI01 vocab in disguise (e.g. pq2 japanese)
@@ -127,7 +127,7 @@ bool Vocabulary::loadParserWords() {
 	else
 		seeker = 26 * 2; // vocab.000 starts with 26 16-bit pointers which we don't use
 
-	if (resource->size < seeker) {
+	if (resource->size() < seeker) {
 		warning("Invalid main vocabulary encountered: Too small");
 		return false;
 		// Now this ought to be critical, but it'll just cause parse() and said() not to work
@@ -135,25 +135,25 @@ bool Vocabulary::loadParserWords() {
 
 	_parserWords.clear();
 
-	while (seeker < resource->size) {
+	while (seeker < resource->size()) {
 		byte c;
 
-		currentWordPos = resource->data[seeker++]; // Parts of previous words may be re-used
+		currentWordPos = resource->getUint8At(seeker++); // Parts of previous words may be re-used
 
 		if (resourceType == kVocabularySCI1) {
 			c = 1;
-			while (seeker < resource->size && currentWordPos < 255 && c) {
-				c = resource->data[seeker++];
+			while (seeker < resource->size() && currentWordPos < 255 && c) {
+				c = resource->getUint8At(seeker++);
 				currentWord[currentWordPos++] = c;
 			}
-			if (seeker == resource->size) {
+			if (seeker == resource->size()) {
 				warning("SCI1: Vocabulary not usable, disabling");
 				_parserWords.clear();
 				return false;
 			}
 		} else {
 			do {
-				c = resource->data[seeker++];
+				c = resource->getUint8At(seeker++);
 				currentWord[currentWordPos++] = c & 0x7f; // 0x80 is used to terminate the string
 			} while (c < 0x80);
 		}
@@ -161,10 +161,10 @@ bool Vocabulary::loadParserWords() {
 		currentWord[currentWordPos] = 0;
 
 		// Now decode class and group:
-		c = resource->data[seeker + 1];
+		c = resource->getUint8At(seeker + 1);
 		ResultWord newWord;
-		newWord._class = ((resource->data[seeker]) << 4) | ((c & 0xf0) >> 4);
-		newWord._group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8);
+		newWord._class = ((resource->getUint8At(seeker)) << 4) | ((c & 0xf0) >> 4);
+		newWord._group = (resource->getUint8At(seeker + 2)) | ((c & 0x0f) << 8);
 
 		// SCI01 was the first version to support multiple class/group pairs
 		// per word, so we clear the list in earlier versions
@@ -198,38 +198,38 @@ const char *Vocabulary::getAnyWordFromGroup(int group) {
 
 bool Vocabulary::loadSuffixes() {
 	// Determine if we can find a SCI1 suffix vocabulary first
-	Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), 1);
+	Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), true);
 	if (!resource)
 		return false; // No vocabulary found
 
 	uint32 seeker = 1;
 
-	while ((seeker < resource->size - 1) && (resource->data[seeker + 1] != 0xff)) {
+	while (seeker < resource->size() - 1 && resource->getUint8At(seeker + 1) != 0xff) {
 		suffix_t suffix;
 
-		int maxSize = resource->size - seeker;
-		suffix.alt_suffix = (const char *)resource->data + seeker;
+		int maxSize = resource->size() - seeker;
+		suffix.alt_suffix = (const char *)resource->getUnsafeDataAt(seeker, maxSize);
 		suffix.alt_suffix_length = Common::strnlen(suffix.alt_suffix, maxSize);
 		if (suffix.alt_suffix_length == maxSize) {
-			error("Vocabulary alt appears truncated for suffix %d in resource %d at %u", _parserSuffixes.size(), resource->getNumber(), seeker);
+			error("Vocabulary alt from %s appears truncated for suffix %d at %u", resource->name().c_str(), _parserSuffixes.size(), seeker);
 		}
 		seeker += suffix.alt_suffix_length + 1; // Hit end of string
 
-		suffix.result_class = (int16)READ_BE_UINT16(resource->data + seeker);
+		suffix.result_class = resource->getInt16BEAt(seeker);
 		seeker += 2;
 
 		// Beginning of next string - skip leading '*'
 		seeker++;
 
-		maxSize = resource->size - seeker;
-		suffix.word_suffix = (const char *)resource->data + seeker;
+		maxSize = resource->size() - seeker;
+		suffix.word_suffix = (const char *)resource->getUnsafeDataAt(seeker, maxSize);
 		suffix.word_suffix_length = Common::strnlen(suffix.word_suffix, maxSize);
 		if (suffix.word_suffix_length == maxSize) {
-			error("Vocabulary word appears truncated for suffix %d in resource %d at %u", _parserSuffixes.size(), resource->getNumber(), seeker);
+			error("Vocabulary word from %s appears truncated for suffix %d at %u", resource->name().c_str(), _parserSuffixes.size(), seeker);
 		}
 		seeker += suffix.word_suffix_length + 1;
 
-		suffix.class_mask = (int16)READ_BE_UINT16(resource->data + seeker);
+		suffix.class_mask = resource->getUint16BEAt(seeker);
 		seeker += 3; // Next entry
 
 		_parserSuffixes.push_back(suffix);
@@ -239,7 +239,7 @@ bool Vocabulary::loadSuffixes() {
 }
 
 void Vocabulary::freeSuffixes() {
-	Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), 0);
+	Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), false);
 	if (resource)
 		_resMan->unlockResource(resource);
 
@@ -254,7 +254,7 @@ bool Vocabulary::loadBranches() {
 	if (!resource)
 		return false;		// No parser tree data found
 
-	int branches_nr = resource->size / 20;
+	int branches_nr = resource->size() / 20;
 
 	if (branches_nr == 0) {
 		warning("Parser tree data is empty");
@@ -264,12 +264,12 @@ bool Vocabulary::loadBranches() {
 	_parserBranches.resize(branches_nr);
 
 	for (int i = 0; i < branches_nr; i++) {
-		byte *base = resource->data + i * 20;
+		const SciSpan<const byte> base = resource->subspan(i * 20);
 
-		_parserBranches[i].id = (int16)READ_LE_UINT16(base);
+		_parserBranches[i].id = base.getInt16LEAt(0);
 
 		for (int k = 0; k < 9; k++)
-			_parserBranches[i].data[k] = READ_LE_UINT16(base + 2 + 2 * k);
+			_parserBranches[i].data[k] = base.getUint16LEAt(2 + 2 * k);
 
 		_parserBranches[i].data[9] = 0; // Always terminate
 	}
@@ -281,38 +281,38 @@ bool Vocabulary::loadBranches() {
 }
 
 bool Vocabulary::loadAltInputs() {
-	Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 1);
+	Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), true);
 
 	if (!resource)
 		return true; // it's not a problem if this resource doesn't exist
 
-	const char *data = (const char*)resource->data;
-	const char *data_end = data + resource->size;
+	Resource::const_iterator it = resource->cbegin();
+	const Resource::const_iterator end = resource->cend();
 
 	_altInputs.clear();
 	_altInputs.resize(256);
 
-	while (data < data_end && *data) {
+	while (it != end && *it) {
 		AltInput t;
-		t._input = data;
+		t._input = (const char *)&*it;
 
-		uint32 maxSize = data_end - data;
-		uint32 l = Common::strnlen(data, maxSize);
+		uint32 maxSize = end - it;
+		uint32 l = Common::strnlen(t._input, maxSize);
 		if (l == maxSize) {
-			error("Alt input from %d appears truncated at %ld", resource->getNumber(), (const byte *)data - resource->data);
+			error("Alt input from %s appears truncated at %ld", resource->name().c_str(), it - resource->cbegin());
 		}
 		t._inputLength = l;
-		data += l + 1;
+		it += l + 1;
 
-		t._replacement = data;
-		maxSize = data_end - data;
-		l = Common::strnlen(data, maxSize);
+		t._replacement = (const char *)&*it;
+		maxSize = end - it;
+		l = Common::strnlen(t._replacement, maxSize);
 		if (l == maxSize) {
-			error("Alt input replacement from %d appears truncated at %ld", resource->getNumber(), (const byte *)data - resource->data);
+			error("Alt input replacement from %s appears truncated at %ld", resource->name().c_str(), it - resource->cbegin());
 		}
-		data += l + 1;
+		it += l + 1;
 
-		if (data < data_end && strncmp(data, t._input, t._inputLength) == 0)
+		if (it < end && strncmp((const char *)&*it, t._input, t._inputLength) == 0)
 			t._prefix = true;
 		else
 			t._prefix = false;
@@ -325,7 +325,7 @@ bool Vocabulary::loadAltInputs() {
 }
 
 void Vocabulary::freeAltInputs() {
-	Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 0);
+	Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), false);
 	if (resource)
 		_resMan->unlockResource(resource);
 
@@ -466,10 +466,12 @@ void Vocabulary::lookupWord(ResultWordList& retval, const char *word, int word_l
 	}
 }
 
-void Vocabulary::debugDecipherSaidBlock(const byte *addr) {
+void Vocabulary::debugDecipherSaidBlock(const SciSpan<const byte> &data) {
 	bool first = true;
 	uint16 nextItem;
 
+	SciSpan<const byte>::const_iterator addr = data.cbegin();
+
 	do {
 		nextItem = *addr++;
 		if (nextItem != 0xff) {
@@ -515,7 +517,7 @@ void Vocabulary::debugDecipherSaidBlock(const byte *addr) {
 					break;
 			}
 		}
-	} while (nextItem != 0xff);
+	} while (nextItem != 0xff && addr != data.cend());
 }
 
 static const byte lowerCaseMap[256] = {
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index 59558ce..763c1fd 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -30,6 +30,7 @@
 
 #include "sci/sci.h"
 #include "sci/engine/vm_types.h"
+#include "sci/util.h"
 
 namespace Common {
 
@@ -260,7 +261,7 @@ public:
 	 * For debugging only.
 	 * @param pos	pointer to the data to dump
 	 */
-	void debugDecipherSaidBlock(const byte *pos);
+	void debugDecipherSaidBlock(const SciSpan<const byte> &data);
 
 	/**
 	 * Prints the parser suffixes to the debug console.
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index aaa1c8c..6f4248e 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -192,27 +192,25 @@ ResourceType ResourceManager::convertResType(byte type) {
 }
 
 //-- Resource main functions --
-Resource::Resource(ResourceManager *resMan, ResourceId id) : _resMan(resMan), _id(id) {
-	data = NULL;
-	size = 0;
+Resource::Resource(ResourceManager *resMan, ResourceId id) : SciSpan<const byte>(nullptr, 0, id.toString()), _resMan(resMan), _id(id) {
 	_fileOffset = 0;
 	_status = kResStatusNoMalloc;
 	_lockers = 0;
-	_source = NULL;
-	_header = NULL;
+	_source = nullptr;
+	_header = nullptr;
 	_headerSize = 0;
 }
 
 Resource::~Resource() {
-	delete[] data;
+	delete[] _data;
 	delete[] _header;
 	if (_source && _source->getSourceType() == kSourcePatch)
 		delete _source;
 }
 
 void Resource::unalloc() {
-	delete[] data;
-	data = NULL;
+	delete[] _data;
+	_data = nullptr;
 	_status = kResStatusNoMalloc;
 }
 
@@ -221,12 +219,12 @@ void Resource::writeToStream(Common::WriteStream *stream) const {
 	stream->writeByte(_headerSize);
 	if (_headerSize > 0)
 		stream->write(_header, _headerSize);
-	stream->write(data, size);
+	stream->write(_data, _size);
 }
 
 #ifdef ENABLE_SCI32
 Common::SeekableReadStream *Resource::makeStream() const {
-	return new Common::MemoryReadStream(data, size, DisposeAfterUse::NO);
+	return new Common::MemoryReadStream(_data, _size, DisposeAfterUse::NO);
 }
 #endif
 
@@ -304,25 +302,26 @@ bool Resource::loadPatch(Common::SeekableReadStream *file) {
 	// We assume that the resource type matches `type`
 	//  We also assume that the current file position is right at the actual data (behind resourceid/headersize byte)
 
-	data = new byte[size];
+	byte *ptr = new byte[size()];
+	_data = ptr;
 
 	if (_headerSize > 0)
 		_header = new byte[_headerSize];
 
-	if (data == nullptr || (_headerSize > 0 && _header == nullptr)) {
-		error("Can't allocate %d bytes needed for loading %s", size + _headerSize, _id.toString().c_str());
+	if (data() == nullptr || (_headerSize > 0 && _header == nullptr)) {
+		error("Can't allocate %lu bytes needed for loading %s", size() + _headerSize, _id.toString().c_str());
 	}
 
-	uint32 really_read;
+	uint32 bytesRead;
 	if (_headerSize > 0) {
-		really_read = file->read(_header, _headerSize);
-		if (really_read != _headerSize)
-			error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), _headerSize);
+		bytesRead = file->read(_header, _headerSize);
+		if (bytesRead != _headerSize)
+			error("Read %d bytes from %s but expected %d", bytesRead, _id.toString().c_str(), _headerSize);
 	}
 
-	really_read = file->read(data, size);
-	if (really_read != size)
-		error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
+	bytesRead = file->read(ptr, size());
+	if (bytesRead != size())
+		error("Read %d bytes from %s but expected %lu", bytesRead, _id.toString().c_str(), size());
 
 	_status = kResStatusAllocated;
 	return true;
@@ -425,10 +424,12 @@ bool MacResourceForkResourceSource::isCompressableResource(ResourceType type) co
 }
 
 #define OUTPUT_LITERAL() \
+	assert(ptr + literalLength <= bufferEnd); \
 	while (literalLength--) \
 		*ptr++ = stream->readByte();
 
 #define OUTPUT_COPY() \
+	assert(ptr + copyLength <= bufferEnd); \
 	while (copyLength--) { \
 		byte value = ptr[-offset]; \
 		*ptr++ = value; \
@@ -450,27 +451,29 @@ void MacResourceForkResourceSource::decompressResource(Common::SeekableReadStrea
 
 	// Get the uncompressed size from the end of the resource
 	if (canBeCompressed && stream->size() > 4) {
-		stream->seek(stream->size() - 4);
+		stream->seek(-4, SEEK_END);
 		uncompressedSize = stream->readUint32BE();
 		stream->seek(0);
 	}
 
 	if (uncompressedSize == 0) {
 		// Not compressed
-		resource->size = stream->size();
+		resource->_size = stream->size();
 
 		// Cut out the 'non-compressed marker' (four zeroes) at the end
 		if (canBeCompressed)
-			resource->size -= 4;
+			resource->_size -= 4;
 
-		resource->data = new byte[resource->size];
-		stream->read(resource->data, resource->size);
+		byte *ptr = new byte[resource->size()];
+		resource->_data = ptr;
+		stream->read(ptr, resource->size());
 	} else {
 		// Decompress
-		resource->size = uncompressedSize;
-		resource->data = new byte[uncompressedSize];
+		resource->_size = uncompressedSize;
+		byte *ptr = new byte[uncompressedSize];
+		resource->_data = ptr;
 
-		byte *ptr = resource->data;
+		const byte *const bufferEnd = resource->data() + uncompressedSize;
 
 		while (stream->pos() < stream->size()) {
 			byte code = stream->readByte();
@@ -812,7 +815,7 @@ void ChunkResourceSource::scanSource(ResourceManager *resMan) {
 	if (!chunk)
 		error("Trying to load non-existent chunk");
 
-	byte *ptr = chunk->data;
+	const byte *ptr = chunk->data();
 	uint32 firstOffset = 0;
 
 	for (;;) {
@@ -838,7 +841,7 @@ void ChunkResourceSource::scanSource(ResourceManager *resMan) {
 		if (!firstOffset)
 			firstOffset = entry.offset;
 
-		if ((size_t)(ptr - chunk->data) >= firstOffset)
+		if ((size_t)(ptr - chunk->data()) >= firstOffset)
 			break;
 	}
 }
@@ -850,14 +853,16 @@ void ChunkResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
 		error("Trying to load non-existent resource from chunk %d: %s %d", _number, getResourceTypeName(res->_id.getType()), res->_id.getNumber());
 
 	ResourceEntry entry = _resMap[res->_id];
-	res->data = new byte[entry.length];
-	res->size = entry.length;
+	assert(entry.offset + entry.length <= chunk->_size);
+	byte *ptr = new byte[entry.length];
+	res->_data = ptr;
+	res->_size = entry.length;
 	res->_header = 0;
 	res->_headerSize = 0;
 	res->_status = kResStatusAllocated;
 
 	// Copy the resource data over
-	memcpy(res->data, chunk->data + entry.offset, entry.length);
+	memcpy(ptr, chunk->data() + entry.offset, entry.length);
 }
 
 void ResourceManager::addResourcesFromChunk(uint16 id) {
@@ -996,7 +1001,7 @@ void ResourceManager::removeFromLRU(Resource *res) {
 		return;
 	}
 	_LRU.remove(res);
-	_memoryLRU -= res->size;
+	_memoryLRU -= res->size();
 	res->_status = kResStatusAllocated;
 }
 
@@ -1006,7 +1011,7 @@ void ResourceManager::addToLRU(Resource *res) {
 		return;
 	}
 	_LRU.push_front(res);
-	_memoryLRU += res->size;
+	_memoryLRU += res->size();
 #if SCI_VERBOSE_RESMAN
 	debug("Adding %s (%d bytes) to lru control: %d bytes total",
 	      res->_id.toString().c_str(), res->size,
@@ -1023,8 +1028,8 @@ void ResourceManager::printLRU() {
 
 	while (it != _LRU.end()) {
 		res = *it;
-		debug("\t%s: %d bytes", res->_id.toString().c_str(), res->size);
-		mem += res->size;
+		debug("\t%s: %lu bytes", res->_id.toString().c_str(), res->size());
+		mem += res->size();
 		++entries;
 		++it;
 	}
@@ -1082,7 +1087,7 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) {
 		if (retval->_status == kResStatusAllocated) {
 			retval->_status = kResStatusLocked;
 			retval->_lockers = 0;
-			_memoryLocked += retval->size;
+			_memoryLocked += retval->_size;
 		}
 		retval->_lockers++;
 	} else if (retval->_status != kResStatusLocked) { // Don't lock it
@@ -1090,11 +1095,11 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) {
 			addToLRU(retval);
 	}
 
-	if (retval->data)
+	if (retval->data())
 		return retval;
 	else {
 		warning("resMan: Failed to read %s", retval->_id.toString().c_str());
-		return NULL;
+		return nullptr;
 	}
 }
 
@@ -1108,7 +1113,7 @@ void ResourceManager::unlockResource(Resource *res) {
 
 	if (!--res->_lockers) { // No more lockers?
 		res->_status = kResStatusAllocated;
-		_memoryLocked -= res->size;
+		_memoryLocked -= res->size();
 		addToLRU(res);
 	}
 
@@ -1780,7 +1785,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
 					}
 					resource->_source = source;
 					resource->_fileOffset = fileOffset;
-					resource->size = 0;
+					resource->_size = 0;
 				}
 			}
 
@@ -1913,7 +1918,7 @@ void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32
 		_resMap.setVal(resId, res);
 		res->_source = src;
 		res->_fileOffset = offset;
-		res->size = size;
+		res->_size = size;
 	}
 }
 
@@ -1931,7 +1936,7 @@ Resource *ResourceManager::updateResource(ResourceId resId, ResourceSource *src,
 	res->_status = kResStatusNoMalloc;
 	res->_source = src;
 	res->_headerSize = 0;
-	res->size = size;
+	res->_size = size;
 
 	return res;
 }
@@ -2001,7 +2006,7 @@ int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream
 		return SCI_ERROR_IO_ERROR;
 
 	_id = ResourceId(type, number);
-	size = szUnpacked;
+	_size = szUnpacked;
 
 	// checking compression method
 	switch (wCompression) {
@@ -2075,9 +2080,10 @@ int Resource::decompress(ResVersion volVersion, Common::SeekableReadStream *file
 		return SCI_ERROR_UNKNOWN_COMPRESSION;
 	}
 
-	data = new byte[size];
+	byte *ptr = new byte[_size];
+	_data = ptr;
 	_status = kResStatusAllocated;
-	errorNum = data ? dec->unpack(file, data, szPacked, size) : SCI_ERROR_RESOURCE_TOO_BIG;
+	errorNum = ptr ? dec->unpack(file, ptr, szPacked, _size) : SCI_ERROR_RESOURCE_TOO_BIG;
 	if (errorNum)
 		unalloc();
 
@@ -2136,7 +2142,7 @@ ViewType ResourceManager::detectViewType() {
 			if (res->_source->getSourceType() == kSourcePatch)
 				continue;
 
-			switch (res->data[1]) {
+			switch (res->getUint8At(1)) {
 			case 128:
 				// If the 2nd byte is 128, it's a VGA game.
 				// However, Longbow Amiga (AGA, 64 colors), also sets this byte
@@ -2149,28 +2155,28 @@ ViewType ResourceManager::detectViewType() {
 			case 0:
 				// EGA or Amiga, try to read as Amiga view
 
-				if (res->size < 10)
+				if (res->size() < 10)
 					return kViewUnknown;
 
 				// Read offset of first loop
-				uint16 offset = READ_LE_UINT16(res->data + 8);
+				uint16 offset = res->getUint16LEAt(8);
 
-				if (offset + 6U >= res->size)
+				if (offset + 6U >= res->size())
 					return kViewUnknown;
 
 				// Read offset of first cel
-				offset = READ_LE_UINT16(res->data + offset + 4);
+				offset = res->getUint16LEAt(offset + 4);
 
-				if (offset + 4U >= res->size)
+				if (offset + 4U >= res->size())
 					return kViewUnknown;
 
 				// Check palette offset, amiga views have no palette
-				if (READ_LE_UINT16(res->data + 6) != 0)
+				if (res->getUint16LEAt(6) != 0)
 					return kViewEga;
 
-				uint16 width = READ_LE_UINT16(res->data + offset);
+				uint16 width = res->getUint16LEAt(offset);
 				offset += 2;
-				uint16 height = READ_LE_UINT16(res->data + offset);
+				uint16 height = res->getUint16LEAt(offset);
 				offset += 6;
 
 				// To improve the heuristic, we skip very small views
@@ -2182,8 +2188,8 @@ ViewType ResourceManager::detectViewType() {
 				for (y = 0; y < height; y++) {
 					int x = 0;
 
-					while ((x < width) && (offset < res->size)) {
-						byte op = res->data[offset++];
+					while ((x < width) && (offset < res->size())) {
+						byte op = res->getUint8At(offset++);
 						x += (op & 0x07) ? op & 0x07 : op >> 3;
 					}
 
@@ -2226,26 +2232,25 @@ static const byte detectSci21NewStringSignature[] = {
 
 bool ResourceManager::checkResourceDataForSignature(Resource *resource, const byte *signature) {
 	byte signatureSize = *signature;
-	const byte *resourceData = resource->data;
 
 	signature++; // skip over size byte
 	if (signatureSize < 4)
 		error("resource signature is too small, internal error");
-	if (signatureSize > resource->size)
+	if (signatureSize > resource->size())
 		return false;
 
 	const uint32 signatureDWord = READ_UINT32(signature);
 	signature += 4; signatureSize -= 4;
 
-	const uint32 searchLimit = resource->size - signatureSize + 1;
+	const uint32 searchLimit = resource->size() - signatureSize + 1;
 	uint32 DWordOffset = 0;
 	while (DWordOffset < searchLimit) {
-		if (signatureDWord == READ_UINT32(resourceData + DWordOffset)) {
+		if (signatureDWord == resource->getUint32At(DWordOffset)) {
 			// magic DWORD found, check if the rest matches as well
 			uint32 offset = DWordOffset + 4;
 			uint32 signaturePos  = 0;
 			while (signaturePos < signatureSize) {
-				if (resourceData[offset] != signature[signaturePos])
+				if (resource->getUint8At(offset) != signature[signaturePos])
 					break;
 				offset++;
 				signaturePos++;
@@ -2474,8 +2479,8 @@ bool ResourceManager::detectFontExtended() {
 
 	Resource *res = findResource(ResourceId(kResourceTypeFont, 0), 0);
 	if (res) {
-		if (res->size >= 4) {
-			uint16 numChars = READ_LE_UINT16(res->data + 2);
+		if (res->size() >= 4) {
+			uint16 numChars = READ_LE_UINT16(res->data() + 2);
 			if (numChars > 0x80)
 				return true;
 		}
@@ -2488,35 +2493,39 @@ bool ResourceManager::detectPaletteMergingSci11() {
 	// Load palette 999 (default palette)
 	Resource *res = findResource(ResourceId(kResourceTypePalette, 999), false);
 
-	if ((res) && (res->size > 30)) {
-		byte *data = res->data;
+	if (res && res->size() > 30) {
 		// Old palette format used in palette resource? -> it's merging
-		if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0))
+		if ((res->getUint8At(0) == 0 && res->getUint8At(1) == 1) ||
+			(res->getUint8At(0) == 0 && res->getUint8At(1) == 0 && res->getUint16LEAt(29) == 0)) {
 			return true;
+		}
+
 		// Hardcoded: Laura Bow 2 floppy uses new palette resource, but still palette merging + 16 bit color matching
-		if ((g_sci->getGameId() == GID_LAURABOW2) && (!g_sci->isCD()) && (!g_sci->isDemo()))
+		if (g_sci->getGameId() == GID_LAURABOW2 && !g_sci->isCD() && !g_sci->isDemo()) {
 			return true;
-		return false;
+		}
 	}
+
 	return false;
 }
 
 // is called on SCI0EARLY games to make sure that sound resources are in fact also SCI0EARLY
 bool ResourceManager::detectEarlySound() {
-	Resource *res = findResource(ResourceId(kResourceTypeSound, 1), 0);
-	if (res) {
-		if (res->size >= 0x22) {
-			if (READ_LE_UINT16(res->data + 0x1f) == 0) // channel 15 voice count + play mask is 0 in SCI0LATE
-				if (res->data[0x21] == 0) // last byte right before actual data is 0 as well
-					return false;
-		}
+	Resource *res = findResource(ResourceId(kResourceTypeSound, 1), false);
+	if (res &&
+		res->size() >= 0x22 &&
+		res->getUint16LEAt(0x1f) == 0 && // channel 15 voice count + play mask is 0 in SCI0LATE
+		res->getUint8At(0x21) == 0) { // last byte right before actual data is 0 as well
+
+		return false;
 	}
+
 	return true;
 }
 
 // Functions below are based on PD code by Brian Provinciano (SCI Studio)
 bool ResourceManager::hasOldScriptHeader() {
-	Resource *res = findResource(ResourceId(kResourceTypeScript, 0), 0);
+	Resource *res = findResource(ResourceId(kResourceTypeScript, 0), false);
 
 	if (!res) {
 		// Script 0 missing -> corrupted / non-SCI resource files.
@@ -2528,13 +2537,13 @@ bool ResourceManager::hasOldScriptHeader() {
 	uint offset = 2;
 	const int objTypes = 17;
 
-	while (offset < res->size) {
-		uint16 objType = READ_LE_UINT16(res->data + offset);
+	while (offset < res->size()) {
+		uint16 objType = res->getUint16LEAt(offset);
 
 		if (!objType) {
 			offset += 2;
 			// We should be at the end of the resource now
-			return offset == res->size;
+			return offset == res->size();
 		}
 
 		if (objType >= objTypes) {
@@ -2542,7 +2551,7 @@ bool ResourceManager::hasOldScriptHeader() {
 			return false;
 		}
 
-		int skip = READ_LE_UINT16(res->data + offset + 2);
+		int skip = res->getUint16LEAt(offset + 2);
 
 		if (skip < 2) {
 			// Invalid size
@@ -2556,34 +2565,34 @@ bool ResourceManager::hasOldScriptHeader() {
 }
 
 bool ResourceManager::hasSci0Voc999() {
-	Resource *res = findResource(ResourceId(kResourceTypeVocab, 999), 0);
+	Resource *res = findResource(ResourceId(kResourceTypeVocab, 999), false);
 
 	if (!res) {
 		// No vocab present, possibly a demo version
 		return false;
 	}
 
-	if (res->size < 2)
+	if (res->size() < 2)
 		return false;
 
-	uint16 count = READ_LE_UINT16(res->data);
+	uint16 count = res->getUint16LEAt(0);
 
 	// Make sure there's enough room for the pointers
-	if (res->size < (uint)count * 2)
+	if (res->size() < (uint)count * 2)
 		return false;
 
 	// Iterate over all pointers
 	for (uint i = 0; i < count; i++) {
 		// Offset to string
-		uint16 offset = READ_LE_UINT16(res->data + 2 + count * 2);
+		uint16 offset = res->getUint16LEAt(2 + count * 2);
 
 		// Look for end of string
 		do {
-			if (offset >= res->size) {
+			if (offset >= res->size()) {
 				// Out of bounds
 				return false;
 			}
-		} while (res->data[offset++]);
+		} while (res->getUint8At(offset++));
 	}
 
 	return true;
@@ -2595,62 +2604,62 @@ bool ResourceManager::hasSci1Voc900() {
 	if (!res )
 		return false;
 
-	if (res->size < 0x1fe)
+	if (res->size() < 0x1fe)
 		return false;
 
 	uint16 offset = 0x1fe;
 
-	while (offset < res->size) {
+	while (offset < res->size()) {
 		offset++;
 		do {
-			if (offset >= res->size) {
+			if (offset >= res->size()) {
 				// Out of bounds;
 				return false;
 			}
-		} while (res->data[offset++]);
+		} while (res->getUint8At(offset++));
 		offset += 3;
 	}
 
-	return offset == res->size;
+	return offset == res->size();
 }
 
 // Same function as Script::findBlockSCI0(). Slight code
 // duplication here, but this has been done to keep the resource
 // manager independent from the rest of the engine
-static byte *findSci0ExportsBlock(byte *buffer) {
-	byte *buf = buffer;
+static SciSpan<const byte>::const_iterator findSci0ExportsBlock(const SciSpan<const byte> &buffer) {
+	SciSpan<const byte>::const_iterator buf = buffer.cbegin();
 	bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
 
 	if (oldScriptHeader)
 		buf += 2;
 
-	do {
-		int seekerType = READ_LE_UINT16(buf);
+	for (;;) {
+		int seekerType = buf.getUint16LE();
 
 		if (seekerType == 0)
 			break;
 		if (seekerType == 7)	// exports
 			return buf;
 
-		int seekerSize = READ_LE_UINT16(buf + 2);
+		int seekerSize = (buf + 2).getUint16LE();
 		assert(seekerSize > 0);
 		buf += seekerSize;
-	} while (1);
+	}
 
-	return NULL;
+	return buffer.cend();
 }
 
 // This code duplicates Script::relocateOffsetSci3, but we can't use
 // that here since we can't instantiate scripts at this point.
-static int relocateOffsetSci3(const byte *buf, uint32 offset) {
-	int relocStart = READ_LE_UINT32(buf + 8);
-	int relocCount = READ_LE_UINT16(buf + 18);
-	const byte *seeker = buf + relocStart;
+static int relocateOffsetSci3(const SciSpan<const byte> &buf, uint32 offset) {
+	int relocStart = buf.getUint32LEAt(8);
+	int relocCount = buf.getUint16LEAt(18);
+	SciSpan<const byte>::const_iterator seeker = buf.cbegin() + relocStart;
 
 	for (int i = 0; i < relocCount; ++i) {
-		if (READ_SCI11ENDIAN_UINT32(seeker) == offset) {
+		if (seeker.getUint32SE() == offset) {
 			// TODO: Find out what UINT16 at (seeker + 8) means
-			return READ_SCI11ENDIAN_UINT16(buf + offset) + READ_SCI11ENDIAN_UINT32(seeker + 4);
+			return buf.getUint16SEAt(offset) + (seeker + 4).getUint32SE();
 		}
 		seeker += 10;
 	}
@@ -2664,41 +2673,41 @@ reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
 	if (!script)
 		return NULL_REG;
 
-	byte *offsetPtr = 0;
+	SciSpan<const byte>::const_iterator offsetPtr;
 
 	if (getSciVersion() <= SCI_VERSION_1_LATE) {
-		byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data;
+		SciSpan<const byte> buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->subspan(2) : *script;
 
 		// Check if the first block is the exports block (in most cases, it is)
-		bool exportsIsFirst = (READ_LE_UINT16(buf + 4) == 7);
+		bool exportsIsFirst = buf.getUint16LEAt(4) == 7;
 		if (exportsIsFirst) {
-			offsetPtr = buf + 4 + 2;
+			offsetPtr = buf.subspan(4 + 2).cbegin();
 		} else {
-			offsetPtr = findSci0ExportsBlock(script->data);
-			if (!offsetPtr)
+			offsetPtr = findSci0ExportsBlock(*script);
+			if (offsetPtr == buf.cend())
 				error("Unable to find exports block from script 0");
 			offsetPtr += 4 + 2;
 		}
 
-		int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
+		int16 offset = !isSci11Mac() ? offsetPtr.getUint16LE() : offsetPtr.getUint16BE();
 		return make_reg(1, offset);
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
-		offsetPtr = script->data + 4 + 2 + 2;
+		offsetPtr = script->cbegin() + 4 + 2 + 2;
 
 		// In SCI1.1 - SCI2.1, the heap is appended at the end of the script,
 		// so adjust the offset accordingly if requested
-		int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
+		int16 offset = !isSci11Mac() ? offsetPtr.getUint16LE() : offsetPtr.getUint16BE();
 		if (addSci11ScriptOffset) {
-			offset += script->size;
+			offset += script->size();
 
 			// Ensure that the start of the heap is word-aligned - same as in Script::init()
-			if (script->size & 2)
+			if (script->size() & 2)
 				offset++;
 		}
 
 		return make_reg(1, offset);
 	} else {
-		return make_reg(1, relocateOffsetSci3(script->data, 22));
+		return make_reg(1, relocateOffsetSci3(*script, 22));
 	}
 }
 
@@ -2726,13 +2735,9 @@ Common::String ResourceManager::findSierraGameId() {
 		return "";
 
 	// Seek to the name selector of the first export
-	byte *offsetPtr = heap->data + gameObjectOffset + nameSelector * 2;
-	uint16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
-	byte *seeker = heap->data + offset;
-	Common::String sierraId;
-	sierraId += (const char *)seeker;
-
-	return sierraId;
+	SciSpan<const byte>::const_iterator offsetPtr = heap->cbegin() + gameObjectOffset + nameSelector * 2;
+	uint16 offset = !isSci11Mac() ? offsetPtr.getUint16LE() : offsetPtr.getUint16BE();
+	return heap->getStringAt(offset);
 }
 
 const Common::String &Resource::getResourceLocation() const {
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 5589129..a9a7a86 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -30,6 +30,7 @@
 #include "sci/graphics/helpers.h"		// for ViewType
 #include "sci/decompressor.h"
 #include "sci/sci.h"
+#include "sci/util.h"
 
 namespace Common {
 class File;
@@ -226,7 +227,7 @@ struct ResourceIdHash : public Common::UnaryFunction<ResourceId, uint> {
 };
 
 /** Class for storing resources in memory */
-class Resource {
+class Resource : public SciSpan<const byte> {
 	friend class ResourceManager;
 
 	// FIXME: These 'friend' declarations are meant to be a temporary hack to
@@ -243,8 +244,6 @@ class Resource {
 // NOTE : Currently most member variables lack the underscore prefix and have
 // public visibility to let the rest of the engine compile without changes.
 public:
-	byte *data;
-	uint32 size;
 	byte *_header;
 	uint32 _headerSize;
 
@@ -595,11 +594,21 @@ public:
 		byte flags;
 		byte poly;
 		uint16 prio;
-		uint16 size;
-		byte *data;
+		SciSpan<const byte> data;
 		uint16 curPos;
 		long time;
 		byte prev;
+
+		Channel() :
+			number(0),
+			flags(0),
+			poly(0),
+			prio(0),
+			data(),
+			curPos(0) {
+			time = 0;
+			prev = 0;
+		}
 	};
 
 	struct Track {
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index f17a684..0125a0e 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -80,11 +80,12 @@ AudioVolumeResourceSource::~AudioVolumeResourceSource() {
 }
 
 bool Resource::loadFromWaveFile(Common::SeekableReadStream *file) {
-	data = new byte[size];
+	byte *ptr = new byte[_size];
+	_data = ptr;
 
-	uint32 really_read = file->read(data, size);
-	if (really_read != size)
-		error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
+	uint32 bytesRead = file->read(ptr, _size);
+	if (bytesRead != _size)
+		error("Read %d bytes from %s but expected %lu", bytesRead, _id.toString().c_str(), _size);
 
 	_status = kResStatusAllocated;
 	return true;
@@ -95,7 +96,7 @@ bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
 	uint32 riffTag = file->readUint32BE();
 	if (riffTag == MKTAG('R','I','F','F')) {
 		_headerSize = 0;
-		size = file->readUint32LE() + 8;
+		_size = file->readUint32LE() + 8;
 		file->seek(-8, SEEK_CUR);
 		return loadFromWaveFile(file);
 	}
@@ -123,7 +124,8 @@ bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
 			if (_headerSize != 7) { // Size is defined already from the map
 				// Load sample size
 				file->seek(7, SEEK_CUR);
-				size = file->readUint32LE();
+				_size = file->readUint32LE();
+				assert(!file->err() && !file->eos());
 				// Adjust offset to point at the header data again
 				file->seek(-11, SEEK_CUR);
 			}
@@ -133,15 +135,16 @@ bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
 }
 
 bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) {
-	data = new byte[size];
+	byte *ptr = new byte[size()];
+	_data = ptr;
 
-	if (data == NULL) {
-		error("Can't allocate %d bytes needed for loading %s", size, _id.toString().c_str());
+	if (!ptr) {
+		error("Can't allocate %lu bytes needed for loading %s", _size, _id.toString().c_str());
 	}
 
-	uint32 really_read = file->read(data, size);
-	if (really_read != size)
-		warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
+	uint32 bytesRead = file->read(ptr, size());
+	if (bytesRead != size())
+		warning("Read %d bytes from %s but expected %lu", bytesRead, _id.toString().c_str(), _size);
 
 	_status = kResStatusAllocated;
 	return true;
@@ -300,11 +303,13 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 		return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
 	}
 
-	byte *ptr = mapRes->data;
+	const uint32 srcSize = getVolumeFile(src)->size();
+
+	SciSpan<const byte>::const_iterator ptr = mapRes->cbegin();
 
 	// Heuristic to detect entry size
 	uint32 entrySize = 0;
-	for (int i = mapRes->size - 1; i >= 0; --i) {
+	for (int i = mapRes->size() - 1; i >= 0; --i) {
 		if (ptr[i] == 0xff)
 			entrySize++;
 		else
@@ -312,52 +317,54 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 	}
 
 	if (map->_mapNumber == 65535) {
-		while (ptr < mapRes->data + mapRes->size) {
-			uint16 n = READ_LE_UINT16(ptr);
+		while (ptr != mapRes->cend()) {
+			uint16 n = ptr.getUint16LE();
 			ptr += 2;
 
 			if (n == 0xffff)
 				break;
 
 			if (entrySize == 6) {
-				offset = READ_LE_UINT32(ptr);
+				offset = ptr.getUint32LE();
 				ptr += 4;
 			} else {
-				offset += READ_LE_UINT24(ptr);
+				offset += ptr.getUint24LE();
 				ptr += 3;
 			}
 
+			assert(offset < srcSize);
 			addResource(ResourceId(kResourceTypeAudio, n), src, offset);
 		}
 	} else if (map->_mapNumber == 0 && entrySize == 10 && ptr[3] == 0) {
 		// QFG3 demo format
 		// ptr[3] would be 'seq' in the normal format and cannot possibly be 0
-		while (ptr < mapRes->data + mapRes->size) {
-			uint16 n = READ_BE_UINT16(ptr);
+		while (ptr != mapRes->cend()) {
+			uint16 n = ptr.getUint16BE();
 			ptr += 2;
 
 			if (n == 0xffff)
 				break;
 
-			offset = READ_LE_UINT32(ptr);
+			offset = ptr.getUint32LE();
 			ptr += 4;
-			uint32 size = READ_LE_UINT32(ptr);
+			uint32 size = ptr.getUint32LE();
 			ptr += 4;
 
+			assert(offset + size <= srcSize);
 			addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
 		}
-	} else if (map->_mapNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
+	} else if (map->_mapNumber == 0 && entrySize == 8 && (ptr + 2).getUint16LE() == 0xffff) {
 		// LB2 Floppy/Mother Goose SCI1.1 format
 		Common::SeekableReadStream *stream = getVolumeFile(src);
 
-		while (ptr < mapRes->data + mapRes->size) {
-			uint16 n = READ_LE_UINT16(ptr);
+		while (ptr != mapRes->cend()) {
+			uint16 n = ptr.getUint16LE();
 			ptr += 4;
 
 			if (n == 0xffff)
 				break;
 
-			offset = READ_LE_UINT32(ptr);
+			offset = ptr.getUint32LE();
 			ptr += 4;
 
 			// The size is not stored in the map and the entries have no order.
@@ -369,18 +376,19 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 			stream->skip(5);
 			uint32 size = stream->readUint32LE() + headerSize + 2;
 
+			assert(offset + size <= srcSize);
 			addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
 		}
 	} else {
 		bool isEarly = (entrySize != 11);
 
 		if (!isEarly) {
-			offset = READ_LE_UINT32(ptr);
+			offset = ptr.getUint32LE();
 			ptr += 4;
 		}
 
-		while (ptr < mapRes->data + mapRes->size) {
-			uint32 n = READ_BE_UINT32(ptr);
+		while (ptr != mapRes->cend()) {
+			uint32 n = ptr.getUint32BE();
 			int syncSize = 0;
 			ptr += 4;
 
@@ -388,21 +396,23 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 				break;
 
 			if (isEarly) {
-				offset = READ_LE_UINT32(ptr);
+				offset = ptr.getUint32LE();
 				ptr += 4;
 			} else {
-				offset += READ_LE_UINT24(ptr);
+				offset += ptr.getUint24LE();
 				ptr += 3;
 			}
 
 			if (isEarly || (n & 0x80)) {
-				syncSize = READ_LE_UINT16(ptr);
+				syncSize = ptr.getUint16LE();
 				ptr += 2;
 
 				// FIXME: The sync36 resource seems to be two bytes too big in KQ6CD
 				// (bytes taken from the RAVE resource right after it)
-				if (syncSize > 0)
+				if (syncSize > 0) {
+					assert(offset + syncSize <= srcSize);
 					addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize);
+				}
 			}
 
 			// Checking for this 0x40 flag breaks at least Laura Bow 2 CD 1.1
@@ -410,15 +420,17 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 			if (g_sci->getGameId() == GID_KQ6 && (n & 0x40)) {
 				// This seems to define the size of raw lipsync data (at least
 				// in KQ6 CD Windows).
-				int kq6HiresSyncSize = READ_LE_UINT16(ptr);
+				int kq6HiresSyncSize = ptr.getUint16LE();
 				ptr += 2;
 
 				if (kq6HiresSyncSize > 0) {
+					assert(offset + syncSize + kq6HiresSyncSize <= srcSize);
 					addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize);
 					syncSize += kq6HiresSyncSize;
 				}
 			}
 
+			assert(offset + syncSize < srcSize);
 			addResource(ResourceId(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize);
 		}
 	}
@@ -445,7 +457,10 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
 	bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
 	file.seek(0);
 
-	while (1) {
+	uint32 volumeSize;
+	int lastVolumeNo = -1;
+
+	for (;;) {
 		uint16 n = file.readUint16LE();
 		uint32 offset = file.readUint32LE();
 		uint32 size = file.readUint32LE();
@@ -472,10 +487,17 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
 		ResourceSource *src = findVolume(map, volume_nr);
 
 		if (src) {
+			if (volume_nr != lastVolumeNo) {
+				volumeSize = getVolumeFile(src)->size();
+				lastVolumeNo = volume_nr;
+			}
+
 			if (unload)
 				removeAudioResource(ResourceId(kResourceTypeAudio, n));
-			else
+			else {
+				assert(offset + size <= volumeSize);
 				addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+			}
 		} else {
 			warning("Failed to find audio volume %i", volume_nr);
 		}
@@ -583,8 +605,6 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
 	_innerResource = resource;
 	_soundPriority = 0xFF;
 
-	byte *data, *data2;
-	byte *dataEnd;
 	Channel *channel, *sampleChannel;
 
 	switch (_soundVersion) {
@@ -597,60 +617,54 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
 		_tracks->type = 0; // Not used for SCI0
 		_tracks->channelCount = 1;
 		// Digital sample data included? -> Add an additional channel
-		if (resource->data[0] == 2)
+		if (resource->getUint8At(0) == 2)
 			_tracks->channelCount++;
 		_tracks->channels = new Channel[_tracks->channelCount];
-		memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
 		channel = &_tracks->channels[0];
 		channel->flags |= 2; // don't remap (SCI0 doesn't have remapping)
 		if (_soundVersion == SCI_VERSION_0_EARLY) {
-			channel->data = resource->data + 0x11;
-			channel->size = resource->size - 0x11;
+			channel->data = resource->subspan(0x11);
 		} else {
-			channel->data = resource->data + 0x21;
-			channel->size = resource->size - 0x21;
+			channel->data = resource->subspan(0x21);
 		}
 		if (_tracks->channelCount == 2) {
 			// Digital sample data included
 			_tracks->digitalChannelNr = 1;
 			sampleChannel = &_tracks->channels[1];
 			// we need to find 0xFC (channel terminator) within the data
-			data = channel->data;
-			dataEnd = channel->data + channel->size;
-			while ((data < dataEnd) && (*data != 0xfc))
-				data++;
+			SciSpan<const byte>::const_iterator it = channel->data.cbegin();
+			while (it != channel->data.cend() && *it != 0xfc)
+				it++;
 			// Skip any following 0xFCs as well
-			while ((data < dataEnd) && (*data == 0xfc))
-				data++;
+			while (it != channel->data.cend() && *it == 0xfc)
+				it++;
 			// Now adjust channels accordingly
-			sampleChannel->data = data;
-			sampleChannel->size = channel->size - (data - channel->data);
-			channel->size = data - channel->data;
+			sampleChannel->data = channel->data.subspan(it - channel->data.cbegin());
+			channel->data = channel->data.subspan(0, it - channel->data.cbegin());
 			// Read sample header information
 			//Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
-			_tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
-			_tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
+			_tracks->digitalSampleRate = sampleChannel->data.getUint16LEAt(14);
+			_tracks->digitalSampleSize = sampleChannel->data.getUint16LEAt(32);
 			_tracks->digitalSampleStart = 0;
 			_tracks->digitalSampleEnd = 0;
 			sampleChannel->data += 44; // Skip over header
-			sampleChannel->size -= 44;
 		}
 		break;
 
 	case SCI_VERSION_1_EARLY:
 	case SCI_VERSION_1_LATE:
-	case SCI_VERSION_2_1_EARLY:
-		data = resource->data;
+	case SCI_VERSION_2_1_EARLY: {
+		SciSpan<const byte> data = *resource;
 		// Count # of tracks
 		_trackCount = 0;
 		while ((*data++) != 0xFF) {
 			_trackCount++;
 			while (*data != 0xFF)
 				data += 6;
-			data++;
+			++data;
 		}
 		_tracks = new Track[_trackCount];
-		data = resource->data;
+		data = *resource;
 
 		byte channelCount;
 
@@ -662,7 +676,7 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
 
 			_tracks[trackNr].type = *data++;
 			// Counting # of channels used
-			data2 = data;
+			SciSpan<const byte> data2 = data;
 			channelCount = 0;
 			while (*data2 != 0xFF) {
 				data2 += 6;
@@ -680,38 +694,37 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
 				channelNr = 0;
 				while (channelCount--) {
 					channel = &_tracks[trackNr].channels[channelNr];
-					uint dataOffset = READ_LE_UINT16(data + 2);
+					const uint16 dataOffset = data.getUint16LEAt(2);
 
-					if (dataOffset >= resource->size) {
+					if (dataOffset >= resource->size()) {
 						warning("Invalid offset inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr);
 						data += 6;
 						continue;
 					}
 
-					channel->data = resource->data + dataOffset;
-					channel->size = READ_LE_UINT16(data + 4);
+					uint16 size = data.getUint16LEAt(4);
 
-					if (dataOffset + channel->size > resource->size) {
+					if (dataOffset + size > resource->size()) {
 						warning("Invalid size inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr);
-						channel->size = resource->size - dataOffset;
+						size = resource->size() - dataOffset;
 					}
 
+					channel->data = resource->subspan(dataOffset, size);
+
 					channel->curPos = 0;
-					channel->number = *channel->data;
+					channel->number = channel->data[0];
 
-					channel->poly = *(channel->data + 1) & 0x0F;
-					channel->prio = *(channel->data + 1) >> 4;
+					channel->poly = channel->data[1] & 0x0F;
+					channel->prio = channel->data[1] >> 4;
 					channel->time = channel->prev = 0;
 					channel->data += 2; // skip over header
-					channel->size -= 2; // remove header size
 					if (channel->number == 0xFE) { // Digital channel
 						_tracks[trackNr].digitalChannelNr = channelNr;
-						_tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
-						_tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
-						_tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
-						_tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
+						_tracks[trackNr].digitalSampleRate = channel->data.getUint16LEAt(0);
+						_tracks[trackNr].digitalSampleSize = channel->data.getUint16LEAt(2);
+						_tracks[trackNr].digitalSampleStart = channel->data.getUint16LEAt(4);
+						_tracks[trackNr].digitalSampleEnd = channel->data.getUint16LEAt(6);
 						channel->data += 8; // Skip over header
-						channel->size -= 8;
 						channel->flags = 0;
 					} else {
 						channel->flags = channel->number >> 4;
@@ -740,9 +753,10 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
 				// Skip over digital track
 				data += 6;
 			}
-			data++; // Skipping 0xFF that closes channels list
+			++data; // Skipping 0xFF that closes channels list
 		}
 		break;
+	}
 
 	default:
 		error("SoundResource: SCI version %d is unsupported", _soundVersion);
@@ -789,13 +803,13 @@ SoundResource::Track *SoundResource::getDigitalTrack() {
 
 // Gets the filter mask for SCI0 sound resources
 int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
-	byte *data = _innerResource->data;
+	SciSpan<const byte> data = *_innerResource;
 	int channelMask = 0;
 
 	if (_soundVersion > SCI_VERSION_0_LATE)
 		return 0;
 
-	data++; // Skip over digital sample flag
+	++data; // Skip over digital sample flag
 
 	for (int channelNr = 0; channelNr < 16; channelNr++) {
 		channelMask = channelMask >> 1;
@@ -823,7 +837,7 @@ int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
 			// by the corresponding hardware
 
 			// Skip voice count
-			data++;
+			++data;
 
 			flags = *data++;
 		}
@@ -853,12 +867,11 @@ int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
 }
 
 byte SoundResource::getInitialVoiceCount(byte channel) {
-	byte *data = _innerResource->data;
-
 	if (_soundVersion > SCI_VERSION_0_LATE)
 		return 0; // TODO
 
-	data++; // Skip over digital sample flag
+	// Skip over digital sample flag
+	SciSpan<const byte> data = _innerResource->subspan(1);
 
 	if (_soundVersion == SCI_VERSION_0_EARLY)
 		return data[channel] >> 4;
@@ -902,7 +915,7 @@ void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *
 					break;
 				default:
 					mappingTable += 2;
-					res->size = *mappingTable - compressedOffset;
+					res->_size = *mappingTable - compressedOffset;
 				}
 				break;
 			}
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 20547f3..34930a6 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -361,7 +361,7 @@ Common::Error SciEngine::run() {
 		// Refer to bug #3036609.
 		Resource *buggyScript = _resMan->findResource(ResourceId(kResourceTypeScript, 180), 0);
 
-		if (buggyScript && (buggyScript->size == 12354 || buggyScript->size == 12362)) {
+		if (buggyScript && (buggyScript->size() == 12354 || buggyScript->size() == 12362)) {
 			showScummVMDialog("A known buggy game script has been detected, which could "
 			                  "prevent you from progressing later on in the game, during "
 			                  "the sequence with the Green Man's riddles. Please, apply "
@@ -477,10 +477,10 @@ bool SciEngine::gameHasFanMadePatch() {
 		if (patchInfo[curEntry].gameID == getGameId()) {
 			Resource *targetScript = _resMan->findResource(ResourceId(kResourceTypeScript, patchInfo[curEntry].targetScript), 0);
 
-			if (targetScript && targetScript->size + 2 == patchInfo[curEntry].targetSize) {
+			if (targetScript && targetScript->size() + 2 == patchInfo[curEntry].targetSize) {
 				if (patchInfo[curEntry].patchedByteOffset == 0)
 					return true;
-				else if (targetScript->data[patchInfo[curEntry].patchedByteOffset - 2] == patchInfo[curEntry].patchedByte)
+				else if (targetScript->getUint8At(patchInfo[curEntry].patchedByteOffset - 2) == patchInfo[curEntry].patchedByte)
 					return true;
 			}
 		}
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 4fb9a58..e470b31 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -365,15 +365,15 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
 	if (audioCompressionType) {
 #if (defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC))
 		// Compressed audio made by our tool
-		byte *compressedData = (byte *)malloc(audioRes->size);
+		byte *compressedData = (byte *)malloc(audioRes->size());
 		assert(compressedData);
 		// We copy over the compressed data in our own buffer. We have to do
 		// this, because ResourceManager may free the original data late. All
 		// other compression types already decompress completely into an
 		// additional buffer here. MP3/OGG/FLAC decompression works on-the-fly
 		// instead.
-		memcpy(compressedData, audioRes->data, audioRes->size);
-		Common::SeekableReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size, DisposeAfterUse::YES);
+		audioRes->unsafeCopyDataTo(compressedData);
+		Common::SeekableReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size(), DisposeAfterUse::YES);
 
 		switch (audioCompressionType) {
 		case MKTAG('M','P','3',' '):
@@ -401,13 +401,13 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
 			// SCI1.1
 			Common::MemoryReadStream headerStream(audioRes->_header, audioRes->_headerSize, DisposeAfterUse::NO);
 
-			if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags, audioRes->size)) {
-				Common::MemoryReadStream dataStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+			if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags, audioRes->size())) {
+				Common::MemoryReadStream dataStream(audioRes->toStream());
 				data = readSOLAudio(&dataStream, size, audioFlags, flags);
 			}
-		} else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKTAG('R','I','F','F')) {
+		} else if (audioRes->size() > 4 && audioRes->getUint32BEAt(0) == MKTAG('R','I','F','F')) {
 			// WAVE detected
-			Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+			Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(audioRes->getUnsafeDataAt(0), audioRes->size(), DisposeAfterUse::NO);
 
 			// Calculate samplelen from WAVE header
 			int waveSize = 0, waveRate = 0;
@@ -420,9 +420,9 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
 
 			waveStream->seek(0, SEEK_SET);
 			audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES);
-		} else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKTAG('F','O','R','M')) {
+		} else if (audioRes->size() > 4 && audioRes->getUint32BEAt(0) == MKTAG('F','O','R','M')) {
 			// AIFF detected
-			Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+			Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(audioRes->getUnsafeDataAt(0), audioRes->size(), DisposeAfterUse::NO);
 			Audio::RewindableAudioStream *rewindStream = Audio::makeAIFFStream(waveStream, DisposeAfterUse::YES);
 			audioSeekStream = dynamic_cast<Audio::SeekableAudioStream *>(rewindStream);
 
@@ -430,10 +430,14 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
 				warning("AIFF file is not seekable");
 				delete rewindStream;
 			}
-		} else if (audioRes->size > 14 && READ_BE_UINT16(audioRes->data) == 1 && READ_BE_UINT16(audioRes->data + 2) == 1
-				&& READ_BE_UINT16(audioRes->data + 4) == 5 && READ_BE_UINT32(audioRes->data + 10) == 0x00018051) {
+		} else if (audioRes->size() > 14 &&
+				   audioRes->getUint16BEAt(0) == 1 &&
+				   audioRes->getUint16BEAt(2) == 1 &&
+				   audioRes->getUint16BEAt(4) == 5 &&
+				   audioRes->getUint32BEAt(10) == 0x00018051) {
+
 			// Mac snd detected
-			Common::SeekableReadStream *sndStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+			Common::SeekableReadStream *sndStream = new Common::MemoryReadStream(audioRes->getUnsafeDataAt(0), audioRes->size(), DisposeAfterUse::NO);
 
 			audioSeekStream = Audio::makeMacSndStream(sndStream, DisposeAfterUse::YES);
 			if (!audioSeekStream)
@@ -441,10 +445,10 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
 
 		} else {
 			// SCI1 raw audio
-			size = audioRes->size;
+			size = audioRes->size();
 			data = (byte *)malloc(size);
 			assert(data);
-			memcpy(data, audioRes->data, size);
+			audioRes->unsafeCopyDataTo(data);
 			flags = Audio::FLAG_UNSIGNED;
 			_audioRate = 11025;
 		}
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 4f557be..b8e9080 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -31,6 +31,7 @@
 
 #include "sci/resource.h"
 #include "sci/sound/drivers/mididriver.h"
+#include "sci/util.h"
 
 namespace Sci {
 
@@ -50,7 +51,7 @@ public:
 		kRhythmKeys = 62
 	};
 
-	MidiDriver_AdLib(Audio::Mixer *mixer) :_playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0), _isOpen(false) { }
+	MidiDriver_AdLib(Audio::Mixer *mixer) :_playSwitch(true), _masterVolume(15), _rhythmKeyMap(), _opl(0), _isOpen(false) { }
 	virtual ~MidiDriver_AdLib() { }
 
 	// MidiDriver
@@ -70,10 +71,10 @@ public:
 
 	void setVolume(byte volume);
 	void playSwitch(bool play);
-	bool loadResource(const byte *data, uint size);
+	bool loadResource(const SciSpan<const byte> &data);
 	virtual uint32 property(int prop, uint32 param);
 
-	bool useRhythmChannel() const { return _rhythmKeyMap != NULL; }
+	bool useRhythmChannel() const { return _rhythmKeyMap; }
 
 private:
 	enum ChannelID {
@@ -139,13 +140,13 @@ private:
 	int _masterVolume;
 	Channel _channels[MIDI_CHANNELS];
 	AdLibVoice _voices[kVoices];
-	byte *_rhythmKeyMap;
+	Common::SpanOwner<SciSpan<const byte> > _rhythmKeyMap;
 	Common::Array<AdLibPatch> _patches;
 
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
 
-	void loadInstrument(const byte *ins);
+	void loadInstrument(const SciSpan<const byte> &ins);
 	void voiceOn(int voice, int note, int velocity);
 	void voiceOff(int voice);
 	void setPatch(int voice, int patch);
@@ -255,7 +256,7 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) {
 
 void MidiDriver_AdLib::close() {
 	delete _opl;
-	delete[] _rhythmKeyMap;
+	_rhythmKeyMap.clear();
 }
 
 void MidiDriver_AdLib::setVolume(byte volume) {
@@ -346,12 +347,12 @@ void MidiDriver_AdLib::onTimer() {
 	}
 }
 
-void MidiDriver_AdLib::loadInstrument(const byte *ins) {
+void MidiDriver_AdLib::loadInstrument(const SciSpan<const byte> &ins) {
 	AdLibPatch patch;
 
 	// Set data for the operators
 	for (int i = 0; i < 2; i++) {
-		const byte *op = ins + i * 13;
+		const byte *op = ins.getUnsafeDataAt(i * 13, 13);
 		patch.op[i].kbScaleLevel = op[0] & 0x3;
 		patch.op[i].frequencyMult = op[1] & 0xf;
 		patch.op[i].attackRate = op[3] & 0xf;
@@ -589,7 +590,7 @@ void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
 
 	_voices[voice].age = 0;
 
-	if ((channel == 9) && _rhythmKeyMap) {
+	if (channel == 9 && _rhythmKeyMap) {
 		patch = CLIP(note, 27, 88) + 101;
 	} else {
 		patch = _channels[channel].patch;
@@ -616,7 +617,7 @@ void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
 	float delta;
 	int bend = _channels[channel].pitchWheel;
 
-	if ((channel == 9) && _rhythmKeyMap) {
+	if (channel == 9 && _rhythmKeyMap) {
 		note = _rhythmKeyMap[CLIP(note, 27, 88) - 27];
 	}
 
@@ -756,30 +757,32 @@ void MidiDriver_AdLib::playSwitch(bool play) {
 	renewNotes(-1, play);
 }
 
-bool MidiDriver_AdLib::loadResource(const byte *data, uint size) {
-	if ((size != 1344) && (size != 2690) && (size != 5382)) {
-		error("ADLIB: Unsupported patch format (%i bytes)", size);
+bool MidiDriver_AdLib::loadResource(const SciSpan<const byte> &data) {
+	const uint32 size = data.size();
+	if (size != 1344 && size != 2690 && size != 5382) {
+		error("ADLIB: Unsupported patch format (%u bytes)", size);
 		return false;
 	}
 
 	for (int i = 0; i < 48; i++)
-		loadInstrument(data + (28 * i));
+		loadInstrument(data.subspan(28 * i));
 
 	if (size == 1344) {
 		byte dummy[28] = {0};
 
 		// Only 48 instruments, add dummies
 		for (int i = 0; i < 48; i++)
-			loadInstrument(dummy);
+			loadInstrument(SciSpan<const byte>(dummy, sizeof(dummy)));
 	} else if (size == 2690) {
 		for (int i = 48; i < 96; i++)
-			loadInstrument(data + 2 + (28 * i));
+			loadInstrument(data.subspan(2 + (28 * i)));
 	} else {
 		// SCI1.1 and later
-		for (int i = 48; i < 190; i++)
-			loadInstrument(data + (28 * i));
-		_rhythmKeyMap = new byte[kRhythmKeys];
-		memcpy(_rhythmKeyMap, data + 5320, kRhythmKeys);
+		for (int i = 48; i < 190; i++) {
+			loadInstrument(data.subspan(28 * i));
+		}
+
+		_rhythmKeyMap->allocateFromSpan(data.subspan(5320, kRhythmKeys));
 	}
 
 	return true;
@@ -802,11 +805,11 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
 	assert(resMan != NULL);
 
 	// Load up the patch.003 file, parse out the instruments
-	Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 3), 0);
+	Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 3), false);
 	bool ok = false;
 
 	if (res) {
-		ok = static_cast<MidiDriver_AdLib *>(_driver)->loadResource(res->data, res->size);
+		ok = static_cast<MidiDriver_AdLib *>(_driver)->loadResource(*res);
 	} else {
 		// Early SCI0 games have the sound bank embedded in the AdLib driver
 
@@ -819,13 +822,13 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
 			// Note: Funseeker's Guide also has another version of adl.drv, 8803 bytes.
 			// This isn't supported, but it's not really used anywhere, as that demo
 			// doesn't have sound anyway.
-			if ((size == 5684) || (size == 5720) || (size == 5727)) {
-				byte *buf = new byte[patchSize];
-
-				if (f.seek(0x45a) && (f.read(buf, patchSize) == patchSize))
-					ok = static_cast<MidiDriver_AdLib *>(_driver)->loadResource(buf, patchSize);
-
-				delete[] buf;
+			if (size == 5684 || size == 5720 || size == 5727) {
+				ok = f.seek(0x45a);
+				if (ok) {
+					Common::SpanOwner<SciSpan<const byte> > patchData;
+					patchData->allocateFromStream(f, patchSize);
+					ok = static_cast<MidiDriver_AdLib *>(_driver)->loadResource(*patchData);
+				}
 			}
 		}
 	}
diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp
index f5daab7..b8b6a32 100644
--- a/engines/sci/sound/drivers/amigamac.cpp
+++ b/engines/sci/sound/drivers/amigamac.cpp
@@ -610,7 +610,7 @@ int MidiDriver_AmigaMac::open() {
 			return Common::kUnknownError;
 		}
 
-		Common::MemoryReadStream stream(resource->data, resource->size);
+		Common::MemoryReadStream stream(resource->toStream());
 
 		if (_isSci1) {
 			if (!loadInstrumentsSCI1(stream))
diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp
index a222090..8b92432 100644
--- a/engines/sci/sound/drivers/cms.cpp
+++ b/engines/sci/sound/drivers/cms.cpp
@@ -29,6 +29,7 @@
 #include "common/system.h"
 
 #include "sci/resource.h"
+#include "sci/util.h"
 
 namespace Sci {
 
@@ -72,7 +73,7 @@ private:
 	bool _playSwitch;
 	uint16 _masterVolume;
 
-	uint8 *_patchData;
+	Common::SpanOwner<SciSpan<uint8> > _patchData;
 
 	struct Channel {
 		Channel()
@@ -96,7 +97,7 @@ private:
 
 	struct Voice {
 		Voice() : channel(0xFF), note(0xFF), sustained(0xFF), ticks(0),
-		    turnOffTicks(0), patchDataPtr(0), patchDataIndex(0),
+		    turnOffTicks(0), patchDataPtr(), patchDataIndex(0),
 		    amplitudeTimer(0), amplitudeModifier(0), turnOff(false),
 		    velocity(0) {
 		}
@@ -106,7 +107,7 @@ private:
 		uint8 sustained;
 		uint16 ticks;
 		uint16 turnOffTicks;
-		const uint8 *patchDataPtr;
+		SciSpan<uint8> patchDataPtr;
 		uint8 patchDataIndex;
 		uint8 amplitudeTimer;
 		uint8 amplitudeModifier;
@@ -172,12 +173,11 @@ int MidiDriver_CMS::open() {
 		return MERR_ALREADY_OPEN;
 
 	assert(_resMan);
-	Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), 0);
+	Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), false);
 	if (!res)
 		return -1;
 
-	_patchData = new uint8[res->size];
-	memcpy(_patchData, res->data, res->size);
+	_patchData->allocateFromSpan(*res);
 
 	for (uint i = 0; i < ARRAYSIZE(_channel); ++i)
 		_channel[i] = Channel();
@@ -218,9 +218,9 @@ int MidiDriver_CMS::open() {
 void MidiDriver_CMS::close() {
 	_mixer->stopHandle(_mixerSoundHandle);
 
-	delete[] _patchData;
+	_patchData.clear();
 	delete _cms;
-	_cms = 0;
+	_cms = nullptr;
 }
 
 void MidiDriver_CMS::send(uint32 b) {
@@ -295,7 +295,7 @@ void MidiDriver_CMS::voiceOn(int voiceNr, int note, int velocity) {
 	voice.amplitudeTimer = 0;
 	voice.ticks = 0;
 	voice.turnOffTicks = 0;
-	voice.patchDataPtr = _patchData + READ_LE_UINT16(&_patchData[_channel[voice.channel].patch * 2]);
+	voice.patchDataPtr = _patchData->subspan(_patchData->getUint16LEAt(_channel[voice.channel].patch * 2));
 	if (velocity)
 		velocity = _velocityTable[(velocity >> 3)];
 	voice.velocity = velocity;
@@ -798,7 +798,7 @@ public:
 		_driver->setTimerCallback(0, 0);
 		_driver->close();
 		delete _driver;
-		_driver = 0;
+		_driver = nullptr;
 	}
 
 	bool hasRhythmChannel() const { return false; }
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
index db9f755..3f3f581 100644
--- a/engines/sci/sound/drivers/fb01.cpp
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -24,6 +24,7 @@
 
 #include "sci/resource.h"
 #include "sci/sound/drivers/mididriver.h"
+#include "sci/util.h"
 
 #include "common/file.h"
 #include "common/system.h"
@@ -71,8 +72,8 @@ private:
 
 	void setVoiceParam(byte voice, byte param, byte value);
 	void setSystemParam(byte sysChan, byte param, byte value);
-	void sendVoiceData(byte instrument, const byte *data);
-	void sendBanks(const byte *data, int size);
+	void sendVoiceData(byte instrument, const SciSpan<const byte> &data);
+	void sendBanks(const SciSpan<const byte> &data);
 	void storeVoiceData(byte instrument, byte bank, byte index);
 	void initVoices();
 
@@ -442,22 +443,22 @@ void MidiPlayer_Fb01::setTimerCallback(void *timer_param, Common::TimerManager::
 	_driver->setTimerCallback(this, midiTimerCallback);
 }
 
-void MidiPlayer_Fb01::sendBanks(const byte *data, int size) {
-	if (size < 3072)
+void MidiPlayer_Fb01::sendBanks(const SciSpan<const byte> &data) {
+	if (data.size() < 3072)
 		error("Failed to read FB-01 patch");
 
 	// SSCI sends bank dumps containing 48 instruments at once. We cannot do that
 	// due to the limited maximum SysEx length. Instead we send the instruments
 	// one by one and store them in the banks.
 	for (int i = 0; i < 48; i++) {
-		sendVoiceData(0, data + i * 64);
+		sendVoiceData(0, data.subspan(i * 64));
 		storeVoiceData(0, 0, i);
 	}
 
 	// Send second bank if available
-	if ((size >= 6146) && (READ_BE_UINT16(data + 3072) == 0xabcd)) {
+	if (data.size() >= 6146 && data.getUint16BEAt(3072) == 0xabcd) {
 		for (int i = 0; i < 48; i++) {
-			sendVoiceData(0, data + 3074 + i * 64);
+			sendVoiceData(0, data.subspan(3074 + i * 64));
 			storeVoiceData(0, 1, i);
 		}
 	}
@@ -479,10 +480,10 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
 	// Turn off memory protection
 	setSystemParam(0, 0x21, 0);
 
-	Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 2), 0);
+	Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 2), false);
 
 	if (res) {
-		sendBanks(res->data, res->size);
+		sendBanks(*res);
 	} else {
 		// Early SCI0 games have the sound bank embedded in the IMF driver.
 		// Note that these games didn't actually support the FB-01 as a device,
@@ -494,27 +495,23 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
 		Common::File f;
 
 		if (f.open("IMF.DRV")) {
-			int size = f.size();
-			byte *buf = new byte[size];
-
-			f.read(buf, size);
+			Common::SpanOwner<SciSpan<const byte> > buf;
+			buf->allocateFromStream(f);
 
 			// Search for start of sound bank
-			int offset;
-			for (offset = 0; offset < size; ++offset) {
-				if (!strncmp((char *)buf + offset, "SIERRA ", 7))
+			uint offset;
+			for (offset = 0; offset < buf->size() - 7; ++offset) {
+				if (!strncmp((const char *)buf->getUnsafeDataAt(offset, 7), "SIERRA ", 7))
 					break;
 			}
 
 			// Skip to voice data
 			offset += 0x20;
 
-			if (offset >= size)
+			if (offset >= buf->size())
 				error("Failed to locate start of FB-01 sound bank");
 
-			sendBanks(buf + offset, size - offset);
-
-			delete[] buf;
+			sendBanks(buf->subspan(offset));
 		} else
 			error("Failed to open IMF.DRV");
 	}
@@ -553,7 +550,7 @@ void MidiPlayer_Fb01::setSystemParam(byte sysChan, byte param, byte value) {
 	sysEx(_sysExBuf, 6);
 }
 
-void MidiPlayer_Fb01::sendVoiceData(byte instrument, const byte *data) {
+void MidiPlayer_Fb01::sendVoiceData(byte instrument, const SciSpan<const byte> &data) {
 	_sysExBuf[2] = 0x00;
 	_sysExBuf[3] = 0x08 | instrument;
 	_sysExBuf[4] = 0x00;
diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp
index f6dbac2..270843c 100644
--- a/engines/sci/sound/drivers/fmtowns.cpp
+++ b/engines/sci/sound/drivers/fmtowns.cpp
@@ -101,7 +101,7 @@ public:
 	~MidiDriver_FMTowns();
 
 	int open();
-	void loadInstruments(const uint8 *data);
+	void loadInstruments(const SciSpan<const uint8> &data);
 	bool isOpen() const { return _isOpen; }
 	void close();
 
@@ -461,14 +461,18 @@ int MidiDriver_FMTowns::open() {
 	return 0;
 }
 
-void MidiDriver_FMTowns::loadInstruments(const uint8 *data) {
-	if (data) {
-		data += 6;
-		for (int i = 0; i < 128; i++) {
-			_intf->callback(5, 0, i, data);
-			data += 48;
+void MidiDriver_FMTowns::loadInstruments(const SciSpan<const uint8> &data) {
+	enum {
+		fmDataSize = 48
+	};
+
+	if (data.size()) {
+		SciSpan<const uint8> instrumentData = data.subspan(6);
+		for (int i = 0; i < 128; i++, instrumentData += fmDataSize) {
+			_intf->callback(5, 0, i, instrumentData.getUnsafeDataAt(0, fmDataSize));
 		}
 	}
+
 	_intf->callback(70, 3);
 	property(MIDI_PROP_MASTER_VOLUME, _masterVolume);
 }
@@ -622,7 +626,7 @@ int MidiPlayer_FMTowns::open(ResourceManager *resMan) {
 	if (_townsDriver) {
 		result = _townsDriver->open();
 		if (!result && _version == SCI_VERSION_1_LATE)
-			_townsDriver->loadInstruments((resMan->findResource(ResourceId(kResourceTypePatch, 8), true))->data);
+			_townsDriver->loadInstruments(*resMan->findResource(ResourceId(kResourceTypePatch, 8), false));
 	}
 	return result;
 }
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 7b2b102..b9035b0 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -70,18 +70,18 @@ public:
 	void playSwitch(bool play);
 
 private:
-	bool isMt32GmPatch(const byte *data, int size);
-	void readMt32GmPatch(const byte *data, int size);
-	void readMt32Patch(const byte *data, int size);
+	bool isMt32GmPatch(const SciSpan<const byte> &data);
+	void readMt32GmPatch(const SciSpan<const byte> &data);
+	void readMt32Patch(const SciSpan<const byte> &data);
 	void readMt32DrvData();
 
-	void mapMt32ToGm(byte *data, size_t size);
+	void mapMt32ToGm(const SciSpan<const byte> &data);
 	uint8 lookupGmInstrument(const char *iname);
 	uint8 lookupGmRhythmKey(const char *iname);
 	uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins);
 
-	void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay);
-	void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay);
+	void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream &data, const int len, bool noDelay);
+	void sendMt32SysEx(const uint32 addr, const SciSpan<const byte> &data, bool noDelay);
 	void setMt32Volume(byte volume);
 	void resetMt32();
 
@@ -382,8 +382,9 @@ int MidiPlayer_Midi::getVolume() {
 void MidiPlayer_Midi::setReverb(int8 reverb) {
 	assert(reverb < kReverbConfigNr);
 
-	if (_hasReverb && (_reverb != reverb))
-		sendMt32SysEx(0x100001, _reverbConfig[reverb], 3, true);
+	if (_hasReverb && _reverb != reverb) {
+		sendMt32SysEx(0x100001, SciSpan<const byte>(_reverbConfig[reverb], 3), true);
+	}
 
 	_reverb = reverb;
 }
@@ -398,7 +399,9 @@ void MidiPlayer_Midi::playSwitch(bool play) {
 	}
 }
 
-bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) {
+bool MidiPlayer_Midi::isMt32GmPatch(const SciSpan<const byte> &data) {
+	uint32 size = data.size();
+
 	// WORKAROUND: Some Mac games (e.g. LSL5) may have an extra byte at the
 	// end, so compensate for that here - bug #6725.
 	if (size == 16890)
@@ -419,21 +422,21 @@ bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) {
 
 	// First, check for a GM patch. The presence of MIDI data after the
 	// initial 1153 + 2 bytes indicates a GM patch
-	if (READ_LE_UINT16(data + 1153) + 1155 == size)
+	if (data.getUint16LEAt(1153) + 1155U == size)
 		isMt32Gm = true;
 
 	// Now check for a regular MT-32 patch. Check readMt32Patch() below for
 	// more info.
 	// 491 = 20 + 20 + 20 + 2 + 1 + 11 + 3 * 11 + 256 + 128
 	byte timbresNr = data[491];
-	int pos = 492 + 246 * timbresNr;
+	uint pos = 492 + 246 * timbresNr;
 
 	// Patches 49-96
-	if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd))
+	if (size >= pos + 386 && data.getUint16BEAt(pos) == 0xabcd)
 		pos += 386;	// 256 + 128 + 2
 
 	// Rhythm key map + partial reserve
-	if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba))
+	if (size >= pos + 267 && data.getUint16BEAt(pos) == 0xdcba)
 		pos += 267;	// 256 + 9 + 2
 
 	if (size == pos)
@@ -445,7 +448,7 @@ bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) {
 	return isMt32Gm;
 }
 
-void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay = false) {
+void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream &stream, int len, bool noDelay = false) {
 	if (len + 8 > kMaxSysExSize) {
 		warning("SysEx message exceed maximum size; ignoring");
 		return;
@@ -457,8 +460,7 @@ void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStrea
 	_sysExBuf[5] = (addr >> 8) & 0xff;
 	_sysExBuf[6] = addr & 0xff;
 
-	for (int i = 0; i < len; i++)
-		_sysExBuf[7 + i] = str->readByte();
+	stream.read(_sysExBuf + 7, len);
 
 	for (int i = 4; i < 7 + len; i++)
 		chk -= _sysExBuf[i];
@@ -471,81 +473,82 @@ void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStrea
 		sysEx(_sysExBuf, len + 8);
 }
 
-void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) {
-	Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len);
-	sendMt32SysEx(addr, str, len, noDelay);
-	delete str;
+void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const SciSpan<const byte> &buf, bool noDelay = false) {
+	Common::MemoryReadStream stream(buf.toStream());
+	sendMt32SysEx(addr, stream, buf.size(), noDelay);
 }
 
-void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) {
+
+void MidiPlayer_Midi::readMt32Patch(const SciSpan<const byte> &data) {
 	// MT-32 patch contents:
-	// - 20 bytes unkown
-	// - 20 bytes before-SysEx message
-	// - 20 bytes goodbye SysEx message
-	// - 2 bytes volume
-	// - 1 byte reverb
-	// - 11 bytes reverb Sysex message
-	// - 3 * 11 reverb data
-	// - 256 + 128 bytes patches 1-48
+	// - 0-19        after-SysEx message
+	// - 20-39       before-SysEx message
+	// - 40-59       goodbye SysEx message
+	// - 60-61       volume
+	// - 62          reverb
+	// - 63-73       reverb Sysex message
+	// - 74-106      [3 * 11] reverb data
+	// - 107-490     [256 + 128] patches 1-48
 	// --> total: 491 bytes
-	// - 1 byte number of timbres (64 max)
-	// - 246 * timbres timbre data
-	// - 2 bytes flag (0xabcd)
-	// - 256 + 128 bytes patches 49-96
-	// - 2 bytes flag (0xdcba)
-	// - 256 bytes rhythm key map
-	// - 9 bytes partial reserve
+	// - 491         number of timbres (64 max)
+	// - 492..n      [246 * number of timbres] timbre data
+	// - n-n+1       flag (0xabcd)
+	// - n+2-n+385   [256 + 128] patches 49-96
+	// - n+386-n+387 flag (0xdcba)
+	// - n+388-n+643 rhythm key map
+	// - n+644-n+652 partial reserve
 
-	Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size);
+	Common::MemoryReadStream stream(data.toStream());
 
 	// Send before-SysEx text
-	str->seek(20);
-	sendMt32SysEx(0x200000, str, 20);
+	stream.seek(20);
+	sendMt32SysEx(0x200000, stream, 20);
 
 	// Save goodbye message
-	str->read(_goodbyeMsg, 20);
+	assert(sizeof(_goodbyeMsg) == 20);
+	stream.read(_goodbyeMsg, 20);
 
-	byte volume = CLIP<uint16>(str->readUint16LE(), 0, 100);
+	const uint8 volume = MIN<uint16>(stream.readUint16LE(), 100);
 	setMt32Volume(volume);
 
 	// Reverb default only used in (roughly) SCI0/SCI01
-	byte reverb = str->readByte();
+	byte reverb = stream.readByte();
 
 	_hasReverb = true;
 
 	// Skip reverb SysEx message
-	str->seek(11, SEEK_CUR);
+	stream.seek(11, SEEK_CUR);
 
 	// Read reverb data (stored vertically - patch #3117434)
 	for (int j = 0; j < 3; ++j) {
 		for (int i = 0; i < kReverbConfigNr; i++) {
-			_reverbConfig[i][j] = str->readByte();
+			_reverbConfig[i][j] = stream.readByte();
 		}
 	}
 
 	// Patches 1-48
-	sendMt32SysEx(0x50000, str, 256);
-	sendMt32SysEx(0x50200, str, 128);
+	sendMt32SysEx(0x50000, stream, 256);
+	sendMt32SysEx(0x50200, stream, 128);
 
 	// Timbres
-	byte timbresNr = str->readByte();
+	const uint8 timbresNr = stream.readByte();
 	for (int i = 0; i < timbresNr; i++)
-		sendMt32SysEx(0x80000 + (i << 9), str, 246);
+		sendMt32SysEx(0x80000 + (i << 9), stream, 246);
 
-	uint16 flag = str->readUint16BE();
+	uint16 flag = stream.readUint16BE();
 
-	if (!str->eos() && (flag == 0xabcd)) {
+	if (!stream.eos() && flag == 0xabcd) {
 		// Patches 49-96
-		sendMt32SysEx(0x50300, str, 256);
-		sendMt32SysEx(0x50500, str, 128);
-		flag = str->readUint16BE();
+		sendMt32SysEx(0x50300, stream, 256);
+		sendMt32SysEx(0x50500, stream, 128);
+		flag = stream.readUint16BE();
 	}
 
-	if (!str->eos() && (flag == 0xdcba)) {
+	if (!stream.eos() && flag == 0xdcba) {
 		// Rhythm key map
-		sendMt32SysEx(0x30110, str, 256);
+		sendMt32SysEx(0x30110, stream, 256);
 		// Partial reserve
-		sendMt32SysEx(0x100004, str, 9);
+		sendMt32SysEx(0x100004, stream, 9);
 	}
 
 	// Reverb for SCI0
@@ -553,16 +556,15 @@ void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) {
 		setReverb(reverb);
 
 	// Send after-SysEx text
-	str->seek(0);
-	sendMt32SysEx(0x200000, str, 20);
+	stream.seek(0);
+	sendMt32SysEx(0x200000, stream, 20);
 
 	// Send the mystery SysEx
-	sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6);
-
-	delete str;
+	Common::MemoryReadStream mystery((const byte *)"\x16\x16\x16\x16\x16\x16", 6);
+	sendMt32SysEx(0x52000a, mystery, 6);
 }
 
-void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {
+void MidiPlayer_Midi::readMt32GmPatch(const SciSpan<const byte> &data) {
 	// GM patch contents:
 	// - 128 bytes patch map
 	// - 128 bytes key shift
@@ -573,21 +575,21 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {
 	// - 512 bytes velocity map
 	// --> total: 1153 bytes
 
-	memcpy(_patchMap, data, 128);
-	memcpy(_keyShift, data + 128, 128);
-	memcpy(_volAdjust, data + 256, 128);
-	memcpy(_percussionMap, data + 384, 128);
+	data.subspan(0, sizeof(_patchMap)).unsafeCopyDataTo(_patchMap);
+	data.subspan(128, sizeof(_keyShift)).unsafeCopyDataTo(_keyShift);
+	data.subspan(256, sizeof(_volAdjust)).unsafeCopyDataTo(_volAdjust);
+	data.subspan(384, sizeof(_percussionMap)).unsafeCopyDataTo(_percussionMap);
 	_channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[512];
-	memcpy(_velocityMapIdx, data + 513, 128);
-	memcpy(_velocityMap, data + 641, 512);
+	data.subspan(513, sizeof(_velocityMapIdx)).unsafeCopyDataTo(_velocityMapIdx);
+	data.subspan(641, sizeof(_velocityMap)).unsafeCopyDataTo(_velocityMap);
 
-	uint16 midiSize = READ_LE_UINT16(data + 1153);
+	uint16 midiSize = data.getUint16LEAt(1153);
 
 	if (midiSize > 0) {
-		if (size < midiSize + 1155)
+		if (data.size() < midiSize + 1155U)
 			error("Failed to read MIDI data");
 
-		const byte *midi = data + 1155;
+		const SciSpan<const byte> midi = data.subspan(1155, midiSize);
 		byte command = 0;
 		uint i = 0;
 
@@ -599,15 +601,16 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {
 
 			switch (command & 0xf0) {
 			case 0xf0: {
-				const byte *sysExEnd = (const byte *)memchr(midi + i, 0xf7, midiSize - i);
+				const byte *sysExStart = midi.getUnsafeDataAt(i, midiSize - i);
+				const byte *sysExEnd = (const byte *)memchr(sysExStart, 0xf7, midiSize - i);
 
 				if (!sysExEnd)
 					error("Failed to find end of sysEx");
 
-				int len = sysExEnd - (midi + i);
-				sysEx(midi + i, len);
+				int len = sysExEnd - sysExStart;
+				sysEx(sysExStart, len);
 
-				i += len + 1; // One more for the 0x7f
+				i += len + 1; // One more for the 0xf7
 				break;
 			}
 			case 0x80:
@@ -656,13 +659,13 @@ void MidiPlayer_Midi::readMt32DrvData() {
 			f.seek(-2, SEEK_CUR);
 
 		// Send before-SysEx text
-		sendMt32SysEx(0x200000, &f, 20);
+		sendMt32SysEx(0x200000, f, 20);
 
 		if (size != 2271) {
 			// Send after-SysEx text (SSCI sends this before every song).
 			// There aren't any SysEx calls in old drivers, so this can
 			// be sent right after the before-SysEx text.
-			sendMt32SysEx(0x200000, &f, 20);
+			sendMt32SysEx(0x200000, f, 20);
 		} else {
 			// Skip the after-SysEx text in the newer patch version, we'll send
 			// it after the SysEx messages are sent.
@@ -696,14 +699,14 @@ void MidiPlayer_Midi::readMt32DrvData() {
 			f.skip(2235);	// skip driver code
 
 			// Patches 1-48
-			sendMt32SysEx(0x50000, &f, 256);
-			sendMt32SysEx(0x50200, &f, 128);
+			sendMt32SysEx(0x50000, f, 256);
+			sendMt32SysEx(0x50200, f, 128);
 
 			setReverb(reverb);
 
 			// Send the after-SysEx text
 			f.seek(0x3d);
-			sendMt32SysEx(0x200000, &f, 20);
+			sendMt32SysEx(0x200000, f, 20);
 		} else {
 			byte reverbSysEx[13];
 			// This old driver should have a full reverb SysEx
@@ -775,11 +778,11 @@ uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) {
 		return Mt32Ins.gmInstr;
 }
 
-void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
+void MidiPlayer_Midi::mapMt32ToGm(const SciSpan<const byte> &data) {
 	// FIXME: Clean this up
 	int memtimbres, patches;
 	uint8 group, number, keyshift, /*finetune,*/ bender_range;
-	uint8 *patchpointer;
+	SciSpan<const byte> patchpointer;
 	uint32 pos;
 	int i;
 
@@ -791,10 +794,10 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
 	for (i = 0; i < 128; i++)
 		_percussionMap[i] = Mt32PresetRhythmKeymap[i];
 
-	memtimbres = *(data + 0x1eb);
+	memtimbres = data[0x1eb];
 	pos = 0x1ec + memtimbres * 0xf6;
 
-	if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) {
+	if (data.size() > pos && data.getUint16BEAt(pos) == 0xabcd) {
 		patches = 96;
 		pos += 2 + 8 * 48;
 	} else {
@@ -807,18 +810,18 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
 	debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches..");
 
 	for (i = 0; i < patches; i++) {
-		char name[11];
+		Common::String name;
 
 		if (i < 48)
-			patchpointer = data + 0x6b + 8 * i;
+			patchpointer = data.subspan(0x6b + 8 * i);
 		else
-			patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2;
+			patchpointer = data.subspan(0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2);
 
-		group = *patchpointer;
-		number = *(patchpointer + 1);
-		keyshift = *(patchpointer + 2);
-		//finetune = *(patchpointer + 3);
-		bender_range = *(patchpointer + 4);
+		group = patchpointer[0];
+		number = patchpointer[1];
+		keyshift = patchpointer[2];
+		//finetune = patchpointer[3];
+		bender_range = patchpointer[4];
 
 		debugCN(kDebugLevelSound, "  [%03d] ", i);
 
@@ -832,10 +835,9 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
 			break;
 		case 2:
 			if (number < memtimbres) {
-				strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10);
-				name[10] = 0;
-				_patchMap[i] = lookupGmInstrument(name);
-				debugCN(kDebugLevelSound, "%s -> ", name);
+				name = data.getStringAt(0x1ec + number * 0xf6, 10);
+				_patchMap[i] = lookupGmInstrument(name.c_str());
+				debugCN(kDebugLevelSound, "%s -> ", name.c_str());
 			} else {
 				_patchMap[i] = 0xff;
 				debugCN(kDebugLevelSound, "[Invalid]  -> ");
@@ -865,21 +867,19 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
 		_pitchBendRange[i] = CLIP<uint8>(bender_range, 0, 24);
 	}
 
-	if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) {
+	if (data.size() > pos && data.getUint16BEAt(pos) == 0xdcba) {
 		debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion..");
 
 		for (i = 0; i < 64; i++) {
-			number = *(data + pos + 4 * i + 2);
+			number = data[pos + 4 * i + 2];
 			byte ins = i + 24;
 
 			debugCN(kDebugLevelSound, "  [%03d] ", ins);
 
 			if (number < 64) {
-				char name[11];
-				strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10);
-				name[10] = 0;
-				debugCN(kDebugLevelSound, "%s -> ", name);
-				_percussionMap[ins] = lookupGmRhythmKey(name);
+				Common::String name = data.getStringAt(0x1ec + number * 0xf6, 10);
+				debugCN(kDebugLevelSound, "%s -> ", name.c_str());
+				_percussionMap[ins] = lookupGmRhythmKey(name.c_str());
 			} else {
 				if (number < 94) {
 					debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name);
@@ -897,17 +897,19 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
 				debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[ins]]);
 #endif
 
-			_percussionVelocityScale[ins] = *(data + pos + 4 * i + 3) * 127 / 100;
+			_percussionVelocityScale[ins] = data[pos + 4 * i + 3] * 127 / 100;
 		}
 	}
 }
 
 void MidiPlayer_Midi::setMt32Volume(byte volume) {
-	sendMt32SysEx(0x100016, &volume, 1);
+	Common::MemoryReadStream s(&volume, 1);
+	sendMt32SysEx(0x100016, s, 1);
 }
 
 void MidiPlayer_Midi::resetMt32() {
-	sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true);
+	Common::MemoryReadStream s((const byte *)"\x01\x00", 2);
+	sendMt32SysEx(0x7f0000, s, 2, true);
 
 	// This seems to require a longer delay than usual
 	g_system->delayMillis(150);
@@ -937,11 +939,11 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
 		_percussionVelocityScale[i] = 127;
 	}
 
-	Resource *res = NULL;
+	Resource *res = nullptr;
 
 	if (g_sci && g_sci->_features->useAltWinGMSound()) {
-		res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0);
-		if (!(res && isMt32GmPatch(res->data, res->size))) {
+		res = resMan->findResource(ResourceId(kResourceTypePatch, 4), false);
+		if (!res || !isMt32GmPatch(*res)) {
 			// Don't do any mapping when a Windows alternative track is selected
 			// and no MIDI patch is available
 			_useMT32Track = false;
@@ -953,15 +955,15 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
 		// MT-32
 		resetMt32();
 
-		res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+		res = resMan->findResource(ResourceId(kResourceTypePatch, 1), false);
 
 		if (res) {
-			if (isMt32GmPatch(res->data, res->size)) {
-				readMt32GmPatch(res->data, res->size);
+			if (isMt32GmPatch(*res)) {
+				readMt32GmPatch(*res);
 				// Note that _goodbyeMsg is not zero-terminated
 				memcpy(_goodbyeMsg, "      ScummVM       ", 20);
 			} else {
-				readMt32Patch(res->data, res->size);
+				readMt32Patch(*res);
 			}
 		} else {
 			// Early SCI0 games have the sound bank embedded in the MT-32 driver
@@ -969,22 +971,22 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
 		}
 	} else {
 		// General MIDI
-		res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0);
+		res = resMan->findResource(ResourceId(kResourceTypePatch, 4), false);
 
-		if (res && isMt32GmPatch(res->data, res->size)) {
+		if (res && isMt32GmPatch(*res)) {
 			// There is a GM patch
-			readMt32GmPatch(res->data, res->size);
+			readMt32GmPatch(*res);
 
 			if (g_sci && g_sci->_features->useAltWinGMSound()) {
 				// Always use the GM track if an alternative GM Windows soundtrack is selected
 				_useMT32Track = false;
 			} else {
 				// Detect the format of patch 1, so that we know what play mask to use
-				res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+				res = resMan->findResource(ResourceId(kResourceTypePatch, 1), false);
 				if (!res)
 					_useMT32Track = false;
 				else
-					_useMT32Track = !isMt32GmPatch(res->data, res->size);
+					_useMT32Track = !isMt32GmPatch(*res);
 
 				// Check if the songs themselves have a GM track
 				if (!_useMT32Track) {
@@ -1013,17 +1015,17 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
 				_velocityMap[3][i] = 0x20 + (i - 1) / 2;
 			}
 
-			res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
+			res = resMan->findResource(ResourceId(kResourceTypePatch, 1), false);
 
 			if (res) {
-				if (!isMt32GmPatch(res->data, res->size)) {
-					mapMt32ToGm(res->data, res->size);
+				if (!isMt32GmPatch(*res)) {
+					mapMt32ToGm(*res);
 				} else {
 					if (getSciVersion() < SCI_VERSION_3) {
 						error("MT-32 patch has wrong type");
 					} else {
 						// Happens in the SCI3 interactive demo of Lighthouse
-						warning("TODO: Ignoring new SCI3 type of MT-32 patch for now (size = %d)", res->size);
+						warning("TODO: Ignoring new SCI3 type of MT-32 patch for now (size = %lu)", res->size());
 					}
 				}
 			} else {
@@ -1053,7 +1055,7 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
 void MidiPlayer_Midi::close() {
 	if (_isMt32) {
 		// Send goodbye message
-		sendMt32SysEx(0x200000, _goodbyeMsg, 20, true);
+		sendMt32SysEx(0x200000, SciSpan<const byte>(_goodbyeMsg, 20), true);
 	}
 
 	_driver->close();
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 21f95f1..20688ca 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -129,7 +129,7 @@ byte MidiParser_SCI::midiGetNextChannel(long ticker) {
 		if (_track->channels[i].time == -1) // channel ended
 			continue;
 		SoundResource::Channel *curChannel = &_track->channels[i];
-		if (curChannel->curPos >= curChannel->size)
+		if (curChannel->curPos >= curChannel->data.size())
 			continue;
 		next = curChannel->data[curChannel->curPos]; // when the next event should occur
 		if (next == 0xF8) // 0xF8 means 240 ticks delay
@@ -151,7 +151,7 @@ byte *MidiParser_SCI::midiMixChannels() {
 		_track->channels[i].time = 0;
 		_track->channels[i].prev = 0;
 		_track->channels[i].curPos = 0;
-		totalSize += _track->channels[i].size;
+		totalSize += _track->channels[i].data.size();
 	}
 
 	byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data
@@ -228,9 +228,9 @@ byte *MidiParser_SCI::midiMixChannels() {
 // certain channels from that data.
 byte *MidiParser_SCI::midiFilterChannels(int channelMask) {
 	SoundResource::Channel *channel = &_track->channels[0];
-	byte *channelData = channel->data;
-	byte *channelDataEnd = channel->data + channel->size;
-	byte *outData = new byte[channel->size + 5];
+	SciSpan<const byte>::const_iterator channelData = channel->data.cbegin();
+	SciSpan<const byte>::const_iterator channelDataEnd = channel->data.cend();
+	byte *outData = new byte[channel->data.size() + 5];
 	byte curChannel = 15, curByte, curDelta;
 	byte command = 0, lastCommand = 0;
 	int delta = 0;
@@ -239,7 +239,7 @@ byte *MidiParser_SCI::midiFilterChannels(int channelMask) {
 
 	_mixedData = outData;
 
-	while (channelData < channelDataEnd) {
+	while (channelData != channelDataEnd) {
 		curDelta = *channelData++;
 		if (curDelta == 0xF8) {
 			delta += 240;
@@ -804,7 +804,7 @@ byte MidiParser_SCI::getSongReverb() {
 		for (int i = 0; i < _track->channelCount; i++) {
 			SoundResource::Channel &channel = _track->channels[i];
 			// Peek ahead in the control channel to get the default reverb setting
-			if (channel.number == 15 && channel.size >= 7)
+			if (channel.number == 15 && channel.data.size() >= 7)
 				return channel.data[6];
 		}
 	}
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 488bd03..f78f146 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -345,16 +345,16 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
 	if (track) {
 		// Play digital sample
 		if (track->digitalChannelNr != -1) {
-			byte *channelData = track->channels[track->digitalChannelNr].data;
+			const SciSpan<const byte> &channelData = track->channels[track->digitalChannelNr].data;
 			delete pSnd->pStreamAud;
 			byte flags = Audio::FLAG_UNSIGNED;
 			// Amiga SCI1 games had signed sound data
 			if (_soundVersion >= SCI_VERSION_1_EARLY && g_sci->getPlatform() == Common::kPlatformAmiga)
 				flags = 0;
 			int endPart = track->digitalSampleEnd > 0 ? (track->digitalSampleSize - track->digitalSampleEnd) : 0;
-			pSnd->pStreamAud = Audio::makeRawStream(channelData + track->digitalSampleStart,
-								track->digitalSampleSize - track->digitalSampleStart - endPart,
-								track->digitalSampleRate, flags, DisposeAfterUse::NO);
+			const uint size = track->digitalSampleSize - track->digitalSampleStart - endPart;
+			pSnd->pStreamAud = Audio::makeRawStream(channelData.getUnsafeDataAt(track->digitalSampleStart),
+								size, track->digitalSampleRate, flags, DisposeAfterUse::NO);
 			assert(pSnd->pStreamAud);
 			delete pSnd->pLoopStream;
 			pSnd->pLoopStream = 0;
diff --git a/engines/sci/sound/sync.cpp b/engines/sci/sound/sync.cpp
index 4e75dab..90567e8 100644
--- a/engines/sci/sound/sync.cpp
+++ b/engines/sci/sound/sync.cpp
@@ -50,14 +50,14 @@ void Sync::start(const ResourceId id, const reg_t syncObjAddr) {
 }
 
 void Sync::next(const reg_t syncObjAddr) {
-	if (_resource && (_offset < _resource->size - 1)) {
+	if (_resource && (_offset < _resource->size() - 1)) {
 		int16 syncCue = -1;
-		int16 syncTime = (int16)READ_SCI11ENDIAN_UINT16(_resource->data + _offset);
+		int16 syncTime = _resource->getInt16SEAt(_offset);
 
 		_offset += 2;
 
-		if ((syncTime != -1) && (_offset < _resource->size - 1)) {
-			syncCue = (int16)READ_SCI11ENDIAN_UINT16(_resource->data + _offset);
+		if ((syncTime != -1) && (_offset < _resource->size() - 1)) {
+			syncCue = _resource->getInt16SEAt(_offset);
 			_offset += 2;
 		}
 
diff --git a/engines/sci/util.cpp b/engines/sci/util.cpp
index ccec41a..095c462 100644
--- a/engines/sci/util.cpp
+++ b/engines/sci/util.cpp
@@ -20,8 +20,6 @@
  *
  */
 
-#include "common/endian.h"
-
 #include "sci/util.h"
 #include "sci/sci.h"
 
diff --git a/engines/sci/util.h b/engines/sci/util.h
index b0fee51..e8b60ad 100644
--- a/engines/sci/util.h
+++ b/engines/sci/util.h
@@ -23,7 +23,13 @@
 #ifndef SCI_UTIL_H
 #define SCI_UTIL_H
 
+#include "common/span.h"
+#include "common/endian.h"
+#include "common/file.h"
+#include "common/memstream.h"
 #include "common/scummsys.h"
+#include "common/str.h"
+#include "common/textconsole.h"
 
 namespace Sci {
 
@@ -40,11 +46,260 @@ void WRITE_SCI11ENDIAN_UINT16(void *ptr, uint16 val);
 #ifdef ENABLE_SCI32
 void WRITE_SCI11ENDIAN_UINT32(void *ptr, uint32 val);
 #endif
-
 // Wrappers for reading integer values in resources that are
 // LE in SCI1.1 Mac, but BE in SCI32 Mac
 uint16 READ_SCI32ENDIAN_UINT16(const void *ptr);
 
+#pragma mark -
+#pragma mark SciSpanImpl - SciSpanIterator
+
+namespace SciSpanInternal {
+	template <typename Span, bool IsConst>
+	class SciSpanIterator : public Common::SpanInternal::SpanIterator<Span, IsConst> {
+		typedef typename Common::SpanInternal::SpanIterator<Span, IsConst> super_type;
+		typedef typename Span::value_type span_value_type;
+		typedef typename Common::Conditional<IsConst, const Span, Span>::type span_type;
+
+	public:
+		typedef typename Span::difference_type difference_type;
+		typedef typename Common::RemoveConst<span_value_type>::type value_type;
+		typedef typename Common::Conditional<IsConst, const span_value_type, span_value_type>::type *pointer;
+		typedef typename Common::Conditional<IsConst, const span_value_type, span_value_type>::type &reference;
+
+		inline SciSpanIterator() : super_type() {}
+
+		inline SciSpanIterator(span_type *span, const difference_type index) : super_type(span, index) {}
+
+		inline SciSpanIterator(const SciSpanIterator &other) : super_type(other) {}
+
+		inline SciSpanIterator &operator=(const SciSpanIterator &other) {
+			super_type::operator=(other);
+			return *this;
+		}
+
+		inline SciSpanIterator operator+(const difference_type delta) const {
+			SciSpanIterator it(*this);
+			it += delta;
+			return it;
+		}
+
+		inline SciSpanIterator operator-(const difference_type delta) const {
+			return operator+(-delta);
+		}
+
+		inline difference_type operator-(const SciSpanIterator &other) const {
+			assert(this->_span == other._span);
+			return this->_index - other._index;
+		}
+
+		inline int16 getInt16SE() const {
+			return this->_span->getInt16SEAt(this->_index);
+		}
+
+		inline uint16 getUint16SE() const {
+			return this->_span->getUint16SEAt(this->_index);
+		}
+
+		inline uint16 getUint16SE32() const {
+			return this->_span->getUint16SE32At(this->_index);
+		}
+
+		inline int32 getInt32SE() const {
+			return this->_span->getInt32SEAt(this->_index);
+		}
+
+		inline uint32 getUint32SE() const {
+			return this->_span->getUint32SEAt(this->_index);
+		}
+
+		inline void setUint16SE(uint16 value) const {
+			this->_span->setUint16SEAt(this->_index, value);
+		}
+
+#ifdef ENABLE_SCI32
+		inline void setUint32SE(uint32 value) const {
+			this->_span->setUint32SEAt(this->_index, value);
+		}
+#endif
+	};
+} // End of namespace SciSpanInternal
+
+/**
+ * Bounds-checked random access into a contiguous block of memory, with
+ * extra methods that automatically read and write using the correct endianness
+ * for the currently loaded game.
+ */
+template <typename ValueType, template <typename> class Derived>
+class SciSpanImpl : public Common::NamedSpanImpl<ValueType, Derived> {
+	typedef Common::NamedSpanImpl<ValueType, Derived> super_type;
+	typedef Derived<ValueType> derived_type;
+	typedef typename Common::AddConst<Derived<ValueType> >::type const_derived_type;
+	typedef typename Common::RemoveConst<Derived<ValueType> >::type mutable_derived_type;
+
+#if !defined(__GNUC__) || GCC_ATLEAST(3, 0)
+	template <typename T, template <typename> class U> friend class SciSpanImpl;
+#endif
+#if CXXTEST_RUNNING
+	friend class ::SpanTestSuite;
+#endif
+
+public:
+	typedef typename super_type::value_type value_type;
+	typedef typename super_type::difference_type difference_type;
+	typedef typename super_type::index_type index_type;
+	typedef typename super_type::size_type size_type;
+	typedef SciSpanInternal::SciSpanIterator<derived_type, true> const_iterator;
+	typedef SciSpanInternal::SciSpanIterator<derived_type, false> iterator;
+	typedef typename super_type::pointer pointer;
+	typedef typename super_type::const_pointer const_pointer;
+	typedef typename super_type::reference reference;
+	typedef typename super_type::const_reference const_reference;
+
+	inline SciSpanImpl() : super_type() {}
+
+	inline SciSpanImpl(const pointer data_,
+					   const size_type size_,
+					   const Common::String &name = Common::String(),
+					   const size_type sourceByteOffset = 0) :
+		super_type(data_, size_, name, sourceByteOffset) {}
+
+	template <typename Other>
+	inline SciSpanImpl(const Other &other) :
+		super_type(other) {}
+
+	template <typename Other>
+	inline mutable_derived_type &operator=(const Other &other) {
+		super_type::operator=(other);
+		return this->impl();
+	}
+
+	inline ~SciSpanImpl() {}
+
+	inline const_iterator cbegin() const { return const_iterator(&this->impl(), 0); }
+	inline const_iterator cend() const { return const_iterator(&this->impl(), this->size()); }
+	inline const_iterator begin() const { return const_iterator(&this->impl(), 0); }
+	inline const_iterator end() const { return const_iterator(&this->impl(), this->size()); }
+	inline iterator begin() { return iterator(&this->impl(), 0); }
+	inline iterator end() { return iterator(&this->impl(), this->size()); }
+
+#pragma mark -
+#pragma mark SciSpan - Data access
+
+public:
+	inline int16 getInt16SEAt(const size_type index) const {
+		return (int16)getUint16SEAt(index);
+	}
+
+	inline uint16 getUint16SEAt(const size_type index) const {
+		this->validate(index, sizeof(uint16));
+		return READ_SCI11ENDIAN_UINT16(this->data() + index);
+	}
+
+	inline uint16 getUint16SE32At(const size_type index) const {
+		this->validate(index, sizeof(uint16));
+		return READ_SCI32ENDIAN_UINT16(this->data() + index);
+	}
+
+	inline int32 getInt32SEAt(const size_type index) const {
+		return (int32)getUint32SEAt(index);
+	}
+
+	inline uint32 getUint32SEAt(const size_type index) const {
+		this->validate(index, sizeof(uint32));
+		return READ_SCI11ENDIAN_UINT32(this->data() + index);
+	}
+
+	inline void setUint16SEAt(const size_type index, uint16 value) {
+		this->validate(index, sizeof(uint16), Common::kValidateWrite);
+		WRITE_SCI11ENDIAN_UINT16(this->data() + index, value);
+	}
+
+#ifdef ENABLE_SCI32
+	inline void setUint32SEAt(const size_type index, uint32 value) {
+		this->validate(index, sizeof(uint32), Common::kValidateWrite);
+		WRITE_SCI11ENDIAN_UINT32(this->data() + index, value);
+	}
+#endif
+
+#pragma mark -
+#pragma mark SciSpanImpl - ForwardIterator
+
+// Spans that are used as ForwardIterators must not be allowed inside of
+// SpanOwner, since this will result in the wrong pointer to memory to be
+// deleted
+public:
+	inline const_reference operator*() const {
+		this->validate(0, sizeof(value_type));
+		return *this->_data;
+	}
+
+	inline reference operator*() {
+		this->validate(0, sizeof(value_type));
+		return *this->_data;
+	}
+
+	inline mutable_derived_type &operator+=(const difference_type delta) {
+		this->validate(0, delta * sizeof(value_type), Common::kValidateSeek);
+		this->_data += delta;
+		this->_size -= delta;
+		return this->impl();
+	}
+
+	inline mutable_derived_type &operator++() {
+		return operator+=(1);
+	}
+
+	inline mutable_derived_type operator++(int) {
+		mutable_derived_type span(this->impl());
+		operator+=(1);
+		return span;
+	}
+
+	inline mutable_derived_type operator+(const difference_type delta) const {
+		mutable_derived_type span(this->impl());
+		span += delta;
+		return span;
+	}
+};
+
+template <typename ValueType>
+class SciSpan : public SciSpanImpl<ValueType, SciSpan> {
+	typedef SciSpanImpl<ValueType, ::Sci::SciSpan> super_type;
+	typedef typename Common::AddConst<SciSpan<ValueType> >::type const_derived_type;
+	typedef typename Common::RemoveConst<SciSpan<ValueType> >::type mutable_derived_type;
+
+public:
+	typedef typename super_type::value_type value_type;
+	typedef typename super_type::difference_type difference_type;
+	typedef typename super_type::index_type index_type;
+	typedef typename super_type::size_type size_type;
+	typedef typename super_type::const_iterator const_iterator;
+	typedef typename super_type::iterator iterator;
+	typedef typename super_type::pointer pointer;
+	typedef typename super_type::const_pointer const_pointer;
+	typedef typename super_type::reference reference;
+	typedef typename super_type::const_reference const_reference;
+
+	inline SciSpan() : super_type() {}
+
+	inline SciSpan(const pointer data_,
+				   const size_type size_,
+				   const Common::String &name = Common::String(),
+				   const size_type sourceByteOffset = 0) :
+		super_type(data_, size_, name, sourceByteOffset) {}
+
+	template <typename Other>
+	inline SciSpan(const Other &other) : super_type(other) {}
+
+	template <typename Other>
+	inline mutable_derived_type &operator=(const Other &other) {
+		super_type::operator=(other);
+		return this->impl();
+	}
+
+	~SciSpan() {}
+};
+
 } // End of namespace Sci
 
 #endif


Commit: 6600fb77eabcfcea540dd4220c8b5b20dd70d759
    https://github.com/scummvm/scummvm/commit/6600fb77eabcfcea540dd4220c8b5b20dd70d759
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-03-27T19:42:31-05:00

Commit Message:
SCI: Use containers in GfxView and remove some SCI32 code

Changed paths:
    engines/sci/graphics/view.cpp
    engines/sci/graphics/view.h


diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 2dc7775..ca029a7 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -110,13 +110,13 @@ void GfxView::initData(GuiResourceId resourceId) {
 	uint32 palOffset = 0;
 	uint16 headerSize = 0;
 	uint16 loopSize = 0, celSize = 0;
-	int loopNo, celNo, EGAmapNr;
+	uint loopNo, celNo, EGAmapNr;
 	byte seekEntry;
 	bool isEGA = false;
 	bool isCompressed = true;
 	ViewType curViewType = _resMan->getViewType();
 
-	_loopCount = 0;
+	_loop.resize(0);
 	_embeddedPal = false;
 	_EGAmapping.clear();
 	_sci2ScaleRes = SCI_VIEW_NATIVERES_NONE;
@@ -146,7 +146,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 	case kViewVga: // View-format SCI1
 		// LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
 
-		_loopCount = _resource->getUint8At(0);
+		_loop.resize(_resource->getUint8At(0));
 		// bit 0x8000 of _resourceData[1] means palette is set
 		if (_resource->getUint8At(1) & 0x40)
 			isCompressed = false;
@@ -183,18 +183,16 @@ void GfxView::initData(GuiResourceId resourceId) {
 			}
 		}
 
-		_loop.resize(_loopCount);
-		for (loopNo = 0; loopNo < _loopCount; loopNo++) {
+		for (loopNo = 0; loopNo < _loop.size(); loopNo++) {
 			loopData = _resource->subspan(_resource->getUint16LEAt(8 + loopNo * 2));
 			// CelCount:WORD Unknown:WORD CelOffset0:WORD CelOffset1:WORD...
 
 			celCount = loopData.getUint16LEAt(0);
-			_loop[loopNo].celCount = celCount;
+			_loop[loopNo].cel.resize(celCount);
 			_loop[loopNo].mirrorFlag = mirrorBits & 1 ? true : false;
 			mirrorBits >>= 1;
 
 			// read cel info
-			_loop[loopNo].cel.resize(celCount);
 			for (celNo = 0; celNo < celCount; celNo++) {
 				celOffset = loopData.getUint16LEAt(4 + celNo * 2);
 				celData = _resource->subspan(celOffset);
@@ -244,21 +242,14 @@ void GfxView::initData(GuiResourceId resourceId) {
 		}
 		break;
 
-	case kViewVga11: // View-format SCI1.1+
+	case kViewVga11: { // View-format SCI1.1+
 		// HeaderSize:WORD LoopCount:BYTE Flags:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
 		headerSize = _resource->getUint16SEAt(0) + 2; // headerSize is not part of the header, so it's added
 		assert(headerSize >= 16);
-		_loopCount = _resource->getUint8At(2);
-		assert(_loopCount);
+		const uint8 loopCount = _resource->getUint8At(2);
+		assert(loopCount);
 		palOffset = _resource->getUint32SEAt(8);
 
-		// For SCI32, this is a scale flag
-		if (getSciVersion() >= SCI_VERSION_2) {
-			_sci2ScaleRes = (Sci32ViewNativeResolution)_resource->getUint8At(5);
-			if (_screen->getUpscaledHires() == GFX_SCREEN_UPSCALED_DISABLED)
-				_sci2ScaleRes = SCI_VIEW_NATIVERES_NONE;
-		}
-
 		// flags is actually a bit-mask
 		//  it seems it was only used for some early sci1.1 games (or even just laura bow 2)
 		//  later interpreters dont support it at all anymore
@@ -289,13 +280,13 @@ void GfxView::initData(GuiResourceId resourceId) {
 			_embeddedPal = true;
 		}
 
-		_loop.resize(_loopCount);
-		for (loopNo = 0; loopNo < _loopCount; loopNo++) {
+		_loop.resize(loopCount);
+		for (loopNo = 0; loopNo < loopCount; loopNo++) {
 			loopData = _resource->subspan(headerSize + (loopNo * loopSize));
 
 			seekEntry = loopData[0];
 			if (seekEntry != 255) {
-				if (seekEntry >= _loopCount)
+				if (seekEntry >= loopCount)
 					error("Bad loop-pointer in sci 1.1 view");
 				_loop[loopNo].mirrorFlag = true;
 				loopData = _resource->subspan(headerSize + (seekEntry * loopSize));
@@ -304,12 +295,11 @@ void GfxView::initData(GuiResourceId resourceId) {
 			}
 
 			celCount = loopData[2];
-			_loop[loopNo].celCount = celCount;
+			_loop[loopNo].cel.resize(celCount);
 
 			const uint32 celDataOffset = loopData.getUint32SEAt(12);
 
 			// read cel info
-			_loop[loopNo].cel.resize(celCount);
 			for (celNo = 0; celNo < celCount; celNo++) {
 				celData = _resource->subspan(celDataOffset + celNo * celSize, celSize);
 
@@ -338,6 +328,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 			}
 		}
 		break;
+	}
 
 	default:
 		error("ViewType was not detected, can't continue");
@@ -351,7 +342,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 		// View 995, Loop 13, Cel 0 = "TEXT"
 		// View 995, Loop 13, Cel 1 = "SPEECH"
 		// View 995, Loop 13, Cel 2 = "DUAL" (<- our injected view)
-		if ((g_sci->isCD()) && (resourceId == 995)) {
+		if (g_sci->isCD() && resourceId == 995) {
 			// security checks
 			if (_loop.size() >= 14 &&
 				_loop[13].cel.size() == 2 &&
@@ -373,7 +364,7 @@ void GfxView::initData(GuiResourceId resourceId) {
 		// View 947, Loop 9, Cel 1 = "TEXT" (pressed)
 		// View 947, Loop 12, Cel 0 = "DUAL" (not pressed) (<- our injected view)
 		// View 947, Loop 12, Cel 1 = "DUAL" (pressed) (<- our injected view)
-		if ((g_sci->isCD()) && (resourceId == 947)) {
+		if (g_sci->isCD() && resourceId == 947) {
 			// security checks
 			if (_loop.size() == 12 &&
 				_loop[8].cel.size() == 2 &&
@@ -400,24 +391,24 @@ GuiResourceId GfxView::getResourceId() const {
 }
 
 int16 GfxView::getWidth(int16 loopNo, int16 celNo) const {
-	return _loopCount ? getCelInfo(loopNo, celNo)->width : 0;
+	return _loop.size() ? getCelInfo(loopNo, celNo)->width : 0;
 }
 
 int16 GfxView::getHeight(int16 loopNo, int16 celNo) const {
-	return _loopCount ? getCelInfo(loopNo, celNo)->height : 0;
+	return _loop.size() ? getCelInfo(loopNo, celNo)->height : 0;
 }
 
 const CelInfo *GfxView::getCelInfo(int16 loopNo, int16 celNo) const {
-	assert(_loopCount);
-	loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
-	celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
+	assert(_loop.size());
+	loopNo = CLIP<int16>(loopNo, 0, _loop.size() - 1);
+	celNo = CLIP<int16>(celNo, 0, _loop[loopNo].cel.size() - 1);
 	return &_loop[loopNo].cel[celNo];
 }
 
 uint16 GfxView::getCelCount(int16 loopNo) const {
-	assert(_loopCount);
-	loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
-	return _loop[loopNo].celCount;
+	assert(_loop.size());
+	loopNo = CLIP<int16>(loopNo, 0, _loop.size() - 1);
+	return _loop[loopNo].cel.size();
 }
 
 Palette *GfxView::getPalette() {
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 808e203..d4ec167 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -48,7 +48,6 @@ struct CelInfo {
 
 struct LoopInfo {
 	bool mirrorFlag;
-	uint16 celCount;
 	Common::Array<CelInfo> cel;
 };
 
@@ -80,7 +79,7 @@ public:
 	const SciSpan<const byte> &getBitmap(int16 loopNo, int16 celNo);
 	void draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
 	void drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY);
-	uint16 getLoopCount() const { return _loopCount; }
+	uint16 getLoopCount() const { return _loop.size(); }
 	uint16 getCelCount(int16 loopNo) const;
 	Palette *getPalette();
 
@@ -103,7 +102,6 @@ private:
 	GuiResourceId _resourceId;
 	Resource *_resource;
 
-	uint16 _loopCount;
 	Common::Array<LoopInfo> _loop;
 	bool _embeddedPal;
 	Palette _viewPalette;


Commit: 816296fd258841977cecdf7507d6a567693ecbd9
    https://github.com/scummvm/scummvm/commit/816296fd258841977cecdf7507d6a567693ecbd9
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-03-27T19:42:31-05:00

Commit Message:
SCI: Remove unused boilerplate from SciSpan

Changed paths:
    engines/sci/util.h


diff --git a/engines/sci/util.h b/engines/sci/util.h
index e8b60ad..1a054e1 100644
--- a/engines/sci/util.h
+++ b/engines/sci/util.h
@@ -133,8 +133,6 @@ template <typename ValueType, template <typename> class Derived>
 class SciSpanImpl : public Common::NamedSpanImpl<ValueType, Derived> {
 	typedef Common::NamedSpanImpl<ValueType, Derived> super_type;
 	typedef Derived<ValueType> derived_type;
-	typedef typename Common::AddConst<Derived<ValueType> >::type const_derived_type;
-	typedef typename Common::RemoveConst<Derived<ValueType> >::type mutable_derived_type;
 
 #if !defined(__GNUC__) || GCC_ATLEAST(3, 0)
 	template <typename T, template <typename> class U> friend class SciSpanImpl;
@@ -164,16 +162,7 @@ public:
 		super_type(data_, size_, name, sourceByteOffset) {}
 
 	template <typename Other>
-	inline SciSpanImpl(const Other &other) :
-		super_type(other) {}
-
-	template <typename Other>
-	inline mutable_derived_type &operator=(const Other &other) {
-		super_type::operator=(other);
-		return this->impl();
-	}
-
-	inline ~SciSpanImpl() {}
+	inline SciSpanImpl(const Other &other) : super_type(other) {}
 
 	inline const_iterator cbegin() const { return const_iterator(&this->impl(), 0); }
 	inline const_iterator cend() const { return const_iterator(&this->impl(), this->size()); }
@@ -227,6 +216,9 @@ public:
 // Spans that are used as ForwardIterators must not be allowed inside of
 // SpanOwner, since this will result in the wrong pointer to memory to be
 // deleted
+private:
+	typedef typename Common::RemoveConst<Derived<ValueType> >::type mutable_derived_type;
+
 public:
 	inline const_reference operator*() const {
 		this->validate(0, sizeof(value_type));
@@ -265,20 +257,9 @@ public:
 template <typename ValueType>
 class SciSpan : public SciSpanImpl<ValueType, SciSpan> {
 	typedef SciSpanImpl<ValueType, ::Sci::SciSpan> super_type;
-	typedef typename Common::AddConst<SciSpan<ValueType> >::type const_derived_type;
-	typedef typename Common::RemoveConst<SciSpan<ValueType> >::type mutable_derived_type;
 
 public:
-	typedef typename super_type::value_type value_type;
-	typedef typename super_type::difference_type difference_type;
-	typedef typename super_type::index_type index_type;
-	typedef typename super_type::size_type size_type;
-	typedef typename super_type::const_iterator const_iterator;
-	typedef typename super_type::iterator iterator;
-	typedef typename super_type::pointer pointer;
-	typedef typename super_type::const_pointer const_pointer;
-	typedef typename super_type::reference reference;
-	typedef typename super_type::const_reference const_reference;
+	COMMON_SPAN_TYPEDEFS
 
 	inline SciSpan() : super_type() {}
 
@@ -290,14 +271,6 @@ public:
 
 	template <typename Other>
 	inline SciSpan(const Other &other) : super_type(other) {}
-
-	template <typename Other>
-	inline mutable_derived_type &operator=(const Other &other) {
-		super_type::operator=(other);
-		return this->impl();
-	}
-
-	~SciSpan() {}
 };
 
 } // End of namespace Sci


Commit: 323e74bda97716cc34ba99a0a5534e2d87845ce9
    https://github.com/scummvm/scummvm/commit/323e74bda97716cc34ba99a0a5534e2d87845ce9
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-03-27T19:42:31-05:00

Commit Message:
SCI: Simplify calculation of locals size in SCI0/1

Changed paths:
    engines/sci/engine/script.cpp


diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index f790b41..e1ab6ea 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -184,7 +184,7 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
 		if (localsTable) {
 			// skip header (4 bytes)
 			_localsOffset = localsTable - *_buf + 4;
-			_localsCount = (_buf->getUint16LEAt(_localsOffset - 2) - 4) >> 1; // half block size
+			_localsCount = localsTable.size() / 2 - 2;
 		}
 	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
 		_numExports = _buf->getUint16SEAt(kSci11NumExportsOffset);


Commit: 532828c143e0872525dadfe8b0ebe804f7071633
    https://github.com/scummvm/scummvm/commit/532828c143e0872525dadfe8b0ebe804f7071633
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-03-27T19:42:31-05:00

Commit Message:
SCI: Remove old SCI32 check from SCI16 drawing code

Changed paths:
    engines/sci/graphics/view.cpp


diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index ca029a7..91303be 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -912,11 +912,6 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
 	const int16 offsetY = clipRect.top - rect.top;
 	const int16 offsetX = clipRect.left - rect.left;
 
-	// TODO: Remove? This class is not for SCI32
-	// Happens in SQ6, first room
-	if (offsetX < 0 || offsetY < 0)
-		return;
-
 	assert(scaledHeight + offsetY <= ARRAYSIZE(scalingY));
 	assert(scaledWidth + offsetX <= ARRAYSIZE(scalingX));
 	const byte *bitmapData = bitmap.getUnsafeDataAt(0, celWidth * celHeight);





More information about the Scummvm-git-logs mailing list