aboutsummaryrefslogtreecommitdiff
path: root/awe/video.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'awe/video.cpp')
-rw-r--r--awe/video.cpp516
1 files changed, 516 insertions, 0 deletions
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);
+ }
+}
+
+}