/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "scumm/scumm.h" #include "scumm/actor.h" #include "scumm/akos.h" #include "scumm/bomp.h" #include "scumm/imuse/imuse.h" #include "scumm/imuse_digi/dimuse.h" #include "scumm/he/intern_he.h" #include "scumm/resource.h" #include "scumm/scumm_v7.h" #include "scumm/sound.h" #include "scumm/util.h" #include "scumm/he/wiz_he.h" namespace Scumm { #include "common/pack-start.h" // START STRUCT PACKING struct AkosHeader { byte unk_1[2]; byte flags; byte unk_2; uint16 num_anims; uint16 unk_3; uint16 codec; } PACKED_STRUCT; struct AkosOffset { uint32 akcd; // offset into the akcd data uint16 akci; // offset into the akci data } PACKED_STRUCT; #include "common/pack-end.h" // END STRUCT PACKING enum AkosOpcodes { AKC_Return = 0xC001, AKC_SetVar = 0xC010, AKC_CmdQue3 = 0xC015, AKC_C016 = 0xC016, AKC_C017 = 0xC017, AKC_C018 = 0xC018, AKC_C019 = 0xC019, AKC_ComplexChan = 0xC020, AKC_C021 = 0xC021, AKC_C022 = 0xC022, AKC_ComplexChan2 = 0xC025, AKC_Jump = 0xC030, AKC_JumpIfSet = 0xC031, AKC_AddVar = 0xC040, AKC_C042 = 0xC042, AKC_C044 = 0xC044, AKC_C045 = 0xC045, AKC_C046 = 0xC046, AKC_C047 = 0xC047, AKC_C048 = 0xC048, AKC_Ignore = 0xC050, AKC_IncVar = 0xC060, AKC_CmdQue3Quick = 0xC061, AKC_JumpStart = 0xC070, AKC_JumpE = 0xC070, AKC_JumpNE = 0xC071, AKC_JumpL = 0xC072, AKC_JumpLE = 0xC073, AKC_JumpG = 0xC074, AKC_JumpGE = 0xC075, AKC_StartAnim = 0xC080, AKC_StartVarAnim = 0xC081, AKC_Random = 0xC082, AKC_SetActorClip = 0xC083, AKC_StartAnimInActor = 0xC084, AKC_SetVarInActor = 0xC085, AKC_HideActor = 0xC086, AKC_SetDrawOffs = 0xC087, AKC_JumpTable = 0xC088, AKC_SoundStuff = 0xC089, AKC_Flip = 0xC08A, AKC_Cmd3 = 0xC08B, AKC_Ignore3 = 0xC08C, AKC_Ignore2 = 0xC08D, AKC_C08E = 0xC08E, AKC_SkipStart = 0xC090, AKC_SkipE = 0xC090, AKC_SkipNE = 0xC091, AKC_SkipL = 0xC092, AKC_SkipLE = 0xC093, AKC_SkipG = 0xC094, AKC_SkipGE = 0xC095, AKC_ClearFlag = 0xC09F, AKC_C0A0 = 0xC0A0, AKC_C0A1 = 0xC0A1, AKC_C0A2 = 0xC0A2, AKC_C0A3 = 0xC0A3, AKC_C0A4 = 0xC0A4, AKC_C0A5 = 0xC0A5, AKC_C0A6 = 0xC0A6, AKC_C0A7 = 0xC0A7, AKC_EndSeq = 0xC0FF }; static bool akos_compare(int a, int b, byte cmd) { switch (cmd) { case 0: return a == b; case 1: return a != b; case 2: return a < b; case 3: return a <= b; case 4: return a > b; default: return a >= b; } } void AkosCostumeLoader::loadCostume(int id) { _akos = _vm->getResourceAddress(rtCostume, id); assert(_akos); } bool AkosCostumeLoader::hasManyDirections() { const AkosHeader *akhd; akhd = (const AkosHeader *)_vm->findResourceData(MKTAG('A','K','H','D'), _akos); return (akhd->flags & 2) != 0; } void AkosCostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) { uint anim; const byte *r; const AkosHeader *akhd; uint offs; int i; byte code; uint16 start, len; uint16 mask; if (a->_costume == 0) return; loadCostume(a->_costume); if (_vm->_game.version >= 7 && hasManyDirections()) anim = toSimpleDir(1, a->getFacing()) + frame * 8; else anim = newDirToOldDir(a->getFacing()) + frame * 4; akhd = (const AkosHeader *)_vm->findResourceData(MKTAG('A','K','H','D'), _akos); if (anim >= READ_LE_UINT16(&akhd->num_anims)) return; r = _vm->findResourceData(MKTAG('A','K','C','H'), _akos); assert(r); offs = READ_LE_UINT16(r + anim * sizeof(uint16)); if (offs == 0) return; r += offs; const uint8 *akstPtr = _vm->findResourceData(MKTAG('A','K','S','T'), _akos); const uint8 *aksfPtr = _vm->findResourceData(MKTAG('A','K','S','F'), _akos); i = 0; mask = READ_LE_UINT16(r); r += 2; do { if (mask & 0x8000) { const uint8 *akst = akstPtr; const uint8 *aksf = aksfPtr; code = *r++; if (usemask & 0x8000) { switch (code) { case 1: a->_cost.active[i] = 0; a->_cost.frame[i] = frame; a->_cost.end[i] = 0; a->_cost.start[i] = 0; a->_cost.curpos[i] = 0; a->_cost.heCondMaskTable[i] = 0; if (akst) { int size = _vm->getResourceDataSize(akst) / 8; if (size > 0) { bool found = false; while (size--) { if (READ_LE_UINT32(akst) == 0) { a->_cost.heCondMaskTable[i] = READ_LE_UINT32(akst + 4); found = true; break; } akst += 8; } if (!found) { error("Sequence not found in actor %p costume %d", (void *)a, a->_costume); } } } break; case 4: a->_cost.stopped |= 1 << i; break; case 5: a->_cost.stopped &= ~(1 << i); break; default: start = READ_LE_UINT16(r); r += 2; len = READ_LE_UINT16(r); r += 2; a->_cost.heJumpOffsetTable[i] = 0; a->_cost.heJumpCountTable[i] = 0; if (aksf) { int size = _vm->getResourceDataSize(aksf) / 6; if (size > 0) { bool found = false; while (size--) { if (READ_LE_UINT16(aksf) == start) { a->_cost.heJumpOffsetTable[i] = READ_LE_UINT16(aksf + 2); a->_cost.heJumpCountTable[i] = READ_LE_UINT16(aksf + 4); found = true; break; } aksf += 6; } if (!found) { error("Sequence not found in actor %p costume %d", (void *)a, a->_costume); } } } a->_cost.active[i] = code; a->_cost.frame[i] = frame; a->_cost.end[i] = start + len; a->_cost.start[i] = start; a->_cost.curpos[i] = start; a->_cost.heCondMaskTable[i] = 0; if (akst) { int size = _vm->getResourceDataSize(akst) / 8; if (size > 0) { bool found = false; while (size--) { if (READ_LE_UINT32(akst) == start) { a->_cost.heCondMaskTable[i] = READ_LE_UINT32(akst + 4); found = true; break; } akst += 8; } if (!found) { error("Sequence not found in actor %p costume %d", (void *)a, a->_costume); } } } break; } } else { if (code != 1 && code != 4 && code != 5) r += sizeof(uint16) * 2; } } i++; mask <<= 1; usemask <<= 1; } while ((uint16)mask); } void AkosRenderer::setPalette(uint16 *new_palette) { uint size, i; size = _vm->getResourceDataSize(akpl); if (size == 0) return; if (size > 256) error("akos_setPalette: %d is too many colors", size); if (_vm->_game.features & GF_16BIT_COLOR) { if (_paletteNum) { for (i = 0; i < size; i++) _palette[i] = READ_LE_UINT16(_vm->_hePalettes + _paletteNum * _vm->_hePaletteSlot + 768 + akpl[i] * 2); } else if (rgbs) { for (i = 0; i < size; i++) { if (new_palette[i] == 0xFF) { uint8 col = akpl[i]; _palette[i] = _vm->get16BitColor(rgbs[col * 3 + 0], rgbs[col * 3 + 1], rgbs[col * 3 + 2]); } else { _palette[i] = new_palette[i]; } } } } else if (_vm->_game.heversion >= 99 && _paletteNum) { for (i = 0; i < size; i++) _palette[i] = (byte)_vm->_hePalettes[_paletteNum * _vm->_hePaletteSlot + 768 + akpl[i]]; } else { for (i = 0; i < size; i++) { _palette[i] = new_palette[i] != 0xFF ? new_palette[i] : akpl[i]; } } if (_vm->_game.heversion == 70) { for (i = 0; i < size; i++) _palette[i] = _vm->_HEV7ActorPalette[_palette[i]]; } if (size == 256) { byte color = new_palette[0]; if (color == 255) { _palette[0] = color; } else { _useBompPalette = true; } } } void AkosRenderer::setCostume(int costume, int shadow) { const byte *akos = _vm->getResourceAddress(rtCostume, costume); assert(akos); akhd = (const AkosHeader *) _vm->findResourceData(MKTAG('A','K','H','D'), akos); akof = (const AkosOffset *) _vm->findResourceData(MKTAG('A','K','O','F'), akos); akci = _vm->findResourceData(MKTAG('A','K','C','I'), akos); aksq = _vm->findResourceData(MKTAG('A','K','S','Q'), akos); akcd = _vm->findResourceData(MKTAG('A','K','C','D'), akos); akpl = _vm->findResourceData(MKTAG('A','K','P','L'), akos); _codec = READ_LE_UINT16(&akhd->codec); akct = _vm->findResourceData(MKTAG('A','K','C','T'), akos); rgbs = _vm->findResourceData(MKTAG('R','G','B','S'), akos); xmap = 0; if (shadow) { const uint8 *xmapPtr = _vm->getResourceAddress(rtImage, shadow); assert(xmapPtr); xmap = _vm->findResourceData(MKTAG('X','M','A','P'), xmapPtr); assert(xmap); } } void AkosRenderer::setFacing(const Actor *a) { _mirror = (newDirToOldDir(a->getFacing()) != 0 || akhd->flags & 1); if (a->_flip) _mirror = !_mirror; } byte AkosRenderer::drawLimb(const Actor *a, int limb) { uint code; const byte *p; const AkosOffset *off; const CostumeData &cost = a->_cost; const CostumeInfo *costumeInfo; uint i, extra; byte result = 0; int xmoveCur, ymoveCur; uint32 heCondMaskIndex[32]; bool useCondMask; int lastDx, lastDy; lastDx = lastDy = 0; for (i = 0; i < 32; ++i) { heCondMaskIndex[i] = i; } if (_skipLimbs) return 0; if (_vm->_game.heversion >= 70 && cost.active[limb] == 8) return 0; if (!cost.active[limb] || cost.stopped & (1 << limb)) return 0; useCondMask = false; p = aksq + cost.curpos[limb]; code = p[0]; if (code & 0x80) code = READ_BE_UINT16(p); if (_vm->_game.heversion >= 90) _shadow_mode = 0; if (code == AKC_C021 || code == AKC_C022) { uint16 s = cost.curpos[limb] + 4; uint j = 0; extra = p[3]; uint8 n = extra; assert(n <= ARRAYSIZE(heCondMaskIndex)); while (n--) { heCondMaskIndex[j++] = aksq[s++]; } useCondMask = true; p += extra + 2; code = (code == AKC_C021) ? AKC_ComplexChan : AKC_ComplexChan2; } if (code == AKC_Return || code == AKC_EndSeq) return 0; if (code != AKC_ComplexChan && code != AKC_ComplexChan2) { off = akof + (code & 0xFFF); assert((code & 0xFFF) * 6 < READ_BE_UINT32((const byte *)akof - 4) - 8); assert((code & 0x7000) == 0); _srcptr = akcd + READ_LE_UINT32(&off->akcd); costumeInfo = (const CostumeInfo *) (akci + READ_LE_UINT16(&off->akci)); _width = READ_LE_UINT16(&costumeInfo->width); _height = READ_LE_UINT16(&costumeInfo->height); xmoveCur = _xmove + (int16)READ_LE_UINT16(&costumeInfo->rel_x); ymoveCur = _ymove + (int16)READ_LE_UINT16(&costumeInfo->rel_y); _xmove += (int16)READ_LE_UINT16(&costumeInfo->move_x); _ymove -= (int16)READ_LE_UINT16(&costumeInfo->move_y); switch (_codec) { case 1: result |= codec1(xmoveCur, ymoveCur); break; case 5: result |= codec5(xmoveCur, ymoveCur); break; case 16: result |= codec16(xmoveCur, ymoveCur); break; default: error("akos_drawLimb: invalid _codec %d", _codec); } } else { if (code == AKC_ComplexChan2) { lastDx = (int16)READ_LE_UINT16(p + 2); lastDy = (int16)READ_LE_UINT16(p + 4); p += 4; } extra = p[2]; p += 3; uint32 decflag = heCondMaskIndex[0]; for (i = 0; i != extra; i++) { code = p[4]; if (code & 0x80) code = READ_BE_UINT16(p + 4); off = akof + (code & 0xFFF); _srcptr = akcd + READ_LE_UINT32(&off->akcd); costumeInfo = (const CostumeInfo *) (akci + READ_LE_UINT16(&off->akci)); _width = READ_LE_UINT16(&costumeInfo->width); _height = READ_LE_UINT16(&costumeInfo->height); xmoveCur = _xmove + (int16)READ_LE_UINT16(p + 0); ymoveCur = _ymove + (int16)READ_LE_UINT16(p + 2); if (i == extra - 1) { _xmove += lastDx; _ymove -= lastDy; } uint16 shadowMask = 0; if (!useCondMask || !akct) { decflag = 1; } else { uint32 cond = READ_LE_UINT32(akct + cost.heCondMaskTable[limb] + heCondMaskIndex[i] * 4); if (cond == 0) { decflag = 1; } else { uint32 type = cond & ~0x3FFFFFFF; cond &= 0x3FFFFFFF; if (_vm->_game.heversion >= 90) { shadowMask = cond & 0xE000; cond &= ~0xE000; } if (_vm->_game.heversion >= 90 && cond == 0) { decflag = 1; } else if (type == 0x40000000) { // restored_bit decflag = (a->_heCondMask & cond) == cond ? 1 : 0; } else if (type == 0x80000000) { // dirty_bit decflag = (a->_heCondMask & cond) ? 0 : 1; } else { decflag = (a->_heCondMask & cond) ? 1 : 0; } } } p += (p[4] & 0x80) ? 6 : 5; if (decflag == 0) continue; if (_vm->_game.heversion >= 90) { if (_vm->_game.heversion >= 99) _shadow_mode = 0; if (xmap && (shadowMask & 0x8000)) _shadow_mode = 3; } switch (_codec) { case 1: result |= codec1(xmoveCur, ymoveCur); break; case 5: result |= codec5(xmoveCur, ymoveCur); break; case 16: result |= codec16(xmoveCur, ymoveCur); break; case 32: result |= codec32(xmoveCur, ymoveCur); break; default: error("akos_drawLimb: invalid _codec %d", _codec); } } } return result; } void AkosRenderer::codec1_genericDecode(Codec1 &v1) { const byte *mask, *src; byte *dst; byte len, maskbit; int y; uint16 color, height, pcolor; const byte *scaleytab; bool masked; bool skip_column = false; y = v1.y; src = _srcptr; dst = v1.destptr; len = v1.replen; color = v1.repcolor; height = _height; scaleytab = &v1.scaletable[v1.scaleYindex]; maskbit = revBitMask(v1.x & 7); mask = _vm->getMaskBuffer(v1.x - (_vm->_virtscr[kMainVirtScreen].xstart & 7), v1.y, _zbuf); if (len) goto StartPos; do { len = *src++; color = len >> v1.shr; len &= v1.mask; if (!len) len = *src++; do { if (_scaleY == 255 || *scaleytab++ < _scaleY) { if (_actorHitMode) { if (color && y == _actorHitY && v1.x == _actorHitX) { _actorHitResult = true; return; } } else { masked = (y < v1.boundsRect.top || y >= v1.boundsRect.bottom) || (v1.x < 0 || v1.x >= v1.boundsRect.right) || (*mask & maskbit); if (color && !masked && !skip_column) { pcolor = _palette[color]; if (_shadow_mode == 1) { if (pcolor == 13) pcolor = _shadow_table[*dst]; } else if (_shadow_mode == 2) { error("codec1_spec2"); // TODO } else if (_shadow_mode == 3) { if (_vm->_game.features & GF_16BIT_COLOR) { uint16 srcColor = (pcolor >> 1) & 0x7DEF; uint16 dstColor = (READ_UINT16(dst) >> 1) & 0x7DEF; pcolor = srcColor + dstColor; } else if (_vm->_game.heversion >= 90) { pcolor = (pcolor << 8) + *dst; pcolor = xmap[pcolor]; } else if (pcolor < 8) { pcolor = (pcolor << 8) + *dst; pcolor = _shadow_table[pcolor]; } } if (_vm->_bytesPerPixel == 2) { WRITE_UINT16(dst, pcolor); } else { *dst = pcolor; } } } dst += _out.pitch; mask += _numStrips; y++; } if (!--height) { if (!--v1.skip_width) return; height = _height; y = v1.y; scaleytab = &v1.scaletable[v1.scaleYindex]; if (_scaleX == 255 || v1.scaletable[v1.scaleXindex] < _scaleX) { v1.x += v1.scaleXstep; if (v1.x < 0 || v1.x >= v1.boundsRect.right) return; maskbit = revBitMask(v1.x & 7); v1.destptr += v1.scaleXstep * _vm->_bytesPerPixel; skip_column = false; } else skip_column = true; v1.scaleXindex += v1.scaleXstep; dst = v1.destptr; mask = _vm->getMaskBuffer(v1.x - (_vm->_virtscr[kMainVirtScreen].xstart & 7), v1.y, _zbuf); } StartPos:; } while (--len); } while (1); } // This is exact duplicate of smallCostumeScaleTable[] in costume.cpp // See FIXME below for explanation const byte smallCostumeScaleTableAKOS[256] = { 0xFF, 0xFD, 0x7D, 0xBD, 0x3D, 0xDD, 0x5D, 0x9D, 0x1D, 0xED, 0x6D, 0xAD, 0x2D, 0xCD, 0x4D, 0x8D, 0x0D, 0xF5, 0x75, 0xB5, 0x35, 0xD5, 0x55, 0x95, 0x15, 0xE5, 0x65, 0xA5, 0x25, 0xC5, 0x45, 0x85, 0x05, 0xF9, 0x79, 0xB9, 0x39, 0xD9, 0x59, 0x99, 0x19, 0xE9, 0x69, 0xA9, 0x29, 0xC9, 0x49, 0x89, 0x09, 0xF1, 0x71, 0xB1, 0x31, 0xD1, 0x51, 0x91, 0x11, 0xE1, 0x61, 0xA1, 0x21, 0xC1, 0x41, 0x81, 0x01, 0xFB, 0x7B, 0xBB, 0x3B, 0xDB, 0x5B, 0x9B, 0x1B, 0xEB, 0x6B, 0xAB, 0x2B, 0xCB, 0x4B, 0x8B, 0x0B, 0xF3, 0x73, 0xB3, 0x33, 0xD3, 0x53, 0x93, 0x13, 0xE3, 0x63, 0xA3, 0x23, 0xC3, 0x43, 0x83, 0x03, 0xF7, 0x77, 0xB7, 0x37, 0xD7, 0x57, 0x97, 0x17, 0xE7, 0x67, 0xA7, 0x27, 0xC7, 0x47, 0x87, 0x07, 0xEF, 0x6F, 0xAF, 0x2F, 0xCF, 0x4F, 0x8F, 0x0F, 0xDF, 0x5F, 0x9F, 0x1F, 0xBF, 0x3F, 0x7F, 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE }; const byte bigCostumeScaleTable[768] = { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFE, 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFE, 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, }; byte AkosRenderer::codec1(int xmoveCur, int ymoveCur) { int num_colors; bool use_scaling; int i, j; int skip = 0, startScaleIndexX, startScaleIndexY; Common::Rect rect; int step; byte drawFlag = 1; Codec1 v1; const int scaletableSize = (_vm->_game.heversion >= 61) ? 128 : 384; /* implement custom scale table */ // FIXME. HACK // For some illogical reason gcc 3.4.x produces wrong code if // smallCostumeScaleTable from costume.cpp is used here // So I had to put copy of it back here as it was before 1.227 revision // of this file. v1.scaletable = (_vm->_game.heversion >= 61) ? smallCostumeScaleTableAKOS : bigCostumeScaleTable; if (_vm->VAR_CUSTOMSCALETABLE != 0xFF && _vm->_res->isResourceLoaded(rtString, _vm->VAR(_vm->VAR_CUSTOMSCALETABLE))) { v1.scaletable = _vm->getStringAddressVar(_vm->VAR_CUSTOMSCALETABLE); } // Setup color decoding variables num_colors = _vm->getResourceDataSize(akpl); if (num_colors == 32) { v1.mask = 7; v1.shr = 3; } else if (num_colors == 64) { v1.mask = 3; v1.shr = 2; } else { v1.mask = 15; v1.shr = 4; } use_scaling = (_scaleX != 0xFF) || (_scaleY != 0xFF); v1.x = _actorX; v1.y = _actorY; v1.boundsRect.left = 0; v1.boundsRect.top = 0; v1.boundsRect.right = _out.w; v1.boundsRect.bottom = _out.h; if (use_scaling) { /* Scale direction */ v1.scaleXstep = -1; if (xmoveCur < 0) { xmoveCur = -xmoveCur; v1.scaleXstep = 1; } if (_mirror) { /* Adjust X position */ startScaleIndexX = j = scaletableSize - xmoveCur; for (i = 0; i < xmoveCur; i++) { if (v1.scaletable[j++] < _scaleX) v1.x -= v1.scaleXstep; } rect.left = rect.right = v1.x; j = startScaleIndexX; for (i = 0, skip = 0; i < _width; i++) { if (rect.right < 0) { skip++; startScaleIndexX = j; } if (v1.scaletable[j++] < _scaleX) rect.right++; } } else { /* No mirror */ /* Adjust X position */ startScaleIndexX = j = scaletableSize + xmoveCur; for (i = 0; i < xmoveCur; i++) { if (v1.scaletable[j--] < _scaleX) v1.x += v1.scaleXstep; } rect.left = rect.right = v1.x; j = startScaleIndexX; for (i = 0; i < _width; i++) { if (rect.left >= v1.boundsRect.right) { startScaleIndexX = j; skip++; } if (v1.scaletable[j--] < _scaleX) rect.left--; } } if (skip) skip--; step = -1; if (ymoveCur < 0) { ymoveCur = -ymoveCur; step = -step; } startScaleIndexY = scaletableSize - ymoveCur; for (i = 0; i < ymoveCur; i++) { if (v1.scaletable[startScaleIndexY++] < _scaleY) v1.y -= step; } rect.top = rect.bottom = v1.y; startScaleIndexY = scaletableSize - ymoveCur; for (i = 0; i < _height; i++) { if (v1.scaletable[startScaleIndexY++] < _scaleY) rect.bottom++; } startScaleIndexY = scaletableSize - ymoveCur; } else { if (!_mirror) xmoveCur = -xmoveCur; v1.x += xmoveCur; v1.y += ymoveCur; if (_mirror) { rect.left = v1.x; rect.right = v1.x + _width; } else { rect.left = v1.x - _width; rect.right = v1.x; } rect.top = v1.y; rect.bottom = rect.top + _height; startScaleIndexX = scaletableSize; startScaleIndexY = scaletableSize; } v1.scaleXindex = startScaleIndexX; v1.scaleYindex = startScaleIndexY; v1.skip_width = _width; v1.scaleXstep = _mirror ? 1 : -1; if (_vm->_game.heversion >= 71 && !use_scaling) { if (_clipOverride.right > _clipOverride.left && _clipOverride.bottom > _clipOverride.top) { v1.boundsRect = _clipOverride; } } if (_actorHitMode) { if (_actorHitX < rect.left || _actorHitX >= rect.right || _actorHitY < rect.top || _actorHitY >= rect.bottom) return 0; } else markRectAsDirty(rect); if (rect.top >= v1.boundsRect.bottom || rect.bottom <= v1.boundsRect.top) return 0; if (rect.left >= v1.boundsRect.right || rect.right <= v1.boundsRect.left) return 0; v1.replen = 0; if (_mirror) { if (!use_scaling) skip = v1.boundsRect.left - v1.x; if (skip > 0) { v1.skip_width -= skip; codec1_ignorePakCols(v1, skip); v1.x = v1.boundsRect.left; } else { skip = rect.right - v1.boundsRect.right; if (skip <= 0) { drawFlag = 2; } else { v1.skip_width -= skip; } } } else { if (!use_scaling) skip = rect.right - v1.boundsRect.right + 1; if (skip > 0) { v1.skip_width -= skip; codec1_ignorePakCols(v1, skip) ; v1.x = v1.boundsRect.right - 1; } else { skip = (v1.boundsRect.left -1) - rect.left; if (skip <= 0) drawFlag = 2; else v1.skip_width -= skip; } } if (v1.skip_width <= 0 || _height <= 0) return 0; if (rect.left < v1.boundsRect.left) rect.left = v1.boundsRect.left; if (rect.top < v1.boundsRect.top) rect.top = v1.boundsRect.top; if (rect.top > v1.boundsRect.bottom) rect.top = v1.boundsRect.bottom; if (rect.bottom > v1.boundsRect.bottom) rect.bottom = v1.boundsRect.bottom; if (_draw_top > rect.top) _draw_top = rect.top; if (_draw_bottom < rect.bottom) _draw_bottom = rect.bottom; v1.destptr = (byte *)_out.getBasePtr(v1.x, v1.y); codec1_genericDecode(v1); return drawFlag; } void AkosRenderer::markRectAsDirty(Common::Rect rect) { rect.left -= _vm->_virtscr[kMainVirtScreen].xstart & 7; rect.right -= _vm->_virtscr[kMainVirtScreen].xstart & 7; _vm->markRectAsDirty(kMainVirtScreen, rect, _actorID); } byte AkosRenderer::codec5(int xmoveCur, int ymoveCur) { Common::Rect clip; int32 maxw, maxh; if (_actorHitMode) { error("codec5: _actorHitMode not yet implemented"); return 0; } if (!_mirror) { clip.left = (_actorX - xmoveCur - _width) + 1; } else { clip.left = _actorX + xmoveCur - 1; } clip.top = _actorY + ymoveCur; clip.right = clip.left + _width; clip.bottom = clip.top + _height; maxw = _out.w; maxh = _out.h; markRectAsDirty(clip); clip.clip(maxw, maxh); if ((clip.left >= clip.right) || (clip.top >= clip.bottom)) return 0; if (_draw_top > clip.top) _draw_top = clip.top; if (_draw_bottom < clip.bottom) _draw_bottom = clip.bottom; BompDrawData bdd; bdd.dst = _out; if (!_mirror) { bdd.x = (_actorX - xmoveCur - _width) + 1; } else { bdd.x = _actorX + xmoveCur; } bdd.y = _actorY + ymoveCur; bdd.src = _srcptr; bdd.srcwidth = _width; bdd.srcheight = _height; bdd.scale_x = 255; bdd.scale_y = 255; bdd.maskPtr = _vm->getMaskBuffer(0, 0, _zbuf); bdd.numStrips = _numStrips; bdd.shadowMode = _shadow_mode; bdd.shadowPalette = _vm->_shadowPalette; bdd.actorPalette = _useBompPalette ? _palette : 0; bdd.mirror = !_mirror; drawBomp(bdd); _useBompPalette = false; return 0; } void AkosRenderer::akos16SetupBitReader(const byte *src) { _akos16.repeatMode = false; _akos16.numbits = 16; _akos16.mask = (1 << *src) - 1; _akos16.shift = *(src); _akos16.color = *(src + 1); _akos16.bits = (*(src + 2) | *(src + 3) << 8); _akos16.dataptr = src + 4; } #define AKOS16_FILL_BITS() \ if (_akos16.numbits <= 8) { \ _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \ _akos16.numbits += 8; \ } #define AKOS16_EAT_BITS(n) \ _akos16.numbits -= (n); \ _akos16.bits >>= (n); void AkosRenderer::akos16SkipData(int32 numbytes) { akos16DecodeLine(0, numbytes, 0); } void AkosRenderer::akos16DecodeLine(byte *buf, int32 numbytes, int32 dir) { uint16 bits, tmp_bits; while (numbytes != 0) { if (buf) { *buf = _akos16.color; buf += dir; } if (!_akos16.repeatMode) { AKOS16_FILL_BITS() bits = _akos16.bits & 3; if (bits & 1) { AKOS16_EAT_BITS(2) if (bits & 2) { tmp_bits = _akos16.bits & 7; AKOS16_EAT_BITS(3) if (tmp_bits != 4) { // A color change _akos16.color += (tmp_bits - 4); } else { // Color does not change, but rather identical pixels get repeated _akos16.repeatMode = true; AKOS16_FILL_BITS() _akos16.repeatCount = (_akos16.bits & 0xff) - 1; AKOS16_EAT_BITS(8) AKOS16_FILL_BITS() } } else { AKOS16_FILL_BITS() _akos16.color = ((byte)_akos16.bits) & _akos16.mask; AKOS16_EAT_BITS(_akos16.shift) AKOS16_FILL_BITS() } } else { AKOS16_EAT_BITS(1); } } else { if (--_akos16.repeatCount == 0) { _akos16.repeatMode = false; } } numbytes--; } } void AkosRenderer::akos16Decompress(byte *dest, int32 pitch, const byte *src, int32 t_width, int32 t_height, int32 dir, int32 numskip_before, int32 numskip_after, byte transparency, int maskLeft, int maskTop, int zBuf) { byte *tmp_buf = _akos16.buffer; int maskpitch; byte *maskptr; const byte maskbit = revBitMask(maskLeft & 7); if (dir < 0) { dest -= (t_width - 1); tmp_buf += (t_width - 1); } akos16SetupBitReader(src); if (numskip_before != 0) { akos16SkipData(numskip_before); } maskpitch = _numStrips; maskptr = _vm->getMaskBuffer(maskLeft, maskTop, zBuf); assert(t_height > 0); assert(t_width > 0); while (t_height--) { akos16DecodeLine(tmp_buf, t_width, dir); bompApplyMask(_akos16.buffer, maskptr, maskbit, t_width, transparency); bool HE7Check = (_vm->_game.heversion == 70); bompApplyShadow(_shadow_mode, _shadow_table, _akos16.buffer, dest, t_width, transparency, HE7Check); if (numskip_after != 0) { akos16SkipData(numskip_after); } dest += pitch; maskptr += maskpitch; } } byte AkosRenderer::codec16(int xmoveCur, int ymoveCur) { assert(_vm->_bytesPerPixel == 1); Common::Rect clip; int32 minx, miny, maxw, maxh; int32 skip_x, skip_y, cur_x, cur_y; byte transparency = (_vm->_game.heversion >= 61) ? _palette[0] : 255; if (_actorHitMode) { error("codec16: _actorHitMode not yet implemented"); return 0; } if (!_mirror) { clip.left = (_actorX - xmoveCur - _width) + 1; } else { clip.left = _actorX + xmoveCur; } clip.top = _actorY + ymoveCur; clip.right = clip.left + _width; clip.bottom = clip.top + _height; minx = miny = 0; maxw = _out.w; maxh = _out.h; if (_vm->_game.heversion >= 71) { if (_clipOverride.right > _clipOverride.left && _clipOverride.bottom > _clipOverride.top) { minx = _clipOverride.left; miny = _clipOverride.top; maxw = _clipOverride.right; maxh = _clipOverride.bottom; } } markRectAsDirty(clip); skip_x = 0; skip_y = 0; cur_x = _width - 1; cur_y = _height - 1; if (clip.left < minx) { skip_x = -clip.left; clip.left = 0; } if (clip.right > maxw) { cur_x -= clip.right - maxw; clip.right = maxw; } if (clip.top < miny) { skip_y -= clip.top; clip.top = 0; } if (clip.bottom > maxh) { cur_y -= clip.bottom - maxh; clip.bottom = maxh; } if ((clip.left >= clip.right) || (clip.top >= clip.bottom)) return 0; if (_draw_top > clip.top) _draw_top = clip.top; if (_draw_bottom < clip.bottom) _draw_bottom = clip.bottom; int32 width_unk, height_unk; height_unk = clip.top; int32 dir; if (!_mirror) { dir = -1; int tmp_skip_x = skip_x; skip_x = _width - 1 - cur_x; cur_x = _width - 1 - tmp_skip_x; width_unk = clip.right - 1; } else { dir = 1; width_unk = clip.left; } int32 out_height; out_height = cur_y - skip_y; if (out_height < 0) { out_height = -out_height; } out_height++; cur_x -= skip_x; if (cur_x < 0) { cur_x = -cur_x; } cur_x++; int32 numskip_before = skip_x + (skip_y * _width); int32 numskip_after = _width - cur_x; byte *dst = (byte *)_out.getBasePtr(width_unk, height_unk); akos16Decompress(dst, _out.pitch, _srcptr, cur_x, out_height, dir, numskip_before, numskip_after, transparency, clip.left, clip.top, _zbuf); return 0; } byte AkosRenderer::codec32(int xmoveCur, int ymoveCur) { #ifdef ENABLE_HE Common::Rect src, dst; if (!_mirror) { dst.left = (_actorX - xmoveCur - _width) + 1; } else { dst.left = _actorX + xmoveCur; } src.top = src.left = 0; src.right = _width; src.bottom = _height; dst.top = _actorY + ymoveCur; dst.right = dst.left + _width; dst.bottom = dst.top + _height; int diff; diff = dst.left - _clipOverride.left; if (diff < 0) { src.left -= diff; dst.left -= diff; } diff = dst.right - _clipOverride.right; if (diff > 0) { src.right -= diff; dst.right -= diff; } diff = dst.top - _clipOverride.top; if (diff < 0) { src.top -= diff; dst.top -= diff; } diff = dst.bottom - _clipOverride.bottom; if (diff > 0) { src.bottom -= diff; dst.bottom -= diff; } if (dst.isValidRect() == false) return 0; markRectAsDirty(dst); if (_draw_top > dst.top) _draw_top = dst.top; if (_draw_bottom < dst.bottom) _draw_bottom = dst.bottom; const uint8 *palPtr = NULL; if (_vm->_game.features & GF_16BIT_COLOR) { palPtr = _vm->_hePalettes + _vm->_hePaletteSlot + 768; if (_paletteNum) { palPtr = _vm->_hePalettes + _paletteNum * _vm->_hePaletteSlot + 768; } else if (rgbs) { for (uint i = 0; i < 256; i++) WRITE_LE_UINT16(_palette + i, _vm->get16BitColor(rgbs[i * 3 + 0], rgbs[i * 3 + 1], rgbs[i * 3 + 2])); palPtr = (uint8 *)_palette; } } else if (_vm->_game.heversion >= 99) { palPtr = _vm->_hePalettes + _vm->_hePaletteSlot + 768; } byte *dstPtr = (byte *)_out.getBasePtr(dst.left, dst.top); if (_shadow_mode == 3) { Wiz::decompressWizImage(dstPtr, _out.pitch, kDstScreen, _srcptr, src, 0, palPtr, xmap, _vm->_bytesPerPixel); } else { if (palPtr != NULL) { Wiz::decompressWizImage(dstPtr, _out.pitch, kDstScreen, _srcptr, src, 0, palPtr, NULL, _vm->_bytesPerPixel); } else { Wiz::decompressWizImage(dstPtr, _out.pitch, kDstScreen, _srcptr, src, 0, NULL, NULL, _vm->_bytesPerPixel); } } #endif return 0; } byte AkosCostumeLoader::increaseAnims(Actor *a) { return ((ScummEngine_v6 *)_vm)->akos_increaseAnims(_akos, a); } bool ScummEngine_v6::akos_increaseAnims(const byte *akos, Actor *a) { const byte *aksq, *akfo; int i; uint size; bool result; aksq = findResourceData(MKTAG('A','K','S','Q'), akos); akfo = findResourceData(MKTAG('A','K','F','O'), akos); size = getResourceDataSize(akfo) / 2; result = false; for (i = 0; i < 16; i++) { if (a->_cost.active[i] != 0) result |= akos_increaseAnim(a, i, aksq, (const uint16 *)akfo, size); } return result; } #define GW(o) ((int16)READ_LE_UINT16(aksq+curpos+(o))) #define GUW(o) READ_LE_UINT16(aksq+curpos+(o)) #define GB(o) aksq[curpos+(o)] bool ScummEngine_v6::akos_increaseAnim(Actor *a, int chan, const byte *aksq, const uint16 *akfo, int numakfo) { byte active; uint old_curpos, curpos, end; uint code; bool flag_value, needRedraw; int tmp, tmp2; active = a->_cost.active[chan]; end = a->_cost.end[chan]; old_curpos = curpos = a->_cost.curpos[chan]; flag_value = false; needRedraw = false; do { code = aksq[curpos]; if (code & 0x80) code = READ_BE_UINT16(aksq + curpos); switch (active) { case 6: case 8: switch (code) { case AKC_JumpIfSet: case AKC_AddVar: case AKC_SetVar: case AKC_SkipGE: case AKC_SkipG: case AKC_SkipLE: case AKC_SkipL: case AKC_SkipNE: case AKC_SkipE: case AKC_C016: case AKC_C017: case AKC_C018: case AKC_C019: curpos += 5; break; case AKC_JumpTable: case AKC_SetActorClip: case AKC_Ignore3: case AKC_Ignore2: case AKC_Ignore: case AKC_StartAnim: case AKC_StartVarAnim: case AKC_CmdQue3: case AKC_C042: case AKC_C044: case AKC_C0A3: curpos += 3; break; case AKC_SoundStuff: if (_game.heversion >= 61) curpos += 6; else curpos += 8; break; case AKC_Cmd3: case AKC_SetVarInActor: case AKC_SetDrawOffs: curpos += 6; break; case AKC_ClearFlag: case AKC_HideActor: case AKC_IncVar: case AKC_CmdQue3Quick: case AKC_Return: case AKC_EndSeq: curpos += 2; break; case AKC_JumpGE: case AKC_JumpG: case AKC_JumpLE: case AKC_JumpL: case AKC_JumpNE: case AKC_JumpE: case AKC_Random: curpos += 7; break; case AKC_Flip: case AKC_Jump: case AKC_StartAnimInActor: case AKC_C0A0: case AKC_C0A1: case AKC_C0A2: curpos += 4; break; case AKC_ComplexChan2: curpos += 4; // Fall through case AKC_ComplexChan: curpos += 3; tmp = aksq[curpos - 1]; while (--tmp >= 0) { curpos += 4; curpos += (aksq[curpos] & 0x80) ? 2 : 1; } break; case AKC_C021: case AKC_C022: case AKC_C045: case AKC_C046: case AKC_C047: case AKC_C048: needRedraw = 1; curpos += aksq[curpos + 2]; break; case AKC_C08E: akos_queCommand(7, a, GW(2), 0); curpos += 4; break; default: curpos += (code & 0x8000) ? 2 : 1; break; } break; case 2: curpos += (code & 0x8000) ? 2 : 1; if (curpos > end) curpos = a->_cost.start[chan]; break; case 3: if (curpos != end) curpos += (code & 0x8000) ? 2 : 1; break; default: break; } code = aksq[curpos]; if (code & 0x80) code = READ_BE_UINT16(aksq + curpos); if (flag_value && code != AKC_ClearFlag) continue; switch (code) { case AKC_StartAnimInActor: akos_queCommand(4, derefActor(a->getAnimVar(GB(2)), "akos_increaseAnim:29"), a->getAnimVar(GB(3)), 0); continue; case AKC_Random: a->setAnimVar(GB(6), _rnd.getRandomNumberRng(GW(2), GW(4))); continue; case AKC_JumpGE: case AKC_JumpG: case AKC_JumpLE: case AKC_JumpL: case AKC_JumpNE: case AKC_JumpE: if (akos_compare(a->getAnimVar(GB(4)), GW(5), code - AKC_JumpStart) != 0) { curpos = GUW(2); break; } continue; case AKC_IncVar: a->setAnimVar(0, a->getAnimVar(0) + 1); continue; case AKC_SetVar: a->setAnimVar(GB(4), GW(2)); continue; case AKC_AddVar: a->setAnimVar(GB(4), a->getAnimVar(GB(4)) + GW(2)); continue; case AKC_Flip: a->_flip = GW(2) != 0; continue; case AKC_CmdQue3: if (_game.heversion >= 61) tmp = GB(2); else tmp = GB(2) - 1; if ((uint) tmp < 24) akos_queCommand(3, a, a->_sound[tmp], 0); continue; case AKC_CmdQue3Quick: akos_queCommand(3, a, a->_sound[0], 0); continue; case AKC_StartAnim: akos_queCommand(4, a, GB(2), 0); continue; case AKC_StartVarAnim: akos_queCommand(4, a, a->getAnimVar(GB(2)), 0); continue; case AKC_SetVarInActor: derefActor(a->getAnimVar(GB(2)), "akos_increaseAnim:9")->setAnimVar(GB(3), GW(4)); continue; case AKC_HideActor: akos_queCommand(1, a, 0, 0); continue; case AKC_SetActorClip: akos_queCommand(5, a, GB(2), 0); continue; case AKC_SoundStuff: if (_game.heversion >= 61) continue; tmp = GB(2) - 1; if (tmp >= 8) continue; tmp2 = GB(4); if (tmp2 < 1 || tmp2 > 3) error("akos_increaseAnim:8 invalid code %d", tmp2); akos_queCommand(tmp2 + 6, a, a->_sound[tmp], GB(6)); continue; case AKC_SetDrawOffs: akos_queCommand(6, a, GW(2), GW(4)); continue; case AKC_JumpTable: if (akfo == NULL) error("akos_increaseAnim: no AKFO table"); tmp = a->getAnimVar(GB(2)) - 1; if (_game.heversion >= 80) { if (tmp < 0 || tmp > a->_cost.heJumpCountTable[chan] - 1) error("akos_increaseAnim: invalid jump value %d", tmp); curpos = READ_LE_UINT16(akfo + a->_cost.heJumpOffsetTable[chan] + tmp * 2); } else { if (tmp < 0 || tmp > numakfo - 1) error("akos_increaseAnim: invalid jump value %d", tmp); curpos = READ_LE_UINT16(&akfo[tmp]); } break; case AKC_JumpIfSet: if (!a->getAnimVar(GB(4))) continue; a->setAnimVar(GB(4), 0); curpos = GUW(2); break; case AKC_ClearFlag: flag_value = false; continue; case AKC_Jump: curpos = GUW(2); // WORKAROUND bug #3813: In the German version of SPY Fox 3: Operation Ozone // the wig maker room 21 contains a costume animation 352 of an LED ticker // with a jump to an erroneous position 846. // To prevent an undefined 'uSweat token' the animation is reset to its start. if (_game.id == GID_HEGAME && _language == Common::DE_DEU && \ _currentRoom == 21 && a->_costume == 352 && curpos == 846) { curpos = a->_cost.start[chan]; } break; case AKC_Return: case AKC_EndSeq: case AKC_ComplexChan: case AKC_C08E: case AKC_ComplexChan2: break; case AKC_C021: case AKC_C022: needRedraw = 1; break; case AKC_Cmd3: case AKC_Ignore: case AKC_Ignore3: continue; case AKC_Ignore2: if (_game.heversion >= 71) akos_queCommand(3, a, a->_sound[a->getAnimVar(GB(2))], 0); continue; case AKC_SkipE: case AKC_SkipNE: case AKC_SkipL: case AKC_SkipLE: case AKC_SkipG: case AKC_SkipGE: if (akos_compare(a->getAnimVar(GB(4)), GW(2), code - AKC_SkipStart) == 0) flag_value = true; continue; case AKC_C016: if (_sound->isSoundRunning( a->_sound[a->getAnimVar(GB(4))])) { curpos = GUW(2); break; } continue; case AKC_C017: if (!_sound->isSoundRunning(a->_sound[a->getAnimVar(GB(4))])) { curpos = GUW(2); break; } continue; case AKC_C018: if (_sound->isSoundRunning(a->_sound[GB(4)])) { curpos = GUW(2); break; } continue; case AKC_C019: if (!_sound->isSoundRunning(a->_sound[GB(4)])) { curpos = GUW(2); break; } continue; case AKC_C042: akos_queCommand(9, a, a->_sound[GB(2)], 0); continue; case AKC_C044: akos_queCommand(9, a, a->_sound[a->getAnimVar(GB(2))], 0); continue; case AKC_C045: ((ActorHE *)a)->setUserCondition(GB(3), a->getAnimVar(GB(4))); continue; case AKC_C046: a->setAnimVar(GB(4), ((ActorHE *)a)->isUserConditionSet(GB(3))); continue; case AKC_C047: ((ActorHE *)a)->setTalkCondition(GB(3)); continue; case AKC_C048: a->setAnimVar(GB(4), ((ActorHE *)a)->isTalkConditionSet(GB(3))); continue; case AKC_C0A0: akos_queCommand(8, a, GB(2), 0); continue; case AKC_C0A1: if (((ActorHE *)a)->_heTalking != 0) { curpos = GUW(2); break; } continue; case AKC_C0A2: if (((ActorHE *)a)->_heTalking == 0) { curpos = GUW(2); break; } continue; case AKC_C0A3: akos_queCommand(8, a, a->getAnimVar(GB(2)), 0); continue; case AKC_C0A4: if (VAR(VAR_TALK_ACTOR) != 0) { curpos = GUW(2); break; } continue; case AKC_C0A5: if (VAR(VAR_TALK_ACTOR) == 0) { curpos = GUW(2); break; } continue; default: if ((code & 0xC000) == 0xC000) error("Undefined uSweat token %X", code); } break; } while (1); int code2 = aksq[curpos]; if (code2 & 0x80) code2 = READ_BE_UINT16(aksq + curpos); if ((code2 & 0xC000) == 0xC000 && code2 != AKC_ComplexChan && code2 != AKC_Return && code2 != AKC_EndSeq && code2 != AKC_C08E && code2 != AKC_ComplexChan2 && code2 != AKC_C021 && code2 != AKC_C022) error("Ending with undefined uSweat token %X", code2); a->_cost.curpos[chan] = curpos; if (needRedraw) return 1; else return curpos != old_curpos; } void ScummEngine_v6::akos_queCommand(byte cmd, Actor *a, int param_1, int param_2) { _akosQueuePos++; assertRange(0, _akosQueuePos, 31, "akos_queCommand: _akosQueuePos"); _akosQueue[_akosQueuePos].cmd = cmd; _akosQueue[_akosQueuePos].actor = a->_number; _akosQueue[_akosQueuePos].param1 = param_1; _akosQueue[_akosQueuePos].param2 = param_2; } void ScummEngine_v6::akos_processQueue() { byte cmd; int actor, param_1, param_2; while (_akosQueuePos) { cmd = _akosQueue[_akosQueuePos].cmd; actor = _akosQueue[_akosQueuePos].actor; param_1 = _akosQueue[_akosQueuePos].param1; param_2 = _akosQueue[_akosQueuePos].param2; _akosQueuePos--; Actor *a = derefActor(actor, "akos_processQueue"); switch (cmd) { case 1: a->putActor(0, 0, 0); break; case 3: _sound->addSoundToQueue(param_1, 0, -1, 0); break; case 4: a->startAnimActor(param_1); break; case 5: a->_forceClip = param_1; break; case 6: a->_heOffsX = param_1; a->_heOffsY = param_2; break; case 7: #ifdef ENABLE_HE assert(_game.heversion >= 71); ((ScummEngine_v71he *)this)->queueAuxEntry(a->_number, param_1); #endif break; case 8: _actorToPrintStrFor = a->_number; a->_talkPosX = ((ActorHE *)a)->_heTalkQueue[param_1].posX; a->_talkPosY = ((ActorHE *)a)->_heTalkQueue[param_1].posY; a->_talkColor = ((ActorHE *)a)->_heTalkQueue[param_1].color; _string[0].loadDefault(); _string[0].color = a->_talkColor; actorTalk(((ActorHE *)a)->_heTalkQueue[param_1].sentence); break; case 9: _sound->addSoundToQueue(param_1, 0, -1, 4); break; default: error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param_1, param_2); } } } #ifdef ENABLE_SCUMM_7_8 void ScummEngine_v7::akos_processQueue() { byte cmd; int actor, param_1, param_2; while (_akosQueuePos) { cmd = _akosQueue[_akosQueuePos].cmd; actor = _akosQueue[_akosQueuePos].actor; param_1 = _akosQueue[_akosQueuePos].param1; param_2 = _akosQueue[_akosQueuePos].param2; _akosQueuePos--; Actor *a = derefActor(actor, "akos_processQueue"); switch (cmd) { case 1: a->putActor(0, 0, 0); break; case 3: if (param_1 != 0) { if (_imuseDigital) { _imuseDigital->startSfx(param_1, 63); } } break; case 4: a->startAnimActor(param_1); break; case 5: a->_forceClip = param_1; break; case 6: a->_heOffsX = param_1; a->_heOffsY = param_2; break; case 7: if (param_1 != 0) { if (_imuseDigital) { _imuseDigital->setVolume(param_1, param_2); } } break; case 8: if (param_1 != 0) { if (_imuseDigital) { _imuseDigital->setPan(param_1, param_2); } } break; case 9: if (param_1 != 0) { if (_imuseDigital) { _imuseDigital->setPriority(param_1, param_2); } } break; default: error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param_1, param_2); } } } #endif } // End of namespace Scumm