From e29ec6e79c325bcea2c95ecbfeb3a64c80a30630 Mon Sep 17 00:00:00 2001 From: Paweł Kołodziejski Date: Wed, 5 May 2004 07:25:32 +0000 Subject: adding initial code for Another World engine svn-id: r13783 --- awe/video.cpp | 516 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 awe/video.cpp (limited to 'awe/video.cpp') diff --git a/awe/video.cpp b/awe/video.cpp new file mode 100644 index 0000000000..52df154d05 --- /dev/null +++ b/awe/video.cpp @@ -0,0 +1,516 @@ +/* AWE - Another World Engine + * Copyright (C) 2004 Gregory Montoir + * Copyright (C) 2004 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "stdafx.h" + +#include "awe.h" +#include "video.h" +#include "resource.h" +#include "serializer.h" +#include "systemstub.h" + +namespace Awe { + +void Polygon::init(const uint8 *p, uint16 zoom) { + bbw = (*p++) * zoom / 64; + bbh = (*p++) * zoom / 64; + numPoints = *p++; + assert((numPoints & 1) == 0 && numPoints < MAX_POINTS); + for (int i = 0; i < numPoints; ++i) { + Point *pt = &points[i]; + pt->x = (*p++) * zoom / 64; + pt->y = (*p++) * zoom / 64; + } +} + +Video::Video(Resource *res, SystemStub *stub) + : _res(res), _stub(stub) { +} + +void Video::init() { + _newPal = 0xFF; + for (int i = 0; i < 4; ++i) { + _pagePtrs[i] = allocPage(); + } + _curPagePtr3 = getPagePtr(1); + _curPagePtr2 = getPagePtr(2); + changePagePtr1(0xFE); + _interpTable[0] = 0x4000; + for (int i = 1; i < 0x400; ++i) { + _interpTable[i] = 0x4000 / i; + } +} + +void Video::setDataBuffer(uint8 *dataBuf, uint16 offset) { + _dataBuf = dataBuf; + _pData.pc = dataBuf + offset; +} + +void Video::drawShape(uint8 color, uint16 zoom, const Point &pt) { + uint8 i = _pData.fetchByte(); + if (i >= 0xC0) { + if (color & 0x80) { + color = i & 0x3F; + } + _pg.init(_pData.pc, zoom); + fillPolygon(color, zoom, pt); + } else { + i &= 0x3F; + if (i == 1) { + ::warning("Video::drawShape() ec=0x%X (i != 2)", 0xF80); + } else if (i == 2) { + drawShapeParts(zoom, pt); + } else { + ::warning("Video::drawShape() ec=0x%X (i != 2)", 0xFBB); + } + } +} + +void Video::fillPolygon(uint16 color, uint16 zoom, const Point &pt) { + if (_pg.bbw == 0 && _pg.bbh == 1 && _pg.numPoints == 4) { + drawPoint(color, pt.x, pt.y); + return; + } + + int16 x1 = pt.x - _pg.bbw / 2; + int16 x2 = pt.x + _pg.bbw / 2; + int16 y1 = pt.y - _pg.bbh / 2; + int16 y2 = pt.y + _pg.bbh / 2; + + if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0) + return; + + _hliney = y1; + + uint16 i, j; + i = 0; + j = _pg.numPoints - 1; + + x2 = _pg.points[i].x + x1; + x1 = _pg.points[j].x + x1; + + ++i; + --j; + + drawLine pdl; + if (color < 0x10) { + pdl = &Video::drawLineN; + } else if (color > 0x10) { + pdl = &Video::drawLineP; + } else { + pdl = &Video::drawLineT; + } + + uint32 cpt1 = x1 << 16; + uint32 cpt2 = x2 << 16; + + while (1) { + _pg.numPoints -= 2; + if (_pg.numPoints == 0) { + return; + } + uint16 h; + int32 step1 = calcStep(_pg.points[j + 1], _pg.points[j], h); + int32 step2 = calcStep(_pg.points[i - 1], _pg.points[i], h); + + ++i; + --j; + + cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF; + cpt2 = (cpt2 & 0xFFFF0000) | 0x8000; + + if (h == 0) { + cpt1 += step1; + cpt2 += step2; + } else { + for (; h != 0; --h) { + if (_hliney >= 0) { + x1 = cpt1 >> 16; + x2 = cpt2 >> 16; + if (x1 <= 319 && x2 >= 0) { + if (x1 < 0) x1 = 0; + if (x2 > 319) x2 = 319; + (this->*pdl)(x1, x2, color); + } + } + cpt1 += step1; + cpt2 += step2; + ++_hliney; + if (_hliney > 199) return; + } + } + } +} + +void Video::drawShapeParts(uint16 zoom, const Point &pgc) { + Point pt(pgc); + pt.x -= _pData.fetchByte() * zoom / 64; + pt.y -= _pData.fetchByte() * zoom / 64; + int16 n = _pData.fetchByte(); + debug(DBG_VIDEO, "Video::drawShapeParts n=%d", n); + for ( ; n >= 0; --n) { + uint16 off = _pData.fetchWord(); + Point po(pt); + po.x += _pData.fetchByte() * zoom / 64; + po.y += _pData.fetchByte() * zoom / 64; + uint16 color = 0xFF; + uint16 _bp = off; + off &= 0x7FFF; + if (_bp & 0x8000) { + color = *_pData.pc & 0x7F; + _pData.pc += 2; + } + uint8 *bak = _pData.pc; + _pData.pc = _dataBuf + off * 2; + drawShape(color, zoom, po); + _pData.pc = bak; + } +} + +int32 Video::calcStep(const Point &p1, const Point &p2, uint16 &dy) { + dy = p2.y - p1.y; + return (p2.x - p1.x) * _interpTable[dy] * 4; +} + +void Video::drawString(uint8 color, uint16 x, uint16 y, uint16 strId) { + const StrEntry *se = _stringsTableEng; + while (se->id != 0xFFFF && se->id != strId) ++se; + debug(DBG_VIDEO, "drawString(%d, %d, %d, '%s')", color, x, y, se->str); + uint16 xx = x; + int len = strlen(se->str); + for (int i = 0; i < len; ++i) { + if (se->str[i] == '\n') { + y += 8; + x = xx; + } else { + drawChar(se->str[i], x, y, color, _curPagePtr1); + ++x; + } + } +} + +void Video::drawChar(uint8 c, uint16 x, uint16 y, uint8 color, uint8 *buf) { + if (x <= 39 && y <= 192) { + const uint8 *ft = _font + (c - 0x20) * 8; + uint8 *p = buf + x * 4 + y * 160; + for (int j = 0; j < 8; ++j) { + uint8 ch = *(ft + j); + for (int i = 0; i < 4; ++i) { + uint8 b = *(p + i); + uint8 cmask = 0xFF; + uint8 colb = 0; + if (ch & 0x80) { + colb |= color << 4; + cmask &= 0x0F; + } + ch <<= 1; + if (ch & 0x80) { + colb |= color; + cmask &= 0xF0; + } + ch <<= 1; + *(p + i) = (b & cmask) | colb; + } + p += 160; + } + } +} + +void Video::drawPoint(uint8 color, int16 x, int16 y) { + debug(DBG_VIDEO, "drawPoint(%d, %d, %d)", color, x, y); + if (x >= 0 && x <= 319 && y >= 0 && y <= 199) { + uint16 off = y * 160 + x / 2; + + uint8 cmasko, cmaskn; + if (x & 1) { + cmaskn = 0x0F; + cmasko = 0xF0; + } else { + cmaskn = 0xF0; + cmasko = 0x0F; + } + + uint8 colb = (color << 4) | color; + if (color == 0x10) { + cmaskn &= 0x88; + cmasko = ~cmaskn; + colb = 0x88; + } else if (color == 0x11) { + colb = *(_pagePtrs[0] + off); + } + uint8 b = *(_curPagePtr1 + off); + *(_curPagePtr1 + off) = (b & cmasko) | (colb & cmaskn); + } +} + +void Video::drawLineT(int16 x1, int16 x2, uint8 color) { + debug(DBG_VIDEO, "drawLineT(%d, %d, %d)", x1, x2, color); + int16 xmax = MAX(x1, x2); + int16 xmin = MIN(x1, x2); + uint8 *p = _curPagePtr1 + _hliney * 160 + xmin / 2; + + uint16 w = xmax / 2 - xmin / 2 + 1; + uint8 cmaske = 0; + uint8 cmasks = 0; + if (xmin & 1) { + --w; + cmasks = 0xF7; + } + if (!(xmax & 1)) { + --w; + cmaske = 0x7F; + } + + if (cmasks != 0) { + *p = (*p & cmasks) | 0x08; + ++p; + } + while (w--) { + *p = (*p & 0x77) | 0x88; + ++p; + } + if (cmaske != 0) { + *p = (*p & cmaske) | 0x80; + ++p; + } +} + +void Video::drawLineN(int16 x1, int16 x2, uint8 color) { + debug(DBG_VIDEO, "drawLineN(%d, %d, %d)", x1, x2, color); + int16 xmax = MAX(x1, x2); + int16 xmin = MIN(x1, x2); + uint8 *p = _curPagePtr1 + _hliney * 160 + xmin / 2; + + uint16 w = xmax / 2 - xmin / 2 + 1; + uint8 cmaske = 0; + uint8 cmasks = 0; + if (xmin & 1) { + --w; + cmasks = 0xF0; + } + if (!(xmax & 1)) { + --w; + cmaske = 0x0F; + } + + uint8 colb = ((color & 0xF) << 4) | (color & 0xF); + if (cmasks != 0) { + *p = (*p & cmasks) | (colb & 0x0F); + ++p; + } + while (w--) { + *p++ = colb; + } + if (cmaske != 0) { + *p = (*p & cmaske) | (colb & 0xF0); + ++p; + } +} + +void Video::drawLineP(int16 x1, int16 x2, uint8 color) { + debug(DBG_VIDEO, "drawLineP(%d, %d, %d)", x1, x2, color); + int16 xmax = MAX(x1, x2); + int16 xmin = MIN(x1, x2); + uint16 off = _hliney * 160 + xmin / 2; + uint8 *p = _curPagePtr1 + off; + uint8 *q = _pagePtrs[0] + off; + + uint8 w = xmax / 2 - xmin / 2 + 1; + uint8 cmaske = 0; + uint8 cmasks = 0; + if (xmin & 1) { + --w; + cmasks = 0xF0; + } + if (!(xmax & 1)) { + --w; + cmaske = 0x0F; + } + + if (cmasks != 0) { + *p = (*p & cmasks) | (*q & 0x0F); + ++p; + ++q; + } + while (w--) { + *p++ = *q++; + } + if (cmaske != 0) { + *p = (*p & cmaske) | (*q & 0xF0); + ++p; + ++q; + } +} + +uint8 *Video::getPagePtr(uint8 page) { + uint8 *p; + if (page <= 3) { + p = _pagePtrs[page]; + } else { + switch (page) { + case 0xFF: + p = _curPagePtr3; + break; + case 0xFE: + p = _curPagePtr2; + break; + default: + p = _pagePtrs[0]; // XXX check + ::warning("Video::getPagePtr() p != [0,1,2,3,0xFF,0xFE] == 0x%X", page); + break; + } + } + return p; +} + +void Video::changePagePtr1(uint8 page) { + debug(DBG_VIDEO, "Video::changePagePtr1(%d)", page); + _curPagePtr1 = getPagePtr(page); +} + +void Video::fillPage(uint8 page, uint8 color) { + debug(DBG_VIDEO, "Video::fillPage(%d, %d)", page, color); + uint8 *p = getPagePtr(page); + uint8 c = (color << 4) | color; + memset(p, c, VID_PAGE_SIZE); +} + +void Video::copyPage(uint8 src, uint8 dst, int16 vscroll) { + debug(DBG_VIDEO, "Video::copyPage(%d, %d)", src, dst); + if (src >= 0xFE || !((src &= 0xBF) & 0x80)) { + uint8 *p = getPagePtr(src); + uint8 *q = getPagePtr(dst); + if (p != q) { + memcpy(q, p, VID_PAGE_SIZE); + } + } else { + uint8 *p = getPagePtr(src & 3); + uint8 *q = getPagePtr(dst); + if (p != q && vscroll >= -199 && vscroll <= 199) { + uint16 h = 200; + if (vscroll < 0) { + h += vscroll; + p += -vscroll * 160; + } else { + h -= vscroll; + q += vscroll * 160; + } + memcpy(q, p, h * 160); + } + } +} + +void Video::copyPagePtr(const uint8 *src) { + debug(DBG_VIDEO, "Video::copyPagePtr()"); + uint8 *dst = _pagePtrs[0]; + int h = 200; + while (h--) { + int w = 40; + while (w--) { + uint8 p[] = { + *(src + 8000 * 3), + *(src + 8000 * 2), + *(src + 8000 * 1), + *(src + 8000 * 0) + }; + for(int j = 0; j < 4; ++j) { + uint8 acc = 0; + for (int i = 0; i < 8; ++i) { + acc <<= 1; + acc |= (p[i & 3] & 0x80) ? 1 : 0; + p[i & 3] <<= 1; + } + *dst++ = acc; + } + ++src; + } + } +} + +uint8 *Video::allocPage() { + uint8 *buf = (uint8 *)malloc(VID_PAGE_SIZE); + memset(buf, 0, VID_PAGE_SIZE); + return buf; +} + +void Video::changePal(uint8 palNum) { + if (palNum < 32) { + uint8 *p = _res->_segVideoPal + palNum * 32; + uint8 pal[16 * 3]; + for (int i = 0; i < 16; ++i) { + uint8 c1 = *(p + 0); + uint8 c2 = *(p + 1); + p += 2; + pal[i * 3 + 0] = ((c1 & 0x0F) << 2) | ((c1 & 0x0F) >> 2); // r + pal[i * 3 + 1] = ((c2 & 0xF0) >> 2) | ((c2 & 0xF0) >> 6); // g + pal[i * 3 + 2] = ((c2 & 0x0F) >> 2) | ((c2 & 0x0F) << 2); // b + } + _stub->setPalette(0, 16, pal); + _curPal = palNum; + } +} + +void Video::updateDisplay(uint8 page) { + debug(DBG_VIDEO, "Video::updateDisplay(%d)", page); + if (page != 0xFE) { + if (page == 0xFF) { + SWAP(_curPagePtr2, _curPagePtr3); + } else { + _curPagePtr2 = getPagePtr(page); + } + } + if (_newPal != 0xFF) { + changePal(_newPal); + _newPal = 0xFF; + } + _stub->copyRect(0, 0, 320, 200, _curPagePtr2, 160); +} + +void Video::saveOrLoad(Serializer &ser) { + uint8 mask = 0; + if (ser._mode == Serializer::SM_SAVE) { + for (int i = 0; i < 4; ++i) { + if (_pagePtrs[i] == _curPagePtr1) + mask |= i << 4; + if (_pagePtrs[i] == _curPagePtr2) + mask |= i << 2; + if (_pagePtrs[i] == _curPagePtr3) + mask |= i << 0; + } + } + Serializer::Entry entries[] = { + SE_INT(&_curPal, Serializer::SES_INT8, VER(1)), + SE_INT(&_newPal, Serializer::SES_INT8, VER(1)), + SE_INT(&mask, Serializer::SES_INT8, VER(1)), + SE_ARRAY(_pagePtrs[0], Video::VID_PAGE_SIZE, Serializer::SES_INT8, VER(1)), + SE_ARRAY(_pagePtrs[1], Video::VID_PAGE_SIZE, Serializer::SES_INT8, VER(1)), + SE_ARRAY(_pagePtrs[2], Video::VID_PAGE_SIZE, Serializer::SES_INT8, VER(1)), + SE_ARRAY(_pagePtrs[3], Video::VID_PAGE_SIZE, Serializer::SES_INT8, VER(1)), + SE_END() + }; + ser.saveOrLoadEntries(entries); + if (ser._mode == Serializer::SM_LOAD) { + _curPagePtr1 = _pagePtrs[(mask >> 4) & 0x3]; + _curPagePtr2 = _pagePtrs[(mask >> 2) & 0x3]; + _curPagePtr3 = _pagePtrs[(mask >> 0) & 0x3]; + changePal(_curPal); + } +} + +} -- cgit v1.2.3