void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); uint extraLocalsWorkaround = 0; 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 // space to use as the target for its kFormat operation. Fixes bug // #3059871. extraLocalsWorkaround = 10; } _bufSize += extraLocalsWorkaround * 2; _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found matchSignatureAndPatch(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize <= heap->size); memcpy(_heapStart, heap->data, heap->size); } _exportTable = 0; _numExports = 0; _synonyms = 0; _numSynonyms = 0; 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) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } 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 } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found matchSignatureAndPatch(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize <= heap->size); memcpy(_heapStart, heap->data, heap->size); } _exportTable = 0; _numExports = 0; _synonyms = 0; _numSynonyms = 0; 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) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } 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 } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictureX, 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 // [headerSize:WORD] [celCount:BYTE] [Unknown:BYTE] [Unknown:WORD] [paletteOffset:DWORD] [Unknown:DWORD] // cel-header follow afterwards, each is 42 bytes // Cel-Header // [width:WORD] [height:WORD] [displaceX:WORD] [displaceY:WORD] [clearColor:BYTE] [compressed:BYTE] // offset 10-23 is unknown // [rleOffset:DWORD] [literalOffset:DWORD] [Unknown:WORD] [Unknown:WORD] [priority:WORD] [relativeXpos:WORD] [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); cel_headerPos += 42; }
bool init() { if (_headerSize > _size) return false; // Read message count from last word in header _messageCount = READ_SCI11ENDIAN_UINT16(_data + _headerSize - 2); if (_messageCount * _recordSize + _headerSize > _size) return false; return true; }
const HunkPalette::EntryHeader HunkPalette::getEntryHeader() const { const byte *const 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); return header; }
bool findRecord(const MessageTuple &tuple, MessageRecord &record) { const byte *recordPtr = _data + _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]; record.string = (const char *)_data + READ_SCI11ENDIAN_UINT16(recordPtr + 5); return true; } recordPtr += _recordSize; } return false; }
void RobotDecoder::readPaletteChunk(uint16 chunkSize) { byte *paletteData = new byte[chunkSize]; _fileStream->read(paletteData, chunkSize); // SCI1.1 palette byte palFormat = paletteData[32]; uint16 palColorStart = paletteData[25]; uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); int palOffset = 37; memset(_palette, 0, 256 * 3); for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { if (palFormat == kRobotPalVariable) palOffset++; _palette[colorNo * 3 + 0] = paletteData[palOffset++]; _palette[colorNo * 3 + 1] = paletteData[palOffset++]; _palette[colorNo * 3 + 2] = paletteData[palOffset++]; } _dirtyPalette = true; delete[] paletteData; }
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); }
bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) { const Object *baseObj = segMan->getObject(getSpeciesSelector()); if (baseObj) { uint originalVarCount = _variables.size(); if (_variables.size() != baseObj->getVarCount()) _variables.resize(baseObj->getVarCount()); // Copy base from species class, as we need its selector IDs _baseObj = baseObj->_baseObj; assert(_baseObj); if (doInitSuperClass) initSuperClass(segMan, addr); if (_variables.size() != originalVarCount) { // These objects are probably broken. // An example is 'witchCage' in script 200 in KQ5 (#3034714), // but also 'girl' in script 216 and 'door' in script 22. // In LSL3 a number of sound objects trigger this right away. // SQ4-floppy's bug #3037938 also seems related. // The effect is that a number of its method selectors may be // treated as variable selectors, causing unpredictable effects. int objScript = segMan->getScript(_pos.getSegment())->getScriptNumber(); // We have to do a little bit of work to get the name of the object // before any relocations are done. reg_t nameReg = getNameSelector(); const char *name; if (nameReg.isNull()) { name = "<no name>"; } else { nameReg.setSegment(_pos.getSegment()); name = segMan->derefString(nameReg); if (!name) name = "<invalid name>"; } debugC(kDebugLevelVM, "Object %04x:%04x (name %s, script %d) " "varnum doesn't match baseObj's: obj %d, base %d", PRINT_REG(_pos), name, objScript, originalVarCount, baseObj->getVarCount()); #if 0 // We enumerate the methods selectors which could be hidden here if (getSciVersion() <= SCI_VERSION_2_1) { const SegmentRef objRef = segMan->dereference(baseObj->_pos); assert(objRef.isRaw); uint segBound = objRef.maxSize/2 - baseObj->getVarCount(); const byte* buf = (const byte *)baseObj->_baseVars; if (!buf) { // While loading this may happen due to objects being loaded // out of order, and we can't proceed then, unfortunately. segBound = 0; } for (uint i = baseObj->getVarCount(); i < originalVarCount && i < segBound; ++i) { uint16 slc = READ_SCI11ENDIAN_UINT16(buf + 2*i); // Skip any numbers which happen to be varselectors too bool found = false; for (uint j = 0; j < baseObj->getVarCount() && !found; ++j) found = READ_SCI11ENDIAN_UINT16(buf + 2*j) == slc; if (found) continue; // Skip any selectors which aren't method selectors, // so couldn't be mistaken for varselectors if (lookupSelector(segMan, _pos, slc, 0, 0) != kSelectorMethod) continue; warning(" Possibly affected selector: %02x (%s)", slc, g_sci->getKernel()->getSelectorName(slc).c_str()); } } #endif } return true; } return false; }
// Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered. 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; reg_t retval; retval.setSegment(pos.getSegment()); retval.setOffset(pos.getOffset() + 1); uint16 param_value = 0xffff; // Suppress GCC warning by setting default value, chose value as invalid to getKernelName etc. uint i = 0; Kernel *kernel = g_sci->getKernel(); if (!mobj) { warning("Disassembly failed: Segment %04x non-existent or not a script", pos.getSegment()); return retval; } else script_entity = (Script *)mobj; 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); const byte opcode = opsize >> 1; debugN("%04x:%04x: ", PRINT_REG(pos)); if (printBytecode) { if (pos.getOffset() + bytecount > scr_size) { warning("Operation arguments extend beyond end of script"); return retval; } for (i = 0; i < bytecount; i++) debugN("%02x ", scr[pos.getOffset() + i]); for (i = bytecount; i < 5; i++) debugN(" "); } opsize &= 1; // byte if true, word if false if (printBWTag) debugN("[%c] ", opsize ? 'B' : 'W'); if (opcode == op_pushSelf && opsize && g_sci->getGameId() != GID_FANMADE) { // 0x3e (62) // Debug opcode op_file debugN("file \"%s\"\n", scr + pos.getOffset() + 1); // +1: op_pushSelf size retval.incOffset(bytecount - 1); return retval; } #ifndef REDUCE_MEMORY_USAGE debugN("%-5s", opcodeNames[opcode]); #endif static const char *defaultSeparator = "\t\t; "; i = 0; while (g_sci->_opcode_formats[opcode][i]) { switch (g_sci->_opcode_formats[opcode][i++]) { case Script_Invalid: warning("-Invalid operation-"); break; case Script_SByte: case Script_Byte: param_value = scr[retval.getOffset()]; debugN("\t%02x", param_value); if (param_value > 9) { debugN("%s%u", defaultSeparator, param_value); } retval.incOffset(1); break; case Script_Word: case Script_SWord: param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]); debugN("\t%04x", param_value); if (param_value > 9) { debugN("%s%u", defaultSeparator, param_value); } retval.incOffset(2); break; case Script_SVariable: case Script_Variable: case Script_Property: case Script_Global: case Script_Local: case Script_Temp: case Script_Param: if (opsize) { param_value = scr[retval.getOffset()]; retval.incOffset(1); } else { param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]); retval.incOffset(2); } if (opcode == op_callk) { debugN("\t%s[%x],", (param_value < kernel->_kernelFuncs.size()) ? ((param_value < kernel->getKernelNamesSize()) ? kernel->getKernelName(param_value).c_str() : "[Unknown(postulated)]") : "<invalid>", param_value); } else if (opcode == op_class) { const reg_t classAddr = s->_segMan->getClassAddress(param_value, SCRIPT_GET_DONT_LOAD, retval.getSegment()); if (!classAddr.isNull()) { debugN("\t%s", s->_segMan->getObjectName(classAddr)); debugN(opsize ? "[%02x]" : "[%04x]", param_value); } else { debugN(opsize ? "\t%02x" : "\t%04x", param_value); } } else if (opcode == op_super) { Object *obj; if (objAddr != NULL_REG && (obj = s->_segMan->getObject(objAddr)) != nullptr) { debugN("\t%s", s->_segMan->getObjectName(obj->getSuperClassSelector())); debugN(opsize ? "[%02x]" : "[%04x]", param_value); } else { debugN(opsize ? "\t%02x" : "\t%04x", param_value); } debugN(","); #ifdef ENABLE_SCI32 } else if (getSciVersion() == SCI_VERSION_3 && ( opcode == op_pToa || opcode == op_aTop || opcode == op_pTos || opcode == op_sTop || opcode == op_ipToa || opcode == op_dpToa || opcode == op_ipTos || opcode == op_dpTos)) { const char *selectorName = "<invalid>"; if (param_value < kernel->getSelectorNamesSize()) { selectorName = kernel->getSelectorName(param_value).c_str(); } debugN("\t%s[%x]", selectorName, param_value); #endif } else { const char *separator = defaultSeparator; debugN(opsize ? "\t%02x" : "\t%04x", param_value); if (param_value > 9) { debugN("%s%u", separator, param_value); separator = ", "; } if (param_value >= 0x20 && param_value <= 0x7e) { debugN("%s'%c'", separator, param_value); separator = ", "; } if (opcode == op_pushi && param_value < kernel->getSelectorNamesSize()) { debugN("%s%s", separator, kernel->getSelectorName(param_value).c_str()); } } break; case Script_Offset: { assert(opcode == op_lofsa || opcode == op_lofss); if (opsize) { param_value = scr[retval.getOffset()]; retval.incOffset(1); } else { param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]); retval.incOffset(2); } const uint32 offset = findOffset(param_value, script_entity, retval.getOffset()); reg_t addr; addr.setSegment(retval.getSegment()); addr.setOffset(offset); debugN("\t%s", s->_segMan->getObjectName(addr)); debugN(opsize ? "[%02x]" : "[%04x]", offset); break; } case Script_SRelative: if (opsize) { int8 offset = (int8)scr[retval.getOffset()]; retval.incOffset(1); debugN("\t%02x [%04x]", 0xff & offset, kOffsetMask & (retval.getOffset() + offset)); } else { int16 offset = (int16)READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]); retval.incOffset(2); debugN("\t%04x [%04x]", 0xffff & offset, kOffsetMask & (retval.getOffset() + offset)); } break; case Script_End: retval = NULL_REG; break; default: error("Internal assertion failed in disassemble()"); } } if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) || (opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) { const Object *obj = s->_segMan->getObject(s->xs->objp); if (!obj) { warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(s->xs->objp)); } else { if (getSciVersion() == SCI_VERSION_3) debugN("\t(%s)", g_sci->getKernel()->getSelectorName(param_value).c_str()); else debugN("\t(%s)", g_sci->getKernel()->getSelectorName(obj->propertyOffsetToId(s->_segMan, param_value)).c_str()); } } } debugN("\n"); if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode if (opcode == op_callk) { int stackframe = (scr[pos.getOffset() + 2] >> 1) + (s->r_rest); int argc = ((s->xs->sp)[- stackframe - 1]).getOffset(); bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); if (!oldScriptHeader) argc += (s->r_rest); debugN(" Kernel params: ("); for (int j = 0; j < argc; j++) { debugN("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe])); if (j + 1 < argc) debugN(", "); } debugN(")\n"); } else if ((opcode == op_send) || (opcode == op_self)) {
void RobotDecoder::preallocateCelMemory(const byte *rawVideoData, const int16 numCels) { for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) { CelHandleInfo &celHandle = _celHandles[i]; if (celHandle.status == CelHandleInfo::kFrameLifetime) { _segMan->freeBitmap(celHandle.bitmapId); celHandle.bitmapId = NULL_REG; celHandle.status = CelHandleInfo::kNoCel; celHandle.area = 0; } } _celHandles.resize(numCels); const int numFixedCels = MIN(numCels, (int16)kFixedCelListSize); for (int i = 0; i < numFixedCels; ++i) { CelHandleInfo &celHandle = _celHandles[i]; // NOTE: There was a check to see if the cel handle was not allocated // here, for some reason, which would mean that nothing was ever // allocated from fixed cels, because the _celHandles array just got // deleted and recreated... if (celHandle.bitmapId == NULL_REG) { break; } celHandle.bitmapId = _fixedCels[i]; celHandle.status = CelHandleInfo::kRobotLifetime; celHandle.area = _maxCelArea[i]; } uint maxFrameArea = 0; for (int i = 0; i < numCels; ++i) { const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2); const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4); const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14); const uint area = celWidth * celHeight; if (area > maxFrameArea) { maxFrameArea = area; } CelHandleInfo &celHandle = _celHandles[i]; if (celHandle.status == CelHandleInfo::kRobotLifetime) { if (_maxCelArea[i] < area) { _segMan->freeBitmap(celHandle.bitmapId); _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false); celHandle.area = area; celHandle.status = CelHandleInfo::kFrameLifetime; } } else if (celHandle.status == CelHandleInfo::kNoCel) { _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false); celHandle.area = area; celHandle.status = CelHandleInfo::kFrameLifetime; } else { error("Cel Handle has bad status"); } rawVideoData += kCelHeaderSize + dataSize; } if (maxFrameArea > _celDecompressionBuffer.size()) { _celDecompressionBuffer.resize(maxFrameArea); } }
uint32 RobotDecoder::createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette) { _verticalScaleFactor = rawVideoData[1]; const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2); const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4); const Common::Point celPosition((int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 10), (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 12)); const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14); const int16 numDataChunks = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 16); rawVideoData += kCelHeaderSize; const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; Common::Point origin; if (scriptWidth == kLowResX && scriptHeight == kLowResY) { const Ratio lowResToScreenX(screenWidth, kLowResX); const Ratio lowResToScreenY(screenHeight, kLowResY); const Ratio screenToLowResX(kLowResX, screenWidth); const Ratio screenToLowResY(kLowResY, screenHeight); const int16 scaledX = celPosition.x + (_position.x * lowResToScreenX).toInt(); const int16 scaledY1 = celPosition.y + (_position.y * lowResToScreenY).toInt(); const int16 scaledY2 = scaledY1 + celHeight - 1; const int16 lowResX = (scaledX * screenToLowResX).toInt(); const int16 lowResY = (scaledY2 * screenToLowResY).toInt(); origin.x = (scaledX - (lowResX * lowResToScreenX).toInt()) * -1; origin.y = (lowResY * lowResToScreenY).toInt() - scaledY1; _screenItemX[screenItemIndex] = lowResX; _screenItemY[screenItemIndex] = lowResY; debugC(kDebugLevelVideo, "Low resolution position c: %d %d l: %d/%d %d/%d d: %d %d s: %d/%d %d/%d x: %d y: %d", celPosition.x, celPosition.y, lowResX, scriptWidth, lowResY, scriptHeight, origin.x, origin.y, scaledX, screenWidth, scaledY2, screenHeight, scaledX - origin.x, scaledY2 - origin.y); } else { const int16 highResX = celPosition.x + _position.x; const int16 highResY = celPosition.y + _position.y + celHeight - 1; origin.x = 0; origin.y = celHeight - 1; _screenItemX[screenItemIndex] = highResX; _screenItemY[screenItemIndex] = highResY; debugC(kDebugLevelVideo, "High resolution position c: %d %d s: %d %d d: %d %d", celPosition.x, celPosition.y, highResX, highResY, origin.x, origin.y); } _originalScreenItemX[screenItemIndex] = celPosition.x; _originalScreenItemY[screenItemIndex] = celPosition.y; assert(_celHandles[screenItemIndex].area >= celWidth * celHeight); SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[screenItemIndex].bitmapId); assert(bitmap.getWidth() == celWidth && bitmap.getHeight() == celHeight); assert(bitmap.getXResolution() == _xResolution && bitmap.getYResolution() == _yResolution); assert(bitmap.getHunkPaletteOffset() == (uint32)bitmap.getWidth() * bitmap.getHeight() + SciBitmap::getBitmapHeaderSize()); bitmap.setOrigin(origin); byte *targetBuffer = nullptr; if (_verticalScaleFactor == 100) { // direct copy to bitmap targetBuffer = bitmap.getPixels(); } else { // go through squashed cel decompressor _celDecompressionBuffer.resize(_celDecompressionArea >= celWidth * (celHeight * _verticalScaleFactor / 100)); targetBuffer = _celDecompressionBuffer.begin(); } for (int i = 0; i < numDataChunks; ++i) { uint compressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData); uint decompressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData + 4); uint16 compressionType = READ_SCI11ENDIAN_UINT16(rawVideoData + 8); rawVideoData += 10; switch (compressionType) { case kCompressionLZS: { Common::MemoryReadStream videoDataStream(rawVideoData, compressedSize, DisposeAfterUse::NO); _decompressor.unpack(&videoDataStream, targetBuffer, compressedSize, decompressedSize); break; } case kCompressionNone: Common::copy(rawVideoData, rawVideoData + decompressedSize, targetBuffer); break; default: error("Unknown compression type %d!", compressionType); } rawVideoData += compressedSize; targetBuffer += decompressedSize; } if (_verticalScaleFactor != 100) { expandCel(bitmap.getPixels(), _celDecompressionBuffer.begin(), celWidth, celHeight); } if (usePalette) { Common::copy(_rawPalette, _rawPalette + kRawPaletteSize, bitmap.getHunkPalette()); } return kCelHeaderSize + dataSize; }
void RobotDecoder::doVersion5(const bool shouldSubmitAudio) { const RobotScreenItemList::size_type oldScreenItemCount = _screenItemList.size(); const int videoSize = _videoSizes[_currentFrameNo]; _doVersion5Scratch.resize(videoSize); byte *videoFrameData = _doVersion5Scratch.begin(); if (!_stream->read(videoFrameData, videoSize)) { error("RobotDecoder::doVersion5: Read error"); } const RobotScreenItemList::size_type screenItemCount = READ_SCI11ENDIAN_UINT16(videoFrameData); if (screenItemCount > kScreenItemListSize) { return; } if (_hasAudio && (getSciVersion() < SCI_VERSION_3 || shouldSubmitAudio)) { int audioPosition, audioSize; if (readAudioDataFromRecord(_currentFrameNo, _audioBuffer, audioPosition, audioSize)) { _audioList.addBlock(audioPosition, audioSize, _audioBuffer); } } if (screenItemCount > oldScreenItemCount) { _screenItemList.resize(screenItemCount); _screenItemX.resize(screenItemCount); _screenItemY.resize(screenItemCount); _originalScreenItemX.resize(screenItemCount); _originalScreenItemY.resize(screenItemCount); } createCels5(videoFrameData + 2, screenItemCount, true); for (RobotScreenItemList::size_type i = 0; i < screenItemCount; ++i) { Common::Point position(_screenItemX[i], _screenItemY[i]); // TODO: Version 6 robot? // int scaleXRemainder; if (_scaleInfo.signal == kScaleSignalManual) { position.x = (position.x * _scaleInfo.x) / 128; // TODO: Version 6 robot? // scaleXRemainder = (position.x * _scaleInfo.x) % 128; position.y = (position.y * _scaleInfo.y) / 128; } if (_screenItemList[i] == nullptr) { CelInfo32 celInfo; celInfo.bitmap = _celHandles[i].bitmapId; ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo, position, _scaleInfo); _screenItemList[i] = screenItem; // TODO: Version 6 robot? // screenItem->_field_30 = scaleXRemainder; if (_priority == -1) { screenItem->_fixedPriority = false; } else { screenItem->_fixedPriority = true; screenItem->_priority = _priority; } g_sci->_gfxFrameout->addScreenItem(*screenItem); } else { ScreenItem *screenItem = _screenItemList[i]; screenItem->_celInfo.bitmap = _celHandles[i].bitmapId; screenItem->_position = position; // TODO: Version 6 robot? // screenItem->_field_30 = scaleXRemainder; if (_priority == -1) { screenItem->_fixedPriority = false; } else { screenItem->_fixedPriority = true; screenItem->_priority = _priority; } g_sci->_gfxFrameout->updateScreenItem(*screenItem); } } for (RobotScreenItemList::size_type i = screenItemCount; i < oldScreenItemCount; ++i) { if (_screenItemList[i] != nullptr) { g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]); _screenItemList[i] = nullptr; } } if (screenItemCount < oldScreenItemCount) { _screenItemList.resize(screenItemCount); _screenItemX.resize(screenItemCount); _screenItemY.resize(screenItemCount); _originalScreenItemX.resize(screenItemCount); _originalScreenItemY.resize(screenItemCount); } }
void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) { freeScript(); Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); if (!script) error("Script %d not found", script_nr); _nr = script_nr; _bufSize = _scriptSize = script->size; if (getSciVersion() == SCI_VERSION_0_EARLY) { _bufSize += READ_LE_UINT16(script->data) * 2; } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { // 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. // However, since we address the heap with a 16-bit pointer, the // combined size of the stack and the heap must be 64KB. So far this has // 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; // Ensure that the start of the heap resource can be word-aligned. 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) 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"); } else if (getSciVersion() == SCI_VERSION_3) { // Check for scripts over 64KB. These won't work with the current 16-bit address // scheme. We need an overlaying mechanism, or a mechanism to split script parts // in different segments to handle these. For now, simply stop when such a script // is found. // // Known large SCI 3 scripts are: // Lighthouse: 9, 220, 270, 351, 360, 490, 760, 765, 800 // LSL7: 240, 511, 550 // Phantasmagoria 2: none (hooray!) // RAMA: 70 // // TODO: Remove this once such a mechanism is in place if (script->size > 65535) error("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't " "be handled at the moment, thus stopping", script_nr, script->size); } uint extraLocalsWorkaround = 0; 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 // space to use as the target for its kFormat operation. Fixes bug // #3059871. extraLocalsWorkaround = 10; } _bufSize += extraLocalsWorkaround * 2; _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize >= heap->size); memcpy(_heapStart, heap->data, heap->size); } // Check scripts (+ possibly SCI 1.1 heap) for matching signatures and patch those, if found scriptPatcher->processScript(_nr, _buf, _bufSize); 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) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } 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 } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap) { _position.x = readSelectorValue(segMan, object, SELECTOR(x)); _position.y = readSelectorValue(segMan, object, SELECTOR(y)); _scale.x = readSelectorValue(segMan, object, SELECTOR(scaleX)); _scale.y = readSelectorValue(segMan, object, SELECTOR(scaleY)); _scale.max = readSelectorValue(segMan, object, SELECTOR(maxScale)); _scale.signal = (ScaleSignals32)(readSelectorValue(segMan, object, SELECTOR(scaleSignal)) & 3); if (updateCel) { _celInfo.resourceId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(view)); _celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop)); _celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel)); if (_celInfo.resourceId <= kPlanePic) { // TODO: Enhance GfxView or ResourceManager to allow // metadata for resources to be retrieved once, from a // single location Resource *view = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _celInfo.resourceId), false); if (!view) { error("Failed to load resource %d", _celInfo.resourceId); } // 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]; if (_celInfo.loopNo >= loopCount) { const int maxLoopNo = loopCount - 1; _celInfo.loopNo = maxLoopNo; writeSelectorValue(segMan, object, SELECTOR(loop), maxLoopNo); } byte *loopData = view->data + headerSize + (_celInfo.loopNo * loopSize); const int8 seekEntry = loopData[0]; if (seekEntry != -1) { loopData = view->data + headerSize + (seekEntry * loopSize); } const uint8 celCount = loopData[2]; if (_celInfo.celNo >= celCount) { const int maxCelNo = celCount - 1; _celInfo.celNo = maxCelNo; writeSelectorValue(segMan, object, SELECTOR(cel), maxCelNo); } } } if (updateBitmap) { const reg_t bitmap = readSelector(segMan, object, SELECTOR(bitmap)); if (!bitmap.isNull()) { _celInfo.bitmap = bitmap; _celInfo.type = kCelTypeMem; } else { _celInfo.bitmap = NULL_REG; _celInfo.type = kCelTypeView; } } if (updateCel || updateBitmap) { delete _celObj; _celObj = nullptr; } if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) { _fixedPriority = true; _priority = readSelectorValue(segMan, object, SELECTOR(priority)); } else { _fixedPriority = false; writeSelectorValue(segMan, object, SELECTOR(priority), _position.y); } _z = readSelectorValue(segMan, object, SELECTOR(z)); _position.y -= _z; if (readSelectorValue(segMan, object, SELECTOR(useInsetRect))) { _useInsetRect = true; _insetRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft)); _insetRect.top = readSelectorValue(segMan, object, SELECTOR(inTop)); _insetRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1; _insetRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1; } else { _useInsetRect = false; } segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible); }
void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; byte *rlePtr = inbuffer + rlePos; int16 displaceX, displaceY; byte priority = _addToFlag ? _priority : 0; byte clearColor; bool compression = true; byte curByte; int16 y, lastY, x, leftX, rightX; int pixelCount; uint16 width, height; #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 if (displaceX || displaceY) error("unsupported embedded cel-data in picture"); // 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]; if (!celBitmap) error("Unable to allocate temporary memory for picture drawing"); 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 if (clearColor == 0) clearColor = 0xff; else if (clearColor == 0xff) clearColor = 0; } if (compression) unpackCelData(inbuffer, celBitmap, clearColor, pixelCount, 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; } } Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); uint16 skipCelBitmapPixels = 0; int16 displayWidth = width; if (pictureX) { // scroll position for picture active, we need to adjust drawX accordingly drawX -= pictureX; if (drawX < 0) { skipCelBitmapPixels = -drawX; displayWidth -= skipCelBitmapPixels; drawX = 0; } } if (displayWidth > 0) { y = displayArea.top + drawY; lastY = MIN<int16>(height + y, displayArea.bottom); leftX = displayArea.left + drawX; rightX = MIN<int16>(displayWidth + leftX, displayArea.right); uint16 sourcePixelSkipPerRow = 0; if (width > rightX - leftX) sourcePixelSkipPerRow = width - (rightX - leftX); // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen // 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) clearColor = _screen->getColorWhite(); byte drawMask = priority > 15 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY; ptr = celBitmap; ptr += skipCelBitmapPixels; if (!_mirroredFlag) { // Draw bitmap to screen x = leftX; while (y < lastY) { curByte = *ptr++; if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) _screen->putPixel(x, y, drawMask, curByte, priority, 0); x++; if (x >= rightX) { ptr += sourcePixelSkipPerRow; x = leftX; y++; } } } else { // Draw bitmap to screen (mirrored) x = rightX - 1; while (y < lastY) { curByte = *ptr++; if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) _screen->putPixel(x, y, drawMask, curByte, priority, 0); if (x == leftX) { ptr += sourcePixelSkipPerRow; x = rightX; y++; } x--; } } } delete[] celBitmap; }
void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) const { int palFormat = 0; int palOffset = 0; int palColorStart = 0; int palColorCount = 0; int colorNo = 0; memset(paletteOut, 0, sizeof(Palette)); // Setup 1:1 mapping for (colorNo = 0; colorNo < 256; colorNo++) { paletteOut->mapping[colorNo] = colorNo; } if (bytesLeft < 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); 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)) { // SCI0/SCI1 palette palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT; palOffset = 260; palColorStart = 0; palColorCount = 256; //memcpy(&paletteOut->mapping, data, 256); } else { // SCI1.1 palette palFormat = data[32]; palOffset = 37; palColorStart = data[25]; palColorCount = READ_SCI11ENDIAN_UINT16(data + 29); } switch (palFormat) { case SCI_PAL_FORMAT_CONSTANT: // Check, if enough bytes left if (bytesLeft < palOffset + (3 * palColorCount)) { warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors"); return; } for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { paletteOut->colors[colorNo].used = 1; paletteOut->colors[colorNo].r = data[palOffset++]; paletteOut->colors[colorNo].g = data[palOffset++]; paletteOut->colors[colorNo].b = data[palOffset++]; } break; case SCI_PAL_FORMAT_VARIABLE: if (bytesLeft < palOffset + (4 * palColorCount)) { warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors"); return; } for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { paletteOut->colors[colorNo].used = data[palOffset++]; paletteOut->colors[colorNo].r = data[palOffset++]; paletteOut->colors[colorNo].g = data[palOffset++]; paletteOut->colors[colorNo].b = data[palOffset++]; } break; } }
void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; byte *rlePtr = inbuffer + rlePos; byte *literalPtr = inbuffer + literalPos; int16 displaceX, displaceY; byte priority = _addToFlag ? _priority : 0; byte clearColor; bool compression = true; byte curByte, runLength; int16 y, lastY, x, leftX, rightX; int pixelNr, pixelCount; uint16 width, height; #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 if (displaceX || displaceY) error("unsupported embedded cel-data in picture"); pixelCount = width * height; celBitmap = new byte[pixelCount]; if (!celBitmap) error("Unable to allocate temporary memory for picture drawing"); if (compression) { // 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 memset(celBitmap, clearColor, pixelCount); pixelNr = 0; ptr = celBitmap; if (literalPos == 0) { // decompression for data that has only one stream (vecor embedded view data) switch (_resMan->getViewType()) { case kViewEga: while (pixelNr < pixelCount) { curByte = *rlePtr++; runLength = curByte >> 4; memset(ptr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); pixelNr += runLength; } break; case kViewVga: case kViewVga11: while (pixelNr < pixelCount) { curByte = *rlePtr++; runLength = curByte & 0x3F; switch (curByte & 0xC0) { case 0: // copy bytes as-is while (runLength-- && pixelNr < pixelCount) ptr[pixelNr++] = *rlePtr++; break; case 0x80: // fill with color memset(ptr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); pixelNr += runLength; break; case 0xC0: // fill with transparent pixelNr += runLength; break; } } break; case kViewAmiga: while (pixelNr < pixelCount) { curByte = *rlePtr++; if (curByte & 0x07) { // fill with color runLength = curByte & 0x07; curByte = curByte >> 3; while (runLength-- && pixelNr < pixelCount) { ptr[pixelNr++] = curByte; } } else { // fill with transparent runLength = curByte >> 3; pixelNr += runLength; } } break; default: error("Unsupported picture viewtype"); } } else {