From 4e7a5ab5eec20004ec96d8efdccddfdb75d16749 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 12 Feb 2023 19:41:15 -0800 Subject: [PATCH] XEEN: Move SpriteResource into Shared namespace All the Xeen specific parts of SpriteResource has been split off into a derived SpriteResource class, so that MM1 won't accidentally use methods it shouldn't --- engines/mm/mm.h | 6 + engines/mm/mm1/data/roster.cpp | 1 + engines/mm/mm1/globals.h | 8 +- engines/mm/mm1/mm1.cpp | 3 +- engines/mm/mm1/utils/mouse.cpp | 4 +- engines/mm/mm1/utils/mouse.h | 4 +- engines/mm/mm1/views_enh/button_container.cpp | 4 +- engines/mm/mm1/views_enh/button_container.h | 10 +- engines/mm/mm1/views_enh/character_info.h | 4 +- engines/mm/mm1/views_enh/game_commands.cpp | 2 +- engines/mm/mm1/views_enh/scroll_view.cpp | 2 +- engines/mm/mm1/views_enh/scroll_view.h | 6 +- engines/mm/module.mk | 1 + engines/mm/shared/xeen/sprites.cpp | 595 ++++++++++++++++++ engines/mm/shared/xeen/sprites.h | 317 ++++++++++ engines/mm/xeen/sprites.cpp | 569 +---------------- engines/mm/xeen/sprites.h | 300 ++------- 17 files changed, 990 insertions(+), 846 deletions(-) create mode 100644 engines/mm/shared/xeen/sprites.cpp create mode 100644 engines/mm/shared/xeen/sprites.h diff --git a/engines/mm/mm.h b/engines/mm/mm.h index dec644e669e..137d4cf2d08 100644 --- a/engines/mm/mm.h +++ b/engines/mm/mm.h @@ -67,6 +67,12 @@ public: */ uint32 getGameID() const; + /** + * Get a random number + */ + uint getRandomNumber(int max) { + return _randomSource.getRandomNumber(max); + } }; } // namespace MM diff --git a/engines/mm/mm1/data/roster.cpp b/engines/mm/mm1/data/roster.cpp index 2b0ca105b67..10d1ce4fe02 100644 --- a/engines/mm/mm1/data/roster.cpp +++ b/engines/mm/mm1/data/roster.cpp @@ -20,6 +20,7 @@ */ #include "common/file.h" +#include "common/memstream.h" #include "common/savefile.h" #include "common/system.h" #include "mm/mm1/data/roster.h" diff --git a/engines/mm/mm1/globals.h b/engines/mm/mm1/globals.h index c267f0b86f0..e8ec9e20665 100644 --- a/engines/mm/mm1/globals.h +++ b/engines/mm/mm1/globals.h @@ -66,10 +66,10 @@ public: public: // Enhanced mode globals - Xeen::SpriteResource _mainIcons; - Xeen::SpriteResource _confirmIcons; - Xeen::SpriteResource _globalSprites; - Xeen::SpriteResource _tileSprites; + Shared::Xeen::SpriteResource _mainIcons; + Shared::Xeen::SpriteResource _confirmIcons; + Shared::Xeen::SpriteResource _globalSprites; + Shared::Xeen::SpriteResource _tileSprites; byte SYMBOLS[20][64]; XeenFont _fontNormal; XeenFont _fontReduced; diff --git a/engines/mm/mm1/mm1.cpp b/engines/mm/mm1/mm1.cpp index 5e3932793db..e1e3d5b16ad 100644 --- a/engines/mm/mm1/mm1.cpp +++ b/engines/mm/mm1/mm1.cpp @@ -31,6 +31,7 @@ #include "mm/mm1/gfx/gfx.h" #include "mm/mm1/views/game.h" #include "mm/mm1/views_enh/game.h" +#include "mm/shared/xeen/cc_archive.h" namespace MM { namespace MM1 { @@ -93,7 +94,7 @@ bool MM1Engine::setupEnhanced() { } // Add the Xeen cc archives - ::MM::Xeen::CCArchive *xeenCC = new ::MM::Xeen::CCArchive( + Shared::Xeen::CCArchive *xeenCC = new Shared::Xeen::CCArchive( "xeen.cc", "xeen", true); SearchMan.add("xeen", xeenCC); diff --git a/engines/mm/mm1/utils/mouse.cpp b/engines/mm/mm1/utils/mouse.cpp index 7759b59e3c3..b99dad2fe40 100644 --- a/engines/mm/mm1/utils/mouse.cpp +++ b/engines/mm/mm1/utils/mouse.cpp @@ -30,8 +30,8 @@ void Mouse::loadCursors() { } void Mouse::setCursor(int cursorId) { - Xeen::XSurface cursor; - _sprites.draw(cursor, cursorId, Common::Point(0, 0), Xeen::SPRFLAG_RESIZE); + Shared::Xeen::XSurface cursor; + _sprites.draw(cursor, cursorId, Common::Point(0, 0), Shared::Xeen::SPRFLAG_RESIZE); CursorMan.replaceCursor(cursor.getPixels(), cursor.w, cursor.h, 0, 0, 0); showCursor(); diff --git a/engines/mm/mm1/utils/mouse.h b/engines/mm/mm1/utils/mouse.h index 119a9bb6ec4..124cdb7fdbb 100644 --- a/engines/mm/mm1/utils/mouse.h +++ b/engines/mm/mm1/utils/mouse.h @@ -22,14 +22,14 @@ #ifndef MM1_UTILS_MOUSE_H #define MM1_UTILS_MOUSE_H -#include "mm/xeen/sprites.h" +#include "mm/shared/xeen/sprites.h" namespace MM { namespace MM1 { class Mouse { private: - Xeen::SpriteResource _sprites; + Shared::Xeen::SpriteResource _sprites; public: Mouse() {} diff --git a/engines/mm/mm1/views_enh/button_container.cpp b/engines/mm/mm1/views_enh/button_container.cpp index 45dbe3a5d4d..c72ecfbe2c0 100644 --- a/engines/mm/mm1/views_enh/button_container.cpp +++ b/engines/mm/mm1/views_enh/button_container.cpp @@ -51,12 +51,12 @@ void ButtonContainer::restoreButtons() { } void ButtonContainer::addButton(const Common::Rect &bounds, KeybindingAction action, - Xeen::SpriteResource *sprites) { + Shared::Xeen::SpriteResource *sprites) { _buttons.push_back(UIButton(this, bounds, action, _buttons.size() * 2, sprites, sprites != nullptr)); } void ButtonContainer::addButton(const Common::Rect &bounds, KeybindingAction action, - int frameNum, Xeen::SpriteResource *sprites) { + int frameNum, Shared::Xeen::SpriteResource *sprites) { _buttons.push_back(UIButton(this, bounds, action, frameNum, sprites, sprites != nullptr)); } diff --git a/engines/mm/mm1/views_enh/button_container.h b/engines/mm/mm1/views_enh/button_container.h index f451a37a049..aa4ee8814d3 100644 --- a/engines/mm/mm1/views_enh/button_container.h +++ b/engines/mm/mm1/views_enh/button_container.h @@ -25,7 +25,7 @@ #include "common/array.h" #include "common/stack.h" #include "common/rect.h" -#include "mm/xeen/sprites.h" +#include "mm/shared/xeen/sprites.h" #include "mm/mm1/views/text_view.h" #include "mm/mm1/events.h" #include "mm/mm1/metaengine.h" @@ -39,7 +39,7 @@ class ButtonContainer; class UIButton { public: Common::Rect _bounds; - Xeen::SpriteResource *_sprites; + Shared::Xeen::SpriteResource *_sprites; KeybindingAction _action; uint _frameNum, _selectedFrame; bool _draw; @@ -48,7 +48,7 @@ public: * Constructor */ UIButton(ButtonContainer *owner, const Common::Rect &bounds, - KeybindingAction action, uint frameNum, Xeen::SpriteResource *sprites, + KeybindingAction action, uint frameNum, Shared::Xeen::SpriteResource *sprites, bool draw) : _bounds(bounds), _action(action), _frameNum(frameNum), _selectedFrame(frameNum | 1), _sprites(sprites), _draw(draw) { @@ -104,9 +104,9 @@ public: void restoreButtons(); void addButton(const Common::Rect &bounds, KeybindingAction action, - Xeen::SpriteResource *sprites = nullptr); + Shared::Xeen::SpriteResource *sprites = nullptr); void addButton(const Common::Rect &bounds, KeybindingAction action, - int frameNum, Xeen::SpriteResource *sprites = nullptr); + int frameNum, Shared::Xeen::SpriteResource *sprites = nullptr); void draw() override; bool msgMouseDown(const MouseDownMessage &msg) override; diff --git a/engines/mm/mm1/views_enh/character_info.h b/engines/mm/mm1/views_enh/character_info.h index 7d72f8463ed..876af592b47 100644 --- a/engines/mm/mm1/views_enh/character_info.h +++ b/engines/mm/mm1/views_enh/character_info.h @@ -24,7 +24,7 @@ #include "mm/mm1/views_enh/scroll_view.h" #include "mm/mm1/views_enh/scroll_popup.h" -#include "mm/xeen/sprites.h" +#include "mm/shared/xeen/sprites.h" namespace MM { namespace MM1 { @@ -37,7 +37,7 @@ class CharacterInfo : public ScrollView { int _frame; int _x; int _y; }; private: - Xeen::SpriteResource _viewIcon; + Shared::Xeen::SpriteResource _viewIcon; static const IconPos ICONS[CHAR_ICONS_COUNT]; const char *ICONS_TEXT[CHAR_ICONS_COUNT]; int _cursorCell = 0; diff --git a/engines/mm/mm1/views_enh/game_commands.cpp b/engines/mm/mm1/views_enh/game_commands.cpp index d4192680768..cfe8a832927 100644 --- a/engines/mm/mm1/views_enh/game_commands.cpp +++ b/engines/mm/mm1/views_enh/game_commands.cpp @@ -29,7 +29,7 @@ namespace ViewsEnh { GameCommands::GameCommands(UIElement *owner) : ButtonContainer("GameCommands", owner), _minimap(this) { - Xeen::SpriteResource *spr = &g_globals->_mainIcons; + Shared::Xeen::SpriteResource *spr = &g_globals->_mainIcons; addButton(Common::Rect(286, 75, 310, 95), KEYBIND_NONE, spr); // Unlock addButton(Common::Rect(235, 75, 259, 95), KEYBIND_PROTECT, spr); // Protect addButton(Common::Rect(260, 75, 284, 95), KEYBIND_REST, spr); // Rest diff --git a/engines/mm/mm1/views_enh/scroll_view.cpp b/engines/mm/mm1/views_enh/scroll_view.cpp index c46fdc4efcf..5311d00a3fd 100644 --- a/engines/mm/mm1/views_enh/scroll_view.cpp +++ b/engines/mm/mm1/views_enh/scroll_view.cpp @@ -41,7 +41,7 @@ ScrollView::ScrollView(const Common::String &name, _bounds.setBorderSize(FRAME_BORDER_SIZE); } -void ScrollView::addButton(Xeen::SpriteResource *sprites, +void ScrollView::addButton(Shared::Xeen::SpriteResource *sprites, const Common::Point &pos, int frame, const Common::KeyState &key) { _buttons.push_back(Button(sprites, pos, frame, key)); diff --git a/engines/mm/mm1/views_enh/scroll_view.h b/engines/mm/mm1/views_enh/scroll_view.h index 99045447b2e..f8ba6eb5e7b 100644 --- a/engines/mm/mm1/views_enh/scroll_view.h +++ b/engines/mm/mm1/views_enh/scroll_view.h @@ -32,12 +32,12 @@ namespace ViewsEnh { class ScrollView : public TextView { struct Button { - Xeen::SpriteResource *_sprites; + Shared::Xeen::SpriteResource *_sprites; Common::Point _pos; int _frame; Common::KeyState _key; - Button(Xeen::SpriteResource *sprites, + Button(Shared::Xeen::SpriteResource *sprites, const Common::Point &pos, int frame, const Common::KeyState &key) : _sprites(sprites), _pos(pos), _frame(frame), _key(key) { @@ -83,7 +83,7 @@ public: /** * Add a button for display */ - void addButton(Xeen::SpriteResource *sprites, + void addButton(Shared::Xeen::SpriteResource *sprites, const Common::Point &pos, int frame, const Common::KeyState &key); diff --git a/engines/mm/module.mk b/engines/mm/module.mk index f4ba0a97322..69969865f3b 100644 --- a/engines/mm/module.mk +++ b/engines/mm/module.mk @@ -248,6 +248,7 @@ MODULE_OBJS := \ xeen/xeen.o \ shared/xeen/cc_archive.o \ shared/xeen/file.o \ + shared/xeen/sprites.o \ shared/xeen/xsurface.o # This module can be built as a plugin diff --git a/engines/mm/shared/xeen/sprites.cpp b/engines/mm/shared/xeen/sprites.cpp new file mode 100644 index 00000000000..1e40aa6bba6 --- /dev/null +++ b/engines/mm/shared/xeen/sprites.cpp @@ -0,0 +1,595 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "common/scummsys.h" +#include "common/archive.h" +#include "common/memstream.h" +#include "common/textconsole.h" +#include "graphics/palette.h" +#include "mm/shared/xeen/sprites.h" +#include "mm/mm.h" + +namespace MM { +namespace Shared { +namespace Xeen { + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define PALETTE_COUNT 256 +#define PALETTE_SIZE (256 * 3) +#define SCENE_CLIP_LEFT 8 +#define SCENE_CLIP_RIGHT 223 + +int SpriteResource::_clippedBottom; + +SpriteResource::SpriteResource() { + _filesize = 0; + _data = nullptr; +} + +SpriteResource::SpriteResource(const Common::String &filename) { + _data = nullptr; + load(filename); +} + +SpriteResource::SpriteResource(const SpriteResource &src) { + copy(src); +} + +SpriteResource::~SpriteResource() { + clear(); +} + +void SpriteResource::copy(const SpriteResource &src) { + _filesize = src._filesize; + _data = new byte[_filesize]; + Common::copy(src._data, src._data + _filesize, _data); + + _index.resize(src._index.size()); + for (uint i = 0; i < src._index.size(); ++i) + _index[i] = src._index[i]; +} + +SpriteResource &SpriteResource::operator=(const SpriteResource &src) { + delete[] _data; + _index.clear(); + + copy(src); + + return *this; +} + +void SpriteResource::load(const Common::String &filename) { + _filename = filename; + Common::File f; + if (f.open(filename)) { + load(f); + } else { + File f2(filename); + load(f2); + } +} + +void SpriteResource::load(Common::SeekableReadStream &f) { + // Read in a copy of the file + _filesize = f.size(); + delete[] _data; + _data = new byte[_filesize]; + f.read(_data, _filesize); + + // Read in the index + f.seek(0); + int count = f.readUint16LE(); + _index.resize(count); + + for (int i = 0; i < count; ++i) { + _index[i]._offset1 = f.readUint16LE(); + _index[i]._offset2 = f.readUint16LE(); + } +} + +void SpriteResource::clear() { + delete[] _data; + _data = nullptr; + _filesize = 0; + _index.clear(); +} + +void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, + uint flags, int scale) { + draw(dest, frame, destPos, Common::Rect(0, 0, dest.w, dest.h), flags, scale); +} + +void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, + const Common::Rect &bounds, uint flags, int scale) { + Common::Rect r = bounds; + if (flags & SPRFLAG_BOTTOM_CLIPPED) + r.clip(SCREEN_WIDTH, _clippedBottom); + + // Create drawer to handle the rendering + SpriteDrawer *drawer; + switch (flags & SPRFLAG_MODE_MASK) { + case SPRFLAG_DRAWER1: + drawer = new SpriteDrawer1(_data, _filesize, flags & 0x1F); + break; + case SPRFLAG_DRAWER2: + drawer = new SpriteDrawer2(_data, _filesize, flags & 0x1F); + break; + case SPRFLAG_DRAWER3: + drawer = new SpriteDrawer3(_data, _filesize, flags & 0x1F); + break; + case SPRFLAG_DRAWER5: + drawer = new SpriteDrawer5(_data, _filesize, flags & 0x1F); + break; + case SPRFLAG_DRAWER6: + drawer = new SpriteDrawer6(_data, _filesize, flags & 0x1F); + break; + default: + drawer = new SpriteDrawer(_data, _filesize); + break; + } + + // WORKAROUND: Crash clicking Vertigo well in Clouds + if (frame < (int)_index.size()) { + // Sprites can consist of separate background & foreground + drawer->draw(dest, _index[frame]._offset1, destPos, r, flags, scale); + if (_index[frame]._offset2) + drawer->draw(dest, _index[frame]._offset2, destPos, r, flags, scale); + } + + delete drawer; +} + +void SpriteResource::draw(XSurface &dest, int frame) { + draw(dest, frame, Common::Point()); +} + +void SpriteResource::draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos) { + XSurface tmp; + tmp.w = dest->w; + tmp.h = dest->h; + tmp.pitch = dest->pitch; + tmp.format = dest->format; + tmp.setPixels(dest->getPixels()); + + draw(tmp, frame, destPos); +} + + +Common::Point SpriteResource::getFrameSize(int frame) const { + Common::MemoryReadStream f(_data, _filesize); + Common::Point frameSize; + + for (int idx = 0; idx < (_index[frame]._offset2 ? 2 : 1); ++idx) { + f.seek((idx == 0) ? _index[frame]._offset1 : _index[frame]._offset2); + int xOffset = f.readUint16LE(); + int width = f.readUint16LE(); + int yOffset = f.readUint16LE(); + int height = f.readUint16LE(); + + frameSize.x = MAX((int)frameSize.x, xOffset + width); + frameSize.y = MAX((int)frameSize.y, yOffset + height); + } + + return frameSize; +} + +/*------------------------------------------------------------------------*/ + +void SpriteDrawer::draw(XSurface &dest, uint16 offset, const Common::Point &pt, + const Common::Rect &clipRect, uint flags, int scale) { + static const uint SCALE_TABLE[] = { + 0xFFFF, 0xFFEF, 0xEFEF, 0xEFEE, 0xEEEE, 0xEEAE, 0xAEAE, 0xAEAA, + 0xAAAA, 0xAA8A, 0x8A8A, 0x8A88, 0x8888, 0x8880, 0x8080, 0x8000 + }; + static const int PATTERN_STEPS[] = { 0, 1, 1, 1, 2, 2, 3, 3, 0, -1, -1, -1, -2, -2, -3, -3 }; + + assert((scale & SCALE_MASK) < 16); + uint16 scaleMask = SCALE_TABLE[scale & SCALE_MASK]; + uint16 scaleMaskX = scaleMask, scaleMaskY = scaleMask; + bool flipped = (flags & SPRFLAG_HORIZ_FLIPPED) != 0; + int xInc = flipped ? -1 : 1; + bool enlarge = (scale & SCALE_ENLARGE) != 0; + + _destTop = (byte *)dest.getBasePtr(clipRect.left, clipRect.top); + _destBottom = (byte *)dest.getBasePtr(clipRect.right, clipRect.bottom - 1); + _pitch = dest.pitch; + + // Get cell header + Common::MemoryReadStream f(_data, _filesize); + f.seek(offset); + int xOffset = f.readUint16LE(); + int width = f.readUint16LE(); + int yOffset = f.readUint16LE(); + int height = f.readUint16LE(); + + // Figure out drawing x, y + Common::Point destPos; + destPos.x = pt.x + getScaledVal(xOffset, scaleMaskX); + destPos.x += (width - getScaledVal(width, scaleMaskX)) / 2; + + destPos.y = pt.y + getScaledVal(yOffset, scaleMaskY); + + // If the flags allow the dest surface to be resized, ensure dest surface is big enough + Common::Rect bounds = clipRect; + if (flags & SPRFLAG_RESIZE) { + if (dest.w < (xOffset + width) || dest.h < (yOffset + height)) + dest.create(xOffset + width, yOffset + height); + bounds = Common::Rect(0, 0, dest.w, dest.h); + } + if (flags & SPRFLAG_SCENE_CLIPPED) { + bounds.clip(Common::Rect(8, 8, 223, 141)); + } + + uint16 scaleMaskXCopy = scaleMaskX; + Common::Rect drawBounds; + drawBounds.left = SCREEN_WIDTH; + drawBounds.top = SCREEN_HEIGHT; + drawBounds.right = drawBounds.bottom = 0; + + // Main loop + for (int yCtr = height; yCtr > 0; --yCtr) { + // The number of bytes in this scan line + int lineLength = f.readByte(); + + if (lineLength == 0) { + // Skip the specified number of scan lines + int numLines = f.readByte(); + destPos.y += getScaledVal(numLines + 1, scaleMaskY); + yCtr -= numLines; + continue; + } + + // Roll the scale mask + uint bit = (scaleMaskY >> 15) & 1; + scaleMaskY = ((scaleMaskY & 0x7fff) << 1) + bit; + + if (!bit) { + // Not a line to be drawn due to scaling down + f.skip(lineLength); + } else if (destPos.y < bounds.top || destPos.y >= bounds.bottom) { + // Skip over the bytes of the line + f.skip(lineLength); + destPos.y++; + } else { + scaleMaskX = scaleMaskXCopy; + xOffset = f.readByte(); + + // Initialize the array to hold the temporary data for the line. We do this to make it simpler + // to handle both deciding which pixels to draw in a scaled image, as well as when images + // have been horizontally flipped. Note that we allocate an extra line for before and after our + // work line, just in case the sprite is screwed up and overruns the line + int tempLine[SCREEN_WIDTH * 3]; + Common::fill(&tempLine[SCREEN_WIDTH], &tempLine[SCREEN_WIDTH * 3], -1); + int *lineP = flipped ? &tempLine[SCREEN_WIDTH + width - 1 - xOffset] : &tempLine[SCREEN_WIDTH + xOffset]; + + // Build up the line + int byteCount, opr1, opr2; + int32 pos; + for (byteCount = 1; byteCount < lineLength; ) { + // The next byte is an opcode that determines what operators are to follow and how to interpret them. + int opcode = f.readByte(); ++byteCount; + + // Decode the opcode + int len = opcode & 0x1F; + int cmd = (opcode & 0xE0) >> 5; + + switch (cmd) { + case 0: // The following len + 1 bytes are stored as indexes into the color table. + case 1: // The following len + 33 bytes are stored as indexes into the color table. + for (int i = 0; i < opcode + 1; ++i, ++byteCount) { + byte b = f.readByte(); + *lineP = b; + lineP += xInc; + } + break; + + case 2: // The following byte is an index into the color table, draw it len + 3 times. + opr1 = f.readByte(); ++byteCount; + for (int i = 0; i < len + 3; ++i) { + *lineP = opr1; + lineP += xInc; + } + break; + + case 3: // Stream copy command. + opr1 = f.readUint16LE(); byteCount += 2; + pos = f.pos(); + f.seek(-opr1, SEEK_CUR); + + for (int i = 0; i < len + 4; ++i) { + *lineP = f.readByte(); + lineP += xInc; + } + + f.seek(pos, SEEK_SET); + break; + + case 4: // The following two bytes are indexes into the color table, draw the pair len + 2 times. + opr1 = f.readByte(); ++byteCount; + opr2 = f.readByte(); ++byteCount; + for (int i = 0; i < len + 2; ++i) { + *lineP = opr1; + lineP += xInc; + *lineP = opr2; + lineP += xInc; + } + break; + + case 5: // Skip len + 1 pixels + lineP += (len + 1) * xInc; + break; + + case 6: // Pattern command. + case 7: + // The pattern command has a different opcode format + len = opcode & 0x07; + cmd = (opcode >> 2) & 0x0E; + + opr1 = f.readByte(); ++byteCount; + for (int i = 0; i < len + 3; ++i) { + *lineP = opr1; + lineP += xInc; + opr1 += PATTERN_STEPS[cmd + (i % 2)]; + } + break; + + default: + break; + } + } + assert(byteCount == lineLength); + + drawBounds.top = MIN(drawBounds.top, destPos.y); + drawBounds.bottom = MAX((int)drawBounds.bottom, destPos.y + 1); + + // Handle drawing out the line + byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y); + _destLeft = (byte *)dest.getBasePtr( + (flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_LEFT : clipRect.left, destPos.y); + _destRight = (byte *)dest.getBasePtr( + (flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_RIGHT : clipRect.right, destPos.y); + int16 xp = destPos.x; + lineP = &tempLine[SCREEN_WIDTH]; + + for (int xCtr = 0; xCtr < width; ++xCtr, ++lineP) { + bit = (scaleMaskX >> 15) & 1; + scaleMaskX = ((scaleMaskX & 0x7fff) << 1) + bit; + + if (bit) { + // Check whether there's a pixel to write, and we're within the allowable bounds. Note that for + // the SPRFLAG_SCENE_CLIPPED or when enlarging, we also have an extra horizontal bounds check + if (*lineP != -1 && xp >= bounds.left && xp < bounds.right) { + drawBounds.left = MIN(drawBounds.left, xp); + drawBounds.right = MAX((int)drawBounds.right, xp + 1); + drawPixel(destP, (byte)*lineP); + if (enlarge) { + drawPixel(destP + SCREEN_WIDTH, (byte)*lineP); + drawPixel(destP + 1, (byte)*lineP); + drawPixel(destP + 1 + SCREEN_WIDTH, (byte)*lineP); + } + } + + ++xp; + ++destP; + if (enlarge) { + ++destP; + ++xp; + } + } + } + + ++destPos.y; + if (enlarge) + ++destPos.y; + } + } + + if (drawBounds.isValidRect()) { + drawBounds.clip(Common::Rect(0, 0, dest.w, dest.h)); + if (!drawBounds.isEmpty()) + dest.addDirtyRect(drawBounds); + } +} + +uint SpriteDrawer::getScaledVal(int xy, uint16 &scaleMask) { + if (!xy) + return 0; + + uint result = 0; + for (int idx = 0; idx < xy; ++idx) { + uint bit = (scaleMask >> 15) & 1; + scaleMask = ((scaleMask & 0x7fff) << 1) + bit; + result += bit; + } + + return result; +} + +void SpriteDrawer::drawPixel(byte *dest, byte pixel) { + *dest = pixel; +} + +void SpriteDrawer::rcr(uint16 &val, bool &cf) { + bool newCf = (val & 1); + val = (val >> 1) | (cf ? 0x8000 : 0); + cf = newCf; +} + +/*------------------------------------------------------------------------*/ + +static const byte DRAWER1_OFFSET[24] = { + 0x30, 0xC0, 0xB0, 0x10, 0x41, 0x20, 0x40, 0x21, 0x48, 0x46, 0x43, 0x40, + 0xD0, 0xD3, 0xD6, 0xD8, 0x01, 0x04, 0x07, 0x0A, 0xEA, 0xEE, 0xF2, 0xF6 +}; + +static const byte DRAWER1_MASK[24] = { + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x0F, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07 +}; + +SpriteDrawer1::SpriteDrawer1(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { + _offset = DRAWER1_OFFSET[index]; + _mask = DRAWER1_MASK[index]; +} + +void SpriteDrawer1::drawPixel(byte *dest, byte pixel) { + *dest = (pixel & _mask) + _offset; +} + +/*------------------------------------------------------------------------*/ + +static const byte DRAWER2_MASK1[32] = { + 3, 0, 3, 0, 3, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const byte DRAWER2_MASK2[16] = { + 0x7E, 0x7E, 0x7E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, + 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E +}; + +static const int8 DRAWER2_DELTA[64] = { + -3, 3, 0, 0, 0, 0, 0, 0, + -5, 5, 0, 0, 0, 0, 0, 0, + -7, 7, 0, 0, 0, 0, 0, 0, + -9, 9, 0, 0, 0, 0, 0, 0, + -7, 7, 0, 0, 0, 0, 0, 0, + -9, 9, 0, 0, 0, 0, 0, 0, + -11, 11, 0, 0, 0, 0, 0, 0, + -13, 13, 0, 0, 0, 0, 0, 0 +}; + +SpriteDrawer2::SpriteDrawer2(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { + _mask1 = DRAWER2_MASK1[index]; + _mask2 = DRAWER2_MASK2[index]; + + MM::MMEngine *engine = static_cast(g_engine); + _random1 = engine->getRandomNumber(0xffff); + _random2 = engine->getRandomNumber(0xffff); +} + +void SpriteDrawer2::drawPixel(byte *dest, byte pixel) { + bool flag = (_random1 & 0x8000) != 0; + _random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0); + + rcr(_random2, flag); + rcr(_random2, flag); + _random2 ^= _random1; + + dest += DRAWER2_DELTA[(_random2 & _mask1 & _mask2) / 2]; + if (dest >= _destLeft && dest < _destRight) { + dest += _pitch * DRAWER2_DELTA[((_random2 >> 8) &_mask1 &_mask2) / 2]; + + if (dest >= _destTop && dest < _destBottom) { + *dest = pixel; + } + } +} + +/*------------------------------------------------------------------------*/ + +static const uint16 DRAWER3_MASK[4] = { 1, 3, 7, 15 }; +static const uint16 DRAWER3_OFFSET[4] = { 1, 2, 4, 8 }; + +SpriteDrawer3::SpriteDrawer3(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { + _offset = DRAWER3_OFFSET[index]; + _mask = DRAWER3_MASK[index]; + + g_system->getPaletteManager()->grabPalette(_palette, 0, PALETTE_COUNT); + _hasPalette = false; + for (byte *pal = _palette; pal < _palette + PALETTE_SIZE && !_hasPalette; ++pal) + _hasPalette = *pal != 0; +} + +void SpriteDrawer3::drawPixel(byte *dest, byte pixel) { + // WORKAROUND: This is slightly different then the original: + // 1) The original has bunches of black pixels appearing. This does index increments to avoid such pixels + // 2) It also prevents any pixels being drawn in the single initial frame until the palette is set + if (_hasPalette) { + byte level = (pixel & _mask) - _offset + (*dest & 0xf); + + if (level >= 0x80) { + *dest &= 0xf0; + } else if (level <= 0xf) { + *dest = (*dest & 0xf0) | level; + } else { + *dest |= 0xf; + } + + // + while (*dest < 0xff && !_palette[*dest * 3] && !_palette[*dest * 3 + 1] && !_palette[*dest * 3 + 2]) + ++ *dest; + } +} + +/*------------------------------------------------------------------------*/ + +static const byte DRAWER4_THRESHOLD[4] = { 4, 7, 10, 13 }; + +SpriteDrawer4::SpriteDrawer4(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { + _threshold = DRAWER4_THRESHOLD[index]; +} + +void SpriteDrawer4::drawPixel(byte *dest, byte pixel) { + if ((pixel & 0xf) >= _threshold) + *dest = pixel; +} + +/*------------------------------------------------------------------------*/ + +static const uint16 DRAWER5_THRESHOLD[4] = { 0x3333, 0x6666, 0x999A, 0xCCCD }; + +SpriteDrawer5::SpriteDrawer5(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { + _threshold = DRAWER5_THRESHOLD[index]; + + MM::MMEngine *engine = static_cast(g_engine); + _random1 = engine->getRandomNumber(0xffff); + _random2 = engine->getRandomNumber(0xffff); +} + +void SpriteDrawer5::drawPixel(byte *dest, byte pixel) { + bool flag = (_random1 & 0x8000) != 0; + _random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0); + + rcr(_random2, flag); + rcr(_random2, flag); + _random2 ^= _random1; + + if (_random2 > _threshold) + *dest = pixel; +} + +/*------------------------------------------------------------------------*/ + +static const byte DRAWER6_MASK[16] = { 1, 2, 4, 8, 1, 3, 7, 15, 8, 12, 14, 15, 1, 2, 1, 2 }; + +SpriteDrawer6::SpriteDrawer6(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { + _mask = DRAWER6_MASK[index]; +} + +void SpriteDrawer6::drawPixel(byte *dest, byte pixel) { + *dest = pixel ^ _mask; +} + +} // End of namespace Xeen +} // End of namespace Shared +} // End of namespace MM diff --git a/engines/mm/shared/xeen/sprites.h b/engines/mm/shared/xeen/sprites.h new file mode 100644 index 00000000000..c75bea695aa --- /dev/null +++ b/engines/mm/shared/xeen/sprites.h @@ -0,0 +1,317 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MM_SHARED_XEEN_SPRITES_H +#define MM_SHARED_XEEN_SPRITES_H + +#include "common/scummsys.h" +#include "common/array.h" +#include "common/file.h" +#include "graphics/surface.h" +#include "mm/shared/xeen/file.h" +#include "mm/shared/xeen/xsurface.h" + +namespace MM { +namespace Xeen { +class XeenEngine; +class Window; +} // namespace Xeen + +namespace Shared { +namespace Xeen { + +enum { + SCALE_MASK = 0x7FFF, SCALE_ENLARGE = 0x8000 +}; + +enum SpriteFlags { + SPRFLAG_MODE_MASK = 0xF00, SPRFLAG_DRAWER1 = 0x100, SPRFLAG_DRAWER2 = 0x200, + SPRFLAG_DRAWER3 = 0x300, SPRFLAG_DRAWER4 = 0x400, SPRFLAG_DRAWER5 = 0x500, SPRFLAG_DRAWER6 = 0x600, + SPRFLAG_DRAWER7 = 0x700, SPRFLAG_800 = 0x800, SPRFLAG_SCENE_CLIPPED = 0x2000, + SPRFLAG_BOTTOM_CLIPPED = 0x4000, SPRFLAG_HORIZ_FLIPPED = 0x8000, SPRFLAG_RESIZE = 0x10000 +}; + +class SpriteResource { +protected: + struct IndexEntry { + uint16 _offset1, _offset2; + }; + Common::Array _index; + size_t _filesize; + byte *_data; + Common::String _filename; + static int _clippedBottom; + + /** + * Load a sprite resource from a stream + */ + void load(Common::SeekableReadStream &f); + + /** + * Draw the sprite onto the given surface + */ + void draw(XSurface &dest, int frame, const Common::Point &destPos, + const Common::Rect &bounds, uint flags = 0, int scale = 0); + + /** + * Deep copy assuming that the current instance is clean + */ + void copy(const SpriteResource &src); +public: + SpriteResource(); + SpriteResource(const Common::String &filename); + SpriteResource(const SpriteResource &src); + + virtual ~SpriteResource(); + + /** + * Copy operator for duplicating a sprite resource + */ + SpriteResource &operator=(const SpriteResource &src); + + /** + * Load a sprite resource from a given file + */ + void load(const Common::String &filename); + + /** + * Clears the sprite resource + */ + void clear(); + + /** + * Draw a sprite onto a surface + * @param dest Destination surface + * @param frame Frame number + * @param destPos Destination position + * @param flags Flags + * @param scale Scale: 0=No scale, SCALE_ENLARGE=Enlarge it + * 1..15 -> reduces the sprite: the higher, the smaller it'll be + */ + void draw(XSurface &dest, int frame, const Common::Point &destPos, + uint flags = 0, int scale = 0); + + /** + * Draw a sprite onto a given window + * @param windowIndex Destination window number + * @param frame Frame number + * @param destPos Destination position + * @param flags Flags + * @param scale Scale: 0=No scale, SCALE_ENLARGE=Enlarge it + * 1..15 -> reduces the sprite: the higher, the smaller it'll be + */ + void draw(int windowIndex, int frame, const Common::Point &destPos, + uint flags = 0, int scale = 0); + + /** + * Draw the sprite onto the given surface + * @param dest Destination surface + * @param frame Frame number + */ + void draw(XSurface &dest, int frame); + + /** + * Draw the sprite onto a given surface + */ + void draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos); + + /** + * Gets the size of a sprite + */ + Common::Point getFrameSize(int frame) const; + + /** + * Returns the number of frames the sprite resource has + */ + size_t size() const { + return _index.size(); + } + + /** + * Returns true if the sprite resource is empty (ie. nothing is loaded) + */ + bool empty() const { + return _index.size() == 0; + } + + /** + * Set the bottom Y position where sprites are clipped if SPRFLAG_BOTTOM_CLIPPED + * is applied + */ + static void setClippedBottom(int y) { + _clippedBottom = y; + } +}; + +/** + * Basic sprite drawer + */ +class SpriteDrawer { +private: + byte *_data = nullptr; + size_t _filesize = 0; +protected: + byte *_destTop = nullptr, *_destBottom = nullptr; + byte *_destLeft = nullptr, *_destRight = nullptr; + int _pitch = 0; +private: + /** + * Scale a co-ordinate value based on the passed scaling mask + */ + static uint getScaledVal(int xy, uint16 &scaleMask); +protected: + /** + * Roll carry right opcode emulation + */ + void rcr(uint16 &val, bool &cf); + + /** + * Output a pixel + */ + virtual void drawPixel(byte *dest, byte pixel); +public: + /** + * Constructor + */ + SpriteDrawer(byte *data, size_t filesize) : _data(data), _filesize(filesize) { + } + + /** + * Destructor + */ + virtual ~SpriteDrawer() { + } + + /** + * Draw a sprite frame based on a passed offset into the data stream + */ + void draw(XSurface &dest, uint16 offset, const Common::Point &pt, + const Common::Rect &clipRect, uint flags, int scale); +}; + +class SpriteDrawer1 : public SpriteDrawer { +private: + byte _offset = 0, _mask = 0; +protected: + /** + * Output a pixel + */ + void drawPixel(byte *dest, byte pixel) override; +public: + /** + * Constructor + */ + SpriteDrawer1(byte *data, size_t filesize, int index); +}; + +/** + * Scrambles up the sprite by drawing many of the pixels randomly + * at a horizontal or vertical offset + */ +class SpriteDrawer2 : public SpriteDrawer { +private: + uint16 _mask1 = 0, _mask2 = 0; + uint16 _random1 = 0, _random2 = 0; +private: + /** + * Output a pixel + */ + void drawPixel(byte *dest, byte pixel) override; +public: + /** + * Constructor + */ + SpriteDrawer2(byte *data, size_t filesize, int index); +}; + +/** + * Draws the sprite as faint ghostly, see-through. + */ +class SpriteDrawer3 : public SpriteDrawer { +private: + uint16 _offset = 0, _mask = 0; + byte _palette[256 * 3]; + bool _hasPalette = false; +private: + /** + * Output a pixel + */ + void drawPixel(byte *dest, byte pixel) override; +public: + /** + * Constructor + */ + SpriteDrawer3(byte *data, size_t filesize, int index); +}; + +class SpriteDrawer4 : public SpriteDrawer { +private: + byte _threshold = 0; +protected: + /** + * Output a pixel + */ + void drawPixel(byte *dest, byte pixel) override; +public: + /** + * Constructor + */ + SpriteDrawer4(byte *data, size_t filesize, int index); +}; + +/** + * Draws a sprite with a fuzziness effect where only some pixels of the sprite are randomly drawn + */ +class SpriteDrawer5 : public SpriteDrawer { +private: + uint16 _threshold = 0, _random1 = 0, _random2 = 0; +protected: + /** + * Output a pixel + */ + void drawPixel(byte *dest, byte pixel) override; +public: + /** + * Constructor + */ + SpriteDrawer5(byte *data, size_t filesize, int index); +}; + +class SpriteDrawer6 : public SpriteDrawer { +private: + byte _mask = 0; +protected: + /** + * Output a pixel + */ + void drawPixel(byte *dest, byte pixel) override; +public: + /** + * Constructor + */ + SpriteDrawer6(byte *data, size_t filesize, int index); +}; + +} // End of namespace Xeen +} // End of namespace Shared +} // End of namespace MM + +#endif diff --git a/engines/mm/xeen/sprites.cpp b/engines/mm/xeen/sprites.cpp index 43e9158944f..8d92648fc73 100644 --- a/engines/mm/xeen/sprites.cpp +++ b/engines/mm/xeen/sprites.cpp @@ -8,127 +8,37 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ -#include "common/scummsys.h" -#include "common/archive.h" -#include "common/memstream.h" -#include "common/textconsole.h" -#include "mm/xeen/xeen.h" -#include "mm/xeen/screen.h" #include "mm/xeen/sprites.h" - -#include "graphics/palette.h" +#include "mm/xeen/xeen.h" namespace MM { namespace Xeen { -#define SCENE_CLIP_LEFT 8 -#define SCENE_CLIP_RIGHT 223 - -int SpriteResource::_clippedBottom; - -SpriteResource::SpriteResource() { - _filesize = 0; - _data = nullptr; -} - -SpriteResource::SpriteResource(const Common::String &filename) { - _data = nullptr; - load(filename); -} - SpriteResource::SpriteResource(const Common::String &filename, int ccMode) { _data = nullptr; load(filename, ccMode); } -SpriteResource::SpriteResource(const SpriteResource &src) { - copy(src); -} - -SpriteResource::~SpriteResource() { - clear(); -} - -void SpriteResource::copy(const SpriteResource &src) { - _filesize = src._filesize; - _data = new byte[_filesize]; - Common::copy(src._data, src._data + _filesize, _data); - - _index.resize(src._index.size()); - for (uint i = 0; i < src._index.size(); ++i) - _index[i] = src._index[i]; -} - -SpriteResource &SpriteResource::operator=(const SpriteResource &src) { - delete[] _data; - _index.clear(); - - copy(src); - - return *this; -} - -void SpriteResource::load(const Common::String &filename) { - _filename = filename; - Common::File f; - if (f.open(filename)) { - load(f); - } else { - File f2(filename); - load(f2); - } -} - void SpriteResource::load(const Common::String &filename, int ccMode) { _filename = filename; File f(filename, ccMode); - load(f); -} - -void SpriteResource::load(Common::SeekableReadStream &f) { - // Read in a copy of the file - _filesize = f.size(); - delete[] _data; - _data = new byte[_filesize]; - f.read(_data, _filesize); - - // Read in the index - f.seek(0); - int count = f.readUint16LE(); - _index.resize(count); - - for (int i = 0; i < count; ++i) { - _index[i]._offset1 = f.readUint16LE(); - _index[i]._offset2 = f.readUint16LE(); - } -} - -void SpriteResource::clear() { - delete[] _data; - _data = nullptr; - _filesize = 0; - _index.clear(); -} - -void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, - uint flags, int scale) { - draw(dest, frame, destPos, Common::Rect(0, 0, dest.w, dest.h), flags, scale); + Shared::Xeen::SpriteResource::load(f); } void SpriteResource::draw(Window &dest, int frame, const Common::Point &destPos, uint flags, int scale) { - draw(dest, frame, destPos, dest.getBounds(), flags, scale); + Shared::Xeen::SpriteResource::draw(dest, frame, destPos, dest.getBounds(), flags, scale); } void SpriteResource::draw(int windowIndex, int frame, const Common::Point &destPos, @@ -137,478 +47,9 @@ void SpriteResource::draw(int windowIndex, int frame, const Common::Point &destP draw(win, frame, destPos, flags, scale); } -void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, - const Common::Rect &bounds, uint flags, int scale) { - Common::Rect r = bounds; - if (flags & SPRFLAG_BOTTOM_CLIPPED) - r.clip(SCREEN_WIDTH, _clippedBottom); - - // Create drawer to handle the rendering - SpriteDrawer *drawer; - switch (flags & SPRFLAG_MODE_MASK) { - case SPRFLAG_DRAWER1: - drawer = new SpriteDrawer1(_data, _filesize, flags & 0x1F); - break; - case SPRFLAG_DRAWER2: - drawer = new SpriteDrawer2(_data, _filesize, flags & 0x1F); - break; - case SPRFLAG_DRAWER3: - drawer = new SpriteDrawer3(_data, _filesize, flags & 0x1F); - break; - case SPRFLAG_DRAWER5: - drawer = new SpriteDrawer5(_data, _filesize, flags & 0x1F); - break; - case SPRFLAG_DRAWER6: - drawer = new SpriteDrawer6(_data, _filesize, flags & 0x1F); - break; - default: - drawer = new SpriteDrawer(_data, _filesize); - break; - } - - // WORKAROUND: Crash clicking Vertigo well in Clouds - if (frame < (int)_index.size()) { - // Sprites can consist of separate background & foreground - drawer->draw(dest, _index[frame]._offset1, destPos, r, flags, scale); - if (_index[frame]._offset2) - drawer->draw(dest, _index[frame]._offset2, destPos, r, flags, scale); - } - - delete drawer; -} - -void SpriteResource::draw(XSurface &dest, int frame) { - draw(dest, frame, Common::Point()); -} - void SpriteResource::draw(int windowIndex, int frame) { draw((*g_vm->_windows)[windowIndex], frame, Common::Point()); } -void SpriteResource::draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos) { - XSurface tmp; - tmp.w = dest->w; - tmp.h = dest->h; - tmp.pitch = dest->pitch; - tmp.format = dest->format; - tmp.setPixels(dest->getPixels()); - - draw(tmp, frame, destPos); -} - - -Common::Point SpriteResource::getFrameSize(int frame) const { - Common::MemoryReadStream f(_data, _filesize); - Common::Point frameSize; - - for (int idx = 0; idx < (_index[frame]._offset2 ? 2 : 1); ++idx) { - f.seek((idx == 0) ? _index[frame]._offset1 : _index[frame]._offset2); - int xOffset = f.readUint16LE(); - int width = f.readUint16LE(); - int yOffset = f.readUint16LE(); - int height = f.readUint16LE(); - - frameSize.x = MAX((int)frameSize.x, xOffset + width); - frameSize.y = MAX((int)frameSize.y, yOffset + height); - } - - return frameSize; -} - -/*------------------------------------------------------------------------*/ - -void SpriteDrawer::draw(XSurface &dest, uint16 offset, const Common::Point &pt, - const Common::Rect &clipRect, uint flags, int scale) { - static const uint SCALE_TABLE[] = { - 0xFFFF, 0xFFEF, 0xEFEF, 0xEFEE, 0xEEEE, 0xEEAE, 0xAEAE, 0xAEAA, - 0xAAAA, 0xAA8A, 0x8A8A, 0x8A88, 0x8888, 0x8880, 0x8080, 0x8000 - }; - static const int PATTERN_STEPS[] = { 0, 1, 1, 1, 2, 2, 3, 3, 0, -1, -1, -1, -2, -2, -3, -3 }; - - assert((scale & SCALE_MASK) < 16); - uint16 scaleMask = SCALE_TABLE[scale & SCALE_MASK]; - uint16 scaleMaskX = scaleMask, scaleMaskY = scaleMask; - bool flipped = (flags & SPRFLAG_HORIZ_FLIPPED) != 0; - int xInc = flipped ? -1 : 1; - bool enlarge = (scale & SCALE_ENLARGE) != 0; - - _destTop = (byte *)dest.getBasePtr(clipRect.left, clipRect.top); - _destBottom = (byte *)dest.getBasePtr(clipRect.right, clipRect.bottom - 1); - _pitch = dest.pitch; - - // Get cell header - Common::MemoryReadStream f(_data, _filesize); - f.seek(offset); - int xOffset = f.readUint16LE(); - int width = f.readUint16LE(); - int yOffset = f.readUint16LE(); - int height = f.readUint16LE(); - - // Figure out drawing x, y - Common::Point destPos; - destPos.x = pt.x + getScaledVal(xOffset, scaleMaskX); - destPos.x += (width - getScaledVal(width, scaleMaskX)) / 2; - - destPos.y = pt.y + getScaledVal(yOffset, scaleMaskY); - - // If the flags allow the dest surface to be resized, ensure dest surface is big enough - Common::Rect bounds = clipRect; - if (flags & SPRFLAG_RESIZE) { - if (dest.w < (xOffset + width) || dest.h < (yOffset + height)) - dest.create(xOffset + width, yOffset + height); - bounds = Common::Rect(0, 0, dest.w, dest.h); - } - if (flags & SPRFLAG_SCENE_CLIPPED) { - bounds.clip(Common::Rect(8, 8, 223, 141)); - } - - uint16 scaleMaskXCopy = scaleMaskX; - Common::Rect drawBounds; - drawBounds.left = SCREEN_WIDTH; - drawBounds.top = SCREEN_HEIGHT; - drawBounds.right = drawBounds.bottom = 0; - - // Main loop - for (int yCtr = height; yCtr > 0; --yCtr) { - // The number of bytes in this scan line - int lineLength = f.readByte(); - - if (lineLength == 0) { - // Skip the specified number of scan lines - int numLines = f.readByte(); - destPos.y += getScaledVal(numLines + 1, scaleMaskY); - yCtr -= numLines; - continue; - } - - // Roll the scale mask - uint bit = (scaleMaskY >> 15) & 1; - scaleMaskY = ((scaleMaskY & 0x7fff) << 1) + bit; - - if (!bit) { - // Not a line to be drawn due to scaling down - f.skip(lineLength); - } else if (destPos.y < bounds.top || destPos.y >= bounds.bottom) { - // Skip over the bytes of the line - f.skip(lineLength); - destPos.y++; - } else { - scaleMaskX = scaleMaskXCopy; - xOffset = f.readByte(); - - // Initialize the array to hold the temporary data for the line. We do this to make it simpler - // to handle both deciding which pixels to draw in a scaled image, as well as when images - // have been horizontally flipped. Note that we allocate an extra line for before and after our - // work line, just in case the sprite is screwed up and overruns the line - int tempLine[SCREEN_WIDTH * 3]; - Common::fill(&tempLine[SCREEN_WIDTH], &tempLine[SCREEN_WIDTH * 3], -1); - int *lineP = flipped ? &tempLine[SCREEN_WIDTH + width - 1 - xOffset] : &tempLine[SCREEN_WIDTH + xOffset]; - - // Build up the line - int byteCount, opr1, opr2; - int32 pos; - for (byteCount = 1; byteCount < lineLength; ) { - // The next byte is an opcode that determines what operators are to follow and how to interpret them. - int opcode = f.readByte(); ++byteCount; - - // Decode the opcode - int len = opcode & 0x1F; - int cmd = (opcode & 0xE0) >> 5; - - switch (cmd) { - case 0: // The following len + 1 bytes are stored as indexes into the color table. - case 1: // The following len + 33 bytes are stored as indexes into the color table. - for (int i = 0; i < opcode + 1; ++i, ++byteCount) { - byte b = f.readByte(); - *lineP = b; - lineP += xInc; - } - break; - - case 2: // The following byte is an index into the color table, draw it len + 3 times. - opr1 = f.readByte(); ++byteCount; - for (int i = 0; i < len + 3; ++i) { - *lineP = opr1; - lineP += xInc; - } - break; - - case 3: // Stream copy command. - opr1 = f.readUint16LE(); byteCount += 2; - pos = f.pos(); - f.seek(-opr1, SEEK_CUR); - - for (int i = 0; i < len + 4; ++i) { - *lineP = f.readByte(); - lineP += xInc; - } - - f.seek(pos, SEEK_SET); - break; - - case 4: // The following two bytes are indexes into the color table, draw the pair len + 2 times. - opr1 = f.readByte(); ++byteCount; - opr2 = f.readByte(); ++byteCount; - for (int i = 0; i < len + 2; ++i) { - *lineP = opr1; - lineP += xInc; - *lineP = opr2; - lineP += xInc; - } - break; - - case 5: // Skip len + 1 pixels - lineP += (len + 1) * xInc; - break; - - case 6: // Pattern command. - case 7: - // The pattern command has a different opcode format - len = opcode & 0x07; - cmd = (opcode >> 2) & 0x0E; - - opr1 = f.readByte(); ++byteCount; - for (int i = 0; i < len + 3; ++i) { - *lineP = opr1; - lineP += xInc; - opr1 += PATTERN_STEPS[cmd + (i % 2)]; - } - break; - - default: - break; - } - } - assert(byteCount == lineLength); - - drawBounds.top = MIN(drawBounds.top, destPos.y); - drawBounds.bottom = MAX((int)drawBounds.bottom, destPos.y + 1); - - // Handle drawing out the line - byte *destP = (byte *)dest.getBasePtr(destPos.x, destPos.y); - _destLeft = (byte *)dest.getBasePtr( - (flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_LEFT : clipRect.left, destPos.y); - _destRight = (byte *)dest.getBasePtr( - (flags & SPRFLAG_SCENE_CLIPPED) ? SCENE_CLIP_RIGHT : clipRect.right, destPos.y); - int16 xp = destPos.x; - lineP = &tempLine[SCREEN_WIDTH]; - - for (int xCtr = 0; xCtr < width; ++xCtr, ++lineP) { - bit = (scaleMaskX >> 15) & 1; - scaleMaskX = ((scaleMaskX & 0x7fff) << 1) + bit; - - if (bit) { - // Check whether there's a pixel to write, and we're within the allowable bounds. Note that for - // the SPRFLAG_SCENE_CLIPPED or when enlarging, we also have an extra horizontal bounds check - if (*lineP != -1 && xp >= bounds.left && xp < bounds.right) { - drawBounds.left = MIN(drawBounds.left, xp); - drawBounds.right = MAX((int)drawBounds.right, xp + 1); - drawPixel(destP, (byte)*lineP); - if (enlarge) { - drawPixel(destP + SCREEN_WIDTH, (byte)*lineP); - drawPixel(destP + 1, (byte)*lineP); - drawPixel(destP + 1 + SCREEN_WIDTH, (byte)*lineP); - } - } - - ++xp; - ++destP; - if (enlarge) { - ++destP; - ++xp; - } - } - } - - ++destPos.y; - if (enlarge) - ++destPos.y; - } - } - - if (drawBounds.isValidRect()) { - drawBounds.clip(Common::Rect(0, 0, dest.w, dest.h)); - if (!drawBounds.isEmpty()) - dest.addDirtyRect(drawBounds); - } -} - -uint SpriteDrawer::getScaledVal(int xy, uint16 &scaleMask) { - if (!xy) - return 0; - - uint result = 0; - for (int idx = 0; idx < xy; ++idx) { - uint bit = (scaleMask >> 15) & 1; - scaleMask = ((scaleMask & 0x7fff) << 1) + bit; - result += bit; - } - - return result; -} - -void SpriteDrawer::drawPixel(byte *dest, byte pixel) { - *dest = pixel; -} - -void SpriteDrawer::rcr(uint16 &val, bool &cf) { - bool newCf = (val & 1); - val = (val >> 1) | (cf ? 0x8000 : 0); - cf = newCf; -} - -/*------------------------------------------------------------------------*/ - -static const byte DRAWER1_OFFSET[24] = { - 0x30, 0xC0, 0xB0, 0x10, 0x41, 0x20, 0x40, 0x21, 0x48, 0x46, 0x43, 0x40, - 0xD0, 0xD3, 0xD6, 0xD8, 0x01, 0x04, 0x07, 0x0A, 0xEA, 0xEE, 0xF2, 0xF6 -}; - -static const byte DRAWER1_MASK[24] = { - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x0F, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07 -}; - -SpriteDrawer1::SpriteDrawer1(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { - _offset = DRAWER1_OFFSET[index]; - _mask = DRAWER1_MASK[index]; -} - -void SpriteDrawer1::drawPixel(byte *dest, byte pixel) { - *dest = (pixel & _mask) + _offset; -} - -/*------------------------------------------------------------------------*/ - -static const byte DRAWER2_MASK1[32] = { - 3, 0, 3, 0, 3, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const byte DRAWER2_MASK2[16] = { - 0x7E, 0x7E, 0x7E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, - 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E -}; - -static const int8 DRAWER2_DELTA[64] = { - -3, 3, 0, 0, 0, 0, 0, 0, - -5, 5, 0, 0, 0, 0, 0, 0, - -7, 7, 0, 0, 0, 0, 0, 0, - -9, 9, 0, 0, 0, 0, 0, 0, - -7, 7, 0, 0, 0, 0, 0, 0, - -9, 9, 0, 0, 0, 0, 0, 0, - -11, 11, 0, 0, 0, 0, 0, 0, - -13, 13, 0, 0, 0, 0, 0, 0 -}; - -SpriteDrawer2::SpriteDrawer2(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { - _mask1 = DRAWER2_MASK1[index]; - _mask2 = DRAWER2_MASK2[index]; - - _random1 = g_vm->getRandomNumber(0xffff); - _random2 = g_vm->getRandomNumber(0xffff); -} - -void SpriteDrawer2::drawPixel(byte *dest, byte pixel) { - bool flag = (_random1 & 0x8000) != 0; - _random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0); - - rcr(_random2, flag); - rcr(_random2, flag); - _random2 ^= _random1; - - dest += DRAWER2_DELTA[(_random2 & _mask1 & _mask2) / 2]; - if (dest >= _destLeft && dest < _destRight) { - dest += _pitch * DRAWER2_DELTA[((_random2 >> 8) &_mask1 &_mask2) / 2]; - - if (dest >= _destTop && dest < _destBottom) { - *dest = pixel; - } - } -} - -/*------------------------------------------------------------------------*/ - -static const uint16 DRAWER3_MASK[4] = { 1, 3, 7, 15 }; -static const uint16 DRAWER3_OFFSET[4] = { 1, 2, 4, 8 }; - -SpriteDrawer3::SpriteDrawer3(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { - _offset = DRAWER3_OFFSET[index]; - _mask = DRAWER3_MASK[index]; - - g_system->getPaletteManager()->grabPalette(_palette, 0, PALETTE_COUNT); - _hasPalette = false; - for (byte *pal = _palette; pal < _palette + PALETTE_SIZE && !_hasPalette; ++pal) - _hasPalette = *pal != 0; -} - -void SpriteDrawer3::drawPixel(byte *dest, byte pixel) { - // WORKAROUND: This is slightly different then the original: - // 1) The original has bunches of black pixels appearing. This does index increments to avoid such pixels - // 2) It also prevents any pixels being drawn in the single initial frame until the palette is set - if (_hasPalette) { - byte level = (pixel & _mask) - _offset + (*dest & 0xf); - - if (level >= 0x80) { - *dest &= 0xf0; - } else if (level <= 0xf) { - *dest = (*dest & 0xf0) | level; - } else { - *dest |= 0xf; - } - - // - while (*dest < 0xff && !_palette[*dest * 3] && !_palette[*dest * 3 + 1] && !_palette[*dest * 3 + 2]) - ++ *dest; - } -} - -/*------------------------------------------------------------------------*/ - -static const byte DRAWER4_THRESHOLD[4] = { 4, 7, 10, 13 }; - -SpriteDrawer4::SpriteDrawer4(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { - _threshold = DRAWER4_THRESHOLD[index]; -} - -void SpriteDrawer4::drawPixel(byte *dest, byte pixel) { - if ((pixel & 0xf) >= _threshold) - *dest = pixel; -} - -/*------------------------------------------------------------------------*/ - -static const uint16 DRAWER5_THRESHOLD[4] = { 0x3333, 0x6666, 0x999A, 0xCCCD }; - -SpriteDrawer5::SpriteDrawer5(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { - _threshold = DRAWER5_THRESHOLD[index]; - _random1 = g_vm->getRandomNumber(0xffff); - _random2 = g_vm->getRandomNumber(0xffff); -} - -void SpriteDrawer5::drawPixel(byte *dest, byte pixel) { - bool flag = (_random1 & 0x8000) != 0; - _random1 = (int)((uint16)_random1 << 1) - _random2 - (flag ? 1 : 0); - - rcr(_random2, flag); - rcr(_random2, flag); - _random2 ^= _random1; - - if (_random2 > _threshold) - *dest = pixel; -} - -/*------------------------------------------------------------------------*/ - -static const byte DRAWER6_MASK[16] = { 1, 2, 4, 8, 1, 3, 7, 15, 8, 12, 14, 15, 1, 2, 1, 2 }; - -SpriteDrawer6::SpriteDrawer6(byte *data, size_t filesize, int index) : SpriteDrawer(data, filesize) { - _mask = DRAWER6_MASK[index]; -} - -void SpriteDrawer6::drawPixel(byte *dest, byte pixel) { - *dest = pixel ^ _mask; -} - } // End of namespace Xeen } // End of namespace MM diff --git a/engines/mm/xeen/sprites.h b/engines/mm/xeen/sprites.h index 8a173d51c35..c426b6e937e 100644 --- a/engines/mm/xeen/sprites.h +++ b/engines/mm/xeen/sprites.h @@ -8,12 +8,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * @@ -22,92 +22,42 @@ #ifndef XEEN_SPRITES_H #define XEEN_SPRITES_H -#include "common/scummsys.h" -#include "common/array.h" -#include "common/file.h" -#include "graphics/surface.h" -#include "mm/xeen/files.h" +#include "mm/shared/xeen/sprites.h" #include "mm/shared/xeen/xsurface.h" namespace MM { namespace Xeen { -using Shared::Xeen::XSurface; +using namespace Shared::Xeen; -class XeenEngine; -class Window; - -enum { - SCALE_MASK = 0x7FFF, SCALE_ENLARGE = 0x8000 -}; - -enum SpriteFlags { - SPRFLAG_MODE_MASK = 0xF00, SPRFLAG_DRAWER1 = 0x100, SPRFLAG_DRAWER2 = 0x200, - SPRFLAG_DRAWER3 = 0x300, SPRFLAG_DRAWER4 = 0x400, SPRFLAG_DRAWER5 = 0x500, SPRFLAG_DRAWER6 = 0x600, - SPRFLAG_DRAWER7 = 0x700, SPRFLAG_800 = 0x800, SPRFLAG_SCENE_CLIPPED = 0x2000, - SPRFLAG_BOTTOM_CLIPPED = 0x4000, SPRFLAG_HORIZ_FLIPPED = 0x8000, SPRFLAG_RESIZE = 0x10000 -}; - -class SpriteResource { +class SpriteResource : public MM::Shared::Xeen::SpriteResource { private: - struct IndexEntry { - uint16 _offset1, _offset2; - }; - Common::Array _index; - size_t _filesize; - byte *_data; - Common::String _filename; - static int _clippedBottom; - - /** - * Load a sprite resource from a stream - */ - void load(Common::SeekableReadStream &f); - - /** - * Draw the sprite onto the given surface - */ - void draw(XSurface &dest, int frame, const Common::Point &destPos, - const Common::Rect &bounds, uint flags = 0, int scale = 0); - /** * Draw the sprite onto a given window */ void draw(int windowNum, int frame, const Common::Point &destPos, const Common::Rect &bounds, uint flags = 0, int scale = 0); - /** - * Deep copy assuming that the current instance is clean - */ - void copy(const SpriteResource &src); public: - SpriteResource(); - SpriteResource(const Common::String &filename); + SpriteResource() : Shared::Xeen::SpriteResource() {} + SpriteResource(const Common::String &filename) : + Shared::Xeen::SpriteResource(filename) {} + SpriteResource(const SpriteResource &src) : + Shared::Xeen::SpriteResource(src) {} SpriteResource(const Common::String &filename, int ccMode); - SpriteResource(const SpriteResource &src); - - virtual ~SpriteResource(); - - /** - * Copy operator for duplicating a sprite resource - */ - SpriteResource &operator=(const SpriteResource &src); /** * Load a sprite resource from a given file */ - void load(const Common::String &filename); + void load(const Common::String &filename) { + Shared::Xeen::SpriteResource::load(filename); + } /** * Load a sprite resource from a given file and archive */ void load(const Common::String &filename, int ccMode); - /** - * Clears the sprite resource - */ - void clear(); - /** * Draw a sprite onto a surface * @param dest Destination surface @@ -117,8 +67,26 @@ public: * @param scale Scale: 0=No scale, SCALE_ENLARGE=Enlarge it * 1..15 -> reduces the sprite: the higher, the smaller it'll be */ - void draw(XSurface &dest, int frame, const Common::Point &destPos, - uint flags = 0, int scale = 0); + void draw(Shared::Xeen::XSurface &dest, int frame, const Common::Point &destPos, + uint flags = 0, int scale = 0) { + Shared::Xeen::SpriteResource::draw(dest, frame, destPos, flags, 0); + } + + /** + * Draw the sprite onto the given surface + * @param dest Destination surface + * @param frame Frame number + */ + void draw(Shared::Xeen::XSurface &dest, int frame) { + Shared::Xeen::SpriteResource::draw(dest, frame); + } + + /** + * Draw the sprite onto a given surface + */ + void draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos) { + Shared::Xeen::SpriteResource::draw(dest, frame, destPos); + } /** * Draw a sprite onto a specific window @@ -132,6 +100,13 @@ public: void draw(Window &dest, int frame, const Common::Point &destPos, uint flags = 0, int scale = 0); + /** + * Draw the sprite onto the given window + * @param windowIndex Destination window number + * @param frame Frame number + */ + void draw(int windowIndex, int frame); + /** * Draw a sprite onto a given window * @param windowIndex Destination window number @@ -143,202 +118,9 @@ public: */ void draw(int windowIndex, int frame, const Common::Point &destPos, uint flags = 0, int scale = 0); - - /** - * Draw the sprite onto the given surface - * @param dest Destination surface - * @param frame Frame number - */ - void draw(XSurface &dest, int frame); - - /** - * Draw the sprite onto the given window - * @param windowIndex Destination window number - * @param frame Frame number - */ - void draw(int windowIndex, int frame); - - /** - * Draw the sprite onto a given surface - */ - void draw(Graphics::ManagedSurface *dest, int frame, const Common::Point &destPos); - - /** - * Gets the size of a sprite - */ - Common::Point getFrameSize(int frame) const; - - /** - * Returns the number of frames the sprite resource has - */ - size_t size() const { - return _index.size(); - } - - /** - * Returns true if the sprite resource is empty (ie. nothing is loaded) - */ - bool empty() const { - return _index.size() == 0; - } - - /** - * Set the bottom Y position where sprites are clipped if SPRFLAG_BOTTOM_CLIPPED - * is applied - */ - static void setClippedBottom(int y) { - _clippedBottom = y; - } }; -/** - * Basic sprite drawer - */ -class SpriteDrawer { -private: - byte *_data = nullptr; - size_t _filesize = 0; -protected: - byte *_destTop = nullptr, *_destBottom = nullptr; - byte *_destLeft = nullptr, *_destRight = nullptr; - int _pitch = 0; -private: - /** - * Scale a co-ordinate value based on the passed scaling mask - */ - static uint getScaledVal(int xy, uint16 &scaleMask); -protected: - /** - * Roll carry right opcode emulation - */ - void rcr(uint16 &val, bool &cf); - - /** - * Output a pixel - */ - virtual void drawPixel(byte *dest, byte pixel); -public: - /** - * Constructor - */ - SpriteDrawer(byte *data, size_t filesize) : _data(data), _filesize(filesize) { - } - - /** - * Destructor - */ - virtual ~SpriteDrawer() { - } - - /** - * Draw a sprite frame based on a passed offset into the data stream - */ - void draw(XSurface &dest, uint16 offset, const Common::Point &pt, - const Common::Rect &clipRect, uint flags, int scale); -}; - -class SpriteDrawer1 : public SpriteDrawer { -private: - byte _offset = 0, _mask = 0; -protected: - /** - * Output a pixel - */ - void drawPixel(byte *dest, byte pixel) override; -public: - /** - * Constructor - */ - SpriteDrawer1(byte *data, size_t filesize, int index); -}; - -/** - * Scrambles up the sprite by drawing many of the pixels randomly - * at a horizontal or vertical offset - */ -class SpriteDrawer2 : public SpriteDrawer { -private: - uint16 _mask1 = 0, _mask2 = 0; - uint16 _random1 = 0, _random2 = 0; -private: - /** - * Output a pixel - */ - void drawPixel(byte *dest, byte pixel) override; -public: - /** - * Constructor - */ - SpriteDrawer2(byte *data, size_t filesize, int index); -}; - -/** - * Draws the sprite as faint ghostly, see-through. - */ -class SpriteDrawer3 : public SpriteDrawer { -private: - uint16 _offset = 0, _mask = 0; - byte _palette[256 * 3]; - bool _hasPalette = false; -private: - /** - * Output a pixel - */ - void drawPixel(byte *dest, byte pixel) override; -public: - /** - * Constructor - */ - SpriteDrawer3(byte *data, size_t filesize, int index); -}; - -class SpriteDrawer4 : public SpriteDrawer { -private: - byte _threshold = 0; -protected: - /** - * Output a pixel - */ - void drawPixel(byte *dest, byte pixel) override; -public: - /** - * Constructor - */ - SpriteDrawer4(byte *data, size_t filesize, int index); -}; - -/** - * Draws a sprite with a fuzziness effect where only some pixels of the sprite are randomly drawn - */ -class SpriteDrawer5 : public SpriteDrawer { -private: - uint16 _threshold = 0, _random1 = 0, _random2 = 0; -protected: - /** - * Output a pixel - */ - void drawPixel(byte *dest, byte pixel) override; -public: - /** - * Constructor - */ - SpriteDrawer5(byte *data, size_t filesize, int index); -}; - -class SpriteDrawer6 : public SpriteDrawer { -private: - byte _mask = 0; -protected: - /** - * Output a pixel - */ - void drawPixel(byte *dest, byte pixel) override; -public: - /** - * Constructor - */ - SpriteDrawer6(byte *data, size_t filesize, int index); -}; +using namespace MM::Shared; } // End of namespace Xeen } // End of namespace MM