From 31daa956d62b39429cb6638ed3fb549ac488833a Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Sat, 31 Dec 2016 20:39:57 -0600 Subject: SCI: Implement bounds-checked reads of game resources --- engines/sci/console.cpp | 81 ++--- engines/sci/detection.cpp | 16 +- engines/sci/engine/features.cpp | 2 +- engines/sci/engine/kernel.cpp | 24 +- engines/sci/engine/kernel.h | 4 +- engines/sci/engine/kparse.cpp | 6 +- engines/sci/engine/kvideo.cpp | 14 +- engines/sci/engine/message.cpp | 74 +++-- engines/sci/engine/object.cpp | 65 ++-- engines/sci/engine/object.h | 73 +++-- engines/sci/engine/savegame.cpp | 27 +- engines/sci/engine/script.cpp | 533 ++++++++++++++++----------------- engines/sci/engine/script.h | 49 ++- engines/sci/engine/script_patches.cpp | 34 +-- engines/sci/engine/script_patches.h | 10 +- engines/sci/engine/scriptdebug.cpp | 140 +++++---- engines/sci/engine/seg_manager.cpp | 8 +- engines/sci/engine/workarounds.cpp | 2 +- engines/sci/graphics/animate.cpp | 2 +- engines/sci/graphics/celobj32.cpp | 164 +++++----- engines/sci/graphics/celobj32.h | 11 +- engines/sci/graphics/compare.cpp | 2 +- engines/sci/graphics/cursor.cpp | 63 ++-- engines/sci/graphics/cursor.h | 12 +- engines/sci/graphics/cursor32.cpp | 2 +- engines/sci/graphics/font.cpp | 50 +++- engines/sci/graphics/font.h | 15 +- engines/sci/graphics/maciconbar.cpp | 4 +- engines/sci/graphics/palette.cpp | 30 +- engines/sci/graphics/palette.h | 5 +- engines/sci/graphics/palette32.cpp | 30 +- engines/sci/graphics/palette32.h | 8 +- engines/sci/graphics/picture.cpp | 226 ++++---------- engines/sci/graphics/picture.h | 26 +- engines/sci/graphics/portrait.cpp | 118 ++++---- engines/sci/graphics/portrait.h | 19 +- engines/sci/graphics/ports.cpp | 12 +- engines/sci/graphics/ports.h | 4 +- engines/sci/graphics/screen.cpp | 37 +-- engines/sci/graphics/screen.h | 13 +- engines/sci/graphics/screen_item32.cpp | 10 +- engines/sci/graphics/view.cpp | 268 ++++++++--------- engines/sci/graphics/view.h | 24 +- engines/sci/parser/vocabulary.cpp | 92 +++--- engines/sci/parser/vocabulary.h | 3 +- engines/sci/resource.cpp | 257 ++++++++-------- engines/sci/resource.h | 19 +- engines/sci/resource_audio.cpp | 181 +++++------ engines/sci/sci.cpp | 6 +- engines/sci/sound/audio.cpp | 32 +- engines/sci/sound/drivers/adlib.cpp | 61 ++-- engines/sci/sound/drivers/amigamac.cpp | 2 +- engines/sci/sound/drivers/cms.cpp | 20 +- engines/sci/sound/drivers/fb01.cpp | 39 ++- engines/sci/sound/drivers/fmtowns.cpp | 20 +- engines/sci/sound/drivers/midi.cpp | 248 +++++++-------- engines/sci/sound/midiparser_sci.cpp | 14 +- engines/sci/sound/music.cpp | 8 +- engines/sci/sound/sync.cpp | 8 +- engines/sci/util.cpp | 2 - engines/sci/util.h | 257 +++++++++++++++- 61 files changed, 1888 insertions(+), 1698 deletions(-) (limited to 'engines/sci') diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index ae8ab14d84..eed0be1a82 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(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(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 data = resource->subspan(0); uint32 first = atoi(argv[2]); uint32 last = atoi(argv[3]); Common::Array 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(first, resource->size() / 2 - 2); + last = MIN(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 5270398899..13b3326c08 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 e37a1651ef..40d380195d 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 c7732c6b15..d029923d96 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 335fec06ad..51f4b5dbcb 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 &script, SciSpan object); + void dumpScriptClass(const SciSpan &script, SciSpan clazz); SelectorCache _selectorCache; /**< Shortcut list for important selectors. */ typedef Common::Array KernelFunctionArray; diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index f85f33e3e8..d3bf2d72e9 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 &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 11378d7647..3d689f2b42 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 > 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 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 5e07ead5d7..c30ad3aee4 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 &data, uint headerSize, uint recordSize) + : _data(data), _headerSize(headerSize), _recordSize(recordSize), _messageCount(0) { } - const byte *_data; - const uint _size; + const SciSpan _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 &data) : MessageReader(data, 6, 4) { } bool findRecord(const MessageTuple &tuple, MessageRecord &record) { - const byte *recordPtr = _data + _headerSize; + SciSpan 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 &data) : MessageReader(data, 8, 10) { } bool findRecord(const MessageTuple &tuple, MessageRecord &record) { - const byte *recordPtr = _data + _headerSize; - + SciSpan 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 &data) : MessageReader(data, 10, 11) { } bool findRecord(const MessageTuple &tuple, MessageRecord &record) { - const byte *recordPtr = _data + _headerSize; - + SciSpan 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 &data) : MessageReader(data, 10, 12) { } bool findRecord(const MessageTuple &tuple, MessageRecord &record) { - const byte *recordPtr = _data + _headerSize; - + SciSpan 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 0566d6955f..2a6c96664b 100644 --- a/engines/sci/engine/object.cpp +++ b/engines/sci/engine/object.cpp @@ -51,24 +51,25 @@ static bool relocateBlock(Common::Array &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 &buf, reg_t obj_pos, bool initVariables) { + const SciSpan 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 header = buf.subspan(obj_pos.getOffset() - kOffsetHeaderSize); + _variables.resize(header.getUint16LEAt(kOffsetHeaderSelectorCounter)); + _baseVars = _baseObj.subspan(_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(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 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(0); } else if (getSciVersion() == SCI_VERSION_3) { varnum = _variables.size(); - buf = (const byte *)_baseVars; + buf = _baseVars.subspan(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 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(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 &buf) { + const SciSpan groupInfo = _baseObj.subspan(16); + const SciSpan 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 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 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(propertyIds, properties); _methodCount = methods; _propertyOffsetsSci3 = propertyOffsets; //_methodOffsetsSci3 = methodOffsets; diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h index 74a908a810..61f942c04a 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(_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(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 &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(); _baseMethod = obj ? obj->_baseMethod : Common::Array(); - _baseVars = obj ? obj->_baseVars : NULL; + _baseVars = obj ? obj->_baseVars : SciSpan(); } 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 &ptr) { _baseObj = ptr; } bool mustSetViewVisibleSci3(int selector) const { return _mustSetViewVisible[selector/32]; } private: - void initSelectorsSci3(const byte *buf); + void initSelectorsSci3(const SciSpan &buf); - const byte *_baseObj; /**< base + object offset within base */ - const uint16 *_baseVars; /**< Pointer to the varselector area for this object */ + SciSpan _baseObj; /**< base + object offset within base */ + SciSpan _baseVars; /**< Pointer to the varselector area for this object */ Common::Array _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 _variables; uint16 _methodCount; diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index a3a690be59..f05fdc5cb9 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(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 buf = (SciSpan &)*_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 buf = _heap.subspan(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 8a973bd217..f790b411cf 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 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 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 exportTable = findBlockSCI0(SCI_OBJ_EXPORTS).subspan(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 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 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(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(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 scriptDataPtr; + SciSpan stringStartPtr; + SciSpan 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 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 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 Script::getSci3ObjectsPointer() { + SciSpan 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 &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 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 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 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 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 secondExportTable = findBlockSCI0(SCI_OBJ_EXPORTS, 0).subspan(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 Script::findBlockSCI0(ScriptObjectTypes type, int startBlockIndex) { + SciSpan 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(); } // 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(_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 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 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 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 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 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 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 Script::listAllDeallocatable(SegmentId segId) const { Common::Array Script::listAllOutgoingReferences(reg_t addr) const { Common::Array 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 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 677b367051..52b58eec2e 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 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 > _buf; /**< Static data buffer, or NULL if not used */ + SciSpan _script; /**< Script size includes alignment byte */ + SciSpan _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 _exports; /**< Exports block or 0 if not present */ + uint16 _numExports; /**< Number of export entries */ + SciSpan _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 &getSynonyms() const { return _synonyms; } /** * Retrieves the number of synonyms associated with this script. @@ -243,19 +242,11 @@ public: return _markedAsDeleted; } - /** - * 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 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 getSci3ObjectsPointer(); /** * Initializes the script's objects (SCI0) diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index cf3a981347..d84d2ab780 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 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 &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 &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 &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 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 b5797be847..69f9794764 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 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 &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 &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 &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 scriptData, int32 signatureOffset); Selector *_selectorIdTable; SciScriptPatcherRuntimeEntry *_runtimeTable; diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 6002cbd8e4..d15cf83b71 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 &script, SciSpan 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)) : ""); + debugN("Name: %s\n", namepos ? script.getStringAt(namepos).c_str() : ""); 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 &script, SciSpan 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) : ""); + debugN("Name: %s\n", namepos ? script.getStringAt(namepos).c_str() : ""); 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("\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(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 9ccd1098d3..3157c84f85 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 ed913b27eb..84211fd432 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(curScriptPtr, curScriptSize))) { matched = true; } diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 8d92cb905f..317e98feab 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(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 8540b636fc..75f6280460 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 resource = celObj.getResPointer(); + const uint32 pixelsOffset = resource.getUint32SEAt(celObj._celHeaderOffset + 24); + const int32 numPixels = MIN(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 _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 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 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 &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 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 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 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 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 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 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 resource = getResPointer(); + const uint32 pixelsOffset = resource.getUint32SEAt(_celHeaderOffset + 24); + const int32 numPixels = MIN(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 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 CelObjMem::getResPointer() const { + SciBitmap &bitmap = *g_sci->getEngineState()->_segMan->lookupBitmap(_info.bitmap); + return SciSpan(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 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 3e3f81ac62..351a8856b2 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 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 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 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 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 getResPointer() const override; }; } // End of namespace Sci diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 36026a8134..582dd328dc 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(position.x, 0, celInfo->width - 1); position.y = CLIP(position.y, 0, celInfo->height - 1); - const byte *celData = tmpView->getBitmap(loopNo, celNo); + const SciSpan &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 7cf9a574ef..c29e547b83 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 > 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 > 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 &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 > 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 &cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel); // Pic const CelInfo *picCelInfo = _zoomPicView->getCelInfo(0, 0); - const byte *rawPicBitmap = _zoomPicView->getBitmap(0, 0); + const SciSpan &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 36518ea5db..8d9ce7c0ab 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 > _cursorSurface; CursorCache _cachedCursors; diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp index 34a6d547e1..e8450b773e 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 2268ec0459..ff2f361d93 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 GfxFontFromResource::getCharData(uint16 chr) { + if (chr >= _numChars) { + return SciSpan(); + } + + 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 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(getCharWidth(chr), bufWidth - left); int charHeight = MIN(getCharHeight(chr), bufHeight - top); byte b = 0, mask = 0xFF; int y = 0; int16 greyedTop = top; - byte *pIn = getCharData(chr); + SciSpan 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 b79fb2f0ba..4e26510804 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 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 e10b9fddbf..3a62760398 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 6fe1fb1b49..307c8aedde 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 &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 &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 2178de8a91..af74169976 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 &data, Palette *paletteOut) const; bool setAmiga(); - void modifyAmigaPalette(byte *data); + void modifyAmigaPalette(const SciSpan &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 56e1940224..febb6820d0 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 &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(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 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 c4cfb35096..d6d7d0dbd1 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 &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 _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 getPalPointer() const { + return _data.subspan(kHunkPaletteHeaderSize + (2 * _numPalettes)); } }; diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 864327feaa..9e9dede1ae 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 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 &inBuffer, SciSpan &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 &inbuffer, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA) { + const SciSpan headerPtr = inbuffer.subspan(headerPos); + const SciSpan 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 > 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 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 &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 &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 &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 &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 &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 &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 1be1ae3004..48f80323b1 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 &inbuffer, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA); + void drawVectorData(const SciSpan &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 &data, uint &curPos, int16 &x, int16 &y); + void vectorGetAbsCoordsNoMirror(const SciSpan &data, uint &curPos, int16 &x, int16 &y); + void vectorGetRelCoords(const SciSpan &data, uint &curPos, int16 &x, int16 &y); + void vectorGetRelCoordsMed(const SciSpan &data, uint &curPos, int16 &x, int16 &y); + void vectorGetPatternTexture(const SciSpan &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 045a923569..675883e6d3 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 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 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 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(); } #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 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 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 Portrait::raveGetLipSyncData(const uint16 raveID) { uint lipSyncIDNr = 0; - byte *lipSyncIDPtr = _lipSyncIDTable; + SciSpan 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(); } 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 e0888daa86..b6f5a17af7 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 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 raveGetLipSyncData(const uint16 raveID); ResourceManager *_resMan; EventManager *_event; @@ -66,19 +67,17 @@ private: uint16 _width; Palette _portraitPalette; - uint16 _bitmapCount; - PortraitBitmap *_bitmaps; + Common::Array _bitmaps; Common::String _resourceName; - byte *_fileData; + Common::SpanOwner > _fileData; uint32 _lipSyncIDCount; - byte *_lipSyncIDTable; + SciSpan _lipSyncIDTable; - byte *_lipSyncData; - uint16 *_lipSyncDataOffsetTable; - byte *_lipSyncDataOffsetTableEnd; + SciSpan _lipSyncData; + Common::Array _lipSyncDataOffsetTable; Common::Point _position; }; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 0d00ce01e6..eb66896b53 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 &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 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(priorityBands, 14)); } void GfxPorts::kernelInitPriorityBands() { diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h index 51aca09f54..cd25645df9 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 &data); + void priorityBandsInitSci11(SciSpan data); void kernelInitPriorityBands(); void kernelGraphAdjustPriority(int top, int bottom); diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index de6df39bb9..e69c432712 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 &src, SciSpan &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 63ee4ed09e..46214b71d4 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 &src, SciSpan &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 c07e07538e..77ff9a3d75 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 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 0c09fcbb30..2dc7775345 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 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 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(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(ViewInject_KingsQuest6_Dual1, sizeof(ViewInject_KingsQuest6_Dual1))); + _loop[12].cel[1].rawBitmap->allocateFromSpan(SciSpan(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 &inBuffer, SciSpan &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(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(runLength, pixelCount - pixelNr)); + memset(outPtr + pixelNr, curByte & 0x0F, MIN(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(runLength, pixelCount - pixelNr)); + memset(outPtr + pixelNr, curByte, MIN(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(runLength, pixelCount - pixelNr)); + memset(outPtr + pixelNr, curByte, MIN(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 &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(loopNo, 0, _loopCount -1); - celNo = CLIP(celNo, 0, _loop[loopNo].celCount - 1); - if (_loop[loopNo].cel[celNo].rawBitmap) - return _loop[loopNo].cel[celNo].rawBitmap; +const SciSpan &GfxView::getBitmap(int16 loopNo, int16 celNo) { + loopNo = CLIP(loopNo, 0, _loop.size() - 1); + celNo = CLIP(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 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 &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 &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 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 &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 5e422468b5..808e203682 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 > rawBitmap; }; struct LoopInfo { bool mirrorFlag; uint16 celCount; - CelInfo *cel; + Common::Array 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 &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 &outPtr); + void unditherBitmap(SciSpan &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 _loop; bool _embeddedPal; Palette _viewPalette; // specifies scaling resolution for SCI2 views (see gk1/windows, Wolfgang in room 720) Sci32ViewNativeResolution _sci2ScaleRes; - byte *_EGAmapping; + SciSpan _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 ea8722aefd..4fefff6e3b 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 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 &data) { bool first = true; uint16 nextItem; + SciSpan::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 59558ce18a..763c1fd189 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 &data); /** * Prints the parser suffixes to the debug console. diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index aaa1c8ccfe..6f4248e094 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(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_iterator findSci0ExportsBlock(const SciSpan &buffer) { + SciSpan::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 &buf, uint32 offset) { + int relocStart = buf.getUint32LEAt(8); + int relocCount = buf.getUint16LEAt(18); + SciSpan::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_iterator offsetPtr; if (getSciVersion() <= SCI_VERSION_1_LATE) { - byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data; + SciSpan 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_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 5589129553..a9a7a86c96 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 { }; /** Class for storing resources in memory */ -class Resource { +class Resource : public SciSpan { 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 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 f17a684cc9..0125a0e19b 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_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_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 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 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 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 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 20547f3601..34930a648e 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 4fb9a58003..e470b315cc 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(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 4f557be95e..b8e90802b2 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 &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 > _rhythmKeyMap; Common::Array _patches; Common::TimerManager::TimerProc _adlibTimerProc; void *_adlibTimerParam; - void loadInstrument(const byte *ins); + void loadInstrument(const SciSpan &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 &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 &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(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(_driver)->loadResource(res->data, res->size); + ok = static_cast(_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(_driver)->loadResource(buf, patchSize); - - delete[] buf; + if (size == 5684 || size == 5720 || size == 5727) { + ok = f.seek(0x45a); + if (ok) { + Common::SpanOwner > patchData; + patchData->allocateFromStream(f, patchSize); + ok = static_cast(_driver)->loadResource(*patchData); + } } } } diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp index f5daab726e..b8b6a32214 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 a222090fc8..8b92432cb9 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 > _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 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 db9f7558e2..3f3f581ee2 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 &data); + void sendBanks(const SciSpan &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 &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 > 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 &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 f6dbac2a67..270843c396 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 &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 &data) { + enum { + fmDataSize = 48 + }; + + if (data.size()) { + SciSpan 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 7b2b102284..b9035b07ea 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 &data); + void readMt32GmPatch(const SciSpan &data); + void readMt32Patch(const SciSpan &data); void readMt32DrvData(); - void mapMt32ToGm(byte *data, size_t size); + void mapMt32ToGm(const SciSpan &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 &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(_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 &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 &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 &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(str->readUint16LE(), 0, 100); + const uint8 volume = MIN(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 &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 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 &data) { // FIXME: Clean this up int memtimbres, patches; uint8 group, number, keyshift, /*finetune,*/ bender_range; - uint8 *patchpointer; + SciSpan 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(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(_goodbyeMsg, 20), true); } _driver->close(); diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 21f95f17e0..20688ca351 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_iterator channelData = channel->data.cbegin(); + SciSpan::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 488bd0360c..f78f1466af 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 &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 4e75dab725..90567e8c90 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 ccec41a1ab..095c46281a 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 b0fee5151e..e8b60adf9c 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 + class SciSpanIterator : public Common::SpanInternal::SpanIterator { + typedef typename Common::SpanInternal::SpanIterator super_type; + typedef typename Span::value_type span_value_type; + typedef typename Common::Conditional::type span_type; + + public: + typedef typename Span::difference_type difference_type; + typedef typename Common::RemoveConst::type value_type; + typedef typename Common::Conditional::type *pointer; + typedef typename Common::Conditional::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 class Derived> +class SciSpanImpl : public Common::NamedSpanImpl { + typedef Common::NamedSpanImpl super_type; + typedef Derived derived_type; + typedef typename Common::AddConst >::type const_derived_type; + typedef typename Common::RemoveConst >::type mutable_derived_type; + +#if !defined(__GNUC__) || GCC_ATLEAST(3, 0) + template 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 const_iterator; + typedef SciSpanInternal::SciSpanIterator 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 + inline SciSpanImpl(const Other &other) : + super_type(other) {} + + template + 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 +class SciSpan : public SciSpanImpl { + typedef SciSpanImpl super_type; + typedef typename Common::AddConst >::type const_derived_type; + typedef typename Common::RemoveConst >::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 + inline SciSpan(const Other &other) : super_type(other) {} + + template + inline mutable_derived_type &operator=(const Other &other) { + super_type::operator=(other); + return this->impl(); + } + + ~SciSpan() {} +}; + } // End of namespace Sci #endif -- cgit v1.2.3